前言
为什么我在 Internet 上找不到它?因为聊天回合制解决方案的作者已经百度了秃头,没找到。幸运的是,该公司的一个人脉广泛的团伙提供了一些想法。最终,作者将其完全实现。
分享它,每个人都可以收藏它。万一哪天遇到这样的需求,岂不是省了不少时间。
场景
先说说我这边的场景吧。看过我文章的人都知道我是互联网医疗行业的。我们的项目包括一个聊天功能。我们服务的对象主要是医院的医生和病人。在网上找医生咨询时,经常会出现不断问的情况。
目前医生结束问诊的唯一方式是自己结束问诊,或者等待系统自动结束,这就带来了问题。无论是系统结束还是医生手动结束,患者都喜欢抱怨和负面评价,让医生不敢擅自结束,如果你问腻了不回复,不回复就会被投诉。
最后提出了回合制聊天的需求。主动告诉病人,我们的聊天是有轮次的,所以你要一次问清楚。当轮数已满时,我们不会回复。如果患者坚持投诉,医生也会这样说,这是制造这个产品的公司设定的。
在一天结束的时候,我们需要准备好锅。
其实聊天轮流系统的诞生,与这个场景的诉求基本相似,就是为了减少用户的频繁和无休止的咨询。
思考
结合redis,可以很好的实现聊天转系统。当然也可以直接通过数据库来实现,但是显然redis的操作更简单,性能更好。
大致思路如下:
1),redis中存储了两个key,一个是representation object,声明为chat-who:consultId,value是对象标识。比如这里是医生和病人,医生标为D,患者标为P;另一个key是轮数,声明为chat-num:consultId,value是当前轮数。这里的consultId是动态的,表示本次咨询的id可以根据自己的业务来确定;
2),我们将这两个key的过期时间设置为2天,具体过期时间根据自己的业务规则进行调整;
3),我们在特定位置初始化,只要是在进入聊天之前,比如这里的场景,也就是患者发起会诊成功后开始聊天,我们初始化发起成功后的方法中聊天轮数默认为6轮。这个默认值也可以通过配置的形式动态读取;
4),我们在发送消息的方法中进行判断,获取redis中的chat-who:consultId,看是否存在。如果存在,它将被执行。如果不存在,则表示发送了第一条消息。消息,然后在redis中创建key chat-who:consultId,值为当前发送者D或P;
5),继续 4、如果chat-who存在,我们继续比较当前消息对象和redis中存储的对象值chat-who,如果相同则跳过,如果不一样,将 chat-who 的值更新为当前发送消息的人。同时,我们判断当前发送消息的人是否是医生,即D,如果是D,则更新轮数,进行-1操作。这样做的目的是用医生作为更新轮数的维度。只能有一个维度,这样才能保证最准确的更新轮数。
实施
接下来,我将整个想法用伪代码写出来。
1、定义redis-key
2、初始化聊天轮数
聊天前初始化。我们这里项目的场景是患者发起会诊成功后,在成功后的方法中进行初始化。
/**
* 发起咨询成功
*/
public void consultSuccess() {
// ....其他业务逻辑处理
// 初始化聊天回合数
initChatRoundNum(ConsultDTO consultDTO);
}
/**
* 初始化聊天回合数
* -- 过期时间48小时
* @param consultDTO 咨询信息
*/
private void initChatRoundNum(ConsultDTO consultDTO) {
// 初始6回合
int chatNum = 6;
// 获取系统配置的默认回合数,这里是伪代码根据自己需要编写。
ParameterDTO parameterDTO = getConfigValue();
if(!ObjectUtils.isEmpty(parameterDTO)) {
chatNum = parameterDTO.getPvalue();
}
// 初始化到redis,key是chat-num:consultId
redisService.set(ChatRoundConstants.CHAT_NUM + consultDTO.getId(),
chatNum, ChatRoundConstants.EXPIRE_TIME);
}
3、更新轮次
这里是核心逻辑,主要分为两步:初始化chat-who:consultId,更新chat-num:consultId。
/**
* 发消息
*/
public void sendMsg() {
// ....其他业务逻辑
// 更新聊天回合数
handleChatRoundNum(consultDTO, consultDetailInfoDTO);
}
/**
* 处理聊天回合数
* @param consultDTO 咨询信息
* @param consultDetailInfoDTO 聊天信息
*/
private void handleChatRoundNum(ConsultDTO consultDTO,
ConsultDetailInfoDTO consultDetailInfoDTO) {
// 获取redis保存的医生患者标识key
String chatWhoKey = ChatRoundConstants.CHAT_WHO + consultDTO.getId();
// 获取当前发消息的人对应的标识
String current = ChatWhoEnum.getCodeById(consultDetailInfoDTO.getSource());
// chat-who:consultId是否存在
if(redisService.exists(chatWhoKey)) {
String chatWhoValue = (String) redisService.get(chatWhoKey);
// 判断当前发消息的人和chatWho的值是否相同,如果不同,更新chatWho为当前发消息的人。
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-1
String chatNumKey = ChatRoundConstants.CHAT_NUM + consultDTO.getId();
int chatNumValue = Integer.parseInt(
(String) redisService.get(chatNumKey)
);
if(redisService.exists(chatNumKey) && chatNumValue > 0) {
redisService.decr(chatNumKey);
}
}
}
} else {
// 不存在说明是第一条消息,创建这个key。
redisService.set(chatWhoKey, current, ChatRoundConstants.EXPIRE_TIME);
}
}
定义的消息对象枚举
/**
* 聊天对象来源的枚举类
*/
public enum ChatWhoEnum {
// 来源 :
// 0 医生
// 1 患者
DOCTOR(0, "D", "医生"),
PATIENT(1, "P", "患者");
private final int id;
private final String code;
private final String label;
ChatWhoEnum(final int id, final String code, final String label) {
this.id = id;
this.code = code;
this.label = label;
}
public int getId() {
return id;
}
public String getCode() {
return code;
}
public String getLabel() {
return label;
}
public static String getCodeById(int id) {
for(ChatWhoEnum type: ChatWhoEnum.values()) {
if(type.getId() == id) {
return type.getCode();
}
}
return null;
}
public static Integer getIdByCode(String code) {
for(ChatWhoEnum type: ChatWhoEnum.values()) {
if(code.equalsIgnoreCase(type.getCode())) {
return type.getId();
}
}
return null;
}
}
总结
其实写起来很简单,思路也不难,但是一下子让你实现这个小功能还是挺费劲的。
此功能目前已上线,运行稳定,没有任何问题。如果你有兴趣,你可以保存它。如果有一天你做聊天相关的业务,你可能会遇到类似的需求。
我的原创文章是纯手写的。如果觉得有帮助,请推荐~
我继续分享实际工作经验和主流技术,喜欢的可以关注一下~
暂无评论内容