大家都一样的《策略模式》

不同类型的消息处理

这里抽象层用的是接口

MessageProcess

/**
 * 消息处理
 *
 * @author haiyang
 */
public interface MessageProcess<K,V> {


    /**
     * 处理消息
     *
     * @param messageInfo 消息相关信息
     * @return {@link V}
     */
    V dealWithMessage(final K messageInfo);

}

客户的消息 CustomerMessageProcess
/**
 * 客户消息
 * <pre>
 *     客户回复的消息如果是数字,需要将数字对应的detail和可选项回复关联起来。
 * </pre>
 *
 * @author haiyang
 */
public class CustomerMessageProcess implements MessageProcess<MessageFullInfoDto, MessageProcessFinalResultDto> {
    @Override
    public MessageProcessFinalResultDto dealWithMessage(final MessageFullInfoDto messageFullInfoDto) {
        // 判断是否处于禁止发言期间
        Object disableReplay = redisUtils.get(RedisKeyUtil.getDisableReplayKey(messageFullInfoDto.getRoomId()));
        if (!Objects.isNull(disableReplay)) {
            log.info("群:{}正处于禁止机器人发言期", messageFullInfoDto.getRoomId());
            return new MessageProcessFinalResultDto(false, "正处于禁止机器人发言期", null);
        }
        // 判断文本是否属于数字类型
        String messageContents = messageFullInfoDto.getMessageContents();
        boolean numeric = StringUtils.isNumeric(messageContents);
        Long groupId = messageFullInfoDto.getGroupId();
        if (numeric) {
            // 走数字逻辑
            int parseInt = Integer.parseInt(messageContents);
            if (parseInt > 0 && parseInt <= 10) {
                LambdaQueryWrapper<MsgDetail> lastDetail = new LambdaQueryWrapper<MsgDetail>().eq(MsgDetail::getGroupId, groupId).eq(MsgDetail::getType, MsgDetailEnum.Type.REPLY.getCode()).orderByDesc(MsgDetail::getCreateTime).last("limit 1");
                MsgDetail replyMsgDetail = msgDetailService.getOne(lastDetail);
                MessageProcessFinalResultDto messageProcessFinalResultDto = noOptionReplyHandler(messageFullInfoDto, messageContents, groupId, replyMsgDetail);
                if (messageProcessFinalResultDto != null) {
                    return messageProcessFinalResultDto;
                }
                MsgAnswers groupLastAnswer = msgAnswersService.getGroupLastAnswer(groupId, parseInt);
                messageProcessFinalResultDto = missedReplyHandler(messageFullInfoDto, messageContents, groupId, groupLastAnswer);
                if (messageProcessFinalResultDto != null) {
                    return messageProcessFinalResultDto;
                }
                messageProcessFinalResultDto = handlerInvalidate(messageFullInfoDto, messageContents, groupId, groupLastAnswer);
                if (messageProcessFinalResultDto != null) {
                    return messageProcessFinalResultDto;
                }
                messageProcessFinalResultDto = hitHandler(messageFullInfoDto, messageContents, groupId, groupLastAnswer);
                String replyDetailId = replyMsgDetail.getReplyDetailId();
                if (StringUtils.isNotEmpty(replyDetailId)) {
                    replyDetailId += "," + messageProcessFinalResultDto.getExtendData();
                } else {
                    replyDetailId = "" + messageProcessFinalResultDto.getExtendData();
                }
                replyMsgDetail.setReplyDetailId(replyDetailId);
                msgDetailService.updateById(replyMsgDetail);
                return messageProcessFinalResultDto;
            }
        }
        // 普通文本处理
        saveDetail(messageFullInfoDto, messageContents, groupId, null, true);
        LambdaQueryWrapper<MsgWaitTask> queryWrapper = new LambdaQueryWrapper<MsgWaitTask>().eq(MsgWaitTask::getGroupId, groupId).eq(MsgWaitTask::getMergeStatus, MergeStatusEnum.WAIT_MERGE.getStatus()).orderByDesc(MsgWaitTask::getWaitMergeTime).last("limit 1");
        MsgWaitTask msgWaitTask = msgWaitTaskService.getOne(queryWrapper);
        if (Objects.isNull(msgWaitTask)) {
            saveMsgWaitTask(messageFullInfoDto, groupId);
            return new MessageProcessFinalResultDto(true, null, null);
        }
        LocalDateTime waitMergeTime = msgWaitTask.getWaitMergeTime();
        LocalDateTime now = LocalDateTime.now();
        if (now.isAfter(waitMergeTime)) {
            saveMsgWaitTask(messageFullInfoDto, groupId);
        } else {
            // 更新整合时间和最后一次微信时间
            msgWaitTask.setLastTxMsgTime(messageFullInfoDto.getMsgTime());
            msgWaitTask.setWaitMergeTime(LocalDateTime.now().plusSeconds(15));
            msgWaitTaskService.updateById(msgWaitTask);
        }
        return new MessageProcessFinalResultDto(true, null, null);
    }

    private void saveMsgWaitTask(MessageFullInfoDto messageFullInfoDto, Long groupId) {
        MsgWaitTask msgWaitTask;
        // 说明已经过了整合时间, 重新插入一条数据
        msgWaitTask = new MsgWaitTask();
        msgWaitTask.setGroupId(groupId);
        msgWaitTask.setRoomId(messageFullInfoDto.getRoomId());
        Long msgTime = messageFullInfoDto.getMsgTime();
        msgWaitTask.setWaitMergeTime(LocalDateTime.now().plusSeconds(15));
        msgWaitTask.setLastTxMsgTime(msgTime);
        msgWaitTask.setMergeStatus(MergeStatusEnum.WAIT_MERGE.getStatus());
        msgWaitTaskService.save(msgWaitTask);
    }

    /**
     * 命中处理
     */
    private MessageProcessFinalResultDto hitHandler(MessageFullInfoDto messageFullInfoDto, String messageContents, Long groupId, MsgAnswers groupLastAnswer) {
        String answerContext = groupLastAnswer.getAnswerContext();
        Long rpaMsgDetailId = saveRpaMsgDetail(messageFullInfoDto, groupId, answerContext);
        // 保存客户detail
        Long detailId = saveDetail(messageFullInfoDto, messageContents, groupId, rpaMsgDetailId, false);
        // 发送rpa任务
        sendRpaTask(messageFullInfoDto, answerContext, rpaMsgDetailId,TaskSubTypeEnum.ACCURATE.getType());
        return new MessageProcessFinalResultDto(true, null, detailId);
    }

    /**
     * 无可选项回复处理
     */
    private MessageProcessFinalResultDto noOptionReplyHandler(MessageFullInfoDto messageFullInfoDto, String messageContents, Long groupId, MsgDetail replyMsgDetail) {
        if (Objects.isNull(replyMsgDetail) || DateUtil.isAfterMinus(replyMsgDetail.getCreateTime(), 20)) {
            // 没有可选项回复 存入detail 不执行回复
            saveDetail(messageFullInfoDto, messageContents, groupId, null, false);
            log.info("客服发言的数字文本无需回复, fromAccount:{}, textContext:{}", messageFullInfoDto.getFromAccount(), messageContents);
            noWorkAutoReply(messageFullInfoDto, messageContents, groupId);
            return new MessageProcessFinalResultDto(true, null, null);
        }
 /*       // 判断可选项时间是否在20min有效期
        LocalDateTime createTime = replyMsgDetail.getCreateTime();
        if (DateUtil.isAfterMinus(createTime, 20)) {
            // 超过了有效期, 回复默认文本
            // 没有可选项回复 存入detail 不执行回复
            saveDetail(messageFullInfoDto, messageContents, groupId, null, false);
            log.info("客服发言的数字文本无需回复, fromAccount:{}, textContext:{}", messageFullInfoDto.getFromAccount(), messageContents);
            return new MessageProcessFinalResultDto(true, null, null);
        }*/
        return null;
    }

    private void noWorkAutoReply(MessageFullInfoDto messageFullInfoDto, String messageContents, Long groupId) {
        // 非工自动回复处理
        boolean workTime = DateUtil.workTime(new Date());
        if (!workTime) {
            Object noWorkReplay = redisUtils.get(RedisKeyUtil.getNoWorkReplayKey(messageFullInfoDto.getRoomId()));
            if (Objects.isNull(noWorkReplay)) {
                // 发送rpa任务 推送非工时间的默认回复
                String noWorkAutoReply = ianswerProperties.getNoWorkAutoReply();
                Long rpaMsgDetailId = saveRpaMsgDetail(messageFullInfoDto, groupId, noWorkAutoReply);
                // 保存客户detail
                saveDetail(messageFullInfoDto, messageContents, groupId, rpaMsgDetailId, false);
                // 发送rpa任务
                sendRpaTask(messageFullInfoDto, noWorkAutoReply, rpaMsgDetailId,TaskSubTypeEnum.ACCURATE.getType());
                // 计算非工回复周期
                int tomorrowSeconds = DateUtil.getTomorrowSeconds(new Date());
                redisUtils.setWithExpireSeconds(RedisKeyUtil.getNoWorkReplayKey(messageFullInfoDto.getRoomId()),  true, tomorrowSeconds);
            }
        }
    }

    /**
     * 未命中处理
     */
    private MessageProcessFinalResultDto missedReplyHandler(MessageFullInfoDto messageFullInfoDto, String messageContents, Long groupId, MsgAnswers groupLastAnswer) {
        if (Objects.isNull(groupLastAnswer)) {
            // 保存机器人默认回复文本
            String missedReply = ianswerProperties.getMissedReply();
            Long rpaMsgDetailId = saveRpaMsgDetail(messageFullInfoDto, groupId, missedReply);
            // 保存客户detail
            saveDetail(messageFullInfoDto, messageContents, groupId, rpaMsgDetailId, false);
            // 发送rpa任务
            sendRpaTask(messageFullInfoDto, missedReply, rpaMsgDetailId,TaskSubTypeEnum.FIXED.getType());
            return new MessageProcessFinalResultDto(true, null, null);
        }
        return null;
    }

    /**
     * 以上都不是处理
     */
    private MessageProcessFinalResultDto handlerInvalidate(MessageFullInfoDto messageFullInfoDto, String messageContents, Long groupId, MsgAnswers groupLastAnswer) {
        if (MsgAnswerEnum.Type.INVALIDATE.getCode().equals(groupLastAnswer.getType())) {
            // 以上都不是, 需要回复固定话术
            boolean workTime = DateUtil.workTime(new Date());
            String textContext = null;
            if (workTime) {
                textContext = ianswerProperties.getExitWork();
            } else {
                textContext = ianswerProperties.getExitNonWork();
            }
            Long rpaMsgDetailId = saveRpaMsgDetail(messageFullInfoDto, groupId, textContext);
            // 保存客户detail
            saveDetail(messageFullInfoDto, messageContents, groupId, rpaMsgDetailId, false);
            // 发送rpa任务
            sendRpaTask(messageFullInfoDto, textContext, rpaMsgDetailId,TaskSubTypeEnum.FIXED.getType());
            return new MessageProcessFinalResultDto(true, null, null);
        }
        return null;
    }

    private void sendRpaTask(MessageFullInfoDto messageFullInfoDto, String missedReply, Long rpaMsgDetailId,String taskSubType) {
        SendTaskDto sendTaskDto = new SendTaskDto();
        sendTaskDto.setRoomId(messageFullInfoDto.getRoomId());
        sendTaskDto.setDetailId(rpaMsgDetailId);
        sendTaskDto.setContentSign(messageContextUtil.getSHAStr(missedReply));
        sendTaskDto.setGroupId(messageFullInfoDto.getGroupId());
        SendTaskRequest sendTaskRequest = new SendTaskRequest();
        sendTaskRequest.setContent(missedReply);
        sendTaskRequest.setResourceName(messageFullInfoDto.getOrderNo());
        sendTaskRequest.setWaitingRobotAccounts(messageFullInfoDto.getRobotAccount());
        sendTaskRequest.setWaitingRobotNames(messageFullInfoDto.getRobotName());
        sendTaskRequest.setTaskOverTime(DateUtil.addTimeToString(LocalDateTime.now(), 30));
        sendTaskRequest.setCustomer(messageFullInfoDto.getCustomName());
        sendTaskRequest.setSubResourceName(messageFullInfoDto.getGrowthMember());
        sendTaskRequest.setOrderId(messageFullInfoDto.getOrderNo());
        sendTaskRequest.setTaskSubType(taskSubType);
        sendTaskDto.setSendTaskRequest(sendTaskRequest);
        rpaTaskService.sendTask(sendTaskDto);
    }

    private Long saveRpaMsgDetail(MessageFullInfoDto messageFullInfoDto, Long groupId, String missedReply) {
        MsgDetail msgDetail = new MsgDetail();
        msgDetail.setType(MsgDetailEnum.Type.COMMON.getCode());
        msgDetail.setGroupId(groupId);
        msgDetail.setRoomId(messageFullInfoDto.getRoomId());
        msgDetail.setMsgStatus(MsgDetailEnum.MsgStatus.TO_BE_RECEIVE.getCode());
        msgDetail.setTextContext(missedReply);
        msgDetail.setTextContextSign(messageContextUtil.getSHAStr(missedReply));
        msgDetail.setMergeStatus(MsgDetailEnum.MergeStatus.NONE.getCode());
        msgDetail.setReplyStatus(MsgDetailEnum.ReplyStatus.WAIT_REPLY.getCode());
        msgDetail.setSendAccountType(MsgDetailEnum.SendAccountType.RPA.getCode());
        msgDetail.setSendQwName(messageFullInfoDto.getRobotName());
        msgDetail.setSendQwAccount(messageFullInfoDto.getRobotAccount());
        msgDetailService.save(msgDetail);
        return msgDetail.getId();
    }

    private Long saveDetail(MessageFullInfoDto messageFullInfoDto, String messageContents, Long groupId, Long rpaMsgDetailId, boolean isTextMessage) {
        MsgDetail msgDetail = new MsgDetail();
        msgDetail.setType(MsgDetailEnum.Type.COMMON.getCode());
        msgDetail.setGroupId(groupId);
        msgDetail.setRoomId(messageFullInfoDto.getRoomId());
        msgDetail.setMsgId(messageFullInfoDto.getMsgId());
        msgDetail.setMsgTime(messageFullInfoDto.getMsgTime());
        msgDetail.setMsgFrom(messageFullInfoDto.getFromAccount());
        msgDetail.setMsgStatus(MsgDetailEnum.MsgStatus.RECEIVED.getCode());
        msgDetail.setTextContext(messageContents);
        msgDetail.setTextContextSign(messageContextUtil.getSHAStr(messageContents));
        if (isTextMessage) {
            msgDetail.setMergeStatus(MsgDetailEnum.MergeStatus.WAIT_MERGE.getCode());
            msgDetail.setReplyStatus(MsgDetailEnum.ReplyStatus.WAIT_REPLY.getCode());
        } else {
            msgDetail.setReplyStatus(MsgDetailEnum.ReplyStatus.NONE.getCode());
            msgDetail.setMergeStatus(MsgDetailEnum.MergeStatus.NONE.getCode());
        }
        if (Objects.nonNull(rpaMsgDetailId)) {
            msgDetail.setReplyDetailId(rpaMsgDetailId.toString());
        }
        msgDetailService.save(msgDetail);
        return msgDetail.getId();
    }
}

机器人消息 RobotMessageProcess

/**
 * 机器人消息
 * <pre>
 *     整体处理逻辑:
 *     1.根据群id和机器人的账号+内容签名,确定一条最新的有效的明细和任务
 *     2.更新机器人发言的那条明细记录
 *     3.更新rpa任务相应的状态
 *     4.更新前面整合提问的那些明细
 * </pre>
 *
 * @author haiyang
 */
@Component
@Slf4j
public class RobotMessageProcess implements MessageProcess<MessageFullInfoDto, MessageProcessFinalResultDto> {

    @Override
    public MessageProcessFinalResultDto dealWithMessage(final MessageFullInfoDto messageInfo) {
        log.info("RobotMessageProcess params:{}",JSON.toJSONString(messageInfo));
        // 1.查到对应的rpa task 和 msg detail(这个消息是机器人发送的消息)
        TaskMsgDetailDto taskDetailInfo = rpaTaskService.getTaskDetailInfo(messageInfo.getRoomId(),
                messageInfo.getFromAccount(),
                getShaContentsOfTrimAt(messageInfo.getMessageContents())
        );
        log.info("getTaskDetailInfo result:{}", JSON.toJSONString(taskDetailInfo));
        if (Objects.isNull(taskDetailInfo)) {
            return new MessageProcessFinalResultDto(false,"没有找到对应的task detail",null);
        }
        // 2.更新 rpa task 中的 rpa_msg_status
        rpaTaskService.updateRpaMsgStatus(taskDetailInfo.getTaskId(), RpaTaskEnum.RpaMsgStatus.RECEIVE_SUCCESS.getCode());
        log.info("更新rpa task的状态--完成");


        // 3.更新机器人消息的detail的中的 msg_status 状态 ,
        // 同时需要更新对应的 send_qw_name 和 send_qw_account 为实际发送消息的robot账号信息(实际回复消息的只有一个机器人)
        msgDetailService.updateMsgStatus(taskDetailInfo.getDetailId(),
                MsgDetailEnum.MsgStatus.RECEIVED.getCode(),
                messageInfo.getFromAccountName(),
                messageInfo.getFromAccount(),
                messageInfo.getMsgId(),
                messageInfo.getMsgTime(),
                messageInfo.getFromAccount());
        log.info("更新robot消息detail的状态--完成");


        // 4.更新对应的整合消息的 reply_status
        msgDetailService.refreshReplyStatus(taskDetailInfo.getDetailId(),taskDetailInfo.getMsgId(),taskDetailInfo.getMsgTime());
        log.info("更新对应的整合消息--完成");

        syncNotifyListener(taskDetailInfo);
        log.info("通知操作--完成");
        return new MessageProcessFinalResultDto(true,null,null);
    }


    /**
     * 将机器人消息去除@符号进行签名
     * <pre>
     *     <b>下发rpa任务时,at的对象没有直接跟在内容里面</b>
     * </pre>
     *
     * @param msgContent 味精内容
     * @return {@link String}
     */
    private String getShaContentsOfTrimAt(final String msgContent){
        if (msgContent.contains(CommonConstant.SYMBOL_OF_AT)) {
            int i = msgContent.indexOf(CommonConstant.SYMBOL_OF_AT);
            if (i>0) {
                String trimAtString = msgContent.substring(0, i);
                return messageContextUtil.getSHAStr(trimAtString);
            }
        }
        String sign =  messageContextUtil.getSHAStr(msgContent);
        log.info("msgContent:{},sign:{}",msgContent,sign);
        return sign;
    }


    /**
     * 通知操作
     *
     * @param taskDetailInfo 任务详细信息
     */
    private void syncNotifyListener(TaskMsgDetailDto taskDetailInfo){
        log.info("通知rpa消息接收结果");
        NotifyTaskRequest request = new NotifyTaskRequest();
        request.setTaskId(String.valueOf(taskDetailInfo.getTaskId()));
        request.setMsgStatus(RpaTaskEnum.RpaMsgStatus.RECEIVE_SUCCESS.getStatus());
        request.setReceivedMsgTime(DateUtil.formatYmdHms(DateUtil.secondToLocalDate(taskDetailInfo.getMsgTime())));
        sendTaskClient.notifyReceivedMessageResult(Arrays.asList(request));
    }

}

人工客服消息 StaffServiceMessageProcess
/**
 * 人工客服消息
 *
 * @author haiyang
 */
@Component
@Slf4j
public class StaffServiceMessageProcess implements MessageProcess<MessageFullInfoDto, MessageProcessFinalResultDto> {

    @Override
    public MessageProcessFinalResultDto dealWithMessage(final MessageFullInfoDto messageInfo) {
        log.info("StaffServiceMessageProcess params:{}", JSON.toJSONString(messageInfo));
        // 1.保存人工发言
        MsgDetail msgDetail = new MsgDetail();
        msgDetail.setGroupId(messageInfo.getGroupId());
        msgDetail.setType(MsgDetailEnum.Type.COMMON.getCode());
        msgDetail.setRoomId(messageInfo.getRoomId());
        msgDetail.setMsgId(messageInfo.getMsgId());
        msgDetail.setMsgTime(messageInfo.getMsgTime());
        msgDetail.setMsgFrom(messageInfo.getFromAccount());
        msgDetail.setMsgStatus(MsgDetailEnum.MsgStatus.RECEIVED.getCode());
        msgDetail.setTextContext(messageInfo.getMessageContents());
        msgDetail.setTextContextSign(messageContextUtil.getSHAStr(messageInfo.getMessageContents()));
        msgDetail.setMergeStatus(MsgDetailEnum.MergeStatus.NONE.getCode());
        msgDetail.setReplyStatus(MsgDetailEnum.ReplyStatus.NONE.getCode());
        msgDetailService.save(msgDetail);
        log.info("人工客服发言保存--完成");

        // 2.打断消息整合,明细状态更新为"无需整合"
        LambdaUpdateWrapper<MsgDetail> where = new LambdaUpdateWrapper<>();
        where.eq(MsgDetail::getRoomId, messageInfo.getRoomId());
        where.eq(MsgDetail::getGroupId, messageInfo.getGroupId());
        where.eq(MsgDetail::getMergeStatus, MsgDetailEnum.MergeStatus.WAIT_MERGE.getCode());
        where.set(MsgDetail::getMergeStatus,MsgDetailEnum.MergeStatus.NONE.getCode());
        msgDetailService.update(where);
        log.info("打断消息整合--完成");
        return new MessageProcessFinalResultDto(true,null,null);
    }
}

调用的地方是这样的:⟱⟱⟱
image


不同状态的处理逻辑

这里抽象层用的是抽象类

image

使用的时候是通过枚举指定的类型从IOC容器中获取对应的处理实例的,如下⬇︎⬇︎⬇︎
image

选择使用
image


使用很灵活,理念很简单。

posted @ 2024-12-17 17:30  Eular  阅读(22)  评论(0编辑  收藏  举报