NotificationService.java 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package com.emoon.okr.service;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  4. import com.baomidou.mybatisplus.core.metadata.IPage;
  5. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  6. import com.emoon.okr.dto.NotificationDto;
  7. import com.emoon.okr.entity.Notification;
  8. import com.emoon.okr.mapper.NotificationMapper;
  9. import lombok.RequiredArgsConstructor;
  10. import org.springframework.stereotype.Service;
  11. import org.springframework.transaction.annotation.Transactional;
  12. import com.emoon.okr.exception.BusinessException;
  13. import java.time.LocalDateTime;
  14. import java.time.temporal.ChronoUnit;
  15. @Service
  16. @RequiredArgsConstructor
  17. public class NotificationService {
  18. private final NotificationMapper notificationMapper;
  19. @Transactional
  20. public boolean createOnce(Long userId, String type, String title, String content,
  21. String refType, Long refId) {
  22. long existing = notificationMapper.selectCount(new LambdaQueryWrapper<Notification>()
  23. .eq(Notification::getUserId, userId)
  24. .eq(Notification::getType, type)
  25. .eq(Notification::getRefType, refType)
  26. .eq(Notification::getRefId, refId));
  27. if (existing > 0) {
  28. return false;
  29. }
  30. Notification notification = new Notification();
  31. notification.setUserId(userId);
  32. notification.setType(type);
  33. notification.setTitle(title);
  34. notification.setContent(content);
  35. notification.setRefType(refType);
  36. notification.setRefId(refId);
  37. notification.setIsRead(0);
  38. // ponytail: application-level dedupe is enough for hourly reminders; add a unique index if concurrent producers emerge.
  39. notificationMapper.insert(notification);
  40. return true;
  41. }
  42. public long getUnreadCount(Long userId) {
  43. return notificationMapper.selectCount(
  44. new LambdaQueryWrapper<Notification>()
  45. .eq(Notification::getUserId, userId)
  46. .eq(Notification::getIsRead, 0));
  47. }
  48. public IPage<NotificationDto> getPage(Long userId, int page, int size, String type) {
  49. LambdaQueryWrapper<Notification> qw = new LambdaQueryWrapper<Notification>()
  50. .eq(Notification::getUserId, userId)
  51. .orderByDesc(Notification::getCreatedAt);
  52. if (type != null && !type.isEmpty()) {
  53. qw.eq(Notification::getType, type);
  54. }
  55. IPage<Notification> result = notificationMapper.selectPage(new Page<>(page, size), qw);
  56. return result.convert(this::toDto);
  57. }
  58. public void markRead(Long id, Long userId) {
  59. Notification n = notificationMapper.selectById(id);
  60. if (n == null) {
  61. throw new BusinessException(404, "通知不存在");
  62. }
  63. if (!n.getUserId().equals(userId)) {
  64. throw new BusinessException(403, "无权操作此通知");
  65. }
  66. n.setIsRead(1);
  67. n.setReadAt(LocalDateTime.now());
  68. notificationMapper.updateById(n);
  69. }
  70. public void markAllRead(Long userId) {
  71. notificationMapper.update(null,
  72. new LambdaUpdateWrapper<Notification>()
  73. .eq(Notification::getUserId, userId)
  74. .eq(Notification::getIsRead, 0)
  75. .set(Notification::getIsRead, 1)
  76. .set(Notification::getReadAt, LocalDateTime.now()));
  77. }
  78. public java.util.Map<String, Long> getUnreadByType(Long userId) {
  79. java.util.Map<String, Long> result = new java.util.LinkedHashMap<>();
  80. java.util.List<Notification> unread = notificationMapper.selectList(
  81. new LambdaQueryWrapper<Notification>()
  82. .eq(Notification::getUserId, userId)
  83. .eq(Notification::getIsRead, 0));
  84. long total = 0;
  85. for (Notification n : unread) {
  86. String type = n.getType() != null ? n.getType() : "OTHER";
  87. result.merge(type, 1L, Long::sum);
  88. total++;
  89. }
  90. result.put("_total", total);
  91. return result;
  92. }
  93. private NotificationDto toDto(Notification n) {
  94. NotificationDto dto = new NotificationDto();
  95. dto.setId(n.getId());
  96. dto.setType(n.getType());
  97. dto.setTitle(n.getTitle());
  98. dto.setContent(n.getContent());
  99. dto.setRefType(n.getRefType());
  100. dto.setRefId(n.getRefId());
  101. dto.setIsRead(n.getIsRead());
  102. dto.setCreatedAt(n.getCreatedAt());
  103. long seconds = n.getCreatedAt() != null
  104. ? ChronoUnit.SECONDS.between(n.getCreatedAt(), LocalDateTime.now()) : 0;
  105. if (seconds < 60) dto.setCreatedAtFriendly("刚刚");
  106. else if (seconds < 3600) dto.setCreatedAtFriendly((seconds / 60) + "分钟前");
  107. else if (seconds < 86400) dto.setCreatedAtFriendly((seconds / 3600) + "小时前");
  108. else dto.setCreatedAtFriendly((seconds / 86400) + "天前");
  109. return dto;
  110. }
  111. }