修复:1.详情弹窗截止时间改为时效要求 2.丰富审核通知内容 3.群机器人改用markdown格式

This commit is contained in:
Your Name 2026-01-07 11:59:08 +08:00
parent c7fbcb2b56
commit 943881577f
4 changed files with 119 additions and 29 deletions

View File

@ -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={}",

View File

@ -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();

View File

@ -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 + "小时";
}
}
/**
* 计算任务总积分(用于审核时修改)

View File

@ -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>