feat(通知服务): 实现通知服务基础功能并添加负载均衡依赖
All checks were successful
test/timeline-server/pipeline/head This commit looks good
All checks were successful
test/timeline-server/pipeline/head This commit looks good
- 添加Spring Cloud LoadBalancer依赖以支持服务间调用 - 实现NotificationService接口基础功能,包括通知创建、查询、标记已读等 - 使用内存存储实现通知管理,支持分页查询和按类型统计未读数
This commit is contained in:
@@ -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) + "天前";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,12 @@
|
|||||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Cloud LoadBalancer -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Spring Cloud Alibaba Nacos -->
|
<!-- Spring Cloud Alibaba Nacos -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
|||||||
Reference in New Issue
Block a user