From a17a046e83a84013b1c60112465663f76bed7fba Mon Sep 17 00:00:00 2001 From: wangli <274027703@qq.com> Date: Thu, 24 Oct 2024 19:01:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(REQ-3114)=20-=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E9=92=89=E9=92=89=E6=B6=88=E6=81=AF=E7=9A=84=E5=A4=84=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=85=B3=E9=94=AE=E8=AF=8D=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E8=BD=AC=E5=8F=91=20MQ=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- riven-api/pom.xml | 4 + .../common/enums/DingTalkMsgTypeEnum.java | 2 + .../common/enums/DingtalkEventEnum.java | 45 ++++++++++ .../client/model/DingtalkReceiveMqModel.java | 16 ++++ .../axzo/riven/client/model/ReplyMessage.java | 21 +++++ .../riven/client/model/SampleMarkdown.java | 32 +++++++ .../client/model/SampleMessageQueue.java | 48 ++++++++++ .../axzo/riven/client/model/SampleText.java | 32 +++++++ .../req/ThirdDingtalkConversationReq.java | 25 ++++++ riven-dingtalk/pom.xml | 8 ++ .../keyword/DefaultKeywordProcessor.java | 23 +++++ .../callback/keyword/KeywordProcessor.java | 16 ++++ .../keyword/MenuKeywordProcessor.java | 27 ++++++ .../keyword/RegisterKeywordProcessor.java | 58 +++++++++++++ .../robot/RobotMsgCallbackConsumer.java | 20 +++-- .../strategy/AbstractRobotHandleStrategy.java | 67 +++++++++++++- .../impl/DefaultTransferToMQStrategy.java | 28 ------ .../impl/KeywordProcessorStrategy.java | 87 +++++++++++++++++++ .../impl/RegisterConversationStrategy.java | 55 ------------ .../strategy/impl/TransferToMQStrategy.java | 85 ++++++++++++++++++ .../axzo/riven/dingtalk/reply/RobotReply.java | 51 +++++++++++ .../reply/strategy/AbstractRobotReply.java | 24 +++++ .../reply/strategy/BasedHttpReply.java | 20 ++++- .../strategy/BasedSessionWebhookReply.java | 51 ++++++++++- .../{ => strategy}/RobotReplyStrategy.java | 7 +- .../entity/ThirdDingtalkConversation.java | 7 +- .../entity/ThirdDingtalkMessageRecord.java | 11 ++- .../ThirdDingtalkConversationMapper.java | 1 - .../robot/connection/model/ReplyContext.java | 53 +++++------ .../ThirdDingtalkConversationService.java | 5 ++ .../ThirdDingtalkMessageRecordService.java | 3 + .../ThirdDingtalkConversationServiceImpl.java | 37 ++++++-- ...ThirdDingtalkMessageRecordServiceImpl.java | 9 ++ riven-dingtalk/src/main/resources/sql/DDL.sql | 46 +++++----- 34 files changed, 863 insertions(+), 161 deletions(-) create mode 100644 riven-api/src/main/java/cn/axzo/riven/client/common/enums/DingtalkEventEnum.java create mode 100644 riven-api/src/main/java/cn/axzo/riven/client/model/DingtalkReceiveMqModel.java create mode 100644 riven-api/src/main/java/cn/axzo/riven/client/model/ReplyMessage.java create mode 100644 riven-api/src/main/java/cn/axzo/riven/client/model/SampleMarkdown.java create mode 100644 riven-api/src/main/java/cn/axzo/riven/client/model/SampleMessageQueue.java create mode 100644 riven-api/src/main/java/cn/axzo/riven/client/model/SampleText.java create mode 100644 riven-api/src/main/java/cn/axzo/riven/client/req/ThirdDingtalkConversationReq.java create mode 100644 riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/DefaultKeywordProcessor.java create mode 100644 riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/KeywordProcessor.java create mode 100644 riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/MenuKeywordProcessor.java create mode 100644 riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/RegisterKeywordProcessor.java delete mode 100644 riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/DefaultTransferToMQStrategy.java create mode 100644 riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/KeywordProcessorStrategy.java delete mode 100644 riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/RegisterConversationStrategy.java create mode 100644 riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/TransferToMQStrategy.java create mode 100644 riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/RobotReply.java create mode 100644 riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/AbstractRobotReply.java rename riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/{ => strategy}/RobotReplyStrategy.java (50%) diff --git a/riven-api/pom.xml b/riven-api/pom.xml index e9486df..b6906b5 100644 --- a/riven-api/pom.xml +++ b/riven-api/pom.xml @@ -18,5 +18,9 @@ cn.axzo.framework axzo-consumer-spring-cloud-starter + + cn.axzo.framework.rocketmq + axzo-common-rocketmq + diff --git a/riven-api/src/main/java/cn/axzo/riven/client/common/enums/DingTalkMsgTypeEnum.java b/riven-api/src/main/java/cn/axzo/riven/client/common/enums/DingTalkMsgTypeEnum.java index 34669b3..bf84d03 100644 --- a/riven-api/src/main/java/cn/axzo/riven/client/common/enums/DingTalkMsgTypeEnum.java +++ b/riven-api/src/main/java/cn/axzo/riven/client/common/enums/DingTalkMsgTypeEnum.java @@ -20,5 +20,7 @@ public enum DingTalkMsgTypeEnum { sampleAudio, sampleFile, sampleVideo, + // 非钉钉官方 + messageQueue, ; } diff --git a/riven-api/src/main/java/cn/axzo/riven/client/common/enums/DingtalkEventEnum.java b/riven-api/src/main/java/cn/axzo/riven/client/common/enums/DingtalkEventEnum.java new file mode 100644 index 0000000..4146229 --- /dev/null +++ b/riven-api/src/main/java/cn/axzo/riven/client/common/enums/DingtalkEventEnum.java @@ -0,0 +1,45 @@ +package cn.axzo.riven.client.common.enums; + +import cn.axzo.framework.rocketmq.Event; + +/** + * 流程实例相关的 MQ 事件枚举定义 + * + * @author wangli + * @since 2023/9/25 11:47 + */ +public enum DingtalkEventEnum { + + receive("riven-dingtalk", "riven-dingtalk-receive", "钉钉消息"), + ; + private final String module; + private final String tag; + private final String desc; + private Event.EventCode eventCode; + + DingtalkEventEnum(String module, String tag, String desc) { + this.eventCode = Event.EventCode.builder() + .module(module) + .name(tag) + .build(); + this.module = module; + this.tag = tag; + this.desc = desc; + } + + public String getModule() { + return module; + } + + public String getTag() { + return tag; + } + + public String getDesc() { + return desc; + } + + public Event.EventCode getEventCode() { + return eventCode; + } +} diff --git a/riven-api/src/main/java/cn/axzo/riven/client/model/DingtalkReceiveMqModel.java b/riven-api/src/main/java/cn/axzo/riven/client/model/DingtalkReceiveMqModel.java new file mode 100644 index 0000000..0cb812e --- /dev/null +++ b/riven-api/src/main/java/cn/axzo/riven/client/model/DingtalkReceiveMqModel.java @@ -0,0 +1,16 @@ +package cn.axzo.riven.client.model; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 监听由 riven 转发的钉钉群消息模型 + * + * @author wangli + * @since 2024-10-24 18:17 + */ +@Data +public class DingtalkReceiveMqModel implements Serializable { + private final static long serialVersionUID = 1L; +} diff --git a/riven-api/src/main/java/cn/axzo/riven/client/model/ReplyMessage.java b/riven-api/src/main/java/cn/axzo/riven/client/model/ReplyMessage.java new file mode 100644 index 0000000..60b3d3d --- /dev/null +++ b/riven-api/src/main/java/cn/axzo/riven/client/model/ReplyMessage.java @@ -0,0 +1,21 @@ +package cn.axzo.riven.client.model; + +import cn.axzo.framework.jackson.utility.JSON; +import cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum; + +/** + * 回复消息 POJO + * + * @author wangli + * @since 2024-10-24 13:43 + */ +public interface ReplyMessage { + + DingTalkMsgTypeEnum msgType(); + + T messageBody(); + + default String toJson() { + return JSON.toJSONString(messageBody()); + } +} diff --git a/riven-api/src/main/java/cn/axzo/riven/client/model/SampleMarkdown.java b/riven-api/src/main/java/cn/axzo/riven/client/model/SampleMarkdown.java new file mode 100644 index 0000000..844394e --- /dev/null +++ b/riven-api/src/main/java/cn/axzo/riven/client/model/SampleMarkdown.java @@ -0,0 +1,32 @@ +package cn.axzo.riven.client.model; + +import cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * 简单的 MD 内容 + * + * @author wangli + * @since 2024-10-24 14:01 + */ +@Data +@AllArgsConstructor +public class SampleMarkdown implements ReplyMessage { + private final String title; + private final String text; + + @Override + public DingTalkMsgTypeEnum msgType() { + return DingTalkMsgTypeEnum.sampleMarkdown; + } + + @Override + public SampleMarkdown messageBody() { + return this; + } + + public static SampleMarkdown from(String title, String text) { + return new SampleMarkdown(title, text); + } +} diff --git a/riven-api/src/main/java/cn/axzo/riven/client/model/SampleMessageQueue.java b/riven-api/src/main/java/cn/axzo/riven/client/model/SampleMessageQueue.java new file mode 100644 index 0000000..e127295 --- /dev/null +++ b/riven-api/src/main/java/cn/axzo/riven/client/model/SampleMessageQueue.java @@ -0,0 +1,48 @@ +package cn.axzo.riven.client.model; + +import cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum; +import com.google.common.base.Strings; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.slf4j.MDC; + +import java.util.UUID; + +import static cn.azxo.framework.common.constatns.Constants.CTX_LOG_ID_MDC; +import static com.google.common.net.HttpHeaders.X_REQUEST_ID; + +/** + * 转发消息内容到后端服务 + * + * @author wangli + * @since 2024-10-24 17:14 + */ +@Data +@AllArgsConstructor +public class SampleMessageQueue implements ReplyMessage { + private String applicationName; + private String traceId; + private String messageContent; + + @Override + public DingTalkMsgTypeEnum msgType() { + return DingTalkMsgTypeEnum.messageQueue; + } + + @Override + public SampleMessageQueue messageBody() { + return this; + } + + public static SampleMessageQueue from(String applicationName, String messageContent) { + String traceId = MDC.get(X_REQUEST_ID); + if (Strings.isNullOrEmpty(traceId)) { + MDC.put(CTX_LOG_ID_MDC, traceId()); + } + return new SampleMessageQueue(applicationName, traceId, messageContent); + } + + private static String traceId() { + return UUID.randomUUID().toString().replaceAll("-", ""); + } +} diff --git a/riven-api/src/main/java/cn/axzo/riven/client/model/SampleText.java b/riven-api/src/main/java/cn/axzo/riven/client/model/SampleText.java new file mode 100644 index 0000000..f3e1a93 --- /dev/null +++ b/riven-api/src/main/java/cn/axzo/riven/client/model/SampleText.java @@ -0,0 +1,32 @@ +package cn.axzo.riven.client.model; + +import cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * 简单文本 + * + * @author wangli + * @since 2024-10-24 13:45 + */ +@Data +@AllArgsConstructor +public class SampleText implements ReplyMessage { + private final String content; + + @Override + public DingTalkMsgTypeEnum msgType() { + return DingTalkMsgTypeEnum.sampleText; + } + + @Override + public SampleText messageBody() { + return this; + } + + public static SampleText from(String content) { + return new SampleText(content); + } + +} diff --git a/riven-api/src/main/java/cn/axzo/riven/client/req/ThirdDingtalkConversationReq.java b/riven-api/src/main/java/cn/axzo/riven/client/req/ThirdDingtalkConversationReq.java new file mode 100644 index 0000000..110be62 --- /dev/null +++ b/riven-api/src/main/java/cn/axzo/riven/client/req/ThirdDingtalkConversationReq.java @@ -0,0 +1,25 @@ +package cn.axzo.riven.client.req; + +import lombok.Data; + +/** + * 钉钉会话的通用查询入参 + * + * @author wangli + * @since 2024-10-24 17:47 + */ +@Data +public class ThirdDingtalkConversationReq { + + private String conversationId; + + private String conversationTitle; + + private String conversationType; + + private String chatbotCorpId; + + private String chatbotUserId; + + private String applicationName; +} diff --git a/riven-dingtalk/pom.xml b/riven-dingtalk/pom.xml index 7583ea4..3f81759 100644 --- a/riven-dingtalk/pom.xml +++ b/riven-dingtalk/pom.xml @@ -19,9 +19,17 @@ com.dingtalk.open app-stream-client + + cn.hutool + hutool-all + cn.axzo.framework.data axzo-data-mybatis-plus + + cn.axzo.framework.rocketmq + axzo-common-rocketmq + diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/DefaultKeywordProcessor.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/DefaultKeywordProcessor.java new file mode 100644 index 0000000..1a49c24 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/DefaultKeywordProcessor.java @@ -0,0 +1,23 @@ +package cn.axzo.riven.dingtalk.callback.keyword; + +import cn.axzo.riven.client.model.ReplyMessage; +import cn.axzo.riven.client.model.SampleText; +import com.dingtalk.open.app.api.models.bot.ChatbotMessage; + +/** + * 兜底的关键词,不用注册为 Spring 的 Bean + * + * @author wangli + * @since 2024-10-24 17:01 + */ +public class DefaultKeywordProcessor implements KeywordProcessor { + @Override + public String[] getKeywords() { + return new String[0]; + } + + @Override + public ReplyMessage process(ChatbotMessage chatbotMessage) { + return SampleText.from("不能理解你说的话!"); + } +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/KeywordProcessor.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/KeywordProcessor.java new file mode 100644 index 0000000..5bbdb40 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/KeywordProcessor.java @@ -0,0 +1,16 @@ +package cn.axzo.riven.dingtalk.callback.keyword; + +import cn.axzo.riven.client.model.ReplyMessage; +import com.dingtalk.open.app.api.models.bot.ChatbotMessage; + +/** + * 机器人支持的关键词 + * + * @author wangli + * @since 2024-10-24 16:29 + */ +public interface KeywordProcessor { + String[] getKeywords(); + + ReplyMessage process(ChatbotMessage chatbotMessage); +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/MenuKeywordProcessor.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/MenuKeywordProcessor.java new file mode 100644 index 0000000..303601c --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/MenuKeywordProcessor.java @@ -0,0 +1,27 @@ +package cn.axzo.riven.dingtalk.callback.keyword; + +import cn.axzo.riven.client.model.ReplyMessage; +import cn.axzo.riven.client.model.SampleText; +import com.dingtalk.open.app.api.models.bot.ChatbotMessage; +import org.springframework.stereotype.Component; + +/** + * 主菜单 + * + * @author wangli + * @since 2024-10-24 16:28 + */ +@Component +public class MenuKeywordProcessor implements KeywordProcessor { + public final static String[] KEYWORD = {"menu", "菜单"}; + + @Override + public String[] getKeywords() { + return KEYWORD; + } + + @Override + public ReplyMessage process(ChatbotMessage chatbotMessage) { + return SampleText.from("这里是菜单"); + } +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/RegisterKeywordProcessor.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/RegisterKeywordProcessor.java new file mode 100644 index 0000000..67110d6 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/keyword/RegisterKeywordProcessor.java @@ -0,0 +1,58 @@ +package cn.axzo.riven.dingtalk.callback.keyword; + +import cn.axzo.riven.client.model.ReplyMessage; +import cn.axzo.riven.client.model.SampleText; +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkConversation; +import cn.axzo.riven.dingtalk.service.ThirdDingtalkConversationService; +import cn.hutool.core.bean.BeanUtil; +import com.dingtalk.open.app.api.models.bot.ChatbotMessage; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import static cn.axzo.riven.dingtalk.constant.DingTalkConstant.REGISTER; + +/** + * 注册 + * + * @author wangli + * @since 2024-10-24 16:31 + */ +@Component +@AllArgsConstructor +public class RegisterKeywordProcessor implements KeywordProcessor { + public final static String[] KEYWORD = {"register", "注册"}; + private final ThirdDingtalkConversationService thirdDingtalkConversationService; + + @Override + public String[] getKeywords() { + return KEYWORD; + } + + /** + * 主要功能如下: + * 1. 单纯注册会话 + * 2. 注册会话并绑定后端服务 + * 3. 会话重绑定后端服务 + * + * @param chatbotMessage + * @return + */ + @Override + public ReplyMessage process(ChatbotMessage chatbotMessage) { + ThirdDingtalkConversation conversation = new ThirdDingtalkConversation(); + BeanUtil.copyProperties(chatbotMessage, conversation); + + String replyText = String.format("注册成功,但未关联服务。\r\n如需请@机器发送“注册 [服务名]”即可(示例:注册 pudge)。\r\n当前的会话 ID:%s", conversation.getConversationId()); + String content = chatbotMessage.getText().getContent(); + String applicationName; + if (StringUtils.hasText(applicationName = content.replaceAll(REGISTER, "").trim())) { + replyText = String.format("本群于" + applicationName + "服务关联成功。 \r\n本群的会话 ID:%s", conversation.getConversationId()); + conversation.setApplicationName(applicationName); + } + + // 进行机器人和会话的绑定注册 + thirdDingtalkConversationService.upsert(conversation); + return SampleText.from(replyText); + } +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/RobotMsgCallbackConsumer.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/RobotMsgCallbackConsumer.java index 5f6d9dd..491d701 100644 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/RobotMsgCallbackConsumer.java +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/RobotMsgCallbackConsumer.java @@ -1,8 +1,11 @@ package cn.axzo.riven.dingtalk.callback.robot; import cn.axzo.framework.jackson.utility.JSON; +import cn.axzo.riven.client.model.ReplyMessage; import cn.axzo.riven.dingtalk.callback.robot.strategy.RobotHandleStrategy; +import cn.axzo.riven.dingtalk.reply.RobotReply; import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; +import cn.hutool.core.date.DateUtil; import com.dingtalk.open.app.api.callback.OpenDingTalkCallbackListener; import com.dingtalk.open.app.api.models.bot.ChatbotMessage; import lombok.extern.slf4j.Slf4j; @@ -21,7 +24,9 @@ import java.util.List; @Component public class RobotMsgCallbackConsumer implements OpenDingTalkCallbackListener { @Resource - private List robotHandleStrategies; + private List> robotHandleStrategies; + @Resource + private RobotReply robotReply; /** * 执行回调 @@ -34,15 +39,20 @@ public class RobotMsgCallbackConsumer implements OpenDingTalkCallbackListener s.support(keyword)) .findFirst() .ifPresent(handle -> { - Object result = handle.handle(message); - if(result instanceof ReplyContext) { - ReplyContext replyContext = (ReplyContext) result; - } + ReplyContext context = ReplyContext.builder() + .conversationId(message.getConversationId()) + .sessionWebhook(message.getSessionWebhook()) + .sessionWebhookExpiredDate(DateUtil.date(message.getSessionWebhookExpiredTime())) + .replyMessage(handle.handle(message)) + .build(); + robotReply.reply(context); }); return null; } diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/AbstractRobotHandleStrategy.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/AbstractRobotHandleStrategy.java index 89be663..f386401 100644 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/AbstractRobotHandleStrategy.java +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/AbstractRobotHandleStrategy.java @@ -1,10 +1,75 @@ package cn.axzo.riven.dingtalk.callback.robot.strategy; +import cn.axzo.framework.domain.ServiceException; +import cn.axzo.framework.jackson.utility.JSON; +import cn.axzo.riven.client.model.ReplyMessage; +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkMessageRecord; +import cn.axzo.riven.dingtalk.service.ThirdDingtalkMessageRecordService; +import cn.hutool.core.date.DateUtil; +import com.dingtalk.open.app.api.models.bot.ChatbotMessage; + +import javax.annotation.Resource; +import java.util.Objects; + /** * 抽象的机器人监听消息的处理策略 * * @author wangli * @since 2024-10-23 17:35 */ -public abstract class AbstractRobotHandleStrategy implements RobotHandleStrategy { +public abstract class AbstractRobotHandleStrategy implements RobotHandleStrategy { + @Resource + private ThirdDingtalkMessageRecordService thirdDingtalkMessageRecordService; + + protected final String getContent(ChatbotMessage chatbotMessage) { + if (Objects.nonNull(chatbotMessage)) { + return chatbotMessage.getText().getContent(); + } + throw new ServiceException("获取用户发送的消息内容失败"); + } + + @Override + public ReplyMessage handle(ChatbotMessage chatbotMessage) { + // 记录日志 + ThirdDingtalkMessageRecord record = insertLog(chatbotMessage); + + // 后续处理 + ReplyMessage replyMessage = doHandle(chatbotMessage); + + // 更新日志 + updateLog(record, replyMessage); + return replyMessage; + } + + /** + * 更新日志的响应信息 + * + * @param record + * @param replyMessage + */ + private void updateLog(ThirdDingtalkMessageRecord record, ReplyMessage replyMessage) { + // TODO + } + + /** + * 记录日志 + * + * @param chatbotMessage + * @return + */ + private ThirdDingtalkMessageRecord insertLog(ChatbotMessage chatbotMessage) { + ThirdDingtalkMessageRecord record = new ThirdDingtalkMessageRecord(); + record.setConversationId(chatbotMessage.getConversationId()); + record.setConversationTitle(chatbotMessage.getConversationTitle()); + record.setConversationType(chatbotMessage.getConversationType()); + record.setMsgId(chatbotMessage.getMsgId()); + record.setSenderNick(chatbotMessage.getSenderNick()); + record.setSessionWebhook(chatbotMessage.getSessionWebhook()); + record.setSessionWebhookExpiredTime(DateUtil.date(chatbotMessage.getSessionWebhookExpiredTime())); + record.setHandleType(this.getClass().getSimpleName()); + record.setRequestContent(JSON.toJSONString(chatbotMessage)); + return thirdDingtalkMessageRecordService.insert(record); + } + + protected abstract ReplyMessage doHandle(ChatbotMessage chatbotMessage); } diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/DefaultTransferToMQStrategy.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/DefaultTransferToMQStrategy.java deleted file mode 100644 index 5e9b6b0..0000000 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/DefaultTransferToMQStrategy.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.axzo.riven.dingtalk.callback.robot.strategy.impl; - -import cn.axzo.riven.dingtalk.callback.robot.strategy.AbstractRobotHandleStrategy; -import com.dingtalk.open.app.api.models.bot.ChatbotMessage; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - -import java.util.Objects; - -/** - * 默认的将群消息通过 MQ 广播也所有业务方 - * - * @author wangli - * @since 2024-10-23 18:13 - */ -@Component -public class DefaultTransferToMQStrategy extends AbstractRobotHandleStrategy { - @Override - public boolean support(String keyword) { - return StringUtils.hasText(keyword); - } - - @Override - public String handle(ChatbotMessage chatbotMessage) { - // TODO 转发给 MQ 中去 - return ""; - } -} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/KeywordProcessorStrategy.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/KeywordProcessorStrategy.java new file mode 100644 index 0000000..6bb4780 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/KeywordProcessorStrategy.java @@ -0,0 +1,87 @@ +package cn.axzo.riven.dingtalk.callback.robot.strategy.impl; + +import cn.axzo.riven.client.model.ReplyMessage; +import cn.axzo.riven.dingtalk.callback.keyword.DefaultKeywordProcessor; +import cn.axzo.riven.dingtalk.callback.keyword.KeywordProcessor; +import cn.axzo.riven.dingtalk.callback.robot.strategy.AbstractRobotHandleStrategy; +import com.dingtalk.open.app.api.models.bot.ChatbotMessage; +import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Map; + +/** + * 注册会话 + *

+ * 新创建的群,添加机器人后,在群内@机器人并发送【注册】两个字,即可完成注册。 + * + * @author wangli + * @since 2024-10-23 17:30 + */ +@Slf4j +@Component +@AllArgsConstructor +public class KeywordProcessorStrategy extends AbstractRobotHandleStrategy { + + // 容器中所有实现了的关键词 Spring's Bean + private final List keywordProcessorHandles; + + // 关键词对应的处理 Bean Map + private Map keywordMap; + // 具体的关键词 + private List words; + + @PostConstruct + public void init() { + keywordProcessorHandles.forEach(bean -> { + for (String keyword : bean.getKeywords()) { + words.addAll(Lists.newArrayList(bean.getKeywords())); + keywordMap.put(keyword, bean); + } + }); + } + + @Override + public boolean support(String keyword) { + if (!StringUtils.hasText(keyword)) { + return false; + } + for (String word : words) { + if (keyword.trim().startsWith(word)) { + return true; + } + } + return false; + } + + @Override + public ReplyMessage doHandle(ChatbotMessage chatbotMessage) { + if (log.isDebugEnabled()) { + log.debug(" KeywordProcessorStrategy Entrance "); + } + + KeywordProcessor keywordProcessor = getKeywordHandle(chatbotMessage); + + return keywordProcessor.process(chatbotMessage); + } + + private KeywordProcessor getKeywordHandle(ChatbotMessage chatbotMessage) { + String command = chatbotMessage.getText().getContent().trim(); + for (String word : words) { + if (command.startsWith(word)) { + return keywordMap.get(word); + } + } + return new DefaultKeywordProcessor(); + } + + @Override + public int getOrder() { + return Integer.MIN_VALUE + 2; + } +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/RegisterConversationStrategy.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/RegisterConversationStrategy.java deleted file mode 100644 index e3e1f96..0000000 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/RegisterConversationStrategy.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.axzo.riven.dingtalk.callback.robot.strategy.impl; - -import cn.axzo.riven.dingtalk.callback.robot.strategy.AbstractRobotHandleStrategy; -import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkConversation; -import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; -import cn.axzo.riven.dingtalk.service.ThirdDingtalkConversationService; -import com.dingtalk.open.app.api.models.bot.ChatbotMessage; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeanUtils; -import org.springframework.stereotype.Component; - -import java.util.Objects; - -import static cn.axzo.riven.dingtalk.constant.DingTalkConstant.REGISTER; - -/** - * 注册会话 - *

- * 新创建的群,添加机器人后,在群内@机器人并发送【注册】两个字,即可完成注册。 - * - * @author wangli - * @since 2024-10-23 17:30 - */ -@Slf4j -@Component -@AllArgsConstructor -public class RegisterConversationStrategy extends AbstractRobotHandleStrategy { - - private final ThirdDingtalkConversationService thirdDingtalkConversationService; - - @Override - public boolean support(String keyword) { - return Objects.equals(keyword, REGISTER); - } - - @Override - public ReplyContext handle(ChatbotMessage chatbotMessage) { - if (log.isDebugEnabled()) { - log.debug(" RegisterConversationStrategy Entrance "); - } - - ThirdDingtalkConversation conversation = new ThirdDingtalkConversation(); - BeanUtils.copyProperties(chatbotMessage, conversation); - - // 进行机器人和会话的绑定注册 - thirdDingtalkConversationService.upsert(conversation); - return ReplyContext.from(chatbotMessage.getSessionWebhook(), String.format("注册成功,当前的会话 ID:%s", conversation.getConversationId())); - } - - @Override - public int getOrder() { - return Integer.MIN_VALUE + 2; - } -} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/TransferToMQStrategy.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/TransferToMQStrategy.java new file mode 100644 index 0000000..a357545 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/TransferToMQStrategy.java @@ -0,0 +1,85 @@ +package cn.axzo.riven.dingtalk.callback.robot.strategy.impl; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.RocketMQEventProducer; +import cn.axzo.riven.client.common.enums.DingtalkEventEnum; +import cn.axzo.riven.client.model.DingtalkReceiveMqModel; +import cn.axzo.riven.client.model.ReplyMessage; +import cn.axzo.riven.client.model.SampleMessageQueue; +import cn.axzo.riven.client.model.SampleText; +import cn.axzo.riven.client.req.ThirdDingtalkConversationReq; +import cn.axzo.riven.dingtalk.callback.robot.strategy.AbstractRobotHandleStrategy; +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkConversation; +import cn.axzo.riven.dingtalk.service.ThirdDingtalkConversationService; +import com.dingtalk.open.app.api.models.bot.ChatbotMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 默认的将群消息通过 MQ 广播也所有业务方 + * + * @author wangli + * @since 2024-10-23 18:13 + */ +@Slf4j +@Component +public class TransferToMQStrategy extends AbstractRobotHandleStrategy { + @Resource + private RocketMQEventProducer producer; + @Resource + private ThirdDingtalkConversationService thirdDingtalkConversationService; + private final Map conversationMap = new HashMap<>(); + + @PostConstruct + public void init() { + conversationMap.putAll(thirdDingtalkConversationService.genericQuery(new ThirdDingtalkConversationReq()) + .stream() + .collect(Collectors.toMap(ThirdDingtalkConversation::getConversationId, Function.identity(), (s, t) -> s))); + } + + @Override + public boolean support(String keyword) { + return StringUtils.hasText(keyword); + } + + public ReplyMessage doHandle(ChatbotMessage chatbotMessage) { + // TODO 转发给 MQ 中去 + if (log.isDebugEnabled()) { + log.debug(" DefaultTransferToMQStrategy Entrance "); + } + + ThirdDingtalkConversation conversation = checkConversationExists(chatbotMessage); + if (Objects.isNull(conversation)) { + return SampleText.from("这里应该提示用法"); + } + + SampleMessageQueue messageQueue = SampleMessageQueue.from(conversation.getApplicationName(), getContent(chatbotMessage)); + if (log.isDebugEnabled()) { + log.debug("发送 MQ 的消息数据:{}", messageQueue.toJson()); + } + + DingtalkReceiveMqModel model = new DingtalkReceiveMqModel(); + + producer.send(Event.builder() + .shardingKey(chatbotMessage.getConversationId()) + .eventCode(DingtalkEventEnum.receive.getEventCode()) + .targetId(messageQueue.getTraceId())// traceId + .targetType(chatbotMessage.getConversationId()) + .data(model) + .build()); + return messageQueue; + } + + private ThirdDingtalkConversation checkConversationExists(ChatbotMessage chatbotMessage) { + return conversationMap.getOrDefault(chatbotMessage.getConversationId(), null); + } +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/RobotReply.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/RobotReply.java new file mode 100644 index 0000000..83b7ad3 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/RobotReply.java @@ -0,0 +1,51 @@ +package cn.axzo.riven.dingtalk.reply; + +import cn.axzo.framework.domain.ServiceException; +import cn.axzo.framework.jackson.utility.JSON; +import cn.axzo.riven.dingtalk.reply.strategy.RobotReplyStrategy; +import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Objects; + +import static cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum.messageQueue; + +/** + * 机器人回复的入口 + * + * @author wangli + * @since 2024-10-24 10:11 + */ +@Component +public class RobotReply { + @Resource + private List robotReplyStrategies; + + /** + * 回复消息 + * + * @param replyContext + */ + public void reply(ReplyContext replyContext) { + if (Objects.isNull(replyContext)) { + return; + } + if (Objects.equals(messageQueue, replyContext.getReplyMessage().msgType())) { + // 通过 MQ 转发给其他后端,这里统一不在回复 + return; + } + beforeCheck(replyContext); + robotReplyStrategies.forEach(strategy -> strategy.reply(replyContext)); + } + + private void beforeCheck(ReplyContext replyContext) { + boolean useConversation = StringUtils.hasText(replyContext.getConversationId()); + boolean useWebhook = StringUtils.hasText(replyContext.getSessionWebhook()) && Objects.nonNull(replyContext.getSessionWebhookExpiredDate()); + if (!useConversation && !useWebhook) { + throw new ServiceException(String.format("构建的回复上下文缺失参数:%s", JSON.toJSONString(replyContext))); + } + } +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/AbstractRobotReply.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/AbstractRobotReply.java new file mode 100644 index 0000000..171d830 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/AbstractRobotReply.java @@ -0,0 +1,24 @@ +package cn.axzo.riven.dingtalk.reply.strategy; + +import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; + +/** + * 抽象的机器人回复 + * + * @author wangli + * @since 2024-10-24 10:21 + */ +public abstract class AbstractRobotReply implements RobotReplyStrategy { + + @Override + public void reply(ReplyContext replyContext) { + if(doFilter(replyContext)) { + doReply(replyContext); + } + } + + abstract void doReply(ReplyContext replyContext); + + abstract boolean doFilter(ReplyContext replyContext); + +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedHttpReply.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedHttpReply.java index 9797bc0..3d17926 100644 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedHttpReply.java +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedHttpReply.java @@ -1,5 +1,8 @@ package cn.axzo.riven.dingtalk.reply.strategy; +import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; +import org.springframework.stereotype.Component; + /** * 基于 Http Api 的回复 * https://github.com/open-dingtalk/dingtalk-stream-sdk-java-quick-start/blob/main/src/main/java/org/example/service/RobotGroupMessagesService.java @@ -8,5 +11,20 @@ package cn.axzo.riven.dingtalk.reply.strategy; * @author wangli * @since 2024-10-23 17:28 */ -public class BasedHttpReply { +@Component +public class BasedHttpReply extends AbstractRobotReply { + @Override + public int getOrder() { + return Integer.MIN_VALUE + 1; + } + + @Override + void doReply(ReplyContext replyContext) { + + } + + @Override + boolean doFilter(ReplyContext replyContext) { + return false; + } } diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedSessionWebhookReply.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedSessionWebhookReply.java index 60bd1f4..3d21295 100644 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedSessionWebhookReply.java +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedSessionWebhookReply.java @@ -1,9 +1,18 @@ package cn.axzo.riven.dingtalk.reply.strategy; +import cn.axzo.riven.client.model.ReplyMessage; +import cn.axzo.riven.client.model.SampleMarkdown; +import cn.axzo.riven.client.model.SampleText; import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; import com.dingtalk.open.app.api.chatbot.BotReplier; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import javax.annotation.Resource; import java.io.IOException; +import java.util.Date; +import java.util.Objects; /** * 基于 Session Webhook 的回复 @@ -11,15 +20,49 @@ import java.io.IOException; * @author wangli * @since 2024-10-23 17:28 */ -public class BasedSessionWebhookReply { +@Slf4j +@Component +public class BasedSessionWebhookReply extends AbstractRobotReply { + @Resource + private BasedHttpReply basedHttpReply; - public void reply(ReplyContext replyContext) { + @Override + public int getOrder() { + return Integer.MIN_VALUE; + } + + @Override + void doReply(ReplyContext replyContext) { try { // just reply generic text - BotReplier.fromWebhook(replyContext.getSessionWebhook()).replyText(replyContext.getMsgContent()); + BotReplier botReplier = BotReplier.fromWebhook(replyContext.getSessionWebhook()); + + ReplyMessage replyMessage = replyContext.getReplyMessage(); + switch (replyMessage.msgType()) { + case sampleText: + SampleText text = (SampleText) replyMessage.messageBody(); + botReplier.replyText(text.getContent(), replyContext.getAtUserId()); + break; + case sampleMarkdown: + SampleMarkdown markdown = (SampleMarkdown) replyMessage.messageBody(); + botReplier.replyMarkdown(markdown.getTitle(), markdown.getText(), replyContext.getAtUserId()); + break; + default: + // current reply just supported text and markdown + botReplier.replyText(replyMessage.toJson(), replyContext.getAtUserId()); + break; + } } catch (IOException e) { - throw new RuntimeException(e); + log.warn("通过 sessionWebhook 回复发生异常,将通过 HTTP API 回复"); + basedHttpReply.doReply(replyContext); } } + @Override + boolean doFilter(ReplyContext replyContext) { + return Objects.nonNull(replyContext.getSessionWebhook()) && Objects.nonNull(replyContext.getSessionWebhookExpiredDate()) + && StringUtils.hasText(replyContext.getSessionWebhook()) + && new Date().before(replyContext.getSessionWebhookExpiredDate()); + } + } diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/RobotReplyStrategy.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/RobotReplyStrategy.java similarity index 50% rename from riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/RobotReplyStrategy.java rename to riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/RobotReplyStrategy.java index 203762b..109b107 100644 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/RobotReplyStrategy.java +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/RobotReplyStrategy.java @@ -1,14 +1,15 @@ -package cn.axzo.riven.dingtalk.reply; +package cn.axzo.riven.dingtalk.reply.strategy; import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; +import org.springframework.core.Ordered; /** - * 回复机器人 + * 回复机器人的策略 * * @author wangli * @since 2024-10-23 20:37 */ -public interface RobotReplyStrategy { +public interface RobotReplyStrategy extends Ordered { void reply(ReplyContext replyContext); } diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkConversation.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkConversation.java index 85d0e73..4d44407 100644 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkConversation.java +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkConversation.java @@ -31,7 +31,7 @@ public class ThirdDingtalkConversation extends BaseEntity atUserId; - private ReplyContext(String sessionWebhook, DingTalkMsgTypeEnum msgType, String msgContent, JSONObject data) { - this.sessionWebhook = sessionWebhook; - this.msgType = msgType; - this.msgContent = msgContent; - this.data = data; - } - public static ReplyContext from(String msgContent) { - return new ReplyContext(null, DingTalkMsgTypeEnum.sampleText, msgContent, null); - } - - public static ReplyContext from(String sessionWebhook, String msgContent) { - return new ReplyContext(sessionWebhook, DingTalkMsgTypeEnum.sampleText, msgContent, null); - } - - public static ReplyContext from(DingTalkMsgTypeEnum msgType, JSONObject data) { - return new ReplyContext(null, msgType, null, data); - } - - public static ReplyContext from(String sessionWebhook, DingTalkMsgTypeEnum msgType, JSONObject data) { - return new ReplyContext(sessionWebhook, msgType, null, data); - } } diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkConversationService.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkConversationService.java index 6bba118..b6e91fd 100644 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkConversationService.java +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkConversationService.java @@ -1,7 +1,10 @@ package cn.axzo.riven.dingtalk.service; +import cn.axzo.riven.client.req.ThirdDingtalkConversationReq; import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkConversation; +import java.util.List; + /** * 三方钉钉群会话信息 * @@ -15,4 +18,6 @@ public interface ThirdDingtalkConversationService { * @param conversation */ void upsert(ThirdDingtalkConversation conversation); + + List genericQuery(ThirdDingtalkConversationReq req); } diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkMessageRecordService.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkMessageRecordService.java index b9f5f6e..464f4b9 100644 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkMessageRecordService.java +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkMessageRecordService.java @@ -1,5 +1,7 @@ package cn.axzo.riven.dingtalk.service; +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkMessageRecord; + /** * 三方钉钉群消息记录 * @@ -7,4 +9,5 @@ package cn.axzo.riven.dingtalk.service; * @since 2024-10-23 19:48 */ public interface ThirdDingtalkMessageRecordService { + ThirdDingtalkMessageRecord insert(ThirdDingtalkMessageRecord record); } diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkConversationServiceImpl.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkConversationServiceImpl.java index 54edabc..96310bb 100644 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkConversationServiceImpl.java +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkConversationServiceImpl.java @@ -1,14 +1,21 @@ package cn.axzo.riven.dingtalk.service.impl; +import cn.axzo.riven.client.common.enums.CommonStatusEnum; +import cn.axzo.riven.client.common.enums.sync.Channel; +import cn.axzo.riven.client.req.ThirdApplicationReq; +import cn.axzo.riven.client.req.ThirdDingtalkConversationReq; +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkConversation; import cn.axzo.riven.dingtalk.repository.mapper.ThirdDingtalkConversationMapper; -import cn.axzo.riven.dingtalk.repository.mapper.ThirdDingtalkMessageRecordMapper; import cn.axzo.riven.dingtalk.service.ThirdDingtalkConversationService; +import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.AllArgsConstructor; -import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -24,14 +31,30 @@ public class ThirdDingtalkConversationServiceImpl implements ThirdDingtalkConver @Override public void upsert(ThirdDingtalkConversation conversation) { - if (Objects.nonNull(conversation.getConversationId())) { - ThirdDingtalkConversation oldEntity = thirdDingtalkConversationMapper - .selectOne(new LambdaQueryWrapper() - .eq(ThirdDingtalkConversation::getConversationId, conversation.getConversationId())); - BeanUtils.copyProperties(conversation, oldEntity); + ThirdDingtalkConversation oldEntity = thirdDingtalkConversationMapper + .selectOne(new LambdaQueryWrapper() + .eq(ThirdDingtalkConversation::getConversationId, conversation.getConversationId())); + if (Objects.nonNull(oldEntity)) { + BeanUtil.copyProperties(conversation, oldEntity, "id", "idDelete", "createAt", "updateAt"); thirdDingtalkConversationMapper.updateById(oldEntity); } else { thirdDingtalkConversationMapper.insert(conversation); } } + + @Override + public List genericQuery(ThirdDingtalkConversationReq req) { + return thirdDingtalkConversationMapper.selectList(buildQueryWrapper(req)); + } + + private LambdaQueryWrapper buildQueryWrapper(ThirdDingtalkConversationReq req) { + return new LambdaQueryWrapper() + .eq(StringUtils.hasText(req.getConversationId()), ThirdDingtalkConversation::getConversationId, req.getConversationId()) + .like(StringUtils.hasText(req.getConversationTitle()), ThirdDingtalkConversation::getConversationTitle, req.getConversationTitle()) + .eq(StringUtils.hasText(req.getConversationType()), ThirdDingtalkConversation::getConversationType, req.getConversationType()) + .eq(StringUtils.hasText(req.getChatbotCorpId()), ThirdDingtalkConversation::getChatbotCorpId, req.getChatbotCorpId()) + .eq(StringUtils.hasText(req.getChatbotUserId()), ThirdDingtalkConversation::getChatbotUserId, req.getChatbotUserId()) + .eq(StringUtils.hasText(req.getApplicationName()), ThirdDingtalkConversation::getApplicationName, req.getApplicationName()) + ; + } } diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkMessageRecordServiceImpl.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkMessageRecordServiceImpl.java index d24cf72..b4c69d8 100644 --- a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkMessageRecordServiceImpl.java +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkMessageRecordServiceImpl.java @@ -1,5 +1,7 @@ package cn.axzo.riven.dingtalk.service.impl; +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkMessageRecord; +import cn.axzo.riven.dingtalk.repository.mapper.ThirdDingtalkMessageRecordMapper; import cn.axzo.riven.dingtalk.service.ThirdDingtalkMessageRecordService; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; @@ -13,4 +15,11 @@ import org.springframework.stereotype.Service; @Service @AllArgsConstructor public class ThirdDingtalkMessageRecordServiceImpl implements ThirdDingtalkMessageRecordService { + private final ThirdDingtalkMessageRecordMapper thirdDingtalkMessageRecordMapper; + + @Override + public ThirdDingtalkMessageRecord insert(ThirdDingtalkMessageRecord record) { + thirdDingtalkMessageRecordMapper.insert(record); + return record; + } } diff --git a/riven-dingtalk/src/main/resources/sql/DDL.sql b/riven-dingtalk/src/main/resources/sql/DDL.sql index 94e52d6..fde9a82 100644 --- a/riven-dingtalk/src/main/resources/sql/DDL.sql +++ b/riven-dingtalk/src/main/resources/sql/DDL.sql @@ -22,31 +22,35 @@ insert into third_application value (1, 'dingtalk', '多群公共机器人', ' create table third_dingtalk_conversation ( - id bigint not null auto_increment comment '主键' + id bigint not null auto_increment comment '主键' primary key, - conversation_id varchar(255) not null comment '会话ID', - conversation_title varchar(255) not null comment '会话名称', - conversation_type tinyint(1) not null comment '会话类型 1:单聊 2:群聊', - chatbot_corp_id varchar(255) not null comment '机器人所属企业', - chatbot_user_id varchar(255) not null comment '机器人用户ID', - create_at datetime default CURRENT_TIMESTAMP not null comment '创建时间', - update_at datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', - is_delete bigint default 0 not null comment '删除标志' + conversation_id varchar(255) not null comment '会话ID', + conversation_title varchar(255) not null comment '会话名称', + conversation_type varchar(1) not null comment '会话类型 1:单聊 2:群聊', + chatbot_corp_id varchar(255) not null comment '机器人所属企业', + chatbot_user_id varchar(255) not null comment '机器人用户ID', + application_name varchar(255) default '' not null comment '会话关联的应用名称', + create_at datetime default CURRENT_TIMESTAMP not null comment '创建时间', + update_at datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', + is_delete bigint default 0 not null comment '删除标志' ) comment '会话信息'; create table third_dingtalk_msg_record ( - id bigint auto_increment comment '主键' + id bigint auto_increment comment '主键' primary key, - conversation_id varchar(255) not null comment '会话ID', - conversation_title varchar(255) not null comment '会话名称', - conversation_type tinyint(1) not null comment '会话类型 1:单聊 2:群聊', - msg_id varchar(255) not null comment '消息ID', - sender_nick varchar(255) not null comment '发送人昵称', - session_webhook varchar(255) not null comment '会话webhook url', - session_webhook_expire_time datetime not null comment '会话webhook url过期时间', - request_content varchar(255) not null comment '请求内容', - handle_type varchar(64) not null comment '处理类型', - response_content varchar(4000) default '' not null comment '响应内容', - response_msg_type varchar(255) default 'sampleText' not null comment '响应消息类型' + conversation_id varchar(255) not null comment '会话ID', + conversation_title varchar(255) not null comment '会话名称', + conversation_type varchar(1) not null comment '会话类型 1:单聊 2:群聊', + msg_id varchar(255) not null comment '消息ID', + sender_nick varchar(64) not null comment '发送人昵称', + session_webhook varchar(512) not null comment '会话webhook url', + session_webhook_expired_time datetime not null comment '会话webhook url过期时间', + request_content varchar(4000) not null comment '请求内容', + handle_type varchar(64) not null comment '处理类型', + response_content varchar(4000) default '' not null comment '响应内容', + response_msg_type varchar(32) default '' not null comment '响应消息类型', + create_at datetime default CURRENT_TIMESTAMP not null comment '创建时间', + update_at datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', + is_delete bigint default 0 not null comment '删除标志' ) comment '钉钉群消息记录'; \ No newline at end of file