最近公司接到一个医疗行业的app项目,此app包含医生端和病人端。项目的核心就是实现医生对病人的在线问诊。
一、在线问诊需求
1、同一个时间段可以有多个病人预约同一个医生(10-11点)
2、医生对患者进行问诊的时候,患者端有相应的提示(弹框提示)
3、医生对其中一个病人A问诊的时候,其他病人来问诊的时候会通知其他病人,该医生正在问诊其他患者,请等待医生问诊。
4、在线问诊的时候需要在病人端和医生端同时倒计时该问诊时间
5、病人端异常崩溃再次进入问诊界面需要和医生端倒计时同步
6、医生端异常崩溃再次进入问诊界面需要通知病人端倒计时重置(对病人问诊一种补偿机制)
7、医生或者病人退出问诊都会提示对方
8、医生问诊结束根据问诊的类型(免费、收费)填写不同的问诊报告
9、病人可以查看自己的问诊报告
二.在线问诊的实现逻辑:
1.同一个时间段有多个病人去预约一个医生(例如 10点-11点)有 A B C三位病人同时预约 医生Jack。
2.因为是 医生问诊病人,病人端必须被动等待医生对他进行问诊。因此问诊的发起端就是医生端。
3.因为同一个时间段(例如 10点-11点) 医生不知道哪些病人在线,所以医生会按照订单顺序 依次询问 A B C是否在线。
此时用融云发送一个普通聊天消息“xx你好,xx医生请求与你问诊”。同时还要发送一个自定义的通知消息便于弹出问诊提示框。消息为RCCommandMessage 类型 。
消息格式:"type"="Start";targetId= “医生的融云baseid”;"title"=“医生的名字”
;chatTime"=“普通聊天时间剩余”;videoTime"=“视频通话剩余时间”
a,因为考虑到网络延时 所以需要医生端每隔一段时间告诉病人来进行问诊,
b,所以会循环发送该消息,病人端如果该消息就应该告诉医生端,我已经收到了问诊提示消息,请停止发送问诊提示消息
命令类型 RCCommandMessage
命令参数 name=@"already";data=@"准备完毕";
c,同时病人端点击问诊确定按钮会通知医生端,我已经准备好了,可以开始同步倒计时了
然后此时进行正常的在线问诊。
命令 RCCommandMessage
参数 name=@"online"; data=@"进入聊天";
4 .当医生询问病人A的时候,等待一会发现病人A没有在线,(医生给病人发一个问诊提示框,病人没有响应,并且没有反馈消息)医生就会退出对病人A的问诊。然后医生jack继续问诊病人B,发现病人B在线。就对病人B进行问诊。但是过一会病人A在线了,当病人A点击医生jack对他的问题提示框,此时需要提示病人A,医生正在对其他病人进行问诊,请等待医生...
这里要结合服务器,就是通过查询服务器当前医生是否 正在等待对我的问诊
5. 医生端异常退出,再次进来病人端同步医生端时间。
医生端发送命令
参数:"Start""chatTime" "videoTime" "targetId"
病人端收到命令后马上刷新倒计时
6.病人端异常退出再次进来同步医生端时间。
注意在整个问诊的聊天界面 医生端每隔10秒会把自己的时间发送给病人端
参数 type="Refresh";remainTime=“剩余聊天时间”;"videoTime"=”剩余视频时间”
感兴趣的同学可以去appStore上面下载 速医病患端和速医医生端 来看看具体逻辑,有需要可以留言联系。
另外附上问诊通知的核心代码(病人端)
CHLog==NSLog
/*!
在会话列表中,收到新消息的回调
@param notification 收到新消息的notification
@discussion SDK在此方法中有针对消息接收有默认的处理(如刷新等),如果您重写此方法,请注意调用super。
notification的object为RCMessage消息对象,userInfo为NSDictionary对象,其中key值为@"left",value为还剩余未接收的消息数的NSNumber对象。
*/
- (void)didReceiveMessageNotification:(NSNotification *)notification
{
RCMessage *message=notification.object;
if ([message.content isKindOfClass: [RCCommandMessage class]]) {
RCCommandMessage *commandText=(RCCommandMessage *)message.content;
NSDictionary *contentDict=[commandText.data mj_JSONObject];
if (contentDict) {
//CHLog(@"commandText.data:==%@",commandText.data);
}
//获取会话基础数据
//获取回话id
NSString *type=contentDict[@"type"];
NSString *targetId=contentDict[@"targetId"];
NSString *title=contentDict[@"title"];
NSString *chatTime=contentDict[@"chatTime"];
NSString *videoTime=contentDict[@"videoTime"];
if ([type isKindOfClass:[NSString class]] && [type isEqualToString:@"Start"])
{
CHLog(@"Start==>commandText.data:==%@",contentDict);
//打开聊天
JNConversationViewController *conversationVC=[[JNConversationViewController alloc] init];
conversationVC.conversationType = ConversationType_PRIVATE;
conversationVC.targetId = targetId;
conversationVC.doctorTaget=targetId;
conversationVC.title=title;
// 设置头像
[conversationVC setMessageAvatarStyle:RC_USER_AVATAR_CYCLE];
// 在聊天界面,隐藏对方头像的聊天名字
conversationVC.displayUserNameInCell = NO;
//设置可以聊天的时间
conversationVC.maxChaitTime=[chatTime doubleValue];
conversationVC.maxVideoTime=[videoTime doubleValue];
//设置是否隐藏视频按钮
if ([videoTime doubleValue]<=0) {
conversationVC.isHaveVideo=NO;
//保存隐藏视频按钮
[[NSUserDefaults standardUserDefaults]setBool:NO forKey:@"isHaveVideo"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
else
{
conversationVC.isHaveVideo=YES;
//保存显示视频按钮
[[NSUserDefaults standardUserDefaults]setBool:YES forKey:@"isHaveVideo"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
JNNavigationController *newNav=[[JNNavigationController alloc] initWithRootViewController:conversationVC];
//通知医生端,病人端已经准备完毕
RCCommandMessage *commondReadyMsg = [[RCCommandMessage alloc] init];
commondReadyMsg.name=@"already";
commondReadyMsg.data=@"准备完毕";
[[RCIMClient sharedRCIMClient]sendMessage:ConversationType_PRIVATE targetId:targetId content:commondReadyMsg pushContent:@"" pushData:@"" success:^(long messageId) {
CHLog(@"===================准备完毕!!!===============");
} error:^(RCErrorCode nErrorCode, long messageId) {
CHLog(@"发送失败!");
}];
//如果 app当前状态是 前台运行状态,先判断医生是否正在对我进行问诊。如果没有进行问诊就不要走下面的逻辑
if ([UIApplication sharedApplication].applicationState==UIApplicationStateActive) {
CHLog(@"app当前状态是 前台运行状态==");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL isEnterChart = [defaults boolForKey:@"isEnterChart"];
if (!isEnterChart)//如果已经进入聊天就不 进行判断了
{
[JNDepartListNetTool getDoctorWenZhenObject:targetId handleSuccess:^(NSString *chartBaseId) {
if ([chartBaseId isEqualToString:[self ToGetAccount].baseId])
{
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"请点击确定和预约医生进行咨询!" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self showLoadingView:@""];
//yes表示 开始检查医生正在问诊对象
self.isStartCheckDoctor=YES;
[JNDepartListNetTool getDoctorWenZhenObject:targetId handleSuccess:^(NSString *chartBaseId) {
[self hideLoadingView];
if ([chartBaseId isEqualToString:[self ToGetAccount].baseId]) {
[self presentViewController:newNav animated:YES completion:^{
CHLog(@"聊天计时开始");
//通知医生端,病人端进入聊天
RCCommandMessage *commondMsg = [[RCCommandMessage alloc] init];
commondMsg.name=@"online";
commondMsg.data=@"进入聊天";
[[RCIMClient sharedRCIMClient]sendMessage:ConversationType_PRIVATE targetId:targetId content:commondMsg pushContent:@"" pushData:@"" success:^(long messageId) {
CHLog(@"=========前台==========进入聊天成功!!!===============");
self.isStartCheckDoctor=NO;
//设置为已经进入问诊聊天界面
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isEnterChart"];
[[NSUserDefaults standardUserDefaults] synchronize];
} error:^(RCErrorCode nErrorCode, long messageId) {
CHLog(@"发送失败!");
self.isStartCheckDoctor=NO;
}];
}];
}
else
{
UIWindow *keyWind=[UIApplication sharedApplication].keyWindow;
MBProgressHUD *showText = [MBProgressHUD showHUDAddedTo:keyWind animated:YES];
showText.mode=MBProgressHUDModeText;
showText.labelText = @"医生正在对其他病人问诊,请再次等待通知!";
showText.labelFont=[UIFont systemFontOfSize:12];
[showText hide:YES afterDelay:2];
self.isStartCheckDoctor=NO;
}
} handleFalse:^(NSString *message) {
[self hideLoadingView];
[self showTipsView:message];
self.isStartCheckDoctor=NO;
} requestfailure:^(NSError *error) {
[self hideLoadingView];
[self showTipsView:@"网络错误"];
self.isStartCheckDoctor=NO;
}];
}];
[alertController addAction:okAction];
if (!self.isStartCheckDoctor) {
[self presentViewController:alertController animated:YES completion:nil];
}
});
}
else
{
CHLog(@"医生正在对其他病人问诊,停止逻辑");
}
} handleFalse:^(NSString *message)
{
}
requestfailure:^(NSError *error)
{
}];
}
}
else//app当前状态是 后台运行状态
{
CHLog(@"app当前状态是 后台运行状态==");
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"请点击确定和预约医生进行咨询!" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action)
{
//yes表示 开始检查医生正在问诊对象
self.isStartCheckDoctor=YES;
[JNDepartListNetTool getDoctorWenZhenObject:targetId handleSuccess:^(NSString *chartBaseId) {
if ([chartBaseId isEqualToString:[self ToGetAccount].baseId])
{
[self presentViewController:newNav animated:YES completion:^{
CHLog(@"聊天计时开始");
//通知医生端,病人端进入聊天
RCCommandMessage *commondMsg = [[RCCommandMessage alloc] init];
commondMsg.name=@"online";
commondMsg.data=@"进入聊天";
[[RCIMClient sharedRCIMClient]sendMessage:ConversationType_PRIVATE targetId:targetId content:commondMsg pushContent:@"" pushData:@"" success:^(long messageId) {
CHLog(@"===========从后台========进入聊天通!!!===============");
self.isStartCheckDoctor=NO;
//设置为已经进入问诊聊天界面
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isEnterChart"];
[[NSUserDefaults standardUserDefaults] synchronize];
} error:^(RCErrorCode nErrorCode, long messageId) {
CHLog(@"发送失败!");
self.isStartCheckDoctor=NO;
}];
}];
}
else
{
UIWindow *keyWind=[UIApplication sharedApplication].keyWindow;
MBProgressHUD *showText = [MBProgressHUD showHUDAddedTo:keyWind animated:YES];
showText.mode=MBProgressHUDModeText;
showText.labelText = @"医生正在对其他病人问诊,请再次等待通知!";
showText.labelFont=[UIFont systemFontOfSize:12];
[showText hide:YES afterDelay:2];
self.isStartCheckDoctor=NO;
}
} handleFalse:^(NSString *message) {
self.isStartCheckDoctor=NO;
} requestfailure:^(NSError *error) {
self.isStartCheckDoctor=NO;
}];
}];
[alertController addAction:okAction];
if (!self.isStartCheckDoctor)
{
[self presentViewController:alertController animated:YES completion:nil];
}
});
}
}
}
[[UIApplication sharedApplication]setApplicationIconBadgeNumber:UnreadCount];
}
这里区分了前后台,部分代码类似。不需太在意,只需要关心核心逻辑即可。