大家都一样的《策略模式》
不同类型的消息处理
这里抽象层用的是接口
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);
}
}
调用的地方是这样的:⟱⟱⟱
不同状态的处理逻辑
这里抽象层用的是抽象类
使用的时候是通过枚举指定的类型从IOC容器中获取对应的处理实例的,如下⬇︎⬇︎⬇︎
选择使用
使用很灵活,理念很简单。
本文来自博客园,作者:Eular,转载请注明原文链接:https://www.cnblogs.com/euler-blog/p/18613061