来自云龙湖轮廓分明的月亮 发表于 2024-12-17 16:46:40

大家都一样的《计谋模式》

不同类型的消息处理

这里抽象层用的是接口
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();
    }
}人工客服消息 StaffServiceMessageProcess ⬇︎⬇︎⬇︎
点击查看代码/**
* 机器人消息
* <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));
    }

}调用的地方是这样的:⟱⟱⟱
https://img2024.cnblogs.com/blog/3019051/202412/3019051-20241217165337111-228843466.png
不同状态的处理逻辑

这里抽象层用的是抽象类
https://img2024.cnblogs.com/blog/3019051/202412/3019051-20241217171415362-262722397.png
使用的时候是通过枚举指定的类型从IOC容器中获取对应的处理实例的,如下⬇︎⬇︎⬇︎
https://img2024.cnblogs.com/blog/3019051/202412/3019051-20241217172821796-630109294.png
选择使用
https://img2024.cnblogs.com/blog/3019051/202412/3019051-20241217171756150-459363975.png
使用很灵活,理念很简单。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 大家都一样的《计谋模式》