当前位置: 首页 > 科技观察

分享一个网上搜不到的「Redis」实现「聊天回合制」的方案

时间:2023-03-19 22:03:59 科技观察

分享一个网上找不到的“Redis”解决方案,实现“聊天转系统”最后,公司里一位人脉广的老大提供了一些思路,笔者终于彻底实现了。分享出来,大家可以收藏。万一哪天遇到这样的需求,岂不是可以节省很多时间?场景先说说我的场景。看过我文章的都知道,我是做互联网医疗行业的。我们的项目包括聊天功能。我们的服务对象主要是医院的医生。找医生看病时,经常会出现问个不停的情况。医生只能自己结束会诊或者等待系统自动结束,这就带来了问题。无论是系统端还是医生手动端,患者都喜欢抱怨、留差评,这让医生不敢擅自结束。不回复很烦也不好,不回复会被投诉。最后提出了轮询聊天系统的需求。我们主动告诉患者,我们的聊天是有轮次的,所以你要第一时间问清楚。轮数已满恕不回复。如果病人非要投诉,医生也可以说,这是做这个产品的公司定的。最重要的是,我们必须照顾好底池。其实,回合制聊天系统的诞生,与这个场景的诉求基本类似,就是为了减少用户频繁无休止的咨询。结合redis的思路可以实现回合制的聊天系统,当然也可以直接通过数据库实现,但是显然redis的操作更简单,性能也更好。大致思路如下:1)redis中存储了两个key,一个代表对象,声明为chat-who:consultId,value为对象标识,比如这里是医生和病人,医生是标识的通过D,患者通过P识别;另一个key表示轮数,声明为chat-num:consultId,值为当前轮数。这里的consultId是动态的,表示本次咨询的id可以根据自己的业务来确定;2)我们将这两个key的过期时间设置为2天,具体过期时间要根据自己的业务规则进行适配;3)我们在一个特定的位置进行初始化,只要是在进入聊天之前。比如这里的场景,问诊成功后,患者开始聊天。我们在发起成功后的方法中将聊天轮数初始化为默认值6。round,这个默认值也可以通过配置的形式动态读取;4)我们在发送消息的方法中进行判断,获取redis中的chat-who:consultId,检查是否存在,如果存在则执行,如果不存在则表示发送了第一条消息,然后在redis中创建keychat-who:consultId,值为当前发送者D或P;5)、进行4、如果chat-who存在,我们继续比较当前发送消息的对象和redis的chat-who中存储的对象值。如果它们相同,则跳过它。如果它们不相同,则将chat-who的值更新为当前的messager。同时我们判断当前发消息的人是医生还是D,如果是D就更新轮数,进行-1操作。这样做的目的是为了将医生作为更新轮数的维度。只能有一个维度,所以只有这样才能最准确的更新轮数。实现接下来,我使用伪代码写出整个想法。1.定义redis-key/***Chatroundconstants*/publicfinalclassChatRoundConstants{/***Chatroundnumberkeyprefix*/publicstaticfinalStringCHAT_NUM="chat-num:";/***聊天对象键前缀*/publicstaticfinalStringCHAT_WHO="chat-who:";/***redis-key过期时间*/publicstaticfinalLongEXPIRE_TIME=48*3600L;/***聊天对象值,Doctor-D,Patient-P。*/publicstaticfinalStringDOCTOR="D";publicstaticfinalStringPATIENT="P";}2.聊天前初始化聊天轮数。我们这里项目的场景是患者发起会诊成功后,就是在这个success在下面的方法中初始化。/***发起咨询成功*/publicvoidconsultSuccess(){//....其他业务逻辑处理//初始化聊天轮数initChatRoundNum(ConsultDTOconsultDTO);}/***初始化聊天轮数*--过期时间48小时*@paramconsultDTO咨询信息*/privatevoidinitChatRoundNum(ConsultDTOconsultDTO){//初始6轮intchatNum=6;//获取系统默认配置的轮数,这里是伪代码根据自己的需要来写。ParameterDTOparameterDTO=getConfigValue();if(!ObjectUtils.isEmpty(parameterDTO)){chatNum=parameterDTO.getPvalue();}//初始化到redis,key是chat-num:consultIdredisService.set(ChatRoundConstants.CHAT_NUM+consultDTO.getId(),chatNum,ChatRoundConstants.EXPIRE_TIME);}3.更新轮数这里是核心逻辑,主要分为两步:初始化chat-who:consultId,更新chat-num:consultId。/***发送消息*/publicvoidsendMsg(){//....其他业务逻辑//更新聊天轮数handleChatRoundNum(consultDTO,consultDetailInfoDTO);}/***处理聊天轮数*@paramconsultDTO咨询信息*@paramconsultDetailInfoDTO聊天信息*/privatevoidhandleChatRoundNum(ConsultDTOconsultDTO,ConsultDetailInfoDTOconsultDetailInfoDTO){//获取redis保存的医患标识keyStringchatWhoKey=ChatRoundConstants.CHAT_WHO+consultDTO.getId();//获取当前发送的消息人对应的身份Stringcurrent=ChatWhoEnum.getCodeById(consultDetailInfoDTO.getSource());//chat-who:consultId是否存在if(redisService.exists(chatWhoKey)){StringchatWhoValue=(String)redisService.get(chatWhoKey);//判断当前sender和chatWho的值是否相同,如果不相同则更新chatWho为当前sender。if(!Objects.equals(ChatWhoEnum.getIdByCode(chatWhoValue),consultDetailInfoDTO.getSource())){//将chatWho更新为当前发送者redisService.setRange(chatWhoKey,current,0);//判断当前发送者是否是D,如果是D则更新轮数。if(Objects.equals(ChatWhoEnum.DOCTOR.getId(),consultDetailInfoDTO.getSource())){//更新chatNum-1StringchatNumKey=ChatRoundConstants.CHAT_NUM+consultDTO.getId();intchatNumValue=Integer.parseInt((String)redisService.get(chatNumKey));if(redisService.exists(chatNumKey)&&chatNumValue>0){redisService.decr(chatNumKey);}}}}else{//如果不存在,则为第一条消息,创建此键。redisService.set(chatWhoKey,当前,ChatRoundConstants.EXPIRE_TIME);}}定义消息对象枚举/***聊天对象枚举类source*/publicenumChatWhoEnum{//source://0doctor//1PatientDOCTOR(0,"D","Doctor"),PATIENT(1,"P","患者");私有最终intid;私有最终字符串代码;私有最终字符串标签;ChatWhoEnum(finalintid,finalStringcode,finalStringlabel){this.id=id;这个。代码=代码;this.label=标签;}publicintgetId(){返回ID;}publicStringgetCode(){返回代码;}publicStringgetLabel(){返回标签;}publicstaticStringgetCodeById(intid){for(ChatWhoEnumtype:ChatWhoEnum.values()){if(type.getId()==id){returntype.getCode();}}返回空值;}publicstaticIntegergetIdByCode(Stringcode){for(ChatWhoEnumtype:ChatWhoEnum.values()){if(code.equalsIgnoreCase(type.getCode())){returntype.getId();}}返回空值;}}Summary其实写起来很简单,思路也不难,但是突然让你实现这个小功能,如果你不明白,你就会一直卡在里面。如果你了解它,你将能够理解这个功能。该功能目前已上线,运行稳定,没有任何问题。有兴趣的可以收藏一下。如果有一天你做聊天相关的业务的话,说不定你也会遇到类似的需求。