feat(REQ-3114) - 完善钉钉消息的处理

This commit is contained in:
wangli 2024-10-28 19:36:16 +08:00
parent 7312f9cf4e
commit 4d5943d923
13 changed files with 192 additions and 41 deletions

View File

@ -0,0 +1,57 @@
package cn.axzo.riven.client.model;
import lombok.Data;
import java.util.Map;
/**
* 企业内部应用注册
*
* @author wangli
* @since 2024-10-28 18:31
*/
@Data
public class DingtalkAppRegModel implements CommandParser<DingtalkAppRegModel> {
private String name;
private String description;
private String appId;
private String agentId;
private String clientId;
private String clientSecret;
private String robotCode;
@Override
public DingtalkAppRegModel transferToModel(String content) {
DingtalkAppRegModel model = new DingtalkAppRegModel();
Map<String, String> map = parseStringToKeyValuePairs(content);
map.forEach((k, v) -> {
switch (k) {
case "-n":
model.setName(v.trim());
break;
case "-d":
model.setDescription(v.trim());
break;
case "-a":
model.setAppId(v.trim());
break;
case "-A":
model.setAgentId(v.trim());
break;
case "-ci":
model.setClientId(v.trim());
break;
case "-cs":
model.setClientSecret(v.trim());
break;
case "-r":
model.setRobotCode(v.trim());
break;
default:
break;
}
});
return model;
}
}

View File

@ -48,9 +48,6 @@ public class DingtalkTodoModel implements CommandParser<DingtalkTodoModel> {
default:
break;
}
if (Objects.equals("-t", k)) {
model.setTitle(v.trim());
}
});
return model;
}

View File

@ -41,5 +41,11 @@ public abstract class AbstractKeywordProcessor implements KeywordProcessor {
return doProcess(content);
}
/**
* 这里的 content 已经移除了命令字符串仅需对内容或者参数做解析处理即可
*
* @param content
* @return
*/
protected abstract ReplyMessage doProcess(String content);
}

View File

@ -1,7 +1,12 @@
package cn.axzo.riven.dingtalk.callback.keyword.impl;
import cn.axzo.riven.client.common.enums.sync.Channel;
import cn.axzo.riven.client.model.DingtalkAppRegModel;
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.callback.keyword.AbstractKeywordProcessor;
import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication;
import cn.axzo.riven.dingtalk.service.ThirdApplicationService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
@ -19,7 +24,7 @@ import javax.annotation.Resource;
@Component
@AllArgsConstructor
public class ApplicationRegisterKeywordProcessor extends AbstractKeywordProcessor {
private final static String[] KEYWORD = {"appRegister", "应用注册"};
private final static String[] KEYWORD = {"appReg", "应用注册"};
@Resource
private ThirdApplicationService thirdApplicationService;
@ -30,13 +35,37 @@ public class ApplicationRegisterKeywordProcessor extends AbstractKeywordProcesso
@Override
public ReplyMessage help() {
return null;
return SampleMarkdown.from("使用示例", "> ### 请按以下帮助信息发送消息哦\n" +
"> appReg ([option] text)*\n" +
"> \n" +
"> 完整语法:<font color=green>appReg -n 应用名称 -d 应用描述 -a appId -A agentId -ci clientId -cs clientSecret -r 机器人Code</font>\n" +
"> \n" +
"> 命令appReg/应用注册\n" +
"\n" +
"> 以上参数信息请从[钉钉后台](https://open-dev.dingtalk.com/)获取\n" +
"\n" +
"#### 使用示例:\n" +
"appReg -n 测试应用 -d 测试应用描述 -a 123 -A 123 -ci 123 -cs 123 -r robotCode123");
}
@Override
public ReplyMessage doProcess(String content) {
DingtalkAppRegModel model = new DingtalkAppRegModel().transferToModel(content);
return null;
ThirdApplication application = new ThirdApplication();
application.setChannel(Channel.DingTalk.name());
application.setName(model.getName());
application.setDescription(model.getDescription());
application.setAppId(model.getAppId());
application.setAgentId(model.getAgentId());
application.setClientId(model.getClientId());
application.setClientSecret(model.getClientSecret());
application.setRobotCode(model.getRobotCode());
try {
thirdApplicationService.insert(application);
} catch (Exception e) {
return SampleText.from(String.format("注册应用发生异常:%s", e.getMessage()));
}
return SampleText.from("注册应用成功");
}
}

View File

@ -7,6 +7,7 @@ import cn.axzo.riven.client.model.SampleMarkdown;
import cn.axzo.riven.client.model.SampleText;
import cn.axzo.riven.dingtalk.callback.keyword.AbstractKeywordProcessor;
import cn.axzo.riven.dingtalk.callback.robot.model.ChatbotMessageWrapper;
import cn.axzo.riven.dingtalk.repository.entity.ThirdPartyUserV2;
import cn.axzo.riven.dingtalk.robot.basic.ApplicationAccessTokenService;
import cn.axzo.riven.dingtalk.service.ThirdPartyUserService;
import com.aliyun.dingtalktodo_1_0.Client;
@ -34,11 +35,13 @@ import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* TODO
* 创建待办关键词的处理器
*
* @author wangli
* @since 2024-10-25 17:36
@ -63,7 +66,7 @@ public class CreateTodoKeywordProcessor extends AbstractKeywordProcessor {
*/
@Override
public ReplyMessage help() {
return SampleMarkdown.from("使用示例", "> ### 请按以下说明发送信息\n" +
return SampleMarkdown.from("使用示例", "> ### 请按以下帮助信息发送消息哦\n" +
"> createTodo <@用户...> ([option] text)*\n" +
"> \n" +
"> 完整语法:\n" +
@ -100,10 +103,11 @@ public class CreateTodoKeywordProcessor extends AbstractKeywordProcessor {
/**
* https://open.dingtalk.com/document/orgapp/add-dingtalk-to-do-task
*
* <p>
* 测试使用的数据
* senderId: vgmlrP0L0iiTiP0Y6lVhiiarQiEiE
* executorId: rcnSH26ZiPevENaCkHgFHwQiEiE
*
* @return
* @throws Exception
*/
@ -114,12 +118,9 @@ public class CreateTodoKeywordProcessor extends AbstractKeywordProcessor {
Set<String> users = new HashSet<>();
users.add(chatbotMessage.getSenderStaffId());
OapiV2UserGetResponse sender = getUserInfo(accessToken, chatbotMessage.getSenderStaffId());
OapiV2UserGetResponse executor = null;
if (chatbotMessage.getAtUsers().size() == 2) {
// 为单人创建
users.add(chatbotMessage.getAtUsers().get(1).getStaffId());
executor = getUserInfo(accessToken, chatbotMessage.getAtUsers().get(1).getStaffId());
} else {
// 批量创建
users.addAll(chatbotMessage.getAtUsers().stream().map(MentionUser::getStaffId).distinct().collect(Collectors.toList()));
@ -128,7 +129,7 @@ public class CreateTodoKeywordProcessor extends AbstractKeywordProcessor {
log.info("使用 userId 查询三方用户信息: {}", JSON.toJSONString(users));
}
/*Map<String, ThirdPartyUserV2> userMap = thirdPartyUserService.getUserInfos(Lists.newArrayList(users))
Map<String, ThirdPartyUserV2> userMap = thirdPartyUserService.getUserInfos(Lists.newArrayList(users))
.stream().collect(Collectors.toMap(ThirdPartyUserV2::getUserId, Function.identity(), (s, t) -> s));
if (users.size() != userMap.size()) {
@ -141,7 +142,7 @@ public class CreateTodoKeywordProcessor extends AbstractKeywordProcessor {
List<String> executorUnionIds = userMap.values().stream().map(ThirdPartyUserV2::getUnionId).distinct().collect(Collectors.toList());
if (CollectionUtils.isEmpty(executorUnionIds)) {
return SampleText.from("创建不成功:执行人为空,请确认组织人员基础数据是否完整");
}*/
}
Client client = CreateTodoKeywordProcessor.createClient();
CreateTodoTaskHeaders createTodoTaskHeaders = new CreateTodoTaskHeaders();
@ -149,8 +150,6 @@ public class CreateTodoKeywordProcessor extends AbstractKeywordProcessor {
CreateTodoTaskRequest.CreateTodoTaskRequestNotifyConfigs notifyConfigs = new CreateTodoTaskRequest.CreateTodoTaskRequestNotifyConfigs()
.setDingNotify("1");
String senderUnionId = sender.getResult().getUnionid();
List<String> executorUnionIds = Lists.newArrayList(executor.getResult().getUnionid());
CreateTodoTaskRequest createTodoTaskRequest = new CreateTodoTaskRequest()
.setOperatorId(senderUnionId)
.setSubject(model.getTitle())

View File

@ -1,6 +1,7 @@
package cn.axzo.riven.dingtalk.callback.keyword.impl;
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.callback.keyword.AbstractKeywordProcessor;
import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkConversation;
@ -8,12 +9,9 @@ import cn.axzo.riven.dingtalk.service.ThirdDingtalkConversationService;
import cn.hutool.core.bean.BeanUtil;
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
@ -21,7 +19,7 @@ import static cn.axzo.riven.dingtalk.constant.DingTalkConstant.REGISTER;
@Component
@AllArgsConstructor
public class GroupRegisterKeywordProcessor extends AbstractKeywordProcessor {
private final static String[] KEYWORD = {"groupRegister", "群注册"};
private final static String[] KEYWORD = {"groupReg", "群注册"};
private final ThirdDingtalkConversationService thirdDingtalkConversationService;
@Override
@ -31,7 +29,15 @@ public class GroupRegisterKeywordProcessor extends AbstractKeywordProcessor {
@Override
public ReplyMessage help() {
return null;
return SampleMarkdown.from("使用示例", "> ### 请按以下帮助信息发送消息哦\n" +
"> groupReg (applicationName)\n" +
">\n" +
"> 完整语法:<font color=green>groupReg 应用名称</font>\n" +
"\n" +
"> 应用名称一般建议使用后端服务名,方便后续管理\n" +
"\n" +
"#### 使用示例:\n" +
"appReg workflowEngine");
}
/**
@ -48,15 +54,14 @@ public class GroupRegisterKeywordProcessor extends AbstractKeywordProcessor {
ThirdDingtalkConversation conversation = new ThirdDingtalkConversation();
BeanUtil.copyProperties(getChatbotMessage(), conversation);
String replyText = String.format("注册成功,但未关联服务。\r\n如需请@机器发送“注册 [服务名]”即可(示例:注册 pudge\r\n当前的会话 ID%s", conversation.getConversationId());
String applicationName;
if (StringUtils.hasText(applicationName = content.replaceAll(REGISTER, "").trim())) {
replyText = String.format("本群于" + applicationName + "服务关联成功。 \r\n本群的会话 ID%s", conversation.getConversationId());
conversation.setApplicationName(applicationName);
}
conversation.setApplicationName(content.trim());
// 进行机器人和会话的绑定注册
thirdDingtalkConversationService.upsert(conversation);
return SampleText.from(replyText);
try {
// 进行机器人和会话的绑定注册
thirdDingtalkConversationService.upsert(conversation);
} catch (Exception e) {
return SampleText.from(String.format("群注册发送异常:%s", e.getMessage()));
}
return SampleText.from(String.format("群注册成功,并已关联[%s]后端服务!", conversation.getApplicationName()));
}
}

View File

@ -1,6 +1,7 @@
package cn.axzo.riven.dingtalk.callback.keyword.impl;
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.callback.keyword.AbstractKeywordProcessor;
import org.springframework.stereotype.Component;
@ -24,10 +25,21 @@ public class MenuKeywordProcessor extends AbstractKeywordProcessor {
@Override
public ReplyMessage help() {
return doProcess(null);
String content = "### 目前支持的命令如下哦\n" +
"\n" +
"> 可以@我并发送命令获取特定命令的帮助\n" +
"\n" +
"| 中文 | 英文 | 命令描述 |\n\n " +
"|:-|:-:|-:|\n\n " +
"| groupRegister | 群注册 | 将当前群与某个后端应用绑定 |\n\n " +
"| createTodo | 创建代办 | 为指定用户创建钉钉待办 |\n\n " +
"| appRegister | 应用注册 | 利用该机器人,直接注册本公司下的‘其他企业内部应用’ |\n\n ";
return doProcess(content);
}
public ReplyMessage doProcess(String content) {
return SampleText.from("这里是菜单");
return SampleMarkdown.from("菜单", content);
}
}

View File

@ -2,7 +2,9 @@ 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.common.enums.DingTalkMsgTypeEnum;
import cn.axzo.riven.client.model.ReplyMessage;
import cn.axzo.riven.client.model.SampleText;
import cn.axzo.riven.dingtalk.callback.robot.model.ChatbotMessageWrapper;
import cn.axzo.riven.dingtalk.repository.entity.ThirdDingtalkMessageRecord;
import cn.axzo.riven.dingtalk.service.ThirdDingtalkMessageRecordService;
@ -48,7 +50,9 @@ public abstract class AbstractRobotHandleStrategy implements RobotHandleStrategy
* @param replyMessage
*/
private void updateLog(ThirdDingtalkMessageRecord record, ReplyMessage replyMessage) {
// TODO
record.setResponseMsgType(replyMessage.getClz());
record.setResponseContent(replyMessage.toJson());
thirdDingtalkMessageRecordService.update(record);
}
/**

View File

@ -0,0 +1,22 @@
package cn.axzo.riven.dingtalk.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
/**
* 支持刷新的配置
*
* @author wangli
* @since 2024-10-28 19:32
*/
@Component
@RefreshScope
@Data
public class RefreshableConfiguration {
@Value("${dingtalk.stream.enabled:true}")
private Boolean enableDingtalkStream;
}

View File

@ -16,14 +16,11 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@ -79,9 +76,24 @@ public class RocketReplyMessageEventListener implements EventHandler, Initializi
atUserIds.add(record.getSenderId());
});
updateLog(receiveModel);
robotReply.reply(replyContext);
}
private void updateLog(DingtalkSendMqModel receiveModel) {
ThirdDingtalkMessageRecordReq recordQuery = new ThirdDingtalkMessageRecordReq();
recordQuery.setMsgId(receiveModel.getMsgId());
recordQuery.setConversationId(receiveModel.getConversationId());
List<ThirdDingtalkMessageRecord> records = thirdDingtalkMessageRecordService.genericQuery(recordQuery);
if (!CollectionUtils.isEmpty(records) && records.size() == 1) {
ThirdDingtalkMessageRecord record = records.get(0);
record.setResponseMsgType(receiveModel.getMessage().getClz());
record.setResponseContent(receiveModel.getMessage().toJson());
thirdDingtalkMessageRecordService.update(record);
}
}
private ReplyMessage buildReplyMessage(Event event) {
if (Objects.nonNull(event.getData())) {
JSONObject data = (JSONObject) event.getData();

View File

@ -1,6 +1,7 @@
package cn.axzo.riven.dingtalk.robot.connection;
import cn.axzo.riven.client.req.ThirdApplicationReq;
import cn.axzo.riven.dingtalk.config.RefreshableConfiguration;
import cn.axzo.riven.dingtalk.listener.dd.DingTalkAllEventListener;
import cn.axzo.riven.dingtalk.callback.robot.RobotMsgCallbackConsumer;
import cn.axzo.riven.dingtalk.repository.entity.ThirdApplication;
@ -28,8 +29,8 @@ import java.util.List;
@Slf4j
@Component
public class AutoConnector {
@Value("${third.dingtalk.auto-connect: true}")
private Boolean autoConnect;
@Resource
private RefreshableConfiguration refreshableConfiguration;
@Resource
private ThirdApplicationService applicationService;
@Resource
@ -37,7 +38,7 @@ public class AutoConnector {
@PostConstruct
public void init() {
if (autoConnect) {
if (refreshableConfiguration.getEnableDingtalkStream()) {
if (log.isDebugEnabled()) {
log.debug("auto connection robot by stream model");
}

View File

@ -15,4 +15,6 @@ public interface ThirdDingtalkMessageRecordService {
ThirdDingtalkMessageRecord insert(ThirdDingtalkMessageRecord record);
List<ThirdDingtalkMessageRecord> genericQuery(ThirdDingtalkMessageRecordReq req);
void update(ThirdDingtalkMessageRecord record);
}

View File

@ -28,6 +28,11 @@ public class ThirdDingtalkMessageRecordServiceImpl implements ThirdDingtalkMessa
return record;
}
@Override
public void update(ThirdDingtalkMessageRecord record) {
thirdDingtalkMessageRecordMapper.updateById(record);
}
@Override
public List<ThirdDingtalkMessageRecord> genericQuery(ThirdDingtalkMessageRecordReq req) {
return thirdDingtalkMessageRecordMapper.selectList(buildQueryWrapper(req));