qiangdanxitong/backend/src/main/java/com/designhub/service/NotificationService.java
Your Name 9b849ccb40 feat: 优化子任务进度显示为时间线样式 + 修复需求者进行中任务显示
1. 将子任务进度从标签式改为时间线流程图样式,更直观展示任务流转进度
2. 修复需求部门用户在"进行中任务"页面看不到自己发起的任务的问题
3. 添加子任务状态辅助函数(状态类名、标签、状态文本)
4. 在MyTasksParams中添加keyword参数支持搜索
2026-01-07 15:38:59 +08:00

374 lines
14 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.designhub.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.designhub.entity.Notification;
import com.designhub.entity.User;
import com.designhub.mapper.NotificationMapper;
import com.designhub.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
/**
* 通知消息Service
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class NotificationService {
private final NotificationMapper notificationMapper;
private final UserMapper userMapper;
@Autowired(required = false)
private DingTalkService dingTalkService;
/**
* 将优先级字符串转换为整数
* low -> 0, normal -> 1, high -> 2, urgent -> 3
*/
private Integer convertPriorityToInt(String priority) {
if (priority == null) return 1; // 默认普通
return switch (priority.toLowerCase()) {
case "low" -> 0;
case "normal" -> 1;
case "high" -> 2;
case "urgent" -> 3;
default -> 1; // 默认普通
};
}
/**
* 发送通知
*/
@Transactional(rollbackFor = Exception.class)
public Notification sendNotification(Notification notification) {
if (notification.getIsRead() == null) {
notification.setIsRead(0);
}
if (notification.getPriority() == null) {
notification.setPriority(1); // 默认普通
}
if (notification.getPushDingtalk() == null) {
notification.setPushDingtalk(0);
}
notificationMapper.insert(notification);
log.info("发送通知成功: receiverId={}, title={}", notification.getReceiverId(), notification.getTitle());
// TODO: 如果开启了钉钉推送,这里可以异步推送
if (notification.getPushDingtalk() == 1) {
asyncPushToDingtalk(notification);
}
return notification;
}
/**
* 批量发送通知
*/
@Transactional(rollbackFor = Exception.class)
public void batchSendNotification(List<Long> receiverIds, String title, String content,
String notificationType, String relatedType, Long relatedId) {
batchSendNotification(receiverIds, title, content, notificationType, relatedType, relatedId, true);
}
/**
* 批量发送通知(可选钉钉推送)
*/
@Transactional(rollbackFor = Exception.class)
public void batchSendNotification(List<Long> receiverIds, String title, String content,
String notificationType, String relatedType, Long relatedId,
boolean pushDingtalk) {
for (Long receiverId : receiverIds) {
Notification notification = new Notification();
notification.setReceiverId(receiverId);
notification.setTitle(title);
notification.setContent(content);
notification.setNotificationType(notificationType);
notification.setRelatedType(relatedType);
notification.setRelatedId(relatedId);
notification.setIsRead(0);
notification.setPriority(1); // 普通
notification.setPushDingtalk(pushDingtalk ? 1 : 0);
notificationMapper.insert(notification);
// 如果开启了钉钉推送,异步推送
if (pushDingtalk) {
asyncPushToDingtalk(notification);
}
}
log.info("批量发送通知成功: count={}, pushDingtalk={}", receiverIds.size(), pushDingtalk);
}
/**
* 获取用户通知列表(分页)
*/
public Page<Notification> getUserNotifications(Long receiverId, Integer pageNum, Integer pageSize, Boolean unreadOnly) {
Page<Notification> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<Notification> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Notification::getReceiverId, receiverId);
if (unreadOnly != null && unreadOnly) {
queryWrapper.eq(Notification::getIsRead, 0);
}
queryWrapper.orderByDesc(Notification::getCreateTime);
return notificationMapper.selectPage(page, queryWrapper);
}
/**
* 标记为已读
*/
@Transactional(rollbackFor = Exception.class)
public boolean markAsRead(Long notificationId, Long receiverId) {
Notification notification = notificationMapper.selectById(notificationId);
if (notification != null && notification.getReceiverId().equals(receiverId)) {
notification.setIsRead(1);
notification.setReadTime(LocalDateTime.now());
notificationMapper.updateById(notification);
log.info("标记通知已读: id={}", notificationId);
return true;
}
return false;
}
/**
* 批量标记为已读
*/
@Transactional(rollbackFor = Exception.class)
public int markAllAsRead(Long receiverId) {
int count = notificationMapper.markAllAsRead(receiverId);
log.info("批量标记通知已读: receiverId={}, count={}", receiverId, count);
return count;
}
/**
* 获取未读数量
*/
public long getUnreadCount(Long receiverId) {
return notificationMapper.selectCount(
new LambdaQueryWrapper<Notification>()
.eq(Notification::getReceiverId, receiverId)
.eq(Notification::getIsRead, 0)
);
}
/**
* 删除通知
*/
@Transactional(rollbackFor = Exception.class)
public boolean deleteNotification(Long notificationId, Long receiverId) {
Notification notification = notificationMapper.selectById(notificationId);
if (notification != null && notification.getReceiverId().equals(receiverId)) {
notificationMapper.deleteById(notificationId);
log.info("删除通知: id={}", notificationId);
return true;
}
return false;
}
/**
* 异步推送到钉钉
*/
@Async
private void asyncPushToDingtalk(Notification notification) {
try {
// 检查钉钉服务是否可用
if (dingTalkService == null) {
log.warn("钉钉服务未启用,跳过推送");
return;
}
// 检查是否启用了钉钉通知功能
if (!dingTalkService.isFeatureEnabled("notification")) {
log.info("钉钉通知功能未启用,跳过推送: notificationId={}", notification.getId());
return;
}
// 获取接收者信息
User receiver = userMapper.selectById(notification.getReceiverId());
if (receiver == null) {
log.warn("接收者不存在,跳过钉钉推送: receiverId={}", notification.getReceiverId());
return;
}
// 检查用户是否绑定了钉钉ID
if (receiver.getDingUserId() == null || receiver.getDingUserId().isEmpty()) {
// 尝试通过手机号自动获取并绑定
if (org.springframework.util.StringUtils.hasText(receiver.getPhone())) {
String dingUserId = dingTalkService.getUserIdByMobile(receiver.getPhone());
if (dingUserId != null) {
receiver.setDingUserId(dingUserId);
userMapper.updateById(receiver);
log.info("通过手机号自动绑定钉钉ID: userId={}, phone={}, dingUserId={}",
receiver.getId(), receiver.getPhone(), dingUserId);
} else {
log.info("用户未绑定钉钉ID且无法通过手机号获取,跳过推送: userId={}, phone={}",
receiver.getId(), receiver.getPhone());
return;
}
} else {
log.info("用户未绑定钉钉ID且无手机号,跳过推送: userId={}, username={}",
receiver.getId(), receiver.getUsername());
return;
}
}
// 构建钉钉消息内容
String title = notification.getTitle();
String content = buildDingTalkContent(notification);
// 发送工作通知(个人消息)
boolean success = dingTalkService.sendWorkNotification(
Collections.singletonList(receiver.getDingUserId()),
title,
content
);
if (success) {
log.info("钉钉个人通知发送成功: notificationId={}, receiverId={}, dingUserId={}",
notification.getId(), receiver.getId(), receiver.getDingUserId());
} else {
log.warn("钉钉个人通知发送失败: notificationId={}, receiverId={}",
notification.getId(), receiver.getId());
}
} catch (Exception e) {
log.error("推送钉钉通知异常: notificationId={}", notification.getId(), e);
}
}
/**
* 构建钉钉消息内容(Markdown格式)
* 注意:卡片消息的标题会单独显示,这里的内容不要再加标题
*/
private String buildDingTalkContent(Notification notification) {
StringBuilder content = new StringBuilder();
// 如果内容已经是 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");
content.append("请登录系统查看详情");
return content.toString();
}
// ==================== 便捷方法 ====================
/**
* 发送任务通知
*/
public void sendTaskNotification(Long receiverId, Long taskId, String title, String content) {
Notification notification = new Notification();
notification.setReceiverId(receiverId);
notification.setNotificationType("task");
notification.setRelatedType("task");
notification.setRelatedId(taskId);
notification.setTitle(title);
notification.setContent(content);
notification.setLinkUrl("/tasks/" + taskId);
notification.setPushDingtalk(1); // 任务通知默认推送钉钉
sendNotification(notification);
}
/**
* 发送积分通知
*/
public void sendScoreNotification(Long receiverId, String title, String content) {
Notification notification = new Notification();
notification.setReceiverId(receiverId);
notification.setNotificationType("score");
notification.setRelatedType("score");
notification.setTitle(title);
notification.setContent(content);
notification.setLinkUrl("/my-score");
notification.setPushDingtalk(1); // 积分通知推送钉钉
sendNotification(notification);
}
/**
* 发送评审通知
*/
public void sendReviewNotification(Long receiverId, Long taskId, String title, String content) {
Notification notification = new Notification();
notification.setReceiverId(receiverId);
notification.setNotificationType("review");
notification.setRelatedType("task");
notification.setRelatedId(taskId);
notification.setTitle(title);
notification.setContent(content);
notification.setLinkUrl("/tasks/" + taskId);
notification.setPriority(2); // 高
notification.setPushDingtalk(1);
sendNotification(notification);
}
/**
* 发送系统通知
*/
public void sendSystemNotification(Long receiverId, String title, String content, String priority) {
Notification notification = new Notification();
notification.setReceiverId(receiverId);
notification.setNotificationType("system");
notification.setRelatedType("system");
notification.setTitle(title);
notification.setContent(content);
notification.setPriority(priority != null ? convertPriorityToInt(priority) : 1); // 默认普通
notification.setPushDingtalk(1); // 系统通知推送钉钉
sendNotification(notification);
}
/**
* 发送通用通知(便捷方法)
*/
public void sendNotification(Long receiverId, String title, String content, String relatedType, Long relatedId) {
Notification notification = new Notification();
notification.setReceiverId(receiverId);
notification.setNotificationType(relatedType);
notification.setRelatedType(relatedType);
notification.setRelatedId(relatedId);
notification.setTitle(title);
notification.setContent(content);
notification.setPriority(1); // 普通
notification.setPushDingtalk(1); // 默认推送到钉钉
sendNotification(notification);
}
/**
* 发送通用通知(支持指定通知类型和关联类型)
*/
public void sendNotification(Long receiverId, String title, String content,
String notificationType, String relatedType, Long relatedId) {
Notification notification = new Notification();
notification.setReceiverId(receiverId);
notification.setNotificationType(notificationType);
notification.setRelatedType(relatedType);
notification.setRelatedId(relatedId);
notification.setTitle(title);
notification.setContent(content);
notification.setPriority(1); // 普通
notification.setPushDingtalk(1); // 默认推送到钉钉
sendNotification(notification);
}
}