qiangdanxitong/backend/src/main/java/com/designhub/service/NotificationService.java
Your Name 1fd4d245a1 feat: 实现钉钉个人通知功能
- NotificationService添加DingTalkService和UserMapper依赖
- 实现asyncPushToDingtalk方法,支持发送个人工作通知
- 自动检查用户是否绑定钉钉ID
- 构建Markdown格式的通知内容
- 异步发送,不影响主流程性能
2026-01-03 10:29:55 +08:00

323 lines
12 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) {
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); // 普通
notificationMapper.insert(notification);
}
log.info("批量发送通知成功: count={}", receiverIds.size());
}
/**
* 获取用户通知列表(分页)
*/
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()) {
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();
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");
}
content.append("---\n\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");
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); // 默认普通
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); // 普通
sendNotification(notification);
}
}