|
@@ -1,5 +1,7 @@
|
|
|
package com.qqflow.engine.domain.flow.service.impl;
|
|
package com.qqflow.engine.domain.flow.service.impl;
|
|
|
|
|
|
|
|
|
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import com.qqflow.engine.domain.ewechat.service.EnterpriseWechatMsgService;
|
|
import com.qqflow.engine.domain.ewechat.service.EnterpriseWechatMsgService;
|
|
|
import com.qqflow.engine.domain.flow.enums.ApprovalAction;
|
|
import com.qqflow.engine.domain.flow.enums.ApprovalAction;
|
|
|
import com.qqflow.engine.domain.flow.mapper.ProcessInstanceMapper;
|
|
import com.qqflow.engine.domain.flow.mapper.ProcessInstanceMapper;
|
|
@@ -45,6 +47,7 @@ public class WeComNotificationService implements NotificationService {
|
|
|
private final SysNotificationConfigService sysNotificationConfigService;
|
|
private final SysNotificationConfigService sysNotificationConfigService;
|
|
|
private final EnterpriseWechatMsgService wechatMsgService;
|
|
private final EnterpriseWechatMsgService wechatMsgService;
|
|
|
private final ProcessInstanceMapper processInstanceMapper;
|
|
private final ProcessInstanceMapper processInstanceMapper;
|
|
|
|
|
+ private final ObjectMapper objectMapper;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* H5 前端地址(逗号分隔:第一个为实际跳转地址,其余为免登 redirect 白名单);
|
|
* H5 前端地址(逗号分隔:第一个为实际跳转地址,其余为免登 redirect 白名单);
|
|
@@ -54,7 +57,8 @@ public class WeComNotificationService implements NotificationService {
|
|
|
private String h5BaseUrl;
|
|
private String h5BaseUrl;
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
- public void notifyTaskAssigned(List<Long> assigneeIds, String assigneeType, String processName, String instanceTitle, String nodeName) {
|
|
|
|
|
|
|
+ public void notifyTaskAssigned(Long instanceId, List<Long> assigneeIds, String assigneeType,
|
|
|
|
|
+ String processName, String instanceTitle, String nodeName) {
|
|
|
if (isSuppressed() || !this.isEnabled()) return;
|
|
if (isSuppressed() || !this.isEnabled()) return;
|
|
|
List<String> wecomUserIds = new ArrayList<>();
|
|
List<String> wecomUserIds = new ArrayList<>();
|
|
|
for (Long assigneeId : assigneeIds) {
|
|
for (Long assigneeId : assigneeIds) {
|
|
@@ -62,17 +66,93 @@ public class WeComNotificationService implements NotificationService {
|
|
|
if (wecomUserId != null) wecomUserIds.add(wecomUserId);
|
|
if (wecomUserId != null) wecomUserIds.add(wecomUserId);
|
|
|
}
|
|
}
|
|
|
if (wecomUserIds.isEmpty()) return;
|
|
if (wecomUserIds.isEmpty()) return;
|
|
|
- String description = String.format("流程:%s\n标题:%s\n节点:%s\n请及时处理。", processName, instanceTitle, nodeName);
|
|
|
|
|
|
|
+ String title = buildTaskTitle(instanceId, instanceTitle);
|
|
|
|
|
+ String description = buildTaskDescription(instanceId, processName, instanceTitle, nodeName);
|
|
|
String url = h5Url();
|
|
String url = h5Url();
|
|
|
if (url != null) {
|
|
if (url != null) {
|
|
|
- // 文本卡片:可点击跳转企微 H5 待办(点击后免登)
|
|
|
|
|
- wechatMsgService.sendTextCardToUsers(wecomUserIds, textCardTitle("【待办审批】", instanceTitle),
|
|
|
|
|
- description, url + "/m/todo", "去审批");
|
|
|
|
|
|
|
+ // 文本卡片:券面摘要 + 可点击跳转企微 H5 待办(点击后免登)
|
|
|
|
|
+ wechatMsgService.sendTextCardToUsers(wecomUserIds, title, description, url + "/m/todo", "去审批");
|
|
|
} else {
|
|
} else {
|
|
|
- wechatMsgService.sendTextToUsers(wecomUserIds, "【待办提醒】\n" + description);
|
|
|
|
|
|
|
+ wechatMsgService.sendTextToUsers(wecomUserIds, "【待办提醒】\n" + toPlainText(description));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 待办卡片标题:券审批用「【优惠券审批】券名待您审批」,其它流程用「【待办审批】实例标题」
|
|
|
|
|
+ */
|
|
|
|
|
+ private String buildTaskTitle(Long instanceId, String instanceTitle) {
|
|
|
|
|
+ JsonNode task = loadScrmTask(instanceId);
|
|
|
|
|
+ if (task != null && task.path("taskType").asInt() == 10) {
|
|
|
|
|
+ String couponName = task.path("taskName").asText(instanceTitle);
|
|
|
|
|
+ return textCardTitle("【优惠券审批】", couponName + "待您审批");
|
|
|
|
|
+ }
|
|
|
|
|
+ return textCardTitle("【待办审批】", instanceTitle);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 待办卡片描述:券审批从 formData.scrmTask 取券名/力度/数量/推送方;
|
|
|
|
|
+ * 其它流程保持通用的流程/标题/节点描述。
|
|
|
|
|
+ */
|
|
|
|
|
+ private String buildTaskDescription(Long instanceId, String processName, String instanceTitle, String nodeName) {
|
|
|
|
|
+ JsonNode task = loadScrmTask(instanceId);
|
|
|
|
|
+ if (task != null && task.path("taskType").asInt() == 10) {
|
|
|
|
|
+ String couponName = task.path("taskName").asText(instanceTitle);
|
|
|
|
|
+ String face = findItemValue(task, "发行信息", "面额/折扣");
|
|
|
|
|
+ String count = findItemValue(task, "发行信息", "发行数量");
|
|
|
|
|
+ String submitter = task.path("submitUserName").asText("");
|
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
|
+ sb.append("<div class=\"gray\">券名称:").append(couponName).append("</div>");
|
|
|
|
|
+ if (StringUtils.hasText(face)) {
|
|
|
|
|
+ sb.append("<div class=\"gray\">优惠力度:").append(face).append("</div>");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.hasText(count)) {
|
|
|
|
|
+ sb.append("<div class=\"gray\">发行数量:").append(count).append("</div>");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.hasText(submitter)) {
|
|
|
|
|
+ sb.append("<div class=\"gray\">推送方:").append(submitter).append("</div>");
|
|
|
|
|
+ }
|
|
|
|
|
+ sb.append("<div class=\"gray\">当前节点:").append(nodeName).append("</div>");
|
|
|
|
|
+ return sb.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+ return String.format("流程:%s\n标题:%s\n节点:%s\n请及时处理。", processName, instanceTitle, nodeName);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private JsonNode loadScrmTask(Long instanceId) {
|
|
|
|
|
+ if (instanceId == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ ProcessInstance instance = processInstanceMapper.selectById(instanceId);
|
|
|
|
|
+ if (instance == null || !StringUtils.hasText(instance.getFormData())) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ JsonNode task = objectMapper.readTree(instance.getFormData()).path("scrmTask");
|
|
|
|
|
+ return task.isMissingNode() ? null : task;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("[企微消息] 解析表单快照失败 instanceId={}", instanceId, e);
|
|
|
|
|
+ return null;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private String findItemValue(JsonNode task, String groupTitle, String label) {
|
|
|
|
|
+ for (JsonNode group : task.path("groups")) {
|
|
|
|
|
+ if (!groupTitle.equals(group.path("title").asText())) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (JsonNode item : group.path("items")) {
|
|
|
|
|
+ if (label.equals(item.path("label").asText())) {
|
|
|
|
|
+ return item.path("value").asText();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** textcard 的 HTML 描述降级为纯文本(未配置 H5 时使用) */
|
|
|
|
|
+ private String toPlainText(String description) {
|
|
|
|
|
+ return description.replace("<div class=\"gray\">", "").replace("</div>", "\n").trim();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
public void notifyTaskCompleted(Long instanceId, String processName, String instanceTitle,
|
|
public void notifyTaskCompleted(Long instanceId, String processName, String instanceTitle,
|
|
|
String nodeName, String operatorName, String action) {
|
|
String nodeName, String operatorName, String action) {
|