From 5bc4a227a8f1630518539833ee77a8160c87f0bf Mon Sep 17 00:00:00 2001 From: jianghao <332515344@qq.com> Date: Thu, 26 Feb 2026 10:54:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=80=9A=E7=9F=A5=E6=9C=8D=E5=8A=A1):=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E9=80=9A=E7=9F=A5=E6=9C=8D=E5=8A=A1=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E5=8A=9F=E8=83=BD=E5=B9=B6=E6=B7=BB=E5=8A=A0=E8=B4=9F?= =?UTF-8?q?=E8=BD=BD=E5=9D=87=E8=A1=A1=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加Spring Cloud LoadBalancer依赖以支持服务间调用 - 实现NotificationService接口基础功能,包括通知创建、查询、标记已读等 - 使用内存存储实现通知管理,支持分页查询和按类型统计未读数 --- .../service/impl/NotificationServiceImpl.java | 267 ++++++++++++++++++ timeline-user-service/pom.xml | 6 + 2 files changed, 273 insertions(+) create mode 100644 timeline-story-service/src/main/java/com/timeline/story/service/impl/NotificationServiceImpl.java diff --git a/timeline-story-service/src/main/java/com/timeline/story/service/impl/NotificationServiceImpl.java b/timeline-story-service/src/main/java/com/timeline/story/service/impl/NotificationServiceImpl.java new file mode 100644 index 0000000..6cab9d1 --- /dev/null +++ b/timeline-story-service/src/main/java/com/timeline/story/service/impl/NotificationServiceImpl.java @@ -0,0 +1,267 @@ +package com.timeline.story.service.impl; + +import com.timeline.story.entity.Notification; +import com.timeline.story.service.NotificationService; +import com.timeline.story.vo.NotificationVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * NotificationServiceImpl - 消息通知服务实现类 + * + * 功能描述: + * 提供消息通知的管理和推送功能的内存实现。 + * + * @author Timeline Team + * @date 2024 + */ +@Slf4j +@Service +public class NotificationServiceImpl implements NotificationService { + + // 内存存储,实际生产环境应使用数据库 + private final Map> userNotifications = new ConcurrentHashMap<>(); + + @Override + public void createCommentNotification(String storyItemId, String commentId, String senderId) { + log.info("创建评论通知: storyItemId={}, commentId={}, senderId={}", storyItemId, commentId, senderId); + // TODO: 实现评论通知创建逻辑 + } + + @Override + public void createLikeNotification(String storyItemId, String senderId) { + log.info("创建点赞通知: storyItemId={}, senderId={}", storyItemId, senderId); + // TODO: 实现点赞通知创建逻辑 + } + + @Override + public void createMentionNotification(String toUserId, String relatedType, String relatedId, String senderId) { + log.info("创建@提及通知: toUserId={}, relatedType={}, relatedId={}, senderId={}", + toUserId, relatedType, relatedId, senderId); + // TODO: 实现@提及通知创建逻辑 + } + + @Override + public void createInviteNotification(String storyId, String toUserId, String senderId) { + log.info("创建邀请通知: storyId={}, toUserId={}, senderId={}", storyId, toUserId, senderId); + // TODO: 实现邀请通知创建逻辑 + } + + @Override + public void createSystemNotification(String userId, String title, String content) { + log.info("创建系统通知: userId={}, title={}", userId, title); + + Notification notification = new Notification(); + notification.setInstanceId(UUID.randomUUID().toString()); + notification.setUserId(userId); + notification.setType("SYSTEM"); + notification.setTitle(title); + notification.setContent(content); + notification.setIsRead(0); + notification.setCreateTime(LocalDateTime.now()); + + userNotifications.computeIfAbsent(userId, k -> new ArrayList<>()).add(notification); + } + + @Override + public List getNotifications(String userId, String type, int pageNum, int pageSize) { + log.info("获取通知列表: userId={}, type={}, pageNum={}, pageSize={}", userId, type, pageNum, pageSize); + + List notifications = userNotifications.getOrDefault(userId, new ArrayList<>()); + + if (type != null && !type.isEmpty()) { + notifications = notifications.stream() + .filter(n -> type.equals(n.getType())) + .collect(Collectors.toList()); + } + + // 按时间倒序排序 + notifications.sort((a, b) -> b.getCreateTime().compareTo(a.getCreateTime())); + + // 分页 + int start = (pageNum - 1) * pageSize; + int end = Math.min(start + pageSize, notifications.size()); + + if (start >= notifications.size()) { + return new ArrayList<>(); + } + + return notifications.subList(start, end).stream() + .map(this::convertToVo) + .collect(Collectors.toList()); + } + + @Override + public List getUnreadNotifications(String userId, int pageNum, int pageSize) { + log.info("获取未读通知: userId={}, pageNum={}, pageSize={}", userId, pageNum, pageSize); + + List notifications = userNotifications.getOrDefault(userId, new ArrayList<>()); + + List unreadList = notifications.stream() + .filter(n -> n.getIsRead() != null && n.getIsRead() == 0) + .sorted((a, b) -> b.getCreateTime().compareTo(a.getCreateTime())) + .collect(Collectors.toList()); + + int start = (pageNum - 1) * pageSize; + int end = Math.min(start + pageSize, unreadList.size()); + + if (start >= unreadList.size()) { + return new ArrayList<>(); + } + + return unreadList.subList(start, end).stream() + .map(this::convertToVo) + .collect(Collectors.toList()); + } + + @Override + public int getUnreadCount(String userId) { + List notifications = userNotifications.getOrDefault(userId, new ArrayList<>()); + return (int) notifications.stream() + .filter(n -> n.getIsRead() != null && n.getIsRead() == 0) + .count(); + } + + @Override + public Map getUnreadCountByType(String userId) { + List notifications = userNotifications.getOrDefault(userId, new ArrayList<>()); + + Map countByType = new HashMap<>(); + countByType.put("COMMENT", 0); + countByType.put("LIKE", 0); + countByType.put("MENTION", 0); + countByType.put("INVITE", 0); + countByType.put("SYSTEM", 0); + + for (Notification n : notifications) { + if (n.getIsRead() != null && n.getIsRead() == 0) { + countByType.merge(n.getType(), 1, Integer::sum); + } + } + + return countByType; + } + + @Override + public void markAsRead(String notificationInstanceId, String userId) { + log.info("标记通知已读: notificationInstanceId={}, userId={}", notificationInstanceId, userId); + + List notifications = userNotifications.getOrDefault(userId, new ArrayList<>()); + for (Notification n : notifications) { + if (notificationInstanceId.equals(n.getInstanceId())) { + n.setIsRead(1); + n.setReadTime(LocalDateTime.now()); + break; + } + } + } + + @Override + public void markAllAsRead(String userId) { + log.info("标记所有通知已读: userId={}", userId); + + List notifications = userNotifications.getOrDefault(userId, new ArrayList<>()); + for (Notification n : notifications) { + if (n.getIsRead() != null && n.getIsRead() == 0) { + n.setIsRead(1); + n.setReadTime(LocalDateTime.now()); + } + } + } + + @Override + public void markAllAsReadByType(String userId, String type) { + log.info("按类型标记所有通知已读: userId={}, type={}", userId, type); + + List notifications = userNotifications.getOrDefault(userId, new ArrayList<>()); + for (Notification n : notifications) { + if (type.equals(n.getType()) && n.getIsRead() != null && n.getIsRead() == 0) { + n.setIsRead(1); + n.setReadTime(LocalDateTime.now()); + } + } + } + + @Override + public void deleteNotification(String notificationInstanceId, String userId) { + log.info("删除通知: notificationInstanceId={}, userId={}", notificationInstanceId, userId); + + List notifications = userNotifications.getOrDefault(userId, new ArrayList<>()); + notifications.removeIf(n -> notificationInstanceId.equals(n.getInstanceId())); + } + + @Override + public void clearAllNotifications(String userId) { + log.info("清空所有通知: userId={}", userId); + userNotifications.remove(userId); + } + + @Override + public void pushNotification(String userId, Notification notification) { + log.info("推送实时通知: userId={}, notification={}", userId, notification); + // TODO: 实现 WebSocket 推送逻辑 + } + + /** + * 将实体转换为 VO + */ + private NotificationVo convertToVo(Notification notification) { + NotificationVo vo = new NotificationVo(); + vo.setInstanceId(notification.getInstanceId()); + vo.setType(notification.getType()); + vo.setTypeDesc(getTypeDesc(notification.getType())); + vo.setTitle(notification.getTitle()); + vo.setContent(notification.getContent()); + vo.setRelatedId(notification.getRelatedId()); + vo.setRelatedType(notification.getRelatedType()); + vo.setSenderId(notification.getSenderId()); + vo.setSenderName(notification.getSenderName()); + vo.setSenderAvatar(notification.getSenderAvatar()); + vo.setIsRead(notification.getIsRead() != null && notification.getIsRead() == 1); + vo.setReadTime(notification.getReadTime()); + vo.setCreateTime(notification.getCreateTime()); + vo.setTimeAgo(calculateTimeAgo(notification.getCreateTime())); + return vo; + } + + /** + * 获取类型描述 + */ + private String getTypeDesc(String type) { + return switch (type) { + case "COMMENT" -> "评论"; + case "LIKE" -> "点赞"; + case "MENTION" -> "提及"; + case "INVITE" -> "邀请"; + case "SYSTEM" -> "系统"; + default -> "其他"; + }; + } + + /** + * 计算相对时间 + */ + private String calculateTimeAgo(LocalDateTime createTime) { + if (createTime == null) { + return ""; + } + + LocalDateTime now = LocalDateTime.now(); + long minutes = java.time.Duration.between(createTime, now).toMinutes(); + + if (minutes < 1) { + return "刚刚"; + } else if (minutes < 60) { + return minutes + "分钟前"; + } else if (minutes < 1440) { + return (minutes / 60) + "小时前"; + } else { + return (minutes / 1440) + "天前"; + } + } +} diff --git a/timeline-user-service/pom.xml b/timeline-user-service/pom.xml index 219dc0e..c8df582 100644 --- a/timeline-user-service/pom.xml +++ b/timeline-user-service/pom.xml @@ -50,6 +50,12 @@ spring-cloud-starter-openfeign + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + com.alibaba.cloud