修复:1.详情弹窗截止时间改为时效要求 2.丰富审核通知内容 3.群机器人改用markdown格式
This commit is contained in:
parent
c7fbcb2b56
commit
943881577f
@ -336,8 +336,8 @@ public class DingTalkService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送机器人消息(卡片格式),支持@指定人(通过手机号)
|
||||
* 使用整体跳转ActionCard样式,点击可跳转到官网
|
||||
* 发送机器人消息(Markdown格式),支持@指定人(通过手机号)
|
||||
* 注意:群机器人只支持 text/link/markdown/feedCard 类型,不支持 action_card
|
||||
* @param title 消息标题
|
||||
* @param content 消息内容(支持markdown格式)
|
||||
* @param atMobiles 要@的手机号列表,null或空表示不@任何人
|
||||
@ -363,17 +363,22 @@ public class DingTalkService {
|
||||
String siteUrl = getSiteUrl();
|
||||
String jumpUrl = buildDingTalkAppLink(config, siteUrl);
|
||||
|
||||
// 构建卡片消息(ActionCard格式)
|
||||
// 构建 Markdown 消息(群机器人支持的格式)
|
||||
Map<String, Object> message = new HashMap<>();
|
||||
message.put("msgtype", "action_card");
|
||||
message.put("msgtype", "markdown");
|
||||
|
||||
Map<String, Object> actionCard = new HashMap<>();
|
||||
actionCard.put("title", title);
|
||||
Map<String, Object> markdown = new HashMap<>();
|
||||
markdown.put("title", title);
|
||||
|
||||
// 构建 markdown 正文(卡片已有标题,内容中不再重复)
|
||||
// 构建 markdown 正文
|
||||
StringBuilder markdownBuilder = new StringBuilder();
|
||||
markdownBuilder.append("### ").append(title).append("\n\n");
|
||||
markdownBuilder.append(content);
|
||||
|
||||
// 添加跳转链接
|
||||
markdownBuilder.append("\n\n---\n\n");
|
||||
markdownBuilder.append("[>>> 点击查看详情](").append(jumpUrl).append(")");
|
||||
|
||||
// 如果有@人,在内容中添加@标记
|
||||
if (atMobiles != null && !atMobiles.isEmpty()) {
|
||||
markdownBuilder.append("\n\n");
|
||||
@ -382,13 +387,11 @@ public class DingTalkService {
|
||||
}
|
||||
}
|
||||
|
||||
actionCard.put("markdown", markdownBuilder.toString());
|
||||
actionCard.put("single_title", "查看详情 »");
|
||||
actionCard.put("single_url", jumpUrl);
|
||||
markdown.put("text", markdownBuilder.toString());
|
||||
|
||||
message.put("action_card", actionCard);
|
||||
message.put("markdown", markdown);
|
||||
|
||||
// 添加@人功能(对于action_card,@功能可能有限制)
|
||||
// 添加@人功能
|
||||
if (atMobiles != null && !atMobiles.isEmpty()) {
|
||||
Map<String, Object> at = new HashMap<>();
|
||||
at.put("atMobiles", atMobiles);
|
||||
@ -407,7 +410,7 @@ public class DingTalkService {
|
||||
if (body != null) {
|
||||
Object errcode = body.get("errcode");
|
||||
if (errcode != null && (errcode.equals(0) || errcode.equals(0L) || "0".equals(errcode.toString()))) {
|
||||
log.info("钉钉机器人卡片消息发送成功: title={}", title);
|
||||
log.info("钉钉机器人Markdown消息发送成功: title={}", title);
|
||||
return true;
|
||||
} else {
|
||||
log.error("钉钉机器人消息发送失败: errcode={}, errmsg={}, body={}",
|
||||
|
||||
@ -250,24 +250,23 @@ public class NotificationService {
|
||||
|
||||
/**
|
||||
* 构建钉钉消息内容(Markdown格式)
|
||||
* 注意:卡片消息的标题会单独显示,这里的内容不要再加标题
|
||||
*/
|
||||
private String buildDingTalkContent(Notification notification) {
|
||||
StringBuilder content = new StringBuilder();
|
||||
content.append("### ").append(notification.getTitle()).append("\n\n");
|
||||
content.append(notification.getContent()).append("\n\n");
|
||||
|
||||
// 根据通知类型添加不同的提示
|
||||
if ("task".equals(notification.getNotificationType())) {
|
||||
content.append("> 📋 任务相关通知\n\n");
|
||||
} else if ("review".equals(notification.getNotificationType())) {
|
||||
content.append("> ⭐ 评审相关通知\n\n");
|
||||
} else if ("score".equals(notification.getNotificationType())) {
|
||||
content.append("> 💰 积分相关通知\n\n");
|
||||
} else if ("system".equals(notification.getNotificationType())) {
|
||||
content.append("> 🔔 系统通知\n\n");
|
||||
// 如果内容已经是 Markdown 格式(包含 ** 或 ## 等),直接使用
|
||||
String originalContent = notification.getContent();
|
||||
if (originalContent != null && (originalContent.contains("**") || originalContent.contains("##"))) {
|
||||
// 内容已经包含详细信息,直接使用
|
||||
content.append(originalContent);
|
||||
} else {
|
||||
// 简单内容,构建基础格式
|
||||
content.append(originalContent != null ? originalContent : "");
|
||||
}
|
||||
|
||||
content.append("---\n\n");
|
||||
content.append(" \n \n");
|
||||
content.append("--- \n");
|
||||
content.append("请登录系统查看详情");
|
||||
|
||||
return content.toString();
|
||||
|
||||
@ -2192,17 +2192,89 @@ public class TaskService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送审核通知(两级审核流程)
|
||||
* 发送审核通知(两级审核流程)- 包含丰富的任务详情
|
||||
*/
|
||||
private void sendApprovalNotification(Task task) {
|
||||
try {
|
||||
if (task.getCurrentApproverId() != null) {
|
||||
User approver = userService.getUserById(task.getCurrentApproverId());
|
||||
if (approver != null) {
|
||||
// 构建丰富的通知内容
|
||||
StringBuilder contentBuilder = new StringBuilder();
|
||||
contentBuilder.append("**任务名称**:").append(task.getTitle()).append(" \n");
|
||||
contentBuilder.append("**任务编号**:").append(task.getTaskNo()).append(" \n");
|
||||
|
||||
// 品牌、平台、SPU信息
|
||||
if (task.getBrandId() != null) {
|
||||
Brand brand = brandMapper.selectById(task.getBrandId());
|
||||
if (brand != null) {
|
||||
contentBuilder.append("**品牌**:").append(brand.getName()).append(" \n");
|
||||
}
|
||||
}
|
||||
if (task.getPlatformId() != null) {
|
||||
Platform platform = platformMapper.selectById(task.getPlatformId());
|
||||
if (platform != null) {
|
||||
contentBuilder.append("**平台**:").append(platform.getName()).append(" \n");
|
||||
}
|
||||
}
|
||||
if (task.getSpuInfo() != null && !task.getSpuInfo().isEmpty()) {
|
||||
contentBuilder.append("**SPU**:").append(task.getSpuInfo()).append(" \n");
|
||||
}
|
||||
if (task.getTaskSubType() != null && !task.getTaskSubType().isEmpty()) {
|
||||
contentBuilder.append("**任务类型**:").append(task.getTaskSubType()).append(" \n");
|
||||
}
|
||||
|
||||
contentBuilder.append(" \n");
|
||||
|
||||
// 查询子任务获取任务类型明细
|
||||
List<TaskItem> taskItems = taskItemMapper.selectList(
|
||||
new LambdaQueryWrapper<TaskItem>()
|
||||
.eq(TaskItem::getTaskId, task.getId())
|
||||
.orderByAsc(TaskItem::getGroupNo)
|
||||
);
|
||||
|
||||
if (!taskItems.isEmpty()) {
|
||||
contentBuilder.append("**任务明细**: \n");
|
||||
for (TaskItem item : taskItems) {
|
||||
// 获取接单部门名称
|
||||
String deptName = "未指定";
|
||||
if (item.getTaskTypeId() != null) {
|
||||
DemandType demandType = configService.getDemandTypeById(item.getTaskTypeId());
|
||||
if (demandType != null && demandType.getAllowedDeptIds() != null) {
|
||||
String[] deptIds = demandType.getAllowedDeptIds().split(",");
|
||||
Long firstDeptId = Long.parseLong(deptIds[0].trim());
|
||||
Department dept = departmentService.getDepartmentById(firstDeptId);
|
||||
if (dept != null) {
|
||||
deptName = dept.getDeptName();
|
||||
}
|
||||
}
|
||||
}
|
||||
contentBuilder.append("▸ ").append(deptName)
|
||||
.append(" | ").append(item.getScore() != null ? item.getScore() : 0).append("分")
|
||||
.append(" | ").append(formatHours(item.getEstimatedHours())).append(" \n");
|
||||
}
|
||||
}
|
||||
|
||||
contentBuilder.append(" \n");
|
||||
|
||||
// 发布人信息
|
||||
if (task.getPublisherId() != null) {
|
||||
User publisher = userService.getUserById(task.getPublisherId());
|
||||
if (publisher != null) {
|
||||
contentBuilder.append("**发起人**:").append(publisher.getRealName() != null ? publisher.getRealName() : publisher.getUsername()).append(" \n");
|
||||
}
|
||||
}
|
||||
if (task.getPublisherDeptId() != null) {
|
||||
Department dept = departmentService.getDepartmentById(task.getPublisherDeptId());
|
||||
if (dept != null) {
|
||||
contentBuilder.append("**发起部门**:").append(dept.getDeptName()).append(" \n");
|
||||
}
|
||||
}
|
||||
|
||||
Notification notification = new Notification();
|
||||
notification.setReceiverId(task.getCurrentApproverId());
|
||||
notification.setTitle("新任务待审核");
|
||||
notification.setContent(String.format("任务【%s】需要您审核", task.getTitle()));
|
||||
notification.setContent(contentBuilder.toString());
|
||||
notification.setNotificationType("TASK_APPROVAL");
|
||||
notification.setRelatedType("task");
|
||||
notification.setRelatedId(task.getId());
|
||||
@ -2215,6 +2287,23 @@ public class TaskService {
|
||||
log.error("发送审核通知失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化工时(小时转天+小时,8小时=1天)
|
||||
*/
|
||||
private String formatHours(BigDecimal hours) {
|
||||
if (hours == null) return "--";
|
||||
int totalHours = hours.intValue();
|
||||
int days = totalHours / 8;
|
||||
int remainingHours = totalHours % 8;
|
||||
if (days > 0 && remainingHours > 0) {
|
||||
return days + "天" + remainingHours + "小时";
|
||||
} else if (days > 0) {
|
||||
return days + "天";
|
||||
} else {
|
||||
return totalHours + "小时";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算任务总积分(用于审核时修改)
|
||||
|
||||
@ -2108,9 +2108,8 @@
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="任务类型">{{ getTaskTypeName(selectedTask.taskTypeId) }}</a-descriptions-item>
|
||||
<a-descriptions-item label="预估积分">{{ selectedTask.score }} 分</a-descriptions-item>
|
||||
<a-descriptions-item label="预估工时">{{ selectedTask.estimatedHours || '--' }} 小时</a-descriptions-item>
|
||||
<a-descriptions-item label="时效要求">{{ formatEstimatedTime(selectedTask.estimatedHours) }}</a-descriptions-item>
|
||||
<a-descriptions-item v-if="!selectedTask.isSubTask" label="数量">{{ selectedTask.quantity || '--' }} 个</a-descriptions-item>
|
||||
<a-descriptions-item label="截止时间">{{ formatDeadline(selectedTask.deadline) }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user