feat(dingtalk) - 调整钉钉发送消息的模式
This commit is contained in:
parent
ca47825a2e
commit
881492f821
@ -5,6 +5,7 @@ import cn.axzo.workflow.common.annotation.InvokeMode;
|
||||
import cn.axzo.workflow.common.annotation.Manageable;
|
||||
import cn.axzo.workflow.common.enums.AdminDataSource;
|
||||
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
|
||||
import cn.axzo.workflow.common.model.dto.CommonDingTalkDTO;
|
||||
import cn.axzo.workflow.common.model.request.feature.DingTalkStarterAlterDTO;
|
||||
import cn.azxo.framework.common.model.CommonResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@ -47,4 +48,9 @@ public interface FunctionApi {
|
||||
@PostMapping("/api/function/dingtalk/alter")
|
||||
@InvokeMode(SYNC)
|
||||
CommonResponse<Boolean> sendDingtalk(@Validated @RequestBody DingTalkStarterAlterDTO dto);
|
||||
|
||||
@Operation(summary = "发送通用钉钉消息")
|
||||
@PostMapping("/api/function/common/dingtalk/send")
|
||||
@InvokeMode(SYNC)
|
||||
CommonResponse<Boolean> sendCommonDingtalk(@Validated @RequestBody CommonDingTalkDTO dto);
|
||||
}
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
package cn.axzo.workflow.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 节点检测告警对象
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2024-09-13 11:37
|
||||
*/
|
||||
@Data
|
||||
public class NextNodePreCheckAlterDTO {
|
||||
|
||||
private String processDefinitionKey;
|
||||
|
||||
private String processDefinitionName;
|
||||
|
||||
private String processInstanceId;
|
||||
|
||||
private String activityId;
|
||||
|
||||
private String errorMsg;
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package cn.axzo.workflow.common.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通用的 Starter 发送钉钉消息传输对象
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-02-02 16:22
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class CommonDingTalkDTO {
|
||||
/**
|
||||
* 钉钉消息 markdown 的标题
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* 钉钉消息 markdown 的内容
|
||||
*/
|
||||
private String context;
|
||||
|
||||
/**
|
||||
* 是否 @ 接收人
|
||||
*/
|
||||
@Builder.Default
|
||||
private Boolean at = false;
|
||||
|
||||
/**
|
||||
* 钉钉消息 mardown 的接收人手机号
|
||||
*/
|
||||
@Builder.Default
|
||||
private List<String> mobiles = new ArrayList<>();
|
||||
}
|
||||
@ -114,5 +114,9 @@
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.axzo</groupId>
|
||||
<artifactId>riven-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@ -122,7 +122,7 @@ public class FlowableConfiguration {
|
||||
configuration.addCustomJobHandler(new AsyncApproveTaskWithFormJobHandler());
|
||||
configuration.addCustomJobHandler(new AsyncRemindTaskJobHandler(refreshProperties));
|
||||
configuration.addCustomJobHandler(new AsyncResetApproversUserTaskJobHandler(extAxHiTaskInstService));
|
||||
configuration.addCustomJobHandler(new NextActivityConfigCheckJobHandler());
|
||||
configuration.addCustomJobHandler(new NextActivityConfigCheckJobHandler(refreshProperties));
|
||||
configurers.forEach(i -> configuration.addCustomJobHandler(i.getJobHandler()));
|
||||
// 异步任务异常重试时间间隔
|
||||
configuration.setDefaultFailedJobWaitTime(30);
|
||||
|
||||
@ -19,6 +19,8 @@ import java.util.concurrent.TimeUnit;
|
||||
@Data
|
||||
@RefreshScope
|
||||
public class SupportRefreshProperties {
|
||||
@Value("${spring.profiles.active:local}")
|
||||
private String profile;
|
||||
@Value("${workflow.apiLog.enable: false}")
|
||||
private Boolean apiLogEnable;
|
||||
|
||||
@ -84,9 +86,6 @@ public class SupportRefreshProperties {
|
||||
@Value(value = "${workflow.alter.repeat:false}")
|
||||
private Boolean repeatAlter;
|
||||
|
||||
@Value(value = "${workflow.alter.sendDingTalk:true}")
|
||||
private Boolean alterSendDingTalk;
|
||||
|
||||
/**
|
||||
* 用于控制转交管理员的 API
|
||||
*/
|
||||
|
||||
@ -1,173 +0,0 @@
|
||||
package cn.axzo.workflow.core.engine.job;
|
||||
|
||||
import cn.axzo.basics.common.util.NumberUtil;
|
||||
import cn.axzo.workflow.common.model.dto.AlterDTO;
|
||||
import cn.axzo.workflow.common.model.dto.TermNodePausingDTO;
|
||||
import cn.axzo.workflow.common.model.response.category.CategoryItemVO;
|
||||
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
|
||||
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
|
||||
import cn.axzo.workflow.core.listener.Alter;
|
||||
import cn.axzo.workflow.core.service.CategoryService;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.common.engine.impl.interceptor.CommandContext;
|
||||
import org.flowable.engine.ManagementService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||
import org.flowable.engine.impl.util.CommandContextUtil;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.job.service.JobHandler;
|
||||
import org.flowable.job.service.TimerJobService;
|
||||
import org.flowable.job.service.impl.persistence.entity.JobEntity;
|
||||
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.flowable.variable.api.delegate.VariableScope;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_NODE_ALTER;
|
||||
|
||||
/**
|
||||
* 检查指定节点是否长时间卡住,如果卡住则进行钉钉告警
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2024-09-11 13:50
|
||||
*/
|
||||
@Slf4j
|
||||
public class AsyncTermNodeAlterJobHandler extends AbstractJobHandler implements JobHandler {
|
||||
public static final String TYPE = "term-node-alter-cycle";
|
||||
private final SupportRefreshProperties refreshProperties;
|
||||
|
||||
public AsyncTermNodeAlterJobHandler(SupportRefreshProperties refreshProperties) {
|
||||
this.refreshProperties = refreshProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
|
||||
log.warn("AsyncTermNodeAlterJobHandler exec start...");
|
||||
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
|
||||
// JSONObject jsonObject = JSON.parseObject(job.getJobHandlerConfiguration());
|
||||
// if (!jsonObject.containsKey("activityId")) {
|
||||
// return;
|
||||
// }
|
||||
String activityId = job.getJobHandlerConfiguration();
|
||||
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(job.getProcessInstanceId()).singleResult();
|
||||
if(Objects.isNull(processInstance)) {
|
||||
return;
|
||||
}
|
||||
TermNodePausingDTO dto = runtimeService.getVariable(job.getProcessInstanceId(), BIZ_NODE_ALTER + activityId, TermNodePausingDTO.class);
|
||||
TaskService taskService = processEngineConfiguration.getTaskService();
|
||||
List<Task> tasks = taskService.createTaskQuery()
|
||||
.processInstanceId(dto.getProcessInstanceId())
|
||||
.taskDefinitionKey(dto.getActivityId())
|
||||
.active()
|
||||
.list();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
tasks.forEach(e -> {
|
||||
sb.append("id:").append(e.getId()).append(", assignee: ").append(e.getAssignee());
|
||||
});
|
||||
log.info("tasks size:{}, info: {}", tasks.size(), JSON.toJSONString(sb));
|
||||
if (CollectionUtils.isEmpty(tasks) || tasks.size() > 1 || hasAssignee(tasks.get(0).getAssignee())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DateUtil.compare(DateUtil.date(), getDateTime(tasks.get(0).getCreateTime())) <= 0) {
|
||||
ManagementService managementService = processEngineConfiguration.getManagementService();
|
||||
managementService.executeCommand(context -> {
|
||||
TimerJobService timerJobService = CommandContextUtil.getTimerJobService();
|
||||
TimerJobEntity timerJobEntity = timerJobService.createTimerJob();
|
||||
timerJobEntity.setJobType("timer");
|
||||
timerJobEntity.setJobHandlerType(AsyncTermNodeAlterJobHandler.TYPE); // 这里填写你自定义的 JobHandler 类型
|
||||
timerJobEntity.setProcessInstanceId(dto.getProcessInstanceId());
|
||||
timerJobEntity.setExecutionId(null);
|
||||
timerJobEntity.setDuedate(getDateTime(new Date())); // 立即执行
|
||||
timerJobEntity.setRepeat(null); // 不重复
|
||||
timerJobEntity.setRetries(1);
|
||||
timerJobEntity.setJobHandlerConfiguration(dto.getActivityId()); // 可选,传递参数
|
||||
timerJobService.scheduleTimerJob(timerJobEntity);
|
||||
return null;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 不允许重复告警
|
||||
if (!refreshProperties.getRepeatAlter() && dto.getRetries() > 0) {
|
||||
return;
|
||||
}
|
||||
// 超过告警次数
|
||||
if (refreshProperties.getAlterRetries() != 0 && dto.getRetries() >= refreshProperties.getAlterRetries()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CategoryService bean = SpringContextUtils.getBean(CategoryService.class);
|
||||
Optional<CategoryItemVO> bpmModelCategory = bean.get("bpm_model_category", processInstance.getProcessDefinitionKey());
|
||||
// 发送告警对象
|
||||
Alter alter = SpringContextUtils.getBean(Alter.class);
|
||||
AlterDTO alterDTO = new AlterDTO();
|
||||
alterDTO.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
|
||||
alterDTO.setProcessDefinitionName(bpmModelCategory.orElse(new CategoryItemVO()).getLabel());
|
||||
alterDTO.setProcessInstanceId(dto.getProcessInstanceId());
|
||||
alterDTO.setActivityId(dto.getActivityId());
|
||||
alterDTO.setTaskId(tasks.get(0).getId());
|
||||
alterDTO.setStartTime(tasks.get(0).getCreateTime());
|
||||
alterDTO.setPrettyStartTime(DateUtil.formatDateTime(tasks.get(0).getCreateTime()));
|
||||
if (Boolean.TRUE.equals(refreshProperties.getAlterSendDingTalk())) {
|
||||
alter.invoke(alterDTO);
|
||||
|
||||
// 记录告警次数
|
||||
incRetries(job, dto, runtimeService, activityId);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime getDateTime(Date date) {
|
||||
DateTime dateTime;
|
||||
switch (refreshProperties.getAlterIntervalUnit()) {
|
||||
case MINUTES:
|
||||
dateTime = DateUtil.offsetMinute(date, refreshProperties.getPauseDelay());
|
||||
break;
|
||||
case HOURS:
|
||||
dateTime = DateUtil.offsetHour(date, refreshProperties.getPauseDelay());
|
||||
break;
|
||||
default:
|
||||
dateTime = DateUtil.offsetSecond(date, refreshProperties.getPauseDelay());
|
||||
break;
|
||||
}
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
private void incRetries(JobEntity job, TermNodePausingDTO dto, RuntimeService runtimeService, String activityId) {
|
||||
dto.setRetries(dto.getRetries() + 1);
|
||||
runtimeService.setVariable(job.getProcessInstanceId(), BIZ_NODE_ALTER + activityId, dto);
|
||||
}
|
||||
|
||||
// private void deleteTimerJob(TermNodePausingDTO dto) {
|
||||
// Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED,
|
||||
// new DeleteTimerJobTransactionListener(dto));
|
||||
// }
|
||||
|
||||
private Boolean hasAssignee(String assignee) {
|
||||
if (!StringUtils.hasText(assignee)) {
|
||||
return false;
|
||||
}
|
||||
String[] split = assignee.split("\\|");
|
||||
return split.length == 2 && NumberUtil.isPositiveNumber(Long.valueOf(split[1]));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
package cn.axzo.workflow.core.engine.job;
|
||||
|
||||
import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum;
|
||||
import cn.axzo.workflow.common.model.NextNodePreCheckAlterDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
|
||||
import cn.axzo.workflow.common.model.response.category.CategoryItemVO;
|
||||
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
|
||||
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
|
||||
import cn.axzo.workflow.core.deletage.BpmnTaskAssigneeSelector;
|
||||
import cn.axzo.workflow.core.listener.Alter;
|
||||
import cn.axzo.workflow.core.service.CategoryService;
|
||||
import cn.axzo.workflow.core.service.support.FlowNodeForecastService;
|
||||
import cn.axzo.workflow.core.util.RivenDingTalkHelper;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
@ -49,6 +49,12 @@ import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprove
|
||||
public class NextActivityConfigCheckJobHandler extends AbstractJobHandler implements JobHandler {
|
||||
public static final String TYPE = "next-activity-config-check";
|
||||
|
||||
private final SupportRefreshProperties refreshProperties;
|
||||
|
||||
public NextActivityConfigCheckJobHandler(SupportRefreshProperties refreshProperties) {
|
||||
this.refreshProperties = refreshProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
@ -77,15 +83,17 @@ public class NextActivityConfigCheckJobHandler extends AbstractJobHandler implem
|
||||
log.warn("NextActivityConfigCheckJobHandler msg: {}", e.getMessage(), e);
|
||||
CategoryService bean = SpringContextUtils.getBean(CategoryService.class);
|
||||
Optional<CategoryItemVO> bpmModelCategory = bean.get("bpm_model_category", processInstance.getProcessDefinitionKey());
|
||||
Alter alter = SpringContextUtils.getBean(Alter.class);
|
||||
RivenDingTalkHelper rivenDingTalkHelper = SpringContextUtils.getBean(RivenDingTalkHelper.class);
|
||||
FlowElement flowElement = ListUtils.emptyIfNull(flowElements).stream().filter(i -> i instanceof UserTask || i instanceof ReceiveTask || i instanceof ServiceTask).findFirst().orElse(null);
|
||||
NextNodePreCheckAlterDTO alterDTO = new NextNodePreCheckAlterDTO();
|
||||
alterDTO.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
|
||||
alterDTO.setProcessDefinitionName(bpmModelCategory.orElse(new CategoryItemVO()).getLabel());
|
||||
alterDTO.setProcessInstanceId(job.getProcessInstanceId());
|
||||
alterDTO.setActivityId(Objects.nonNull(flowElement) ? flowElement.getId() : null);
|
||||
alterDTO.setErrorMsg(e.getMessage());
|
||||
alter.invoke(alterDTO);
|
||||
|
||||
String title = "Notice 审批模板节点预检查告警, Env:" + refreshProperties.getProfile();
|
||||
String content = "#### [" + refreshProperties.getProfile() + "]审批模板节点预检查告警\n" +
|
||||
"> 审批业务: " + bpmModelCategory.orElse(new CategoryItemVO()).getLabel() + "(" + processInstance.getProcessDefinitionKey() + ")" + "\n\n" +
|
||||
"> 实例 ID:" + job.getProcessInstanceId() + "\n\n" +
|
||||
"> 检测节点 ID:" + (Objects.nonNull(flowElement) ? flowElement.getId() : null) + "\n\n" +
|
||||
"> ##### 错误信息:" + e.getMessage() + "\n\n";
|
||||
// 生产环境需要
|
||||
rivenDingTalkHelper.sendMarkdownMessage(title, content, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import cn.axzo.workflow.core.deletage.BpmnTaskDelegate;
|
||||
import cn.axzo.workflow.core.deletage.MockTaskAssigneeSelector;
|
||||
import cn.axzo.workflow.core.engine.cmd.CustomAbortProcessInstanceAsyncCmd;
|
||||
import cn.axzo.workflow.core.engine.job.NextActivityConfigCheckJobHandler;
|
||||
import cn.axzo.workflow.core.util.DingTalkUtils;
|
||||
import cn.axzo.workflow.core.util.RivenDingTalkHelper;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -90,6 +90,8 @@ public class EngineExecutionStartListener implements ExecutionListener {
|
||||
private List<BpmnTaskAssigneeSelector> selectors;
|
||||
@Resource
|
||||
private SupportRefreshProperties refreshProperties;
|
||||
@Resource
|
||||
private RivenDingTalkHelper rivenDingTalkHelper;
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution execution) {
|
||||
@ -252,7 +254,15 @@ public class EngineExecutionStartListener implements ExecutionListener {
|
||||
//发送钉钉消息
|
||||
if (Boolean.TRUE.equals(refreshProperties.getSendDingTalk())) {
|
||||
CooperationOrgDTO orgScopes = execution.getVariable(BIZ_ORG_RELATION, CooperationOrgDTO.class);
|
||||
DingTalkUtils.sendDingTalkForTransferToAdminError(profile, execution.getProcessInstanceId(), userTask.getId(), orgScopes, targetUrl);
|
||||
|
||||
String title = "Notice 转交管理员后的审批人为空, Env: " + profile;
|
||||
String content = "#### [" + profile + "]转交管理员后的审批人为空\n" +
|
||||
"> 流程实例 ID: " + execution.getProcessInstanceId() + "\n\n" +
|
||||
"> 节点参数(节点 ID): " + userTask.getId() + "\n\n" +
|
||||
"> OrgScopes 参数信息: " + JSONUtil.toJsonStr(orgScopes) + "\n\n" +
|
||||
"> 目标接口: " + targetUrl + "\n\n" +
|
||||
"> ##### 提示:仅作为提示信息,不代表是异常";
|
||||
rivenDingTalkHelper.sendMarkdownMessage(title, content, false, false);
|
||||
}
|
||||
BpmnProcessInstanceAbortDTO abortDTO = new BpmnProcessInstanceAbortDTO();
|
||||
abortDTO.setProcessInstanceId(execution.getProcessInstanceId());
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
package cn.axzo.workflow.core.listener;
|
||||
|
||||
import cn.axzo.workflow.common.model.dto.AlterDTO;
|
||||
|
||||
/**
|
||||
* Core 往外转发的钩子
|
||||
*
|
||||
@ -10,5 +8,5 @@ import cn.axzo.workflow.common.model.dto.AlterDTO;
|
||||
*/
|
||||
public interface Alter {
|
||||
|
||||
void invoke(Object obj);
|
||||
void _invoke(Object obj);
|
||||
}
|
||||
|
||||
@ -1,224 +0,0 @@
|
||||
package cn.axzo.workflow.core.util;
|
||||
|
||||
import cn.axzo.workflow.common.model.NextNodePreCheckAlterDTO;
|
||||
import cn.axzo.workflow.common.model.dto.AlterDTO;
|
||||
import cn.axzo.workflow.common.model.request.feature.DingTalkStarterAlterDTO;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.dingtalk.api.DefaultDingTalkClient;
|
||||
import com.dingtalk.api.DingTalkClient;
|
||||
import com.dingtalk.api.request.OapiRobotSendRequest;
|
||||
import com.dingtalk.api.response.OapiRobotSendResponse;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.azxo.framework.common.constatns.Constants.CTX_LOG_ID_MDC;
|
||||
|
||||
/**
|
||||
* 钉钉告警处理
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2024/1/12 14:34
|
||||
*/
|
||||
@Slf4j
|
||||
public class DingTalkUtils {
|
||||
|
||||
private static final String dingtalk_robot_webhook = "https://oapi.dingtalk" +
|
||||
".com/robot/send?access_token=341ee2907f3ebc15dc495fb7771a646230058710999fec7838066c109849878e";
|
||||
|
||||
private static final Map<String, String> ENV_URL_MAPPING = new HashMap<>();
|
||||
private static final Map<String, String> WEB_URL_MAPPING = new HashMap<>();
|
||||
|
||||
static {
|
||||
ENV_URL_MAPPING.put("local", "https://dev-app.axzo.cn");
|
||||
ENV_URL_MAPPING.put("dev", "https://dev-app.axzo.cn");
|
||||
ENV_URL_MAPPING.put("test", "https://test-api.axzo.cn");
|
||||
ENV_URL_MAPPING.put("pre", "https://pre-api.axzo.cn");
|
||||
ENV_URL_MAPPING.put("live", "https://live-api.axzo.cn");
|
||||
ENV_URL_MAPPING.put("default", "https://api.axzo.cn");
|
||||
|
||||
WEB_URL_MAPPING.put("local", "https://dev-new-workflow-web.axzo.cn");
|
||||
WEB_URL_MAPPING.put("dev", "https://dev-new-workflow-web.axzo.cn");
|
||||
WEB_URL_MAPPING.put("test", "https://test-new-workflow-web.axzo.cn");
|
||||
WEB_URL_MAPPING.put("pre", "https://pre-new-workflow-web.axzo.cn");
|
||||
WEB_URL_MAPPING.put("live", "https://live-new-workflow-web.axzo.cn");
|
||||
WEB_URL_MAPPING.put("default", "https://new-workflow-web.axzo.cn");
|
||||
}
|
||||
|
||||
public static String getEnvUrl(String profile) {
|
||||
String urlPrefix = ENV_URL_MAPPING.get(profile);
|
||||
if (!StringUtils.hasText(urlPrefix)) {
|
||||
urlPrefix = ENV_URL_MAPPING.get("default");
|
||||
}
|
||||
return urlPrefix;
|
||||
}
|
||||
|
||||
public static String getWebUrl(String profile) {
|
||||
String urlPrefix = WEB_URL_MAPPING.get(profile);
|
||||
if (!StringUtils.hasText(urlPrefix)) {
|
||||
urlPrefix = WEB_URL_MAPPING.get("default");
|
||||
}
|
||||
return urlPrefix;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void sendDingTalk(String profile, String title, Object req, String invokeServerName, Throwable throwable) {
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
request.setMsgtype("markdown");
|
||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
markdown.setTitle("Notice " + title + ", Env: " + profile);
|
||||
String text = "#### [" + profile + "]" + title + "\n" +
|
||||
"> 时间: " + DateUtil.now() + "\n" +
|
||||
"> 入参: " + JSONUtil.toJsonStr(req) + "\n\n" +
|
||||
"> ###### 异常信息: " + JSONUtil.toJsonStr(Objects.isNull(throwable.getCause()) ? throwable.getMessage() :
|
||||
throwable.getCause().getMessage()) + " \n\n" +
|
||||
"> ##### traceId: " + MDC.get(CTX_LOG_ID_MDC) + " \n" +
|
||||
"> ##### 调用方服务名称:" + invokeServerName + " \n";
|
||||
String deadLetterStacktrace = getDeadLetterStacktrace(profile, req);
|
||||
text = text + (StringUtils.hasText(deadLetterStacktrace) ? "> ##### [点击查看异常明细](" + deadLetterStacktrace + ") \n" : "");
|
||||
markdown.setText(text);
|
||||
request.setMarkdown(markdown);
|
||||
sendDingTalk(request);
|
||||
}
|
||||
|
||||
private static String getDeadLetterStacktrace(String profile, Object req) {
|
||||
try {
|
||||
JSONObject entries = JSONUtil.parseObj(req);
|
||||
String jobId = entries.getStr("id");
|
||||
if (!StringUtils.hasText(jobId)) {
|
||||
return "";
|
||||
}
|
||||
return getEnvUrl(profile) + "/workflow-engine/web/v1/api/process/job/dead-letter/exception/stacktrace/byId?jobId=" + jobId;
|
||||
} catch (Exception e) {
|
||||
log.warn("构造查询错误堆栈地址异常", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void sendDingTalkForSlowUrl(String profile, Double time, String apiUrl, Object requestParam, Object responseBody) {
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
request.setMsgtype("markdown");
|
||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
markdown.setTitle("Notice 请求二方接口慢 URL, Env: " + profile);
|
||||
markdown.setText("#### [" + profile + "]请求二方接口过慢\n" +
|
||||
"> 接口地址: " + apiUrl + ",经过了" + time + "秒\n\n" +
|
||||
"> 请求参数: " + JSONUtil.toJsonStr(requestParam) + "\n\n" +
|
||||
"> ###### 结果: " + JSONUtil.toJsonStr(responseBody) + " \n");
|
||||
request.setMarkdown(markdown);
|
||||
sendDingTalk(request);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void sendDingTalkForSlowApi(String profile, Double time, String apiUrl, String uniqueId) {
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
request.setMsgtype("markdown");
|
||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
markdown.setTitle("Notice API 处理耗时报告, Env: " + profile);
|
||||
markdown.setText("#### [" + profile + "]API 处理耗时报告\n" +
|
||||
"> 接口地址: " + apiUrl + ",经过了" + time + "秒\n\n" +
|
||||
"> ###### 标识: " + uniqueId + " \n");
|
||||
request.setMarkdown(markdown);
|
||||
sendDingTalk(request);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static void sendDingTalk(OapiRobotSendRequest request) {
|
||||
DingTalkClient client = new DefaultDingTalkClient(dingtalk_robot_webhook);
|
||||
OapiRobotSendResponse response = client.execute(request);
|
||||
}
|
||||
|
||||
public static void sendDingTalkForBizNodeAlter(String profile, AlterDTO alterDTO, List<String> atMobiles) {
|
||||
// if (CollectionUtils.isEmpty(atMobiles)) {
|
||||
// return;
|
||||
// }
|
||||
String processInstanceId = alterDTO.getProcessInstanceId();
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
request.setMsgtype("markdown");
|
||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
markdown.setTitle("Notice 业务节点长时间停止告警, Env: " + profile);
|
||||
markdown.setText("#### [" + profile + "]业务节点长时间停止\n" +
|
||||
"> 审批业务: " + alterDTO.getProcessDefinitionName() + "(" + alterDTO.getProcessDefinitionKey() + ")" + "\n\n" +
|
||||
"> 节点相关信息: " + JSONUtil.toJsonStr(alterDTO) + "\n\n" +
|
||||
"> ##### [点击查看审批日志](" + getWebUrl(profile) + "/#/workflow/examples?processInstanceId=" + processInstanceId + ") \n\n" +
|
||||
"> ##### 提示:如果以上地址提示未登录,请在对应环境 OMS 系统登录后并点击审批管理的功能后,再使用。或者复制实例 ID 到 OMS 中手动查询 \n\n" +
|
||||
mobiles(atMobiles));
|
||||
request.setMarkdown(markdown);
|
||||
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
|
||||
at.setAtMobiles(atMobiles);
|
||||
at.setIsAtAll(false);
|
||||
request.setAt(at);
|
||||
sendDingTalk(request);
|
||||
}
|
||||
|
||||
public static String mobiles(List<String> atMobiles) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < atMobiles.size(); i++) {
|
||||
if (i != 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("@").append(atMobiles.get(i));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void sendDingTalkForNodePreCheck(String profile, NextNodePreCheckAlterDTO alterDTO, List<String> alterMobiles) {
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
request.setMsgtype("markdown");
|
||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
markdown.setTitle("Notice 审批模板节点预检查告警, Env: " + profile);
|
||||
markdown.setText("#### [" + profile + "]审批模板节点预检查告警\n" +
|
||||
"> 审批业务: " + alterDTO.getProcessDefinitionName() + "(" + alterDTO.getProcessDefinitionKey() + ")" + "\n\n" +
|
||||
"> 实例 ID:" + alterDTO.getProcessInstanceId() + "\n\n" +
|
||||
"> 检测节点 ID:" + alterDTO.getActivityId() + "\n\n" +
|
||||
"> ##### 错误信息:" + alterDTO.getErrorMsg() + "\n\n" +
|
||||
mobiles(alterMobiles));
|
||||
request.setMarkdown(markdown);
|
||||
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
|
||||
at.setAtMobiles(alterMobiles);
|
||||
at.setIsAtAll(false);
|
||||
request.setAt(at);
|
||||
sendDingTalk(request);
|
||||
|
||||
}
|
||||
|
||||
public static void sendDingTalkForTransferToAdminError(String profile, String processInstanceId, String taskDefinitionKey, Object orgScopes, String targetUrl) {
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
request.setMsgtype("markdown");
|
||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
markdown.setTitle("Notice 转交管理员后的审批人为空, Env: " + profile);
|
||||
markdown.setText("#### [" + profile + "]转交管理员后的审批人为空\n" +
|
||||
"> 流程实例 ID: " + processInstanceId + "\n\n" +
|
||||
"> 节点参数(节点 ID): " + taskDefinitionKey + "\n\n" +
|
||||
"> OrgScopes 参数信息: " + JSONUtil.toJsonStr(orgScopes) + "\n\n" +
|
||||
"> 目标接口: " + targetUrl + "\n\n" +
|
||||
"> ##### 提示:仅作为提示信息,不代表是异常");
|
||||
request.setMarkdown(markdown);
|
||||
sendDingTalk(request);
|
||||
}
|
||||
|
||||
public static void sendDingTalkForStarter(DingTalkStarterAlterDTO dto, List<String> alterMobiles) {
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
request.setMsgtype("markdown");
|
||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
markdown.setTitle("Notice 应用必接广播 MQ 事件告警, Env: " + dto.getProfile());
|
||||
markdown.setText("#### [" + dto.getProfile() + "]应用必接广播 MQ 事件告警\n" +
|
||||
"> 应用名称:" + dto.getApplicationName() + "\n\n" +
|
||||
"> 检测信息:" + dto.getAlterContent() + "\n\n" +
|
||||
mobiles(alterMobiles));
|
||||
request.setMarkdown(markdown);
|
||||
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
|
||||
at.setAtMobiles(alterMobiles);
|
||||
at.setIsAtAll(false);
|
||||
request.setAt(at);
|
||||
sendDingTalk(request);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,177 @@
|
||||
package cn.axzo.workflow.core.util;
|
||||
|
||||
import cn.axzo.riven.client.feign.DingDingMsgApi;
|
||||
import cn.axzo.riven.client.model.SampleMarkdown;
|
||||
import cn.axzo.riven.client.req.DingDingSendRebootGroupMsgReq;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Riven 钉钉消息发送统一工具类
|
||||
* <p>
|
||||
* 用于替代原有的 webhook 直调方式,统一通过 Riven 服务发送钉钉消息。
|
||||
* 支持所有业务场景,包括:
|
||||
* - 业务节点告警
|
||||
* - 节点预检查告警
|
||||
* - 应用必接事件告警
|
||||
* - DLQ 监控告警
|
||||
* - 慢接口告警
|
||||
* - 异常告警
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-01-21
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RivenDingTalkHelper {
|
||||
@Value("${spring.profiles.active}")
|
||||
private String profile;
|
||||
@Value(value = "${workflow.alter.mobiles:}")
|
||||
private List<String> alterMobiles;
|
||||
|
||||
/**
|
||||
* Riven 钉钉消息 Feign 客户端
|
||||
* 注意:需要在项目中引入 riven-api 依赖
|
||||
*/
|
||||
@Resource
|
||||
private DingDingMsgApi dingDingMsgApi;
|
||||
|
||||
/**
|
||||
* 钉钉消息场景标识(工作流小分队使用)
|
||||
*/
|
||||
private static final String DINGTALK_NOT_MASTER_SCENE = "WORKFLOW_ENGINE_BIZNODE_ALTER";
|
||||
/**
|
||||
* 钉钉消息场景标识(工作流生产环境使用)
|
||||
*/
|
||||
private static final String DINGTALK_MASTER_SCENE = "WORKFLOW_ENGINE_MASTER_ALTER";
|
||||
|
||||
/**
|
||||
* 环境 URL 映射(API)
|
||||
*/
|
||||
private static final Map<String, String> ENV_URL_MAPPING = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 环境 URL 映射(Web)
|
||||
*/
|
||||
private static final Map<String, String> WEB_URL_MAPPING = new HashMap<>();
|
||||
|
||||
static {
|
||||
// API 地址映射
|
||||
ENV_URL_MAPPING.put("local", "https://dev-app.axzo.cn");
|
||||
ENV_URL_MAPPING.put("dev", "https://dev-app.axzo.cn");
|
||||
ENV_URL_MAPPING.put("test", "https://test-api.axzo.cn");
|
||||
ENV_URL_MAPPING.put("pre", "https://pre-api.axzo.cn");
|
||||
ENV_URL_MAPPING.put("live", "https://live-api.axzo.cn");
|
||||
ENV_URL_MAPPING.put("default", "https://api.axzo.cn");
|
||||
|
||||
// Web 地址映射
|
||||
WEB_URL_MAPPING.put("local", "https://dev-new-workflow-web.axzo.cn");
|
||||
WEB_URL_MAPPING.put("dev", "https://dev-new-workflow-web.axzo.cn");
|
||||
WEB_URL_MAPPING.put("test", "https://test-new-workflow-web.axzo.cn");
|
||||
WEB_URL_MAPPING.put("pre", "https://pre-new-workflow-web.axzo.cn");
|
||||
WEB_URL_MAPPING.put("live", "https://live-new-workflow-web.axzo.cn");
|
||||
WEB_URL_MAPPING.put("default", "https://new-workflow-web.axzo.cn");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取环境对应的 API 地址前缀
|
||||
*
|
||||
* @param profile 环境标识(dev/test/pre/live/master 等)
|
||||
* @return API 地址前缀
|
||||
*/
|
||||
public static String getEnvUrl(String profile) {
|
||||
String urlPrefix = ENV_URL_MAPPING.get(profile);
|
||||
if (!StringUtils.hasText(urlPrefix)) {
|
||||
urlPrefix = ENV_URL_MAPPING.get("default");
|
||||
}
|
||||
return urlPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取环境对应的 Web 地址前缀
|
||||
*
|
||||
* @param profile 环境标识
|
||||
* @return Web 地址前缀
|
||||
*/
|
||||
public static String getWebUrl(String profile) {
|
||||
String urlPrefix = WEB_URL_MAPPING.get(profile);
|
||||
if (!StringUtils.hasText(urlPrefix)) {
|
||||
urlPrefix = WEB_URL_MAPPING.get("default");
|
||||
}
|
||||
return urlPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化 @人员列表为字符串
|
||||
*
|
||||
* @param atMobiles 手机号列表
|
||||
* @return @人员字符串,格式:@手机号1,@手机号2
|
||||
*/
|
||||
public static String formatMobiles(List<String> atMobiles) {
|
||||
if (atMobiles == null || atMobiles.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < atMobiles.size(); i++) {
|
||||
if (i != 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("@").append(atMobiles.get(i));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送 Markdown 格式的钉钉消息(核心方法)
|
||||
*
|
||||
* @param title 消息标题
|
||||
* @param text 消息内容(Markdown 格式)
|
||||
*/
|
||||
public void sendMarkdownMessage(String title, String text, Boolean targetIsMaster, Boolean at) {
|
||||
sendMarkdownMessage(title, text, targetIsMaster, at, alterMobiles);
|
||||
}
|
||||
|
||||
public void sendMarkdownMessage(String title, String text, Boolean targetIsMaster, Boolean at, List<String> mobiles) {
|
||||
DingDingSendRebootGroupMsgReq req = new DingDingSendRebootGroupMsgReq();
|
||||
if (Objects.equals(targetIsMaster, Boolean.TRUE) && Objects.equals(profile, "master")) {
|
||||
req.setDingDingScene(DINGTALK_MASTER_SCENE);
|
||||
} else {
|
||||
req.setDingDingScene(DINGTALK_NOT_MASTER_SCENE);
|
||||
}
|
||||
|
||||
// 构建 Markdown 消息
|
||||
SampleMarkdown markdown = new SampleMarkdown(title, text);
|
||||
JSONObject markdownJson = JSONObject.parseObject(markdown.toJson());
|
||||
if (Objects.equals(at, Boolean.TRUE)) {
|
||||
if (CollectionUtils.isEmpty(mobiles)) {
|
||||
mobiles = alterMobiles;
|
||||
}
|
||||
|
||||
// 添加 @人员信息
|
||||
if (mobiles != null && !mobiles.isEmpty()) {
|
||||
JSONObject atMobilesJson = new JSONObject();
|
||||
atMobilesJson.put("atMobiles", alterMobiles);
|
||||
markdownJson.put("at", atMobilesJson);
|
||||
markdownJson.put("isAtAll", false);
|
||||
markdown.setText(markdown.getText() + formatMobiles(alterMobiles));
|
||||
}
|
||||
}
|
||||
req.setDingDingJson(markdownJson.toJSONString());
|
||||
req.setMsgType(cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum.sampleMarkdown);
|
||||
|
||||
// 调用 Riven API
|
||||
dingDingMsgApi.sendRebootGroupMsg(req);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,141 +0,0 @@
|
||||
package cn.axzo.workflow.server.alter;
|
||||
|
||||
import cn.axzo.framework.jackson.utility.JSON;
|
||||
import cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum;
|
||||
import cn.axzo.riven.client.feign.DingDingMsgApi;
|
||||
import cn.axzo.riven.client.model.SampleMarkdown;
|
||||
import cn.axzo.riven.client.req.DingDingSendRebootGroupMsgReq;
|
||||
import cn.axzo.workflow.common.model.NextNodePreCheckAlterDTO;
|
||||
import cn.axzo.workflow.common.model.dto.AlterDTO;
|
||||
import cn.axzo.workflow.common.model.request.feature.DingTalkStarterAlterDTO;
|
||||
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
|
||||
import cn.axzo.workflow.core.listener.Alter;
|
||||
import cn.axzo.workflow.core.util.DingTalkUtils;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.axzo.workflow.core.util.DingTalkUtils.getWebUrl;
|
||||
import static cn.axzo.workflow.core.util.DingTalkUtils.mobiles;
|
||||
|
||||
/**
|
||||
* 钉钉告警实现
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2024-09-13 11:40
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DingTalkAlter implements Alter {
|
||||
|
||||
@Value("${spring.profiles.active}")
|
||||
private String profile;
|
||||
@Resource
|
||||
private SupportRefreshProperties refreshProperties;
|
||||
@Resource
|
||||
private DingDingMsgApi dingDingMsgApi;
|
||||
|
||||
@Override
|
||||
public void invoke(Object obj) {
|
||||
if (!Boolean.TRUE.equals(refreshProperties.getAlterSendDingTalk())) {
|
||||
log.info("ignore send alter");
|
||||
return;
|
||||
}
|
||||
log.info("send biz node alter : {}", JSON.toJSONString(obj));
|
||||
if (obj instanceof AlterDTO) {
|
||||
AlterDTO alterDTO = (AlterDTO) obj;
|
||||
if (Objects.equals(profile, "master")) {
|
||||
DingTalkUtils.sendDingTalkForBizNodeAlter(profile, alterDTO, refreshProperties.getAlterMobiles());
|
||||
} else {
|
||||
rivenDingtalk(alterDTO);
|
||||
}
|
||||
}
|
||||
if (obj instanceof NextNodePreCheckAlterDTO) {
|
||||
NextNodePreCheckAlterDTO alterDTO = (NextNodePreCheckAlterDTO) obj;
|
||||
if (Objects.equals(profile, "master")) {
|
||||
DingTalkUtils.sendDingTalkForNodePreCheck(profile, alterDTO, refreshProperties.getAlterMobiles());
|
||||
} else {
|
||||
rivenDingtalkForNodePreCheck(alterDTO);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj instanceof DingTalkStarterAlterDTO) {
|
||||
DingTalkStarterAlterDTO starterAlterDTO = (DingTalkStarterAlterDTO) obj;
|
||||
if (refreshProperties.getIgnoreMqAlterApplicationNames().contains(starterAlterDTO.getApplicationName())) {
|
||||
// 忽略必接事件的应用
|
||||
return;
|
||||
}
|
||||
DingTalkUtils.sendDingTalkForStarter(starterAlterDTO, refreshProperties.getAlterMobiles());
|
||||
}
|
||||
}
|
||||
|
||||
private void rivenDingtalkForStarter(DingTalkStarterAlterDTO alterDTO) {
|
||||
DingDingSendRebootGroupMsgReq req = new DingDingSendRebootGroupMsgReq();
|
||||
req.setDingDingScene("WORKFLOW_ENGINE_BIZNODE_ALTER");
|
||||
String title = "Notice 应用必接事件告警, Env: " + profile;
|
||||
String text = "#### [" + profile + "]应用必接事件告警\n" +
|
||||
"> 应用名称:" + alterDTO.getApplicationName() + "\n\n" +
|
||||
"> 检测信息:" + alterDTO.getAlterContent() + "\n\n" +
|
||||
mobiles(refreshProperties.getAlterMobiles());
|
||||
SampleMarkdown markdown = new SampleMarkdown(title, text);
|
||||
JSONObject markdownJson = JSONObject.parseObject(markdown.toJson());
|
||||
JSONObject atMobiles = new JSONObject();
|
||||
atMobiles.put("atMobiles", refreshProperties.getAlterMobiles());
|
||||
markdownJson.put("at", atMobiles);
|
||||
markdownJson.put("isAtAll", false);
|
||||
req.setDingDingJson(markdownJson.toJSONString());
|
||||
req.setMsgType(DingTalkMsgTypeEnum.sampleMarkdown);
|
||||
dingDingMsgApi.sendRebootGroupMsg(req);
|
||||
}
|
||||
|
||||
private void rivenDingtalkForNodePreCheck(NextNodePreCheckAlterDTO alterDTO) {
|
||||
DingDingSendRebootGroupMsgReq req = new DingDingSendRebootGroupMsgReq();
|
||||
req.setDingDingScene("WORKFLOW_ENGINE_BIZNODE_ALTER");
|
||||
String processInstanceId = alterDTO.getProcessInstanceId();
|
||||
String title = "Notice 审批模板节点预检查告警, Env: " + profile;
|
||||
String text = "#### [" + profile + "]审批模板节点预检查告警\n" +
|
||||
// "> 相关信息: " + JSONUtil.toJsonStr(alterDTO) + "\n\n" +
|
||||
"> 实例 ID:" + alterDTO.getProcessInstanceId() + "\n\n" +
|
||||
"> 检测节点 ID:" + alterDTO.getActivityId() + "\n\n" +
|
||||
"> ##### 错误信息:" + alterDTO.getErrorMsg() + "\n\n" +
|
||||
mobiles(refreshProperties.getAlterMobiles());
|
||||
SampleMarkdown markdown = new SampleMarkdown(title, text);
|
||||
|
||||
JSONObject markdownJson = JSONObject.parseObject(markdown.toJson());
|
||||
JSONObject atMobiles = new JSONObject();
|
||||
atMobiles.put("atMobiles", refreshProperties.getAlterMobiles());
|
||||
markdownJson.put("at", atMobiles);
|
||||
markdownJson.put("isAtAll", false);
|
||||
req.setDingDingJson(markdownJson.toJSONString());
|
||||
req.setMsgType(DingTalkMsgTypeEnum.sampleMarkdown);
|
||||
dingDingMsgApi.sendRebootGroupMsg(req);
|
||||
}
|
||||
|
||||
private void rivenDingtalk(AlterDTO alterDTO) {
|
||||
DingDingSendRebootGroupMsgReq req = new DingDingSendRebootGroupMsgReq();
|
||||
req.setDingDingScene("WORKFLOW_ENGINE_BIZNODE_ALTER");
|
||||
String processInstanceId = alterDTO.getProcessInstanceId();
|
||||
String title = "Notice 业务节点长时间停止告警, Env: " + profile;
|
||||
String text = "#### [" + profile + "]业务节点长时间停止\n" +
|
||||
"> 节点相关信息: " + JSONUtil.toJsonStr(alterDTO) + "\n\n" +
|
||||
"> ##### [点击查看审批日志](" + getWebUrl(profile) + "/#/workflow/examples?processInstanceId=" + processInstanceId + ") \n\n" +
|
||||
"> ##### 提示:如果以上地址提示未登录,请在对应环境 OMS 系统登录后并点击审批管理的功能后,再使用。或者复制实例 ID 到 OMS 中手动查询 \n\n " +
|
||||
mobiles(refreshProperties.getAlterMobiles());
|
||||
SampleMarkdown markdown = new SampleMarkdown(title, text);
|
||||
|
||||
JSONObject markdownJson = JSONObject.parseObject(markdown.toJson());
|
||||
JSONObject atMobiles = new JSONObject();
|
||||
atMobiles.put("atMobiles", refreshProperties.getAlterMobiles());
|
||||
markdownJson.put("at", atMobiles);
|
||||
markdownJson.put("isAtAll", false);
|
||||
req.setDingDingJson(markdownJson.toJSONString());
|
||||
req.setMsgType(DingTalkMsgTypeEnum.sampleMarkdown);
|
||||
dingDingMsgApi.sendRebootGroupMsg(req);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,9 +1,19 @@
|
||||
package cn.axzo.workflow.server.common.annotation;
|
||||
|
||||
import cn.axzo.workflow.core.util.DingTalkUtils;
|
||||
import cn.axzo.workflow.core.util.RivenDingTalkHelper;
|
||||
import cn.axzo.workflow.server.common.util.SpringUtils;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.axzo.workflow.core.util.RivenDingTalkHelper.getEnvUrl;
|
||||
import static cn.axzo.workflow.server.common.aspectj.RepeatSubmitAspect.argsArrayToString;
|
||||
import static cn.azxo.framework.common.constatns.Constants.CTX_LOG_ID_MDC;
|
||||
|
||||
|
||||
/**
|
||||
@ -22,10 +32,9 @@ public enum ReporterType {
|
||||
@Override
|
||||
public void executeAction(String profile, String title, Boolean sendDingTalk, Object[] args, String shortString, String invokeServerName, Throwable e, Boolean downgrade) {
|
||||
if (sendDingTalk) {
|
||||
DingTalkUtils.sendDingTalk(profile, title, argsArrayToString(args), invokeServerName, e);
|
||||
sendRivenDingTalk(profile, title, args, invokeServerName, e);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
/**
|
||||
* 仅打印日志
|
||||
@ -36,7 +45,6 @@ public enum ReporterType {
|
||||
if (downgrade) {
|
||||
log.warn("ER: {}", e.getMessage());
|
||||
} else {
|
||||
// LogUtil.error(LogUtil.ErrorType.ERROR_BUSINESS, shortString, e.getMessage(), e);
|
||||
logWarn(e, shortString);
|
||||
}
|
||||
}
|
||||
@ -48,7 +56,7 @@ public enum ReporterType {
|
||||
@Override
|
||||
public void executeAction(String profile, String title, Boolean sendDingTalk, Object[] args, String shortString, String invokeServerName, Throwable e, Boolean downgrade) {
|
||||
if (sendDingTalk) {
|
||||
DingTalkUtils.sendDingTalk(profile, title, argsArrayToString(args),invokeServerName, e);
|
||||
sendRivenDingTalk(profile, title, args, invokeServerName, e);
|
||||
}
|
||||
if (downgrade) {
|
||||
log.warn("ER: {}", e.getMessage());
|
||||
@ -76,4 +84,35 @@ public enum ReporterType {
|
||||
// LogUtil.error(LogUtil.ErrorType.ERROR_BUSINESS, shortString, throwable.getMessage(), throwable);
|
||||
// }
|
||||
}
|
||||
|
||||
public void sendRivenDingTalk(String profile, String title, Object[] args, String invokeServerName, Throwable e) {
|
||||
RivenDingTalkHelper rivenDingTalkHelper = SpringUtils.getBean(RivenDingTalkHelper.class);
|
||||
String mdTitle = "Notice " + title + ", Env: " + profile;
|
||||
String mdContent = "#### [" + profile + "]" + title + "\n" +
|
||||
"> 时间: " + DateUtil.now() + "\n" +
|
||||
"> 入参: " + JSONUtil.toJsonStr(argsArrayToString(args)) + "\n\n" +
|
||||
"> ###### 异常信息: " + JSONUtil.toJsonStr(Objects.isNull(e.getCause()) ? e.getMessage() :
|
||||
e.getCause().getMessage()) + " \n\n" +
|
||||
"> ##### traceId: " + MDC.get(CTX_LOG_ID_MDC) + " \n" +
|
||||
"> ##### 调用方服务名称:" + invokeServerName + " \n";
|
||||
String deadLetterStacktrace = getDeadLetterStacktrace(profile, argsArrayToString(args));
|
||||
mdContent = mdContent + (StringUtils.hasText(deadLetterStacktrace) ? "> ##### [点击查看异常明细](" + deadLetterStacktrace + ") \n" : "");
|
||||
|
||||
rivenDingTalkHelper.sendMarkdownMessage(mdTitle, mdContent, false, false);
|
||||
}
|
||||
|
||||
private static String getDeadLetterStacktrace(String profile, Object req) {
|
||||
try {
|
||||
JSONObject entries = JSONUtil.parseObj(req);
|
||||
String jobId = entries.getStr("id");
|
||||
if (!StringUtils.hasText(jobId)) {
|
||||
return "";
|
||||
}
|
||||
return getEnvUrl(profile) + "/workflow-engine/web/v1/api/process/job/dead-letter/exception/stacktrace/byId?jobId=" + jobId;
|
||||
} catch (Exception e) {
|
||||
log.warn("构造查询错误堆栈地址异常", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,12 +11,13 @@ import cn.axzo.workflow.common.exception.WorkflowApproverCalcException;
|
||||
import cn.axzo.workflow.common.exception.WorkflowEngineException;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
|
||||
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
|
||||
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
|
||||
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
|
||||
import cn.axzo.workflow.core.deletage.BpmnTaskAssigneeSelector;
|
||||
import cn.axzo.workflow.core.deletage.approverscope.ApproverScopeDTO;
|
||||
import cn.axzo.workflow.core.deletage.approverscope.ApproverScopeProcessor;
|
||||
import cn.axzo.workflow.core.engine.model.NoticeFlowElement;
|
||||
import cn.axzo.workflow.core.util.DingTalkUtils;
|
||||
import cn.axzo.workflow.core.util.RivenDingTalkHelper;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@ -64,6 +65,8 @@ public abstract class AbstractBpmnTaskAssigneeSelector implements BpmnTaskAssign
|
||||
protected FlowSupportApi flowSupportApi;
|
||||
@Resource
|
||||
protected SupportRefreshProperties refreshProperties;
|
||||
@Resource
|
||||
private RivenDingTalkHelper rivenDingTalkHelper;
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
@ -136,12 +139,12 @@ public abstract class AbstractBpmnTaskAssigneeSelector implements BpmnTaskAssign
|
||||
JSONUtil.toJsonStr(result));
|
||||
try {
|
||||
if (stopWatch.getTotalTimeSeconds() > refreshProperties.getApiTimeout() && Boolean.TRUE.equals(refreshProperties.getSendDingTalk())) {
|
||||
DingTalkUtils.sendDingTalkForSlowUrl(applicationContext.getEnvironment()
|
||||
.getProperty("spring.profiles.active"),
|
||||
stopWatch.getTotalTimeSeconds(),
|
||||
extInfo,
|
||||
param,
|
||||
result);
|
||||
String title = "Notice 请求二方接口慢 URL, Env: " + refreshProperties.getProfile();
|
||||
String content = "#### [" + refreshProperties.getProfile() + "]请求二方接口过慢\n" +
|
||||
"> 接口地址: " + extInfo + ",经过了" + stopWatch.getTotalTimeSeconds() + "秒\n\n" +
|
||||
"> 请求参数: " + JSONUtil.toJsonStr(param) + "\n\n" +
|
||||
"> ###### 结果: " + JSONUtil.toJsonStr(result) + " \n";
|
||||
rivenDingTalkHelper.sendMarkdownMessage(title, content, true, false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
@ -170,12 +173,13 @@ public abstract class AbstractBpmnTaskAssigneeSelector implements BpmnTaskAssign
|
||||
JSONUtil.toJsonStr(result));
|
||||
try {
|
||||
if (stopWatch.getTotalTimeSeconds() > refreshProperties.getApiTimeout() && Boolean.TRUE.equals(refreshProperties.getSendDingTalk())) {
|
||||
DingTalkUtils.sendDingTalkForSlowUrl(applicationContext.getEnvironment()
|
||||
.getProperty("spring.profiles.active"),
|
||||
stopWatch.getTotalTimeSeconds(),
|
||||
extInfo,
|
||||
param,
|
||||
result);
|
||||
String title = "Notice 请求二方接口慢 URL, Env: " + refreshProperties.getProfile();
|
||||
String content = "#### [" + refreshProperties.getProfile() + "]请求二方接口过慢\n" +
|
||||
"> 接口地址: " + extInfo + ",经过了" + stopWatch.getTotalTimeSeconds() + "秒\n\n" +
|
||||
"> 请求参数: " + JSONUtil.toJsonStr(param) + "\n\n" +
|
||||
"> ###### 结果: " + JSONUtil.toJsonStr(result) + " \n";
|
||||
|
||||
rivenDingTalkHelper.sendMarkdownMessage(title, content, true, false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
@ -207,12 +211,14 @@ public abstract class AbstractBpmnTaskAssigneeSelector implements BpmnTaskAssign
|
||||
JSONUtil.toJsonStr(result));
|
||||
try {
|
||||
if (stopWatch.getTotalTimeSeconds() > refreshProperties.getApiTimeout() && refreshProperties.getSendDingTalk()) {
|
||||
DingTalkUtils.sendDingTalkForSlowUrl(context.getEnvironment()
|
||||
.getProperty("spring.profiles.active"),
|
||||
stopWatch.getTotalTimeSeconds(),
|
||||
extInfo,
|
||||
param,
|
||||
result);
|
||||
RivenDingTalkHelper rivenDingTalkHelper = SpringContextUtils.getBean(RivenDingTalkHelper.class);
|
||||
|
||||
String title = "Notice 请求二方接口慢 URL, Env: " + refreshProperties.getProfile();
|
||||
String content = "#### [" + refreshProperties.getProfile() + "]请求二方接口过慢\n" +
|
||||
"> 接口地址: " + extInfo + ",经过了" + stopWatch.getTotalTimeSeconds() + "秒\n\n" +
|
||||
"> 请求参数: " + JSONUtil.toJsonStr(param) + "\n\n" +
|
||||
"> ###### 结果: " + JSONUtil.toJsonStr(result) + " \n";
|
||||
rivenDingTalkHelper.sendMarkdownMessage(title, content, true, false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
|
||||
@ -63,6 +63,7 @@ public class TransferToAdminTaskAssigneeSelector extends AbstractBpmnTaskAssigne
|
||||
protected List<BpmnTaskDelegateAssigner> invokeService(FlowElement flowElement, DelegateExecution execution,
|
||||
ApproverScopeDTO scopeDto) {
|
||||
|
||||
log.info("transferToAdmin invokeService executing...");
|
||||
if (Boolean.TRUE.equals(supportRefreshProperties.getUseNewToAdminApi())) {
|
||||
return invokeNewQuery(flowElement, execution, scopeDto);
|
||||
} else {
|
||||
@ -72,6 +73,7 @@ public class TransferToAdminTaskAssigneeSelector extends AbstractBpmnTaskAssigne
|
||||
}
|
||||
|
||||
private List<BpmnTaskDelegateAssigner> invokeOldQuery(FlowElement flowElement, DelegateExecution execution, ApproverScopeDTO scopeDto) {
|
||||
log.info("transferToAdmin invokeOldQuery executing...");
|
||||
ListFlowTaskAssignerReq.ListFlowTaskAssignerReqBuilder builder = ListFlowTaskAssignerReq.builder();
|
||||
if (!CollectionUtils.isEmpty(scopeDto.getOrgScopes())) {
|
||||
builder.orgScopes(ListUtils.emptyIfNull(scopeDto.getOrgScopes()).stream()
|
||||
@ -106,6 +108,7 @@ public class TransferToAdminTaskAssigneeSelector extends AbstractBpmnTaskAssigne
|
||||
}
|
||||
|
||||
private List<BpmnTaskDelegateAssigner> invokeNewQuery(FlowElement flowElement, DelegateExecution execution, ApproverScopeDTO scopeDto) {
|
||||
log.info("transferToAdmin invokeNewQuery executing...");
|
||||
Optional<ApproverScopeEnum> approverScope = BpmnMetaParserHelper.getApproverScope((UserTask) flowElement);
|
||||
Optional<ApproverSpecifyEnum> optSpecify = BpmnMetaParserHelper.getApproverSpecify((UserTask) flowElement);
|
||||
// 如果是项目部,且审批人指定的配法不是岗位或角色,则默认直接返回空集合,走转交管理员后为空的最终兜底逻辑
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
package cn.axzo.workflow.server.controller.web.manage;
|
||||
|
||||
import cn.axzo.framework.jackson.utility.JSON;
|
||||
import cn.axzo.workflow.client.feign.manage.FunctionApi;
|
||||
import cn.axzo.workflow.common.enums.AdminDataSource;
|
||||
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
|
||||
import cn.axzo.workflow.common.model.dto.CommonDingTalkDTO;
|
||||
import cn.axzo.workflow.common.model.request.feature.DingTalkStarterAlterDTO;
|
||||
import cn.axzo.workflow.server.alter.DingTalkAlter;
|
||||
import cn.axzo.workflow.core.util.RivenDingTalkHelper;
|
||||
import cn.axzo.workflow.server.common.annotation.ErrorReporter;
|
||||
import cn.azxo.framework.common.model.CommonResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@ -31,7 +33,7 @@ import javax.annotation.Resource;
|
||||
@Validated
|
||||
public class FunctionController implements FunctionApi {
|
||||
@Resource
|
||||
private DingTalkAlter dingTalkAlter;
|
||||
private RivenDingTalkHelper rivenDingTalkHelper;
|
||||
|
||||
/**
|
||||
* 获取指定枚举类型的枚举值信息
|
||||
@ -57,7 +59,22 @@ public class FunctionController implements FunctionApi {
|
||||
@PostMapping("/dingtalk/alter")
|
||||
@Override
|
||||
public CommonResponse<Boolean> sendDingtalk(@Validated @RequestBody DingTalkStarterAlterDTO dto) {
|
||||
dingTalkAlter.invoke(dto);
|
||||
log.info("send dingtalk alter, request: {}", JSON.toJSONString(dto));
|
||||
String title = "Notice 应用必接广播 MQ 事件告警, Env: " + dto.getProfile();
|
||||
String content = "#### [" + dto.getProfile() + "]应用必接广播 MQ 事件告警\n" +
|
||||
"> 应用名称:" + dto.getApplicationName() + "\n\n" +
|
||||
"> 检测信息:" + dto.getAlterContent() + "\n\n";
|
||||
|
||||
rivenDingTalkHelper.sendMarkdownMessage(title, content, true, false);
|
||||
return CommonResponse.success(true);
|
||||
}
|
||||
|
||||
@Operation(summary = "发送通用钉钉消息")
|
||||
@PostMapping("/common/dingtalk/send")
|
||||
@Override
|
||||
public CommonResponse<Boolean> sendCommonDingtalk(@Validated @RequestBody CommonDingTalkDTO dto) {
|
||||
log.info("send common dingtalk msg : {}", JSON.toJSONString(dto));
|
||||
rivenDingTalkHelper.sendMarkdownMessage(dto.getTitle(), dto.getContext(), true, dto.getAt(), dto.getMobiles());
|
||||
return CommonResponse.success(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ package cn.axzo.workflow.server.xxljob;
|
||||
import cn.axzo.basics.common.util.NumberUtil;
|
||||
import cn.axzo.framework.jackson.utility.JSON;
|
||||
import cn.axzo.infra.xxl220to250.IJobHandler;
|
||||
import cn.axzo.workflow.common.model.dto.AlterDTO;
|
||||
import cn.axzo.workflow.common.model.response.category.CategoryItemVO;
|
||||
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
|
||||
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
|
||||
@ -11,7 +10,10 @@ import cn.axzo.workflow.core.listener.Alter;
|
||||
import cn.axzo.workflow.core.repository.entity.ExtAxNodeAlterJob;
|
||||
import cn.axzo.workflow.core.service.CategoryService;
|
||||
import cn.axzo.workflow.core.service.ExtAxNodeAlterJobService;
|
||||
import cn.axzo.workflow.core.util.RivenDingTalkHelper;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
import com.xxl.job.core.context.XxlJobHelper;
|
||||
@ -36,6 +38,7 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY;
|
||||
import static cn.axzo.workflow.core.util.RivenDingTalkHelper.getWebUrl;
|
||||
|
||||
/**
|
||||
* 调度业务节点告警定时任务
|
||||
@ -52,6 +55,7 @@ public class NodeAlterJobHandler extends IJobHandler {
|
||||
private final CategoryService categoryService;
|
||||
private final SpringProcessEngineConfiguration processEngineConfiguration;
|
||||
private final SupportRefreshProperties refreshProperties;
|
||||
private final RivenDingTalkHelper rivenDingTalkHelper;
|
||||
|
||||
@XxlJob("nodeAlterJobHandler")
|
||||
@Override
|
||||
@ -109,17 +113,20 @@ public class NodeAlterJobHandler extends IJobHandler {
|
||||
private void sendAlter(ProcessInstance processInstance, Optional<CategoryItemVO> category, ExtAxNodeAlterJob job, Task task) {
|
||||
// 发送告警对象
|
||||
Alter alter = SpringContextUtils.getBean(Alter.class);
|
||||
AlterDTO alterDTO = new AlterDTO();
|
||||
alterDTO.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
|
||||
alterDTO.setProcessDefinitionName(category.orElse(new CategoryItemVO()).getLabel());
|
||||
alterDTO.setProcessInstanceId(job.getProcessInstanceId());
|
||||
alterDTO.setActivityId(job.getActivityId());
|
||||
alterDTO.setTaskId(task.getId());
|
||||
alterDTO.setStartTime(task.getCreateTime());
|
||||
alterDTO.setPrettyStartTime(DateUtil.formatDateTime(task.getCreateTime()));
|
||||
if (Boolean.TRUE.equals(refreshProperties.getAlterSendDingTalk())) {
|
||||
alter.invoke(alterDTO);
|
||||
}
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.set("processInstanceId", job.getProcessInstanceId());
|
||||
jsonObject.set("activityId", job.getActivityId());
|
||||
jsonObject.set("taskId", task.getId());
|
||||
jsonObject.set("startTime", task.getCreateTime());
|
||||
jsonObject.set("prettyStartTime", DateUtil.formatDateTime(task.getCreateTime()));
|
||||
String text = "Notice 业务节点长时间停止告警, Env: " + refreshProperties.getProfile();
|
||||
String content = "#### [" + refreshProperties.getProfile() + "]业务节点长时间停止\n" +
|
||||
"> 审批业务: " + category.orElse(new CategoryItemVO()).getLabel() + "(" + processInstance.getProcessDefinitionKey() + ")" + "\n\n" +
|
||||
"> 节点相关信息: " + JSONUtil.toJsonStr(jsonObject) + "\n\n" +
|
||||
"> ##### [点击查看审批日志](" + getWebUrl(refreshProperties.getProfile()) + "/#/workflow/examples?processInstanceId=" + job.getProcessInstanceId() + ") \n\n" +
|
||||
"> ##### 提示:如果以上地址提示未登录,请在对应环境 OMS 系统登录后并点击审批管理的功能后,再使用。或者复制实例 ID 到 OMS 中手动查询 \n\n";
|
||||
|
||||
rivenDingTalkHelper.sendMarkdownMessage(text, content, true, true);
|
||||
}
|
||||
|
||||
private Boolean hasAssignee(String assignee) {
|
||||
|
||||
@ -163,8 +163,9 @@ public class WorkflowEngineStarterAutoConfiguration {
|
||||
ObjectProvider<BroadcastDLQReporter> broadcastDLQProcessorObjectProvider,
|
||||
ObjectProvider<RpcDLQReporter> rpcDLQProcessorObjectProvider,
|
||||
WorkflowEngineStarterProperties workflowEngineStarterProperties,
|
||||
WorkflowCoreService workflowCoreService,
|
||||
Environment environment) {
|
||||
return new WorkflowEngineStarterDefaultMQMonitor(mqAdminExtObjectProvider, broadcastDLQProcessorObjectProvider, rpcDLQProcessorObjectProvider, workflowEngineStarterProperties, environment);
|
||||
return new WorkflowEngineStarterDefaultMQMonitor(mqAdminExtObjectProvider, broadcastDLQProcessorObjectProvider, rpcDLQProcessorObjectProvider, workflowEngineStarterProperties, workflowCoreService, environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
package cn.axzo.workflow.starter.api;
|
||||
|
||||
import cn.axzo.workflow.common.annotation.InvokeMode;
|
||||
import cn.axzo.workflow.common.model.dto.CommonDingTalkDTO;
|
||||
import cn.axzo.workflow.common.model.dto.SignFileDTO;
|
||||
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
|
||||
import cn.axzo.workflow.common.model.dto.print.PrintFieldDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutCallbackDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutTriggerDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocQueryDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.print.PrintProcessLogPdfDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigUpsertDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.print.QueryProcessLogPdfDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceBatchQueryDTO;
|
||||
@ -22,6 +26,7 @@ import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverRead
|
||||
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ProcessDocQueryDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnNodeBackSystemOperateDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO;
|
||||
@ -35,12 +40,14 @@ import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskRemindDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskResetApproversDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskTransferDTO;
|
||||
import cn.axzo.workflow.common.model.request.feature.DingTalkStarterAlterDTO;
|
||||
import cn.axzo.workflow.common.model.request.form.ConditionPermissionMetaInfo;
|
||||
import cn.axzo.workflow.common.model.request.form.definition.StartFormSearchDTO;
|
||||
import cn.axzo.workflow.common.model.request.form.instance.FormDetailDTO;
|
||||
import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO;
|
||||
import cn.axzo.workflow.common.model.request.form.instance.FromDataSearchDTO;
|
||||
import cn.axzo.workflow.common.model.request.form.model.WpsFileConfigVariableDTO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLogVO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.process.NodesByModelVO;
|
||||
@ -49,6 +56,7 @@ import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo;
|
||||
import cn.axzo.workflow.common.model.response.form.definition.FormDefinitionVO;
|
||||
import cn.axzo.workflow.common.model.response.form.instance.FormDataVO;
|
||||
import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO;
|
||||
import cn.axzo.workflow.common.model.response.print.ProcessLogPdfResultDTO;
|
||||
import cn.axzo.workflow.common.util.ThreadUtil;
|
||||
import cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@ -99,6 +107,30 @@ public interface WorkflowCoreService {
|
||||
@InvokeMode(SYNC)
|
||||
Void printTemplateConfig(@Validated @RequestBody PrintTemplateConfigUpsertDTO dto);
|
||||
|
||||
/**
|
||||
* 后端请求指定流程日志 PDF 文件生成, 实现是异步的。
|
||||
* <p>
|
||||
* 请使用 {@link PrintAdminApi#queryProcessLogPdfResult(QueryProcessLogPdfDTO)} 函数查询,
|
||||
* 或者使用 {@link cn.axzo.nanopart.doc.api.conversion.DocConversionApi#queryConvertResultByBiz(cn.axzo.nanopart.doc.api.conversion.req.QueryConversionTaskRequestV2)} 函数查询,该接口入参默认情况下应该为:bizCode:固定为"workflow-process-log", bizKey:为实例 ID+":"+访问人 personId
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "后端请求指定流程日志 PDF 文件生成")
|
||||
@PostMapping("/api/print/admin/process/log/pdf")
|
||||
@InvokeMode(SYNC)
|
||||
String createProcessLogPdf(@Validated @RequestBody PrintProcessLogPdfDTO dto);
|
||||
|
||||
/**
|
||||
* 后端查询指定审批日志 PDF 文件的生成结果
|
||||
*
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "后端查询指定审批日志 PDF 文件的生成结果")
|
||||
@PostMapping("/api/print/admin/process/log/pdf/result")
|
||||
@InvokeMode(SYNC)
|
||||
ProcessLogPdfResultDTO queryProcessLogPdfResult(@Validated @RequestBody QueryProcessLogPdfDTO dto);
|
||||
|
||||
/**
|
||||
* 业务节点唤醒
|
||||
*
|
||||
@ -155,6 +187,11 @@ public interface WorkflowCoreService {
|
||||
@InvokeMode(SYNC)
|
||||
Boolean sendDingtalk(@Validated @RequestBody DingTalkStarterAlterDTO dto);
|
||||
|
||||
@Operation(summary = "发送通用钉钉消息")
|
||||
@PostMapping("/api/function/common/dingtalk/send")
|
||||
@InvokeMode(SYNC)
|
||||
Boolean sendCommonDingtalk(@Validated @RequestBody CommonDingTalkDTO dto);
|
||||
|
||||
/**
|
||||
* 获取指定审批业务的流程表单设置,
|
||||
*
|
||||
@ -401,6 +438,28 @@ public interface WorkflowCoreService {
|
||||
@InvokeMode(SYNC)
|
||||
List<SignFileDTO> getProcessInstanceFinalDocs(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId);
|
||||
|
||||
/**
|
||||
* 获取流程实例的条件字段信息,仅用于同意抽屉展示
|
||||
*
|
||||
* @param processInstanceId
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "获取流程实例的条件字段信息, 仅用于同意抽屉展示")
|
||||
@GetMapping("/api/process/instance/conditions")
|
||||
@InvokeMode(SYNC)
|
||||
List<ConditionPermissionMetaInfo> getConditions(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId);
|
||||
|
||||
/**
|
||||
* 获取指定模板的原始文档列表
|
||||
*
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "根据业务 ID 获取模型文档列表,自动适配公共模板和代运营")
|
||||
@PostMapping(value = "/api/process/model/doc/list")
|
||||
@InvokeMode(SYNC)
|
||||
List<DocBaseVO> docList(@Validated @RequestBody DocQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 同意
|
||||
*
|
||||
@ -461,6 +520,17 @@ public interface WorkflowCoreService {
|
||||
@InvokeMode(ASYNC)
|
||||
Boolean backTask(@Validated @RequestBody BpmnTaskBackAuditDTO dto);
|
||||
|
||||
/**
|
||||
* 用于系统内部操作,跳转到指定节点
|
||||
*
|
||||
* @param dto 请求参数
|
||||
* @return 是否成功
|
||||
*/
|
||||
@Operation(summary = "系统操作回退任务到指定节点")
|
||||
@PostMapping("/api/process/task/system/back")
|
||||
@InvokeMode(ASYNC)
|
||||
Boolean systemBackTask(@Validated @RequestBody BpmnNodeBackSystemOperateDTO dto);
|
||||
|
||||
/**
|
||||
* 驳回
|
||||
*
|
||||
|
||||
@ -18,12 +18,12 @@ import cn.axzo.workflow.common.model.request.bpmn.model.BpmnModelUpdateDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocByIdDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCreateDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocOrderDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocQueryDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocResetDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocSearchDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocStatusDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocTenantQueryDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocUpdateDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.print.Print4ProcessLogDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.print.PrintFieldQueryDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigQueryDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.print.RestPrintTemplateConfigDTO;
|
||||
@ -31,7 +31,6 @@ import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessDefinitionP
|
||||
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAdminPageReqVO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceMyPageReqVO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.process.SuperBpmnProcessInstanceCancelDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnNodeBackSystemOperateDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAttachmentDTO;
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO;
|
||||
import cn.axzo.workflow.common.model.request.category.CategoryConfigCreateDTO;
|
||||
@ -52,6 +51,7 @@ import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinition
|
||||
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstancePageItemVO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.process.ExtProcessLogVO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.process.PrintData4LogVO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceGroupVO;
|
||||
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO;
|
||||
@ -114,7 +114,7 @@ public interface WorkflowManageService {
|
||||
* 获取指定流程下用于替换打印的相关变量
|
||||
*
|
||||
* @param processInstanceId
|
||||
* @return
|
||||
* @return 仅是 kv 集合,
|
||||
*/
|
||||
@Operation(summary = "获取指定流程下用于替换打印的相关变量")
|
||||
@GetMapping("/api/print/admin/field/variables")
|
||||
@ -122,6 +122,18 @@ public interface WorkflowManageService {
|
||||
@InvokeMode(SYNC)
|
||||
Map<String, Object> getPrintFieldVariables(@NotBlank(message = "流程实例不能为空") @RequestParam String processInstanceId, @RequestParam(required = false, defaultValue = "true") Boolean throwException);
|
||||
|
||||
/**
|
||||
* 获取用于打印审批日志公共模板的数据
|
||||
*
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "获取用于打印审批日志公共模板的数据")
|
||||
@PostMapping("/api/print/admin/process/log/data/v2")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
PrintData4LogVO getPrintDataForProcessLog(@Validated @RequestBody Print4ProcessLogDTO dto);
|
||||
|
||||
/**
|
||||
* 查询管理员
|
||||
* @param dto 管理员数据
|
||||
@ -522,6 +534,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "流程模型列表")
|
||||
@GetMapping("/api/process/model/page")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
BpmPageResult<BpmnModelDetailVO> page(@Validated @RequestBody BpmnModelSearchDTO dto);
|
||||
|
||||
@ -531,6 +544,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "创建流程模型")
|
||||
@PostMapping("/api/process/model/create")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
String create(@Validated @RequestBody BpmnModelCreateDTO dto);
|
||||
|
||||
@ -539,6 +553,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "通过模型ID查询指定流程模型")
|
||||
@GetMapping("/api/process/model/get")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
BpmnModelDetailVO getById(@NotBlank(message = "流程模型 ID 不能为空") @RequestParam(required = false) String processModelId, @RequestParam(required = false) String tenantId);
|
||||
|
||||
@ -547,6 +562,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "通过模型KEY查询指定流程模型")
|
||||
@GetMapping("/api/process/model/getByKey")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
BpmnModelDetailVO getByKey(@NotBlank(message = "流程模型 KEY 不能为空") @RequestParam(required = false) String processModelKey, @NotBlank(message = "租户不能为空") @RequestParam(required = false) String tenantId);
|
||||
|
||||
@ -559,6 +575,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "获取指定模型的扩展属性")
|
||||
@GetMapping("/api/process/model/ext")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
BpmnModelExtVO getModelExt(@NotBlank(message = "模型 ID 不能为空") @RequestParam(required = false) String modelId);
|
||||
|
||||
@ -567,6 +584,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "更新流程模型")
|
||||
@PutMapping("/api/process/model/update")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
String update(@RequestBody BpmnModelUpdateDTO dto);
|
||||
|
||||
@ -577,6 +595,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "通过模型 ID 部署流程模型")
|
||||
@PostMapping("/api/process/model/deploy")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
String deployById(@NotBlank(message = "流程模型 ID 不能为空") @RequestParam(required = false) String processModelId, @RequestParam(required = false, defaultValue = "") String modelTenantId, @RequestParam(required = false) String operator);
|
||||
|
||||
@ -587,6 +606,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "通过模型 KEY 部署流程模型")
|
||||
@PostMapping("/api/process/model/deployByKey")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
String deployByKey(@NotBlank(message = "流程模型 KEY 不能为空") @RequestParam(required = false) String processModelKey, @NotBlank(message = "租户不能为空") @RequestParam(required = false) String modelTenantId, @RequestParam(required = false) String operator);
|
||||
|
||||
@ -600,6 +620,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "通过模型 ID 取消部署流程模型")
|
||||
@PostMapping("/api/process/model/undeploy")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Void unDeployById(@NotBlank(message = "流程模型 ID 不能为空") @RequestParam(required = false) String processModelId, @RequestParam(required = false, defaultValue = "") String tenantId, @RequestParam(required = false) String operator);
|
||||
|
||||
@ -608,6 +629,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "删除指定模型 ID 的流程模型")
|
||||
@DeleteMapping("/api/process/model/delete")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Void deleteById(@NotBlank(message = "流程模型 ID 不能为空") @RequestParam String processModelId, @RequestParam(required = false, defaultValue = "") String tenantId);
|
||||
|
||||
@ -620,6 +642,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "删除指定模型 KEY 的流程模型")
|
||||
@DeleteMapping("/api/process/model/deleteByKey")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Void deleteByKey(@NotBlank(message = "流程模型 KEY 不能为空") @RequestParam String processModelKey, @RequestParam(required = false, defaultValue = "") String tenantId);
|
||||
|
||||
@ -633,6 +656,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "修改模型状态")
|
||||
@PostMapping("/api/process/model/changeStatus")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean changeStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId, @NotNull(message = "状态不能为空") @RequestParam Integer status, @RequestParam(required = false) String operator);
|
||||
|
||||
@ -646,6 +670,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "修改模型打印开关状态")
|
||||
@PostMapping("/api/process/model/print/changeStatus")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean changePrintStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId, @NotNull(message = "状态不能为空") @RequestParam Integer status, @RequestParam(required = false) String operator);
|
||||
|
||||
@ -656,6 +681,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "查询流程模型使用的分类列表")
|
||||
@GetMapping("/api/process/model/category/ids")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
List<String> getModelCategoryList();
|
||||
|
||||
@ -666,6 +692,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "查询模型的租户集合")
|
||||
@GetMapping("/api/process/model/tenant/ids")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
List<String> getModelTenantIds();
|
||||
|
||||
@ -677,6 +704,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "获取打印模板配置内容")
|
||||
@PostMapping("/api/process/model/print/template/config/query")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
PrintModelDTO getPrintTemplateConfig(@Validated @RequestBody PrintTemplateConfigQueryDTO dto);
|
||||
|
||||
@ -688,6 +716,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "代运营重置打印模板")
|
||||
@PostMapping(value = "/api/process/model/print/template/config/reset")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean resetPrintTemplateConfig(@Validated @RequestBody RestPrintTemplateConfigDTO dto);
|
||||
|
||||
@ -699,6 +728,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "搜索文档列表")
|
||||
@PostMapping(value = "/api/process/model/doc/page")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
BpmPageResult<DocBaseVO> docPage(@Validated @RequestBody DocSearchDTO dto);
|
||||
|
||||
@ -709,20 +739,10 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "获取指定 docIds 文档列表")
|
||||
@PostMapping(value = "/api/process/model/doc/ids")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
List<DocBaseVO> docByIds(@Validated @RequestBody DocByIdDTO dto);
|
||||
|
||||
/**
|
||||
* 获取指定模板的原始文档列表
|
||||
*
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "根据业务 ID 获取模型文档列表,自动适配公共模板和代运营")
|
||||
@PostMapping(value = "/api/process/model/doc/list")
|
||||
@InvokeMode(SYNC)
|
||||
List<DocBaseVO> docList(@Validated @RequestBody DocQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 获取关联 HiPrint 类型文档模板内容
|
||||
*
|
||||
@ -731,6 +751,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "获取关联 HiPrint 类型文档模板内容")
|
||||
@PostMapping(value = "/api/process/model/hi-print/content/get")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
String getHiPrintContent(@RequestParam String fileRelationId);
|
||||
|
||||
@ -741,6 +762,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "添加关联文档")
|
||||
@PutMapping(value = "/api/process/model/doc/create")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean createDoc(@Validated @RequestBody DocCreateDTO dto);
|
||||
|
||||
@ -751,6 +773,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "修改关联文档")
|
||||
@PostMapping(value = "/api/process/model/doc/update")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean updateDoc(@Validated @RequestBody DocUpdateDTO dto);
|
||||
|
||||
@ -762,6 +785,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "克隆关联文档")
|
||||
@PostMapping(value = "/api/process/model/doc/clone")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean cloneDoc(@RequestParam("id") Long docId);
|
||||
|
||||
@ -772,6 +796,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "删除指定文档")
|
||||
@DeleteMapping(value = "/api/process/model/doc/delete")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean deleteDoc(@RequestParam("id") Long docId);
|
||||
|
||||
@ -783,6 +808,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "关联文档配置排序")
|
||||
@PostMapping(value = "/api/process/model/doc/order")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean orderDoc(@Validated @RequestBody DocOrderDTO dto);
|
||||
|
||||
@ -794,6 +820,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "重置关联文档配置")
|
||||
@PostMapping(value = "/api/process/model/doc/reset")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean resetDoc(@Validated @RequestBody DocResetDTO dto);
|
||||
|
||||
@ -805,6 +832,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "设置关联文档的停启用状态")
|
||||
@PostMapping(value = "/api/process/model/doc/status")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean statusDoc(@Validated @RequestBody DocStatusDTO dto);
|
||||
|
||||
@ -815,6 +843,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "设置关联文档的必选状态")
|
||||
@PostMapping(value = "/api/process/model/doc/require")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
Boolean requireDoc(@Validated @RequestBody DocStatusDTO dto);
|
||||
|
||||
@ -825,6 +854,7 @@ public interface WorkflowManageService {
|
||||
*/
|
||||
@Operation(summary = "特殊的查询设置过关联过文档的工作台 ID 集合")
|
||||
@PostMapping(value = "/api/process/model/has/docs/tenantId")
|
||||
@Manageable
|
||||
@InvokeMode(SYNC)
|
||||
List<Long> hasFilesTenantIds(@Validated @RequestBody DocTenantQueryDTO dto);
|
||||
|
||||
@ -853,17 +883,6 @@ public interface WorkflowManageService {
|
||||
@InvokeMode(SYNC)
|
||||
Void deleteVariables(@PathVariable("executionId") String executionId, @RequestParam String variableNames, @RequestParam(value = "scope", required = false) String scope);
|
||||
|
||||
/**
|
||||
* 用于系统内部操作,跳转到指定节点
|
||||
*
|
||||
* @param dto 请求参数
|
||||
* @return 是否成功
|
||||
*/
|
||||
@Operation(summary = "系统操作回退任务到指定节点")
|
||||
@PostMapping("/api/process/task/system/back")
|
||||
@Manageable
|
||||
Boolean systemBackTask(@Validated @RequestBody BpmnNodeBackSystemOperateDTO dto);
|
||||
|
||||
/**
|
||||
* 添加附件
|
||||
*
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
package cn.axzo.workflow.starter.mq.monitor;
|
||||
|
||||
import cn.axzo.workflow.common.model.dto.CommonDingTalkDTO;
|
||||
import cn.axzo.workflow.starter.api.WorkflowCoreService;
|
||||
import cn.axzo.workflow.starter.handler.monitor.BroadcastDLQReporter;
|
||||
import com.dingtalk.api.DefaultDingTalkClient;
|
||||
import com.dingtalk.api.DingTalkClient;
|
||||
import com.dingtalk.api.request.OapiRobotSendRequest;
|
||||
import com.dingtalk.api.response.OapiRobotSendResponse;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.rocketmq.common.admin.TopicOffset;
|
||||
import org.slf4j.Logger;
|
||||
@ -20,13 +18,15 @@ import org.springframework.core.env.Environment;
|
||||
public class AlertBroadcastDLQReporter implements BroadcastDLQReporter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AlertBroadcastDLQReporter.class);
|
||||
private final String dingtalk_robot_webhook = "https://oapi.dingtalk.com/robot/send?access_token=341ee2907f3ebc15dc495fb7771a646230058710999fec7838066c109849878e";
|
||||
// private final String dingtalk_robot_webhook = "https://oapi.dingtalk.com/robot/send?access_token=341ee2907f3ebc15dc495fb7771a646230058710999fec7838066c109849878e";
|
||||
private final String profile;
|
||||
private final String applicationName;
|
||||
private final WorkflowCoreService workflowCoreService;
|
||||
|
||||
public AlertBroadcastDLQReporter(Environment environment) {
|
||||
public AlertBroadcastDLQReporter(Environment environment, WorkflowCoreService workflowCoreService) {
|
||||
this.profile = environment.getProperty("spring.profiles.active");
|
||||
this.applicationName = environment.getProperty("spring.application.name");
|
||||
this.workflowCoreService = workflowCoreService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -40,22 +40,34 @@ public class AlertBroadcastDLQReporter implements BroadcastDLQReporter {
|
||||
|
||||
@SneakyThrows
|
||||
public void sendDingTalk(Long count, String dlqName) {
|
||||
DingTalkClient client = new DefaultDingTalkClient(dingtalk_robot_webhook);
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
// DingTalkClient client = new DefaultDingTalkClient(dingtalk_robot_webhook);
|
||||
// OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
//
|
||||
// request.setMsgtype("markdown");
|
||||
// OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
// markdown.setTitle("Notice Env: " + profile);
|
||||
// markdown.setText("#### [" + profile + "]环境[" + applicationName + "]发现“DLQ”数据(Rpc)\n" +
|
||||
// "> ###### 1. 如需查看详情,[请点击这里](" + findDomain() + "m) \n" +
|
||||
// "> ###### 2. 如需调整 MQ DLQ 的监控状态: \n" +
|
||||
// "> ###### 3. DLQ名称:" + dlqName + " \n" +
|
||||
// "[关闭监控](" + findDomain() + "m/set?status=false) \n | " +
|
||||
// "[开启监控](" + findDomain() + "m/set?status=true) \n" +
|
||||
// "> 积累条数:" + count + " \n");
|
||||
// request.setMarkdown(markdown);
|
||||
// log.warn(markdown.getText());
|
||||
// OapiRobotSendResponse response = client.execute(request);
|
||||
|
||||
request.setMsgtype("markdown");
|
||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
markdown.setTitle("Notice Env: " + profile);
|
||||
markdown.setText("#### [" + profile + "]环境[" + applicationName + "]发现“DLQ”数据(Rpc)\n" +
|
||||
"> ###### 1. 如需查看详情,[请点击这里](" + findDomain() + "m) \n" +
|
||||
"> ###### 2. 如需调整 MQ DLQ 的监控状态: \n" +
|
||||
"> ###### 3. DLQ名称:" + dlqName + " \n" +
|
||||
"[关闭监控](" + findDomain() + "m/set?status=false) \n | " +
|
||||
"[开启监控](" + findDomain() + "m/set?status=true) \n" +
|
||||
"> 积累条数:" + count + " \n");
|
||||
request.setMarkdown(markdown);
|
||||
log.warn(markdown.getText());
|
||||
OapiRobotSendResponse response = client.execute(request);
|
||||
|
||||
workflowCoreService.sendCommonDingtalk(CommonDingTalkDTO.builder()
|
||||
.title("Notice Env: " + profile)
|
||||
.context("#### [" + profile + "]环境[" + applicationName + "]发现“DLQ”数据(Rpc)\n" +
|
||||
"> ###### 1. 如需查看详情,[请点击这里](" + findDomain() + "m) \n" +
|
||||
"> ###### 2. 如需调整 MQ DLQ 的监控状态: \n" +
|
||||
"> ###### 3. DLQ名称:" + dlqName + " \n" +
|
||||
"[关闭监控](" + findDomain() + "m/set?status=false) \n | " +
|
||||
"[开启监控](" + findDomain() + "m/set?status=true) \n" +
|
||||
"> 积累条数:" + count + " \n")
|
||||
.build());
|
||||
}
|
||||
|
||||
private String findDomain() {
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
package cn.axzo.workflow.starter.mq.monitor;
|
||||
|
||||
import cn.axzo.workflow.common.model.dto.CommonDingTalkDTO;
|
||||
import cn.axzo.workflow.starter.api.WorkflowCoreService;
|
||||
import cn.axzo.workflow.starter.handler.monitor.RpcDLQReporter;
|
||||
import com.dingtalk.api.DefaultDingTalkClient;
|
||||
import com.dingtalk.api.DingTalkClient;
|
||||
import com.dingtalk.api.request.OapiRobotSendRequest;
|
||||
import com.dingtalk.api.response.OapiRobotSendResponse;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.rocketmq.common.admin.TopicOffset;
|
||||
import org.slf4j.Logger;
|
||||
@ -20,13 +18,15 @@ import org.springframework.core.env.Environment;
|
||||
public class AlertRcpDLQReporter implements RpcDLQReporter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AlertRcpDLQReporter.class);
|
||||
private final String dingtalk_robot_webhook = "https://oapi.dingtalk.com/robot/send?access_token=341ee2907f3ebc15dc495fb7771a646230058710999fec7838066c109849878e";
|
||||
// private final String dingtalk_robot_webhook = "https://oapi.dingtalk.com/robot/send?access_token=341ee2907f3ebc15dc495fb7771a646230058710999fec7838066c109849878e";
|
||||
private final String profile;
|
||||
private final String applicationName;
|
||||
private final WorkflowCoreService workflowCoreService;
|
||||
|
||||
public AlertRcpDLQReporter(Environment environment) {
|
||||
public AlertRcpDLQReporter(Environment environment, WorkflowCoreService workflowCoreService) {
|
||||
this.profile = environment.getProperty("spring.profiles.active");
|
||||
this.applicationName = environment.getProperty("spring.application.name");
|
||||
this.workflowCoreService = workflowCoreService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -40,22 +40,34 @@ public class AlertRcpDLQReporter implements RpcDLQReporter {
|
||||
|
||||
@SneakyThrows
|
||||
public void sendDingTalk(Long count, String dlqName) {
|
||||
DingTalkClient client = new DefaultDingTalkClient(dingtalk_robot_webhook);
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
// DingTalkClient client = new DefaultDingTalkClient(dingtalk_robot_webhook);
|
||||
// OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
//
|
||||
// request.setMsgtype("markdown");
|
||||
// OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
// markdown.setTitle("Notice Env: " + profile);
|
||||
// markdown.setText("#### [" + profile + "]环境[" + applicationName + "]发现“DLQ”数据(Rpc)\n" +
|
||||
// "> ###### 1. 如需查看详情,[请点击这里](" + findDomain() + "m) \n" +
|
||||
// "> ###### 2. 如需调整 MQ DLQ 的监控状态: \n" +
|
||||
// "> ###### 3. DLQ名称:" + dlqName + " \n" +
|
||||
// "[关闭监控](" + findDomain() + "m/set?status=false) \n | " +
|
||||
// "[开启监控](" + findDomain() + "m/set?status=true) \n" +
|
||||
// "> 积累条数:" + count + " \n");
|
||||
// request.setMarkdown(markdown);
|
||||
// log.warn(markdown.getText());
|
||||
// OapiRobotSendResponse response = client.execute(request);
|
||||
|
||||
request.setMsgtype("markdown");
|
||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
markdown.setTitle("Notice Env: " + profile);
|
||||
markdown.setText("#### [" + profile + "]环境[" + applicationName + "]发现“DLQ”数据(Rpc)\n" +
|
||||
"> ###### 1. 如需查看详情,[请点击这里](" + findDomain() + "m) \n" +
|
||||
"> ###### 2. 如需调整 MQ DLQ 的监控状态: \n" +
|
||||
"> ###### 3. DLQ名称:" + dlqName + " \n" +
|
||||
"[关闭监控](" + findDomain() + "m/set?status=false) \n | " +
|
||||
"[开启监控](" + findDomain() + "m/set?status=true) \n" +
|
||||
"> 积累条数:" + count + " \n");
|
||||
request.setMarkdown(markdown);
|
||||
log.warn(markdown.getText());
|
||||
OapiRobotSendResponse response = client.execute(request);
|
||||
workflowCoreService.sendCommonDingtalk(CommonDingTalkDTO.builder()
|
||||
.title("Notice Env: " + profile)
|
||||
.context("#### [" + profile + "]环境[" + applicationName + "]发现“DLQ”数据(Rpc)\n" +
|
||||
"> ###### 1. 如需查看详情,[请点击这里](" + findDomain() + "m) \n" +
|
||||
"> ###### 2. 如需调整 MQ DLQ 的监控状态: \n" +
|
||||
"> ###### 3. DLQ名称:" + dlqName + " \n" +
|
||||
"[关闭监控](" + findDomain() + "m/set?status=false) \n | " +
|
||||
"[开启监控](" + findDomain() + "m/set?status=true) \n" +
|
||||
"> 积累条数:" + count + " \n")
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
private String findDomain() {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package cn.axzo.workflow.starter.mq.monitor;
|
||||
|
||||
import cn.axzo.workflow.starter.WorkflowEngineStarterProperties;
|
||||
import cn.axzo.workflow.starter.api.WorkflowCoreService;
|
||||
import cn.axzo.workflow.starter.handler.monitor.BroadcastDLQReporter;
|
||||
import cn.axzo.workflow.starter.handler.monitor.RpcDLQReporter;
|
||||
import lombok.SneakyThrows;
|
||||
@ -50,12 +51,13 @@ public class WorkflowEngineStarterDefaultMQMonitor implements SmartLifecycle {
|
||||
ObjectProvider<BroadcastDLQReporter> broadcastDLQProcessorObjectProvider,
|
||||
ObjectProvider<RpcDLQReporter> rpcDLQProcessorObjectProvider,
|
||||
WorkflowEngineStarterProperties workflowEngineStarterProperties,
|
||||
WorkflowCoreService workflowCoreService,
|
||||
Environment environment) {
|
||||
this.defaultMQAdminExt = mqAdminExtObjectProvider.getIfAvailable();
|
||||
this.environment = environment;
|
||||
if (workflowEngineStarterProperties.getAlert()) {
|
||||
this.broadcastDLQProcessor = new AlertBroadcastDLQReporter(environment);
|
||||
this.rpcDLQProcessor = new AlertRcpDLQReporter(environment);
|
||||
this.broadcastDLQProcessor = new AlertBroadcastDLQReporter(environment, workflowCoreService);
|
||||
this.rpcDLQProcessor = new AlertRcpDLQReporter(environment, workflowCoreService);
|
||||
} else {
|
||||
this.broadcastDLQProcessor = broadcastDLQProcessorObjectProvider.getIfAvailable(() -> new BroadcastDLQReporter() {
|
||||
});
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package cn.axzo.workflow.starter.mq.monitor.console;
|
||||
|
||||
import cn.axzo.workflow.starter.WorkflowEngineStarterProperties;
|
||||
import cn.axzo.workflow.starter.api.WorkflowCoreService;
|
||||
import cn.axzo.workflow.starter.handler.monitor.BroadcastDLQReporter;
|
||||
import cn.axzo.workflow.starter.handler.monitor.RpcDLQReporter;
|
||||
import cn.axzo.workflow.starter.mq.monitor.AlertBroadcastDLQReporter;
|
||||
@ -59,6 +60,8 @@ public class WorkflowEngineStarterMQMonitorController {
|
||||
private String serviceVersion;
|
||||
public static String BROADCAST_CONSUMER_GROUP = "GID_%s_workflow_engine_%s_consumer";
|
||||
public static String RPC_RETRY_CONSUMER_GROUP = "GID_%s_workflow_engine_starter_%s_consumer";
|
||||
@Resource
|
||||
private WorkflowCoreService workflowCoreService;
|
||||
|
||||
@GetMapping("/m")
|
||||
public CommonResponse<Map<String, Object>> monitor() {
|
||||
@ -137,8 +140,8 @@ public class WorkflowEngineStarterMQMonitorController {
|
||||
return CommonResponse.success("未开启·死信队列·的监控,如需,请设置 workflow.engine.starter.enableDlqMonitor = true 后再重试!");
|
||||
}
|
||||
if (status) {
|
||||
monitor.setBroadcastDLQProcessor(new AlertBroadcastDLQReporter(environment));
|
||||
monitor.setRpcDLQProcessor(new AlertRcpDLQReporter(environment));
|
||||
monitor.setBroadcastDLQProcessor(new AlertBroadcastDLQReporter(environment, workflowCoreService));
|
||||
monitor.setRpcDLQProcessor(new AlertRcpDLQReporter(environment, workflowCoreService));
|
||||
return CommonResponse.success("开启 DLQ 钉钉通知");
|
||||
} else {
|
||||
monitor.setBroadcastDLQProcessor(broadcastDLQProcessorObjectProvider.getIfAvailable(() -> new BroadcastDLQReporter() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user