网上搜不到的方案作者本人快把百度搜秃噜了也没找到

前言

为什么我在 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;
    }
}

总结

其实写起来很简单,思路也不难,但是一下子让你实现这个小功能还是挺费劲的。

此功能目前已上线,运行稳定,没有任何问题。如果你有兴趣,你可以保存它。如果有一天你做聊天相关的业务,你可能会遇到类似的需求。

我的原创文章是纯手写的。如果觉得有帮助,请推荐~

我继续分享实际工作经验和主流技术,喜欢的可以关注一下~

© 版权声明
THE END
喜欢就支持一下吧
点赞295赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容