diff --git a/pom.xml b/pom.xml index cebf035..83679bd 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ 1.4.2.Final 2.0.0-SNAPSHOT 11.8 + 1.3.7 @@ -28,6 +29,7 @@ riven-api integration-test riven-third + riven-dingtalk @@ -52,6 +54,11 @@ feign-httpclient ${feign-httpclient.version} + + com.dingtalk.open + app-stream-client + ${dingtalk.stream.version} + diff --git a/riven-api/src/main/java/cn/axzo/riven/client/common/enums/CommonStatusEnum.java b/riven-api/src/main/java/cn/axzo/riven/client/common/enums/CommonStatusEnum.java new file mode 100644 index 0000000..d375e47 --- /dev/null +++ b/riven-api/src/main/java/cn/axzo/riven/client/common/enums/CommonStatusEnum.java @@ -0,0 +1,28 @@ +package cn.axzo.riven.client.common.enums; + +import lombok.Getter; + +/** + * 通用的状态枚举 + * + * @author wangli + * @since 2024-10-23 14:33 + */ +@Getter +public enum CommonStatusEnum { + DISABLED(0, "DISABLED", "停用"), + ENABLED(1, "ENABLED", "启用"), + SUSPENDED(2, "SUSPENDED", "挂起") + ; + + private final int code; + private final String type; + private final String desc; + + CommonStatusEnum(int code, String type, String description) { + this.code = code; + this.type = type; + this.desc = description; + } + +} 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 new file mode 100644 index 0000000..34669b3 --- /dev/null +++ b/riven-api/src/main/java/cn/axzo/riven/client/common/enums/DingTalkMsgTypeEnum.java @@ -0,0 +1,24 @@ +package cn.axzo.riven.client.common.enums; + +/** + * 本类的信息来自于 {@see https://open.dingtalk.com/document/orgapp/types-of-messages-sent-by-robots} + * + * @author wangli + * @since 2024-10-23 20:13 + */ +public enum DingTalkMsgTypeEnum { + sampleText, + sampleMarkdown, + sampleImageMsg, + sampleLink, + sampleActionCard, + sampleActionCard2, + sampleActionCard3, + sampleActionCard4, + sampleActionCard5, + sampleActionCard6, + sampleAudio, + sampleFile, + sampleVideo, + ; +} diff --git a/riven-api/src/main/java/cn/axzo/riven/client/req/ThirdApplicationReq.java b/riven-api/src/main/java/cn/axzo/riven/client/req/ThirdApplicationReq.java new file mode 100644 index 0000000..953215c --- /dev/null +++ b/riven-api/src/main/java/cn/axzo/riven/client/req/ThirdApplicationReq.java @@ -0,0 +1,43 @@ +package cn.axzo.riven.client.req; + +import cn.axzo.riven.client.common.enums.CommonStatusEnum; +import cn.axzo.riven.client.common.enums.sync.Channel; +import lombok.Data; + +/** + * 三方应用通用查询入参 + * + * @author wangli + * @since 2024-10-23 14:30 + */ +@Data +public class ThirdApplicationReq { + + /** + * 渠道 + */ + private Channel channel = Channel.DingTalk; + + /** + * 应用名称 + */ + private String name; + + /** + * 应用描述 + */ + private String description; + + private String appId; + + private String agentId; + + private String clientId; + + private String clientSecret; + + /** + * 状态 + */ + private CommonStatusEnum status = CommonStatusEnum.ENABLED; +} diff --git a/riven-dingtalk/pom.xml b/riven-dingtalk/pom.xml new file mode 100644 index 0000000..7583ea4 --- /dev/null +++ b/riven-dingtalk/pom.xml @@ -0,0 +1,27 @@ + + 4.0.0 + + riven + cn.axzo + ${revision} + ../pom.xml + + riven-dingtalk + jar + riven-dingtalk + + + cn.axzo + riven-api + + + com.dingtalk.open + app-stream-client + + + cn.axzo.framework.data + axzo-data-mybatis-plus + + + 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 new file mode 100644 index 0000000..5f6d9dd --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/RobotMsgCallbackConsumer.java @@ -0,0 +1,50 @@ +package cn.axzo.riven.dingtalk.callback.robot; + +import cn.axzo.framework.jackson.utility.JSON; +import cn.axzo.riven.dingtalk.callback.robot.strategy.RobotHandleStrategy; +import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; +import com.dingtalk.open.app.api.callback.OpenDingTalkCallbackListener; +import com.dingtalk.open.app.api.models.bot.ChatbotMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 机器人回调处理 + * + * @author wangli + * @since 2024-10-23 15:33 + */ +@Slf4j +@Component +public class RobotMsgCallbackConsumer implements OpenDingTalkCallbackListener { + @Resource + private List robotHandleStrategies; + + /** + * 执行回调 + * + * @param message + * @return + */ + @Override + public Void execute(ChatbotMessage message) { + if (log.isDebugEnabled()) { + log.debug("receive message : {}", JSON.toJSONString(message)); + } + String keyword = message.getText().getContent().trim(); + robotHandleStrategies.stream() + .filter(s -> s.support(keyword)) + .findFirst() + .ifPresent(handle -> { + Object result = handle.handle(message); + if(result instanceof ReplyContext) { + ReplyContext replyContext = (ReplyContext) result; + } + }); + 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 new file mode 100644 index 0000000..89be663 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/AbstractRobotHandleStrategy.java @@ -0,0 +1,10 @@ +package cn.axzo.riven.dingtalk.callback.robot.strategy; + +/** + * 抽象的机器人监听消息的处理策略 + * + * @author wangli + * @since 2024-10-23 17:35 + */ +public abstract class AbstractRobotHandleStrategy implements RobotHandleStrategy { +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/RobotHandleStrategy.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/RobotHandleStrategy.java new file mode 100644 index 0000000..d4853ba --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/RobotHandleStrategy.java @@ -0,0 +1,21 @@ +package cn.axzo.riven.dingtalk.callback.robot.strategy; + +import com.dingtalk.open.app.api.models.bot.ChatbotMessage; +import org.springframework.core.Ordered; + +/** + * 机器人处理消息的策略 + * + * @author wangli + * @since 2024-10-23 17:37 + */ +public interface RobotHandleStrategy extends Ordered { + @Override + default int getOrder() { + return 0; + } + + boolean support(String keyword); + + T handle(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 new file mode 100644 index 0000000..5e9b6b0 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/DefaultTransferToMQStrategy.java @@ -0,0 +1,28 @@ +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/RegisterConversationStrategy.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/RegisterConversationStrategy.java new file mode 100644 index 0000000..e3e1f96 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/callback/robot/strategy/impl/RegisterConversationStrategy.java @@ -0,0 +1,55 @@ +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/constant/DingTalkConstant.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/constant/DingTalkConstant.java new file mode 100644 index 0000000..7258a05 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/constant/DingTalkConstant.java @@ -0,0 +1,12 @@ +package cn.axzo.riven.dingtalk.constant; + +/** + * dingtalk 相关的常量 + * + * @author wangli + * @since 2024-10-23 17:44 + */ +public interface DingTalkConstant { + + String REGISTER = "注册"; +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/listener/DingTalkAllEventListener.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/listener/DingTalkAllEventListener.java new file mode 100644 index 0000000..7313654 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/listener/DingTalkAllEventListener.java @@ -0,0 +1,53 @@ +package cn.axzo.riven.dingtalk.listener; + +import com.dingtalk.open.app.api.GenericEventListener; +import com.dingtalk.open.app.api.message.GenericOpenDingTalkEvent; +import com.dingtalk.open.app.stream.protocol.event.EventAckStatus; +import lombok.extern.slf4j.Slf4j; +import shade.com.alibaba.fastjson2.JSON; +import shade.com.alibaba.fastjson2.JSONObject; + +/** + * Dingtalk 全量的事件监听 + * + * @author wangli + * @since 2024-10-23 15:04 + */ +@Slf4j +public class DingTalkAllEventListener implements GenericEventListener { + /** + * 收到事件 + * + * @param event + * @return + */ + @Override + public EventAckStatus onEvent(GenericOpenDingTalkEvent event) { + if (log.isDebugEnabled()) { + log.debug("receive dingtalk event: {}", JSON.toJSONString(event)); + } + try { + //事件唯一Id + String eventId = event.getEventId(); + //事件类型 + String eventType = event.getEventType(); + //事件产生时间 + Long bornTime = event.getEventBornTime(); + //获取事件体 + JSONObject bizData = event.getData(); + //处理事件 + process(bizData); + //消费成功 + return EventAckStatus.SUCCESS; + } catch (Exception e) { + //消费失败 + return EventAckStatus.LATER; + } + } + + private void process(JSONObject bizData) { + + } + + +} 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/RobotReplyStrategy.java new file mode 100644 index 0000000..203762b --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/RobotReplyStrategy.java @@ -0,0 +1,14 @@ +package cn.axzo.riven.dingtalk.reply; + +import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; + +/** + * 回复机器人 + * + * @author wangli + * @since 2024-10-23 20:37 + */ +public interface RobotReplyStrategy { + + void reply(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 new file mode 100644 index 0000000..9797bc0 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedHttpReply.java @@ -0,0 +1,12 @@ +package cn.axzo.riven.dingtalk.reply.strategy; + +/** + * 基于 Http Api 的回复 + * https://github.com/open-dingtalk/dingtalk-stream-sdk-java-quick-start/blob/main/src/main/java/org/example/service/RobotGroupMessagesService.java + * https://open.dingtalk.com/document/orgapp/types-of-messages-sent-by-robots + * + * @author wangli + * @since 2024-10-23 17:28 + */ +public class BasedHttpReply { +} 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 new file mode 100644 index 0000000..60bd1f4 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedSessionWebhookReply.java @@ -0,0 +1,25 @@ +package cn.axzo.riven.dingtalk.reply.strategy; + +import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; +import com.dingtalk.open.app.api.chatbot.BotReplier; + +import java.io.IOException; + +/** + * 基于 Session Webhook 的回复 + * + * @author wangli + * @since 2024-10-23 17:28 + */ +public class BasedSessionWebhookReply { + + public void reply(ReplyContext replyContext) { + try { + // just reply generic text + BotReplier.fromWebhook(replyContext.getSessionWebhook()).replyText(replyContext.getMsgContent()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdApplication.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdApplication.java new file mode 100644 index 0000000..4a0138e --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdApplication.java @@ -0,0 +1,61 @@ +package cn.axzo.riven.dingtalk.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * 应用基本信息 + * + * @author wangli + * @since 2024-10-23 14:03 + */ +@EqualsAndHashCode(callSuper = true) +@TableName(value = "third_application", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ThirdApplication extends BaseEntity { + /** + * 应用所属渠道 + */ + private String channel; + + /** + * 应用名称 + */ + private String name; + + /** + * 描述信息 + */ + private String description; + + /** + * 应用的 AppId + */ + private String appId; + + /** + * 应用的 AgentId + */ + private String agentId; + + /** + * ClientId + * (原 AppKey 和 SuiteKey) + */ + private String clientId; + + /** + * Client Secret + * (原 AppSecret 和 SuiteSecret) + */ + private String clientSecret; + + /** + * 状态 + */ + private Integer status; +} 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 new file mode 100644 index 0000000..85d0e73 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkConversation.java @@ -0,0 +1,45 @@ +package cn.axzo.riven.dingtalk.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * 三方钉钉群会话信息 + * + * @author wangli + * @since 2024-10-23 19:44 + */ +@EqualsAndHashCode(callSuper = true) +@TableName(value = "third_dingtalk_conversation", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ThirdDingtalkConversation extends BaseEntity { + /** + * 会话 ID=群 ID + */ + private String conversationId; + + /** + * 会话名称=群名称 + */ + private String conversationTitle; + /** + * 会话类型: + * 1:单聊 + * 2:群聊 + */ + private Integer conversationType; + + /** + * 机器人所属企业 + */ + private String chatbotCorpId; + + /** + * 机器人用户 ID(加密数据) + */ + private String chatbotUserId; +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkMessageRecord.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkMessageRecord.java new file mode 100644 index 0000000..3c47b1e --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkMessageRecord.java @@ -0,0 +1,72 @@ +package cn.axzo.riven.dingtalk.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +/** + * 三方钉钉群消息记录 + * + * @author wangli + * @since 2024-10-23 18:18 + */ +@EqualsAndHashCode(callSuper = true) +@TableName(value = "third_dingtalk_msg_record", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ThirdDingtalkMessageRecord extends BaseEntity { + /** + * 会话 ID=群 ID + */ + private String conversationId; + + /** + * 会话名称=群名称 + */ + private String conversationTitle; + + /** + * 这条消息的 ID + */ + private String msgId; + + /** + * 谁@的机器人发的消息 + */ + private String senderNick; + + /** + * 会话类型: + * 1:单聊 + * 2:群聊 + */ + private Integer conversationType; + + /** + * 会话级的 Webhook + */ + private String sessionWebhook; + + /** + * 会话级的 Webhook 过期时间 + */ + private Date sessionWebhookExpiredTime; + + /** + * 请求的内容 + */ + private String requestContext; + + /** + * 响应的内容 + */ + private String responseContext; + /** + * 响应的消息类型 + */ + private String responseMsgType; +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdApplicationMapper.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdApplicationMapper.java new file mode 100644 index 0000000..3981c20 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdApplicationMapper.java @@ -0,0 +1,15 @@ +package cn.axzo.riven.dingtalk.repository.mapper; + +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 应用基本信息 Mapper + * + * @author wangli + * @since 2024-10-23 14:03 + */ +@Mapper +public interface ThirdApplicationMapper extends BaseMapper { +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkConversationMapper.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkConversationMapper.java new file mode 100644 index 0000000..9780199 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkConversationMapper.java @@ -0,0 +1,16 @@ +package cn.axzo.riven.dingtalk.repository.mapper; + +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkConversation; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 应用基本信息 Mapper + * + * @author wangli + * @since 2024-10-23 14:03 + */ +@Mapper +public interface ThirdDingtalkConversationMapper extends BaseMapper { +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkMessageRecordMapper.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkMessageRecordMapper.java new file mode 100644 index 0000000..78582a0 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkMessageRecordMapper.java @@ -0,0 +1,16 @@ +package cn.axzo.riven.dingtalk.repository.mapper; + +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkMessageRecord; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 应用基本信息 Mapper + * + * @author wangli + * @since 2024-10-23 14:03 + */ +@Mapper +public interface ThirdDingtalkMessageRecordMapper extends BaseMapper { +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/AutoConnector.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/AutoConnector.java new file mode 100644 index 0000000..37e38a0 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/AutoConnector.java @@ -0,0 +1,75 @@ +package cn.axzo.riven.dingtalk.robot.connection; + +import cn.axzo.riven.client.req.ThirdApplicationReq; +import cn.axzo.riven.dingtalk.listener.DingTalkAllEventListener; +import cn.axzo.riven.dingtalk.callback.robot.RobotMsgCallbackConsumer; +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; +import cn.axzo.riven.dingtalk.service.ThirdApplicationService; +import com.alibaba.fastjson.JSON; +import com.dingtalk.open.app.api.OpenDingTalkClient; +import com.dingtalk.open.app.api.OpenDingTalkStreamClientBuilder; +import com.dingtalk.open.app.api.callback.DingTalkStreamTopics; +import com.dingtalk.open.app.api.security.AuthClientCredential; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.List; + + +/** + * 机器人应用自动连接 + * + * @author wangli + * @since 2024-10-23 14:18 + */ +@Slf4j +@Component +public class AutoConnector { + @Value("${third.dingtalk.auto-connect: true}") + private Boolean autoConnect; + @Resource + private ThirdApplicationService applicationService; + @Resource + private RobotMsgCallbackConsumer robotMsgCallbackConsumer; + + @PostConstruct + public void init() { + if (autoConnect) { + if (log.isDebugEnabled()) { + log.debug("auto connection robot by stream model"); + } + doConnection(); + } + } + + private void doConnection() { + List thirdApplications = applicationService.genericQuery(new ThirdApplicationReq()); + if (log.isDebugEnabled()) { + log.debug("application query result count: {}", thirdApplications.size()); + } + thirdApplications.forEach(application -> { + try { + OpenDingTalkClient client = OpenDingTalkStreamClientBuilder + .custom() + .credential(new AuthClientCredential(application.getClientId(), application.getClientSecret())) + // 注册机器人回调,固定值 + .registerCallbackListener(DingTalkStreamTopics.BOT_MESSAGE_TOPIC, robotMsgCallbackConsumer) + //注册事件监听 + .registerAllEventListener(new DingTalkAllEventListener()) + .build(); + client.start(); + if (log.isDebugEnabled()) { + log.debug("robot connect success, channel: {}, name: {}, other: {}", + application.getChannel(), application.getName(), JSON.toJSONString(application)); + } + } catch (Exception e) { + log.error("robot connect error, channel: {}, name: {}, other: {}, errorInfo: {}", + application.getChannel(), application.getName(), JSON.toJSONString(application), e.getMessage(), e); + } + }); + + } +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/model/ReplyContext.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/model/ReplyContext.java new file mode 100644 index 0000000..b014713 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/model/ReplyContext.java @@ -0,0 +1,60 @@ +package cn.axzo.riven.dingtalk.robot.connection.model; + +import cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum; +import com.alibaba.fastjson.JSONObject; +import lombok.Data; + +/** + * 统一的回复上下文模型 + * + * @author wangli + * @since 2024-10-23 20:11 + */ +@Data +public class ReplyContext { + + /** + * 会话级的 Webhook + */ + private String sessionWebhook; + + /** + * 消息类型 + */ + private DingTalkMsgTypeEnum msgType = DingTalkMsgTypeEnum.sampleText; + + /** + * 默认的消息类型下,可直接传入要发送的文本 + * 如果要使用其他 msgType,则用 data 属性进行传值 + */ + private String msgContent; + + /** + * 根据不同的 msgType 传入不同请求模型 + * 参考:{@see https://open.dingtalk.com/document/orgapp/types-of-messages-sent-by-robots} + */ + private JSONObject data; + + 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/ThirdApplicationService.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdApplicationService.java new file mode 100644 index 0000000..46c0337 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdApplicationService.java @@ -0,0 +1,23 @@ +package cn.axzo.riven.dingtalk.service; + +import cn.axzo.riven.client.req.ThirdApplicationReq; +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; + +import java.util.List; + +/** + * 三方应用信息 + * + * @author wangli + * @since 2024-10-23 14:15 + */ +public interface ThirdApplicationService { + + List genericQuery(ThirdApplicationReq req); + + ThirdApplication insert(ThirdApplication req); + + ThirdApplication update(ThirdApplication req); + + Integer delete(Long id); +} 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 new file mode 100644 index 0000000..6bba118 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkConversationService.java @@ -0,0 +1,18 @@ +package cn.axzo.riven.dingtalk.service; + +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkConversation; + +/** + * 三方钉钉群会话信息 + * + * @author wangli + * @since 2024-10-23 19:48 + */ +public interface ThirdDingtalkConversationService { + + /** + * insert or update + * @param conversation + */ + void upsert(ThirdDingtalkConversation conversation); +} 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 new file mode 100644 index 0000000..b9f5f6e --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkMessageRecordService.java @@ -0,0 +1,10 @@ +package cn.axzo.riven.dingtalk.service; + +/** + * 三方钉钉群消息记录 + * + * @author wangli + * @since 2024-10-23 19:48 + */ +public interface ThirdDingtalkMessageRecordService { +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdApplicationServiceImpl.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdApplicationServiceImpl.java new file mode 100644 index 0000000..d9fb78c --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdApplicationServiceImpl.java @@ -0,0 +1,62 @@ +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.dingtalk.repository.entity.ThirdApplication; +import cn.axzo.riven.dingtalk.repository.mapper.ThirdApplicationMapper; +import cn.axzo.riven.dingtalk.service.ThirdApplicationService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.util.List; +import java.util.Objects; + +/** + * ThirdParty Application Service Implementation + * + * @author wangli + * @since 2024-10-23 14:16 + */ +@Service +@AllArgsConstructor +public class ThirdApplicationServiceImpl implements ThirdApplicationService { + private final ThirdApplicationMapper thirdApplicationMapper; + + @Override + public List genericQuery(ThirdApplicationReq req) { + return thirdApplicationMapper.selectList(buildQueryWrapper(req)); + } + + @Override + public ThirdApplication insert(ThirdApplication entity) { + thirdApplicationMapper.insert(entity); + return entity; + } + + @Override + public ThirdApplication update(ThirdApplication entity) { + thirdApplicationMapper.updateById(entity); + return entity; + } + + @Override + public Integer delete(Long id) { + return thirdApplicationMapper.deleteById(id); + } + + private LambdaQueryWrapper buildQueryWrapper(ThirdApplicationReq req) { + return new LambdaQueryWrapper() + .eq(ThirdApplication::getChannel, Objects.nonNull(req.getChannel()) ? req.getChannel() : Channel.DingTalk) + .like(StringUtils.hasText(req.getName()), ThirdApplication::getName, req.getName()) + .like(StringUtils.hasText(req.getDescription()), ThirdApplication::getDescription, req.getDescription()) + .eq(StringUtils.hasText(req.getAppId()), ThirdApplication::getAppId, req.getAppId()) + .eq(StringUtils.hasText(req.getAgentId()), ThirdApplication::getAgentId, req.getAgentId()) + .eq(StringUtils.hasText(req.getClientId()), ThirdApplication::getClientId, req.getClientId()) + .eq(StringUtils.hasText(req.getClientSecret()), ThirdApplication::getClientSecret, req.getClientSecret()) + .eq(ThirdApplication::getStatus, Objects.nonNull(req.getStatus()) ? req.getStatus().getCode() : CommonStatusEnum.ENABLED.getCode()) + ; + } +} 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 new file mode 100644 index 0000000..54edabc --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkConversationServiceImpl.java @@ -0,0 +1,37 @@ +package cn.axzo.riven.dingtalk.service.impl; + +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 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.AllArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * 三方钉钉群会话信息 + * + * @author wangli + * @since 2024-10-23 19:48 + */ +@Service +@AllArgsConstructor +public class ThirdDingtalkConversationServiceImpl implements ThirdDingtalkConversationService { + private final ThirdDingtalkConversationMapper thirdDingtalkConversationMapper; + + @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); + thirdDingtalkConversationMapper.updateById(oldEntity); + } else { + thirdDingtalkConversationMapper.insert(conversation); + } + } +} 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 new file mode 100644 index 0000000..d24cf72 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkMessageRecordServiceImpl.java @@ -0,0 +1,16 @@ +package cn.axzo.riven.dingtalk.service.impl; + +import cn.axzo.riven.dingtalk.service.ThirdDingtalkMessageRecordService; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 三方钉钉群消息记录 + * + * @author wangli + * @since 2024-10-23 19:48 + */ +@Service +@AllArgsConstructor +public class ThirdDingtalkMessageRecordServiceImpl implements ThirdDingtalkMessageRecordService { +} diff --git a/riven-dingtalk/src/main/resources/sql/DDL.sql b/riven-dingtalk/src/main/resources/sql/DDL.sql new file mode 100644 index 0000000..94e52d6 --- /dev/null +++ b/riven-dingtalk/src/main/resources/sql/DDL.sql @@ -0,0 +1,52 @@ +create table third_application +( + id bigint not null auto_increment comment '主键' + primary key, + channel varchar(255) not null comment '渠道', + name varchar(255) not null comment '应用名称', + description varchar(255) not null comment '应用描述', + app_id varchar(255) not null comment 'appId', + agent_id varchar(255) not null comment 'agentId', + client_id varchar(255) not null comment 'clientId', + client_secret varchar(255) not null comment 'clientSecret', + status tinyint(1) default 1 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 '第三方应用'; + +insert into third_application value (1, 'dingtalk', '多群公共机器人', '多群公共机器人', + 'c26a6e00-dd35-4bb5-a68a-36455ce1c001', '3282101532', 'dingxga1f6ghiefbzovb', + 'b-SSalEZAoRCWodldDtrdHMKbtJVQpVWP8-x1FYgdSTzPKaGJCDT7RH1j_0d-IKO', 1, now(), + now(), 0); + +create table third_dingtalk_conversation +( + 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 '删除标志' +) comment '会话信息'; + +create table third_dingtalk_msg_record +( + 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 '响应消息类型' +) comment '钉钉群消息记录'; \ No newline at end of file diff --git a/riven-server/pom.xml b/riven-server/pom.xml index 10c78d6..3d5c6d8 100644 --- a/riven-server/pom.xml +++ b/riven-server/pom.xml @@ -36,6 +36,12 @@ ${project.version} + + cn.axzo + riven-dingtalk + ${project.version} + + io.github.openfeign feign-httpclient @@ -109,7 +115,6 @@ com.dingtalk.open app-stream-client - 1.1.0 com.aliyun diff --git a/riven-server/src/main/java/cn/axzo/riven/Application.java b/riven-server/src/main/java/cn/axzo/riven/Application.java index 793c8a2..ad8d3ba 100644 --- a/riven-server/src/main/java/cn/axzo/riven/Application.java +++ b/riven-server/src/main/java/cn/axzo/riven/Application.java @@ -11,6 +11,7 @@ import org.springframework.core.env.Environment; @Slf4j @SpringBootApplication(scanBasePackages = "cn.axzo") @EnableFeignClients(basePackages = {"cn.axzo"}) +@MapperScan({"cn.axzo.riven.dingtalk.**.mapper"}) public class Application { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(Application.class, args); diff --git a/riven-server/src/main/java/cn/axzo/riven/config/XxlJobConfig.java b/riven-server/src/main/java/cn/axzo/riven/config/XxlJobConfig.java index 5fa5ac9..30408c5 100644 --- a/riven-server/src/main/java/cn/axzo/riven/config/XxlJobConfig.java +++ b/riven-server/src/main/java/cn/axzo/riven/config/XxlJobConfig.java @@ -1,5 +1,6 @@ package cn.axzo.riven.config; +import cn.azxo.framework.common.annotation.OnlyPodsEnvironment; import cn.azxo.framework.common.logger.JobLoggerTemplate; import cn.azxo.framework.common.service.JobParamResolver; import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; @@ -13,6 +14,7 @@ import org.springframework.context.annotation.Configuration; * xxl-job config * */ +@OnlyPodsEnvironment @Configuration public class XxlJobConfig {
+ * 新创建的群,添加机器人后,在群内@机器人并发送【注册】两个字,即可完成注册。 + * + * @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/constant/DingTalkConstant.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/constant/DingTalkConstant.java new file mode 100644 index 0000000..7258a05 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/constant/DingTalkConstant.java @@ -0,0 +1,12 @@ +package cn.axzo.riven.dingtalk.constant; + +/** + * dingtalk 相关的常量 + * + * @author wangli + * @since 2024-10-23 17:44 + */ +public interface DingTalkConstant { + + String REGISTER = "注册"; +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/listener/DingTalkAllEventListener.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/listener/DingTalkAllEventListener.java new file mode 100644 index 0000000..7313654 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/listener/DingTalkAllEventListener.java @@ -0,0 +1,53 @@ +package cn.axzo.riven.dingtalk.listener; + +import com.dingtalk.open.app.api.GenericEventListener; +import com.dingtalk.open.app.api.message.GenericOpenDingTalkEvent; +import com.dingtalk.open.app.stream.protocol.event.EventAckStatus; +import lombok.extern.slf4j.Slf4j; +import shade.com.alibaba.fastjson2.JSON; +import shade.com.alibaba.fastjson2.JSONObject; + +/** + * Dingtalk 全量的事件监听 + * + * @author wangli + * @since 2024-10-23 15:04 + */ +@Slf4j +public class DingTalkAllEventListener implements GenericEventListener { + /** + * 收到事件 + * + * @param event + * @return + */ + @Override + public EventAckStatus onEvent(GenericOpenDingTalkEvent event) { + if (log.isDebugEnabled()) { + log.debug("receive dingtalk event: {}", JSON.toJSONString(event)); + } + try { + //事件唯一Id + String eventId = event.getEventId(); + //事件类型 + String eventType = event.getEventType(); + //事件产生时间 + Long bornTime = event.getEventBornTime(); + //获取事件体 + JSONObject bizData = event.getData(); + //处理事件 + process(bizData); + //消费成功 + return EventAckStatus.SUCCESS; + } catch (Exception e) { + //消费失败 + return EventAckStatus.LATER; + } + } + + private void process(JSONObject bizData) { + + } + + +} 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/RobotReplyStrategy.java new file mode 100644 index 0000000..203762b --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/RobotReplyStrategy.java @@ -0,0 +1,14 @@ +package cn.axzo.riven.dingtalk.reply; + +import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; + +/** + * 回复机器人 + * + * @author wangli + * @since 2024-10-23 20:37 + */ +public interface RobotReplyStrategy { + + void reply(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 new file mode 100644 index 0000000..9797bc0 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedHttpReply.java @@ -0,0 +1,12 @@ +package cn.axzo.riven.dingtalk.reply.strategy; + +/** + * 基于 Http Api 的回复 + * https://github.com/open-dingtalk/dingtalk-stream-sdk-java-quick-start/blob/main/src/main/java/org/example/service/RobotGroupMessagesService.java + * https://open.dingtalk.com/document/orgapp/types-of-messages-sent-by-robots + * + * @author wangli + * @since 2024-10-23 17:28 + */ +public class BasedHttpReply { +} 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 new file mode 100644 index 0000000..60bd1f4 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/reply/strategy/BasedSessionWebhookReply.java @@ -0,0 +1,25 @@ +package cn.axzo.riven.dingtalk.reply.strategy; + +import cn.axzo.riven.dingtalk.robot.connection.model.ReplyContext; +import com.dingtalk.open.app.api.chatbot.BotReplier; + +import java.io.IOException; + +/** + * 基于 Session Webhook 的回复 + * + * @author wangli + * @since 2024-10-23 17:28 + */ +public class BasedSessionWebhookReply { + + public void reply(ReplyContext replyContext) { + try { + // just reply generic text + BotReplier.fromWebhook(replyContext.getSessionWebhook()).replyText(replyContext.getMsgContent()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdApplication.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdApplication.java new file mode 100644 index 0000000..4a0138e --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdApplication.java @@ -0,0 +1,61 @@ +package cn.axzo.riven.dingtalk.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * 应用基本信息 + * + * @author wangli + * @since 2024-10-23 14:03 + */ +@EqualsAndHashCode(callSuper = true) +@TableName(value = "third_application", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ThirdApplication extends BaseEntity { + /** + * 应用所属渠道 + */ + private String channel; + + /** + * 应用名称 + */ + private String name; + + /** + * 描述信息 + */ + private String description; + + /** + * 应用的 AppId + */ + private String appId; + + /** + * 应用的 AgentId + */ + private String agentId; + + /** + * ClientId + * (原 AppKey 和 SuiteKey) + */ + private String clientId; + + /** + * Client Secret + * (原 AppSecret 和 SuiteSecret) + */ + private String clientSecret; + + /** + * 状态 + */ + private Integer status; +} 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 new file mode 100644 index 0000000..85d0e73 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkConversation.java @@ -0,0 +1,45 @@ +package cn.axzo.riven.dingtalk.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * 三方钉钉群会话信息 + * + * @author wangli + * @since 2024-10-23 19:44 + */ +@EqualsAndHashCode(callSuper = true) +@TableName(value = "third_dingtalk_conversation", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ThirdDingtalkConversation extends BaseEntity { + /** + * 会话 ID=群 ID + */ + private String conversationId; + + /** + * 会话名称=群名称 + */ + private String conversationTitle; + /** + * 会话类型: + * 1:单聊 + * 2:群聊 + */ + private Integer conversationType; + + /** + * 机器人所属企业 + */ + private String chatbotCorpId; + + /** + * 机器人用户 ID(加密数据) + */ + private String chatbotUserId; +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkMessageRecord.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkMessageRecord.java new file mode 100644 index 0000000..3c47b1e --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/entity/ThirdDingtalkMessageRecord.java @@ -0,0 +1,72 @@ +package cn.axzo.riven.dingtalk.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +/** + * 三方钉钉群消息记录 + * + * @author wangli + * @since 2024-10-23 18:18 + */ +@EqualsAndHashCode(callSuper = true) +@TableName(value = "third_dingtalk_msg_record", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ThirdDingtalkMessageRecord extends BaseEntity { + /** + * 会话 ID=群 ID + */ + private String conversationId; + + /** + * 会话名称=群名称 + */ + private String conversationTitle; + + /** + * 这条消息的 ID + */ + private String msgId; + + /** + * 谁@的机器人发的消息 + */ + private String senderNick; + + /** + * 会话类型: + * 1:单聊 + * 2:群聊 + */ + private Integer conversationType; + + /** + * 会话级的 Webhook + */ + private String sessionWebhook; + + /** + * 会话级的 Webhook 过期时间 + */ + private Date sessionWebhookExpiredTime; + + /** + * 请求的内容 + */ + private String requestContext; + + /** + * 响应的内容 + */ + private String responseContext; + /** + * 响应的消息类型 + */ + private String responseMsgType; +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdApplicationMapper.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdApplicationMapper.java new file mode 100644 index 0000000..3981c20 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdApplicationMapper.java @@ -0,0 +1,15 @@ +package cn.axzo.riven.dingtalk.repository.mapper; + +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 应用基本信息 Mapper + * + * @author wangli + * @since 2024-10-23 14:03 + */ +@Mapper +public interface ThirdApplicationMapper extends BaseMapper { +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkConversationMapper.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkConversationMapper.java new file mode 100644 index 0000000..9780199 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkConversationMapper.java @@ -0,0 +1,16 @@ +package cn.axzo.riven.dingtalk.repository.mapper; + +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkConversation; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 应用基本信息 Mapper + * + * @author wangli + * @since 2024-10-23 14:03 + */ +@Mapper +public interface ThirdDingtalkConversationMapper extends BaseMapper { +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkMessageRecordMapper.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkMessageRecordMapper.java new file mode 100644 index 0000000..78582a0 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/repository/mapper/ThirdDingtalkMessageRecordMapper.java @@ -0,0 +1,16 @@ +package cn.axzo.riven.dingtalk.repository.mapper; + +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkMessageRecord; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 应用基本信息 Mapper + * + * @author wangli + * @since 2024-10-23 14:03 + */ +@Mapper +public interface ThirdDingtalkMessageRecordMapper extends BaseMapper { +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/AutoConnector.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/AutoConnector.java new file mode 100644 index 0000000..37e38a0 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/AutoConnector.java @@ -0,0 +1,75 @@ +package cn.axzo.riven.dingtalk.robot.connection; + +import cn.axzo.riven.client.req.ThirdApplicationReq; +import cn.axzo.riven.dingtalk.listener.DingTalkAllEventListener; +import cn.axzo.riven.dingtalk.callback.robot.RobotMsgCallbackConsumer; +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; +import cn.axzo.riven.dingtalk.service.ThirdApplicationService; +import com.alibaba.fastjson.JSON; +import com.dingtalk.open.app.api.OpenDingTalkClient; +import com.dingtalk.open.app.api.OpenDingTalkStreamClientBuilder; +import com.dingtalk.open.app.api.callback.DingTalkStreamTopics; +import com.dingtalk.open.app.api.security.AuthClientCredential; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.List; + + +/** + * 机器人应用自动连接 + * + * @author wangli + * @since 2024-10-23 14:18 + */ +@Slf4j +@Component +public class AutoConnector { + @Value("${third.dingtalk.auto-connect: true}") + private Boolean autoConnect; + @Resource + private ThirdApplicationService applicationService; + @Resource + private RobotMsgCallbackConsumer robotMsgCallbackConsumer; + + @PostConstruct + public void init() { + if (autoConnect) { + if (log.isDebugEnabled()) { + log.debug("auto connection robot by stream model"); + } + doConnection(); + } + } + + private void doConnection() { + List thirdApplications = applicationService.genericQuery(new ThirdApplicationReq()); + if (log.isDebugEnabled()) { + log.debug("application query result count: {}", thirdApplications.size()); + } + thirdApplications.forEach(application -> { + try { + OpenDingTalkClient client = OpenDingTalkStreamClientBuilder + .custom() + .credential(new AuthClientCredential(application.getClientId(), application.getClientSecret())) + // 注册机器人回调,固定值 + .registerCallbackListener(DingTalkStreamTopics.BOT_MESSAGE_TOPIC, robotMsgCallbackConsumer) + //注册事件监听 + .registerAllEventListener(new DingTalkAllEventListener()) + .build(); + client.start(); + if (log.isDebugEnabled()) { + log.debug("robot connect success, channel: {}, name: {}, other: {}", + application.getChannel(), application.getName(), JSON.toJSONString(application)); + } + } catch (Exception e) { + log.error("robot connect error, channel: {}, name: {}, other: {}, errorInfo: {}", + application.getChannel(), application.getName(), JSON.toJSONString(application), e.getMessage(), e); + } + }); + + } +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/model/ReplyContext.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/model/ReplyContext.java new file mode 100644 index 0000000..b014713 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/robot/connection/model/ReplyContext.java @@ -0,0 +1,60 @@ +package cn.axzo.riven.dingtalk.robot.connection.model; + +import cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum; +import com.alibaba.fastjson.JSONObject; +import lombok.Data; + +/** + * 统一的回复上下文模型 + * + * @author wangli + * @since 2024-10-23 20:11 + */ +@Data +public class ReplyContext { + + /** + * 会话级的 Webhook + */ + private String sessionWebhook; + + /** + * 消息类型 + */ + private DingTalkMsgTypeEnum msgType = DingTalkMsgTypeEnum.sampleText; + + /** + * 默认的消息类型下,可直接传入要发送的文本 + * 如果要使用其他 msgType,则用 data 属性进行传值 + */ + private String msgContent; + + /** + * 根据不同的 msgType 传入不同请求模型 + * 参考:{@see https://open.dingtalk.com/document/orgapp/types-of-messages-sent-by-robots} + */ + private JSONObject data; + + 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/ThirdApplicationService.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdApplicationService.java new file mode 100644 index 0000000..46c0337 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdApplicationService.java @@ -0,0 +1,23 @@ +package cn.axzo.riven.dingtalk.service; + +import cn.axzo.riven.client.req.ThirdApplicationReq; +import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication; + +import java.util.List; + +/** + * 三方应用信息 + * + * @author wangli + * @since 2024-10-23 14:15 + */ +public interface ThirdApplicationService { + + List genericQuery(ThirdApplicationReq req); + + ThirdApplication insert(ThirdApplication req); + + ThirdApplication update(ThirdApplication req); + + Integer delete(Long id); +} 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 new file mode 100644 index 0000000..6bba118 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkConversationService.java @@ -0,0 +1,18 @@ +package cn.axzo.riven.dingtalk.service; + +import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkConversation; + +/** + * 三方钉钉群会话信息 + * + * @author wangli + * @since 2024-10-23 19:48 + */ +public interface ThirdDingtalkConversationService { + + /** + * insert or update + * @param conversation + */ + void upsert(ThirdDingtalkConversation conversation); +} 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 new file mode 100644 index 0000000..b9f5f6e --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/ThirdDingtalkMessageRecordService.java @@ -0,0 +1,10 @@ +package cn.axzo.riven.dingtalk.service; + +/** + * 三方钉钉群消息记录 + * + * @author wangli + * @since 2024-10-23 19:48 + */ +public interface ThirdDingtalkMessageRecordService { +} diff --git a/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdApplicationServiceImpl.java b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdApplicationServiceImpl.java new file mode 100644 index 0000000..d9fb78c --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdApplicationServiceImpl.java @@ -0,0 +1,62 @@ +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.dingtalk.repository.entity.ThirdApplication; +import cn.axzo.riven.dingtalk.repository.mapper.ThirdApplicationMapper; +import cn.axzo.riven.dingtalk.service.ThirdApplicationService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.util.List; +import java.util.Objects; + +/** + * ThirdParty Application Service Implementation + * + * @author wangli + * @since 2024-10-23 14:16 + */ +@Service +@AllArgsConstructor +public class ThirdApplicationServiceImpl implements ThirdApplicationService { + private final ThirdApplicationMapper thirdApplicationMapper; + + @Override + public List genericQuery(ThirdApplicationReq req) { + return thirdApplicationMapper.selectList(buildQueryWrapper(req)); + } + + @Override + public ThirdApplication insert(ThirdApplication entity) { + thirdApplicationMapper.insert(entity); + return entity; + } + + @Override + public ThirdApplication update(ThirdApplication entity) { + thirdApplicationMapper.updateById(entity); + return entity; + } + + @Override + public Integer delete(Long id) { + return thirdApplicationMapper.deleteById(id); + } + + private LambdaQueryWrapper buildQueryWrapper(ThirdApplicationReq req) { + return new LambdaQueryWrapper() + .eq(ThirdApplication::getChannel, Objects.nonNull(req.getChannel()) ? req.getChannel() : Channel.DingTalk) + .like(StringUtils.hasText(req.getName()), ThirdApplication::getName, req.getName()) + .like(StringUtils.hasText(req.getDescription()), ThirdApplication::getDescription, req.getDescription()) + .eq(StringUtils.hasText(req.getAppId()), ThirdApplication::getAppId, req.getAppId()) + .eq(StringUtils.hasText(req.getAgentId()), ThirdApplication::getAgentId, req.getAgentId()) + .eq(StringUtils.hasText(req.getClientId()), ThirdApplication::getClientId, req.getClientId()) + .eq(StringUtils.hasText(req.getClientSecret()), ThirdApplication::getClientSecret, req.getClientSecret()) + .eq(ThirdApplication::getStatus, Objects.nonNull(req.getStatus()) ? req.getStatus().getCode() : CommonStatusEnum.ENABLED.getCode()) + ; + } +} 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 new file mode 100644 index 0000000..54edabc --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkConversationServiceImpl.java @@ -0,0 +1,37 @@ +package cn.axzo.riven.dingtalk.service.impl; + +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 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.AllArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * 三方钉钉群会话信息 + * + * @author wangli + * @since 2024-10-23 19:48 + */ +@Service +@AllArgsConstructor +public class ThirdDingtalkConversationServiceImpl implements ThirdDingtalkConversationService { + private final ThirdDingtalkConversationMapper thirdDingtalkConversationMapper; + + @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); + thirdDingtalkConversationMapper.updateById(oldEntity); + } else { + thirdDingtalkConversationMapper.insert(conversation); + } + } +} 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 new file mode 100644 index 0000000..d24cf72 --- /dev/null +++ b/riven-dingtalk/src/main/java/cn/axzo/riven/dingtalk/service/impl/ThirdDingtalkMessageRecordServiceImpl.java @@ -0,0 +1,16 @@ +package cn.axzo.riven.dingtalk.service.impl; + +import cn.axzo.riven.dingtalk.service.ThirdDingtalkMessageRecordService; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 三方钉钉群消息记录 + * + * @author wangli + * @since 2024-10-23 19:48 + */ +@Service +@AllArgsConstructor +public class ThirdDingtalkMessageRecordServiceImpl implements ThirdDingtalkMessageRecordService { +} diff --git a/riven-dingtalk/src/main/resources/sql/DDL.sql b/riven-dingtalk/src/main/resources/sql/DDL.sql new file mode 100644 index 0000000..94e52d6 --- /dev/null +++ b/riven-dingtalk/src/main/resources/sql/DDL.sql @@ -0,0 +1,52 @@ +create table third_application +( + id bigint not null auto_increment comment '主键' + primary key, + channel varchar(255) not null comment '渠道', + name varchar(255) not null comment '应用名称', + description varchar(255) not null comment '应用描述', + app_id varchar(255) not null comment 'appId', + agent_id varchar(255) not null comment 'agentId', + client_id varchar(255) not null comment 'clientId', + client_secret varchar(255) not null comment 'clientSecret', + status tinyint(1) default 1 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 '第三方应用'; + +insert into third_application value (1, 'dingtalk', '多群公共机器人', '多群公共机器人', + 'c26a6e00-dd35-4bb5-a68a-36455ce1c001', '3282101532', 'dingxga1f6ghiefbzovb', + 'b-SSalEZAoRCWodldDtrdHMKbtJVQpVWP8-x1FYgdSTzPKaGJCDT7RH1j_0d-IKO', 1, now(), + now(), 0); + +create table third_dingtalk_conversation +( + 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 '删除标志' +) comment '会话信息'; + +create table third_dingtalk_msg_record +( + 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 '响应消息类型' +) comment '钉钉群消息记录'; \ No newline at end of file diff --git a/riven-server/pom.xml b/riven-server/pom.xml index 10c78d6..3d5c6d8 100644 --- a/riven-server/pom.xml +++ b/riven-server/pom.xml @@ -36,6 +36,12 @@ ${project.version} + + cn.axzo + riven-dingtalk + ${project.version} + + io.github.openfeign feign-httpclient @@ -109,7 +115,6 @@ com.dingtalk.open app-stream-client - 1.1.0 com.aliyun diff --git a/riven-server/src/main/java/cn/axzo/riven/Application.java b/riven-server/src/main/java/cn/axzo/riven/Application.java index 793c8a2..ad8d3ba 100644 --- a/riven-server/src/main/java/cn/axzo/riven/Application.java +++ b/riven-server/src/main/java/cn/axzo/riven/Application.java @@ -11,6 +11,7 @@ import org.springframework.core.env.Environment; @Slf4j @SpringBootApplication(scanBasePackages = "cn.axzo") @EnableFeignClients(basePackages = {"cn.axzo"}) +@MapperScan({"cn.axzo.riven.dingtalk.**.mapper"}) public class Application { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(Application.class, args); diff --git a/riven-server/src/main/java/cn/axzo/riven/config/XxlJobConfig.java b/riven-server/src/main/java/cn/axzo/riven/config/XxlJobConfig.java index 5fa5ac9..30408c5 100644 --- a/riven-server/src/main/java/cn/axzo/riven/config/XxlJobConfig.java +++ b/riven-server/src/main/java/cn/axzo/riven/config/XxlJobConfig.java @@ -1,5 +1,6 @@ package cn.axzo.riven.config; +import cn.azxo.framework.common.annotation.OnlyPodsEnvironment; import cn.azxo.framework.common.logger.JobLoggerTemplate; import cn.azxo.framework.common.service.JobParamResolver; import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; @@ -13,6 +14,7 @@ import org.springframework.context.annotation.Configuration; * xxl-job config * */ +@OnlyPodsEnvironment @Configuration public class XxlJobConfig {