feat(通知服务): 实现通知服务基础功能并添加负载均衡依赖
All checks were successful
test/timeline-server/pipeline/head This commit looks good

- 添加Spring Cloud LoadBalancer依赖以支持服务间调用
- 实现NotificationService接口基础功能,包括通知创建、查询、标记已读等
- 使用内存存储实现通知管理,支持分页查询和按类型统计未读数
This commit is contained in:
2026-02-26 10:54:59 +08:00
parent fb0e854cee
commit 5bc4a227a8
2 changed files with 273 additions and 0 deletions

View File

@@ -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<String, List<Notification>> 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<NotificationVo> getNotifications(String userId, String type, int pageNum, int pageSize) {
log.info("获取通知列表: userId={}, type={}, pageNum={}, pageSize={}", userId, type, pageNum, pageSize);
List<Notification> 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<NotificationVo> getUnreadNotifications(String userId, int pageNum, int pageSize) {
log.info("获取未读通知: userId={}, pageNum={}, pageSize={}", userId, pageNum, pageSize);
List<Notification> notifications = userNotifications.getOrDefault(userId, new ArrayList<>());
List<Notification> 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<Notification> notifications = userNotifications.getOrDefault(userId, new ArrayList<>());
return (int) notifications.stream()
.filter(n -> n.getIsRead() != null && n.getIsRead() == 0)
.count();
}
@Override
public Map<String, Integer> getUnreadCountByType(String userId) {
List<Notification> notifications = userNotifications.getOrDefault(userId, new ArrayList<>());
Map<String, Integer> 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<Notification> 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<Notification> 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<Notification> 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<Notification> 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) + "天前";
}
}
}

View File

@@ -50,6 +50,12 @@
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Spring Cloud LoadBalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Spring Cloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>