分享一个网上找不到的“Redis”解决方案,实现“聊天转系统”最后,公司里一位人脉广的老大提供了一些思路,笔者终于彻底实现了。分享出来,大家可以收藏。万一哪天遇到这样的需求,岂不是可以节省很多时间?场景先说说我的场景。看过我文章的都知道,我是做互联网医疗行业的。我们的项目包括聊天功能。我们的服务对象主要是医院的医生。找医生看病时,经常会出现问个不停的情况。医生只能自己结束会诊或者等待系统自动结束,这就带来了问题。无论是系统端还是医生手动端,患者都喜欢抱怨、留差评,这让医生不敢擅自结束。不回复很烦也不好,不回复会被投诉。最后提出了轮询聊天系统的需求。我们主动告诉患者,我们的聊天是有轮次的,所以你要第一时间问清楚。轮数已满恕不回复。如果病人非要投诉,医生也可以说,这是做这个产品的公司定的。最重要的是,我们必须照顾好底池。其实,回合制聊天系统的诞生,与这个场景的诉求基本类似,就是为了减少用户频繁无休止的咨询。结合redis的思路可以实现回合制的聊天系统,当然也可以直接通过数据库实现,但是显然redis的操作更简单,性能也更好。总体思路如下:1)。redis中存储了两个key,一个代表对象,声明为chat-who:consultId,value为对象标识。比如这里有医生和病人,医生用D标识,病人用P标识;key代表轮数,声明为chat-num:consultId,value为当前轮数。这里的consultId是动态的,表示本次咨询的id可以根据自己的业务来确定;2)、我们将这两个key的过期时间设置为2天,具体过期时间要根据自己的业务规则进行适配;3)我们在特定位置进行初始化,只要是进入聊天之前即可。例如,在这个场景中,患者在成功发起咨询后开始聊天。我们在发起6轮成功后在方法中初始化聊天轮数为默认值,这个默认值也可以通过配置的形式动态读取;4)、我们在发送消息的方法中进行判断,获取redis中的chat-who:consultId看是否存在,如果存在则往下执行,如果不存在则表示发送第一条消息,然后在redis中创建keychat-who:consultId,值为当前发送者D或P;5)、进行4,如果chat-who存在,我们继续比较当前发送消息的对象和redis中存储的chat-who对象值,相同则跳过,不相同则更新chat的值-谁给当前的消息传递者。同时我们判断当前发消息的人是医生还是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保存的医患ID的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(ChatWhoEnum类型:ChatWhoEnum.values()){if(code.equalsIgnoreCase(type.getCode())){returntype.getId();}}返回空值;但是突然间你要实现这个小功能还是挺费力的。看不清楚,就会一直卡在里面。如果你理解它,你会立即理解你的想法。该功能目前已上线,运行稳定,没有任何问题。有兴趣的可以收藏一下,如果哪天你做聊天相关的业务,可能会遇到类似的需求。我的原创文章纯属手写。如果觉得有帮助,请点赞加收藏~我持续分享实际工作经验和主流技术。】
