钉钉工作消息支持撤回(撤回消息架构模板编写)

pull/11/head
3y 2 years ago
parent d190d2d20a
commit bb31cbb09f

@ -1,6 +1,7 @@
package com.java3y.austin.handler.handler;
import com.java3y.austin.common.domain.TaskInfo;
import com.java3y.austin.support.domain.MessageTemplate;
/**
* @author 3y
@ -10,8 +11,18 @@ public interface Handler {
/**
*
*
* @param taskInfo
*/
void doHandler(TaskInfo taskInfo);
/**
*
*
* @param messageTemplate
* @return
*/
void recall(MessageTemplate messageTemplate);
}

@ -16,6 +16,7 @@ import com.java3y.austin.handler.domain.dingding.DingDingRobotParam;
import com.java3y.austin.handler.domain.dingding.DingDingRobotResult;
import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.support.domain.MessageTemplate;
import com.java3y.austin.support.utils.AccountUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
@ -62,6 +63,8 @@ public class DingDingRobotHandler extends BaseHandler implements Handler {
return false;
}
private DingDingRobotParam assembleParam(TaskInfo taskInfo) {
// 接收者相关
@ -132,5 +135,11 @@ public class DingDingRobotHandler extends BaseHandler implements Handler {
}
return sign;
}
@Override
public void recall(MessageTemplate messageTemplate) {
}
}

@ -5,21 +5,26 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
import com.dingtalk.api.request.OapiMessageCorpconversationRecallRequest;
import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response;
import com.dingtalk.api.response.OapiMessageCorpconversationRecallResponse;
import com.google.common.base.Throwables;
import com.java3y.austin.common.constant.AustinConstant;
import com.java3y.austin.common.constant.SendAccountConstant;
import com.java3y.austin.common.domain.LogParam;
import com.java3y.austin.common.domain.TaskInfo;
import com.java3y.austin.common.dto.account.DingDingWorkNoticeAccount;
import com.java3y.austin.common.dto.model.DingDingRobotContentModel;
import com.java3y.austin.common.dto.model.DingDingWorkContentModel;
import com.java3y.austin.common.enums.ChannelType;
import com.java3y.austin.common.enums.SendMessageType;
import com.java3y.austin.handler.domain.dingding.DingDingRobotParam;
import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.support.config.SupportThreadPoolConfig;
import com.java3y.austin.support.domain.MessageTemplate;
import com.java3y.austin.support.utils.AccountUtils;
import com.java3y.austin.support.utils.LogUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -27,7 +32,6 @@ import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
@ -41,17 +45,22 @@ import java.util.concurrent.TimeUnit;
@Service
public class DingDingWorkNoticeHandler extends BaseHandler implements Handler {
@Autowired
private AccountUtils accountUtils;
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private LogUtils logUtils;
public DingDingWorkNoticeHandler() {
channelCode = ChannelType.DING_DING_WORK_NOTICE.getCode();
}
private static final String URL = "https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2";
private static final String SEND_URL = "https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2";
private static final String RECALL_URL = "https://oapi.dingtalk.com/topapi/message/corpconversation/recall";
private static final String DING_DING_RECALL_KEY_PREFIX = "RECALL_";
private static final String RECALL_BIZ_TYPE = "DingDingWorkNoticeHandler#recall";
@Override
public boolean handler(TaskInfo taskInfo) {
@ -59,13 +68,15 @@ public class DingDingWorkNoticeHandler extends BaseHandler implements Handler {
DingDingWorkNoticeAccount account = accountUtils.getAccount(taskInfo.getSendAccount(), SendAccountConstant.DING_DING_WORK_NOTICE_ACCOUNT_KEY, SendAccountConstant.DING_DING_WORK_NOTICE_PREFIX, DingDingWorkNoticeAccount.class);
OapiMessageCorpconversationAsyncsendV2Request request = assembleParam(account, taskInfo);
String accessToken = redisTemplate.opsForValue().get(SendAccountConstant.DING_DING_ACCESS_TOKEN_PREFIX + taskInfo.getSendAccount());
OapiMessageCorpconversationAsyncsendV2Response response = new DefaultDingTalkClient(URL).execute(request, accessToken);
OapiMessageCorpconversationAsyncsendV2Response response = new DefaultDingTalkClient(SEND_URL).execute(request, accessToken);
// 发送成功后记录TaskId用于消息撤回(支持当天的)
if (response.getErrcode() == 0) {
// 用于消息撤回(支持当天的)
redisTemplate.opsForList().leftPush(DING_DING_RECALL_KEY_PREFIX + taskInfo.getMessageTemplateId(), String.valueOf(response.getTaskId()));
redisTemplate.expire(DING_DING_RECALL_KEY_PREFIX + taskInfo.getMessageTemplateId(), (DateUtil.endOfDay(new Date()).getTime() - DateUtil.current()) / 1000, TimeUnit.SECONDS);
return true;
}
// 常见的错误 应当 关联至 AnchorState,由austin后台统一透出失败原因
log.error("DingDingWorkNoticeHandler#handler fail!result:{},params:{}", JSON.toJSONString(response), JSON.toJSONString(taskInfo));
} catch (Exception e) {
@ -156,5 +167,33 @@ public class DingDingWorkNoticeHandler extends BaseHandler implements Handler {
}
return req;
}
/**
* messageTemplate -> taskIdList
* taskIdList
*
* @param messageTemplate
*/
@Override
public void recall(MessageTemplate messageTemplate) {
SupportThreadPoolConfig.getPendingSingleThreadPool().execute(() -> {
try {
DingDingWorkNoticeAccount account = accountUtils.getAccount(messageTemplate.getSendAccount(), SendAccountConstant.DING_DING_WORK_NOTICE_ACCOUNT_KEY, SendAccountConstant.DING_DING_WORK_NOTICE_PREFIX, DingDingWorkNoticeAccount.class);
String accessToken = redisTemplate.opsForValue().get(SendAccountConstant.DING_DING_ACCESS_TOKEN_PREFIX + messageTemplate.getSendAccount());
while (redisTemplate.opsForList().size(DING_DING_RECALL_KEY_PREFIX + messageTemplate.getId()) > 0) {
String taskId = redisTemplate.opsForList().leftPop(DING_DING_RECALL_KEY_PREFIX + messageTemplate.getId());
DingTalkClient client = new DefaultDingTalkClient(RECALL_URL);
OapiMessageCorpconversationRecallRequest req = new OapiMessageCorpconversationRecallRequest();
req.setAgentId(Long.valueOf(account.getAgentId()));
req.setMsgTaskId(Long.valueOf(taskId));
OapiMessageCorpconversationRecallResponse rsp = client.execute(req, accessToken);
logUtils.print(LogParam.builder().bizType(RECALL_BIZ_TYPE).object(JSON.toJSONString(rsp)).build());
}
} catch (Exception e) {
log.error("DingDingWorkNoticeHandler#recall fail:{}", Throwables.getStackTraceAsString(e));
}
});
}
}

@ -13,6 +13,7 @@ import com.java3y.austin.handler.enums.RateLimitStrategy;
import com.java3y.austin.handler.flowcontrol.FlowControlParam;
import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.support.domain.MessageTemplate;
import com.java3y.austin.support.utils.AccountUtils;
import com.sun.mail.util.MailSSLSocketFactory;
import lombok.extern.slf4j.Slf4j;
@ -73,5 +74,8 @@ public class EmailHandler extends BaseHandler implements Handler {
}
return account;
}
@Override
public void recall(MessageTemplate messageTemplate) {
}
}

@ -10,6 +10,7 @@ import com.java3y.austin.common.dto.model.EnterpriseWeChatContentModel;
import com.java3y.austin.common.enums.ChannelType;
import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.support.domain.MessageTemplate;
import com.java3y.austin.support.utils.AccountUtils;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxMpErrorMsgEnum;
@ -97,6 +98,10 @@ public class EnterpriseWeChatHandler extends BaseHandler implements Handler {
.content(enterpriseWeChatContentModel.getContent())
.build();
}
@Override
public void recall(MessageTemplate messageTemplate) {
}
}

@ -9,6 +9,7 @@ import com.java3y.austin.handler.domain.wechat.WeChatMiniProgramParam;
import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.handler.wechat.MiniProgramAccountService;
import com.java3y.austin.support.domain.MessageTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -59,6 +60,9 @@ public class MiniProgramAccountHandler extends BaseHandler implements Handler {
miniProgramParam.setData(contentModel.getMap());
return miniProgramParam;
}
@Override
public void recall(MessageTemplate messageTemplate) {
}
}

@ -9,6 +9,7 @@ import com.java3y.austin.handler.domain.wechat.WeChatOfficialParam;
import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.handler.wechat.OfficialAccountService;
import com.java3y.austin.support.domain.MessageTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -53,5 +54,9 @@ public class OfficialAccountHandler extends BaseHandler implements Handler {
return false;
}
@Override
public void recall(MessageTemplate messageTemplate) {
}
}

@ -19,6 +19,7 @@ import com.java3y.austin.handler.domain.push.getui.SendPushParam;
import com.java3y.austin.handler.domain.push.getui.SendPushResult;
import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.support.domain.MessageTemplate;
import com.java3y.austin.support.utils.AccountUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -160,5 +161,8 @@ public class PushHandler extends BaseHandler implements Handler {
}
return param;
}
@Override
public void recall(MessageTemplate messageTemplate) {
}
}

@ -17,6 +17,7 @@ import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.handler.script.SmsScriptHolder;
import com.java3y.austin.support.dao.SmsRecordDao;
import com.java3y.austin.support.domain.MessageTemplate;
import com.java3y.austin.support.domain.SmsRecord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -156,5 +157,8 @@ public class SmsHandler extends BaseHandler implements Handler {
}
}
@Override
public void recall(MessageTemplate messageTemplate) {
}
}

@ -6,9 +6,11 @@ import com.java3y.austin.common.domain.AnchorInfo;
import com.java3y.austin.common.domain.LogParam;
import com.java3y.austin.common.domain.TaskInfo;
import com.java3y.austin.common.enums.AnchorState;
import com.java3y.austin.handler.handler.HandlerHolder;
import com.java3y.austin.handler.pending.Task;
import com.java3y.austin.handler.pending.TaskPendingHolder;
import com.java3y.austin.handler.utils.GroupIdMappingUtils;
import com.java3y.austin.support.domain.MessageTemplate;
import com.java3y.austin.support.utils.LogUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
@ -33,6 +35,7 @@ import java.util.Optional;
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Receiver {
private static final String LOG_BIZ_TYPE = "Receiver#consumer";
private static final String LOG_BIZ_RECALL_TYPE = "Receiver#recall";
@Autowired
private ApplicationContext context;
@ -42,13 +45,20 @@ public class Receiver {
@Autowired
private LogUtils logUtils;
@Autowired
private HandlerHolder handlerHolder;
/**
*
* @param consumerRecord
* @param topicGroupId
*/
@KafkaListener(topics = "#{'${austin.business.topic.name}'}")
public void consumer(ConsumerRecord<?, String> consumerRecord, @Header(KafkaHeaders.GROUP_ID) String topicGroupId) {
Optional<String> kafkaMessage = Optional.ofNullable(consumerRecord.value());
if (kafkaMessage.isPresent()) {
List<TaskInfo> taskInfoLists = JSON.parseArray(kafkaMessage.get(), TaskInfo.class);
String messageGroupId = GroupIdMappingUtils.getGroupIdByTaskInfo(CollUtil.getFirst(taskInfoLists.iterator()));
/**
@ -63,4 +73,18 @@ public class Receiver {
}
}
}
/**
*
* @param consumerRecord
*/
@KafkaListener(topics = "#{'${austin.business.recall.topic.name}'}",groupId = "#{'${austin.business.recall.group.name}'}")
public void recall(ConsumerRecord<?,String> consumerRecord){
Optional<String> kafkaMessage = Optional.ofNullable(consumerRecord.value());
if(kafkaMessage.isPresent()){
MessageTemplate messageTemplate = JSON.parseObject(kafkaMessage.get(), MessageTemplate.class);
logUtils.print(LogParam.builder().bizType(LOG_BIZ_RECALL_TYPE).object(messageTemplate).build());
handlerHolder.route(messageTemplate.getSendChannel()).recall(messageTemplate);
}
}
}

@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.base.Throwables;
import com.java3y.austin.common.constant.AustinConstant;
import com.java3y.austin.common.domain.TaskInfo;
@ -13,6 +14,7 @@ import com.java3y.austin.common.enums.ChannelType;
import com.java3y.austin.common.enums.RespStatusEnum;
import com.java3y.austin.common.vo.BasicResultVO;
import com.java3y.austin.service.api.domain.MessageParam;
import com.java3y.austin.service.api.enums.BusinessCode;
import com.java3y.austin.service.api.impl.domain.SendTaskModel;
import com.java3y.austin.support.dao.MessageTemplateDao;
import com.java3y.austin.support.domain.MessageTemplate;
@ -50,9 +52,12 @@ public class AssembleAction implements BusinessProcess<SendTaskModel> {
context.setNeedBreak(true).setResponse(BasicResultVO.fail(RespStatusEnum.TEMPLATE_NOT_FOUND));
return;
}
List<TaskInfo> taskInfos = assembleTaskInfo(sendTaskModel, messageTemplate.get());
sendTaskModel.setTaskInfo(taskInfos);
if (BusinessCode.COMMON_SEND.getCode().equals(context.getCode())) {
List<TaskInfo> taskInfos = assembleTaskInfo(sendTaskModel, messageTemplate.get());
sendTaskModel.setTaskInfo(taskInfos);
} else if (BusinessCode.RECALL.getCode().equals(context.getCode())) {
sendTaskModel.setMessageTemplate(messageTemplate.get());
}
} catch (Exception e) {
context.setNeedBreak(true).setResponse(BasicResultVO.fail(RespStatusEnum.SERVICE_ERROR));
log.error("assemble task fail! templateId:{}, e:{}", messageTemplateId, Throwables.getStackTraceAsString(e));

@ -6,6 +6,7 @@ import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.base.Throwables;
import com.java3y.austin.common.enums.RespStatusEnum;
import com.java3y.austin.common.vo.BasicResultVO;
import com.java3y.austin.service.api.enums.BusinessCode;
import com.java3y.austin.service.api.impl.domain.SendTaskModel;
import com.java3y.austin.support.pipeline.BusinessProcess;
import com.java3y.austin.support.pipeline.ProcessContext;
@ -27,15 +28,22 @@ public class SendMqAction implements BusinessProcess<SendTaskModel> {
private KafkaUtils kafkaUtils;
@Value("${austin.business.topic.name}")
private String topicName;
private String sendMessageTopic;
@Value("${austin.business.recall.topic.name}")
private String austinRecall;
@Override
public void process(ProcessContext<SendTaskModel> context) {
SendTaskModel sendTaskModel = context.getProcessModel();
String message = JSON.toJSONString(sendTaskModel.getTaskInfo(), new SerializerFeature[]{SerializerFeature.WriteClassName});
try {
kafkaUtils.send(topicName, message);
if (BusinessCode.COMMON_SEND.getCode().equals(context.getCode())) {
String message = JSON.toJSONString(sendTaskModel.getTaskInfo(), new SerializerFeature[]{SerializerFeature.WriteClassName});
kafkaUtils.send(sendMessageTopic, message);
} else if (BusinessCode.RECALL.getCode().equals(context.getCode())) {
String message = JSON.toJSONString(sendTaskModel.getMessageTemplate(), new SerializerFeature[]{SerializerFeature.WriteClassName});
kafkaUtils.send(austinRecall, message);
}
} catch (Exception e) {
context.setNeedBreak(true).setResponse(BasicResultVO.fail(RespStatusEnum.SERVICE_ERROR));
log.error("send kafka fail! e:{},params:{}", Throwables.getStackTraceAsString(e)

@ -48,6 +48,19 @@ public class PipelineConfig {
return processTemplate;
}
/**
*
* 1.
* 2.MQ
* @return
*/
@Bean("recallMessageTemplate")
public ProcessTemplate recallMessageTemplate() {
ProcessTemplate processTemplate = new ProcessTemplate();
processTemplate.setProcessList(Arrays.asList(assembleAction, sendMqAction));
return processTemplate;
}
/**
* pipeline
*
@ -60,6 +73,7 @@ public class PipelineConfig {
ProcessController processController = new ProcessController();
Map<String, ProcessTemplate> templateConfig = new HashMap<>(4);
templateConfig.put(BusinessCode.COMMON_SEND.getCode(), commonSendTemplate());
templateConfig.put(BusinessCode.RECALL.getCode(), recallMessageTemplate());
processController.setTemplateConfig(templateConfig);
return processController;
}

@ -2,6 +2,7 @@ package com.java3y.austin.service.api.impl.domain;
import com.java3y.austin.common.domain.TaskInfo;
import com.java3y.austin.service.api.domain.MessageParam;
import com.java3y.austin.support.domain.MessageTemplate;
import com.java3y.austin.support.pipeline.ProcessModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
@ -36,5 +37,9 @@ public class SendTaskModel implements ProcessModel {
*/
private List<TaskInfo> taskInfo;
/**
*
*/
private MessageTemplate messageTemplate;
}

@ -0,0 +1,37 @@
package com.java3y.austin.service.api.impl.service;
import com.java3y.austin.common.vo.BasicResultVO;
import com.java3y.austin.service.api.domain.SendRequest;
import com.java3y.austin.service.api.domain.SendResponse;
import com.java3y.austin.service.api.impl.domain.SendTaskModel;
import com.java3y.austin.service.api.service.RecallService;
import com.java3y.austin.support.pipeline.ProcessContext;
import com.java3y.austin.support.pipeline.ProcessController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
*
* @author 3y
*/
@Service
public class RecallServiceImpl implements RecallService {
@Autowired
private ProcessController processController;
@Override
public SendResponse recall(SendRequest sendRequest) {
SendTaskModel sendTaskModel = SendTaskModel.builder()
.messageTemplateId(sendRequest.getMessageTemplateId())
.build();
ProcessContext context = ProcessContext.builder()
.code(sendRequest.getCode())
.processModel(sendTaskModel)
.needBreak(false)
.response(BasicResultVO.success()).build();
ProcessContext process = processController.process(context);
return new SendResponse(process.getResponse().getStatus(), process.getResponse().getMsg());
}
}

@ -8,7 +8,7 @@ import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
*
* /
* @author 3y
*/
@Data
@ -19,18 +19,22 @@ import lombok.experimental.Accessors;
public class SendRequest {
/**
* ( "send")
*
* send:
* recall:
*/
private String code;
/**
* Id
*
*/
private Long messageTemplateId;
/**
*
* "send"
*/
private MessageParam messageParam;

@ -0,0 +1,22 @@
package com.java3y.austin.service.api.service;
import com.java3y.austin.service.api.domain.BatchSendRequest;
import com.java3y.austin.service.api.domain.SendRequest;
import com.java3y.austin.service.api.domain.SendResponse;
/**
*
*
* @author 3y
*/
public interface RecallService {
/**
* ID
*
* @param sendRequest
* @return
*/
SendResponse recall(SendRequest sendRequest);
}

@ -129,8 +129,8 @@ public class MessageTemplate implements Serializable {
/**
*
* 0
* 1
* 0
* 1
*/
private Integer isDeleted;

@ -11,6 +11,7 @@ import com.java3y.austin.service.api.domain.MessageParam;
import com.java3y.austin.service.api.domain.SendRequest;
import com.java3y.austin.service.api.domain.SendResponse;
import com.java3y.austin.service.api.enums.BusinessCode;
import com.java3y.austin.service.api.service.RecallService;
import com.java3y.austin.service.api.service.SendService;
import com.java3y.austin.support.domain.MessageTemplate;
import com.java3y.austin.web.service.MessageTemplateService;
@ -50,6 +51,9 @@ public class MessageTemplateController {
@Autowired
private SendService sendService;
@Autowired
private RecallService recallService;
@Value("${austin.business.upload.crowd.path}")
private String dataPath;
@ -131,6 +135,22 @@ public class MessageTemplateController {
return BasicResultVO.success(response);
}
/**
*
*/
@PostMapping("recall/{id}")
@ApiOperation("/撤回消息接口")
public BasicResultVO recall(@PathVariable("id") String id) {
SendRequest sendRequest = SendRequest.builder().code(BusinessCode.RECALL.getCode()).
messageTemplateId(Long.valueOf(id)).build();
SendResponse response = recallService.recall(sendRequest);
if (response.getCode() != RespStatusEnum.SUCCESS.getCode()) {
return BasicResultVO.fail(response.getMsg());
}
return BasicResultVO.success(response);
}
/**
*

@ -52,6 +52,8 @@ spring.redis.password=${austin-redis-password}
##################### business properties #####################
austin.business.topic.name=austinBusiness
austin.business.recall.topic.name=austinRecall
austin.business.recall.group.name=recallGroupId
austin.business.log.topic.name=austinLog
austin.business.graylog.ip=${austin-grayLog-ip}
# TODO if windows os ,replace path !

Loading…
Cancel
Save