feat(user-service): 实现用户服务核心功能与数据同步
Some checks failed
test/timeline-server/pipeline/head There was a failure building this commit

- 新增用户资料、偏好设置、自定义字段管理功能
- 实现评论、反应、相册与智能集合的完整业务逻辑
- 添加离线变更记录与数据同步机制支持冲突解决
- 集成 Redis 缓存配置与用户统计数据聚合
- 创建 8 个业务控制器处理用户交互请求
- 新增 Feign 客户端与故事服务集成
- 补充详细的后端实现与 WebSocket 指南文档
- 更新项目依赖配置支持新增功能模块
This commit is contained in:
2026-02-25 15:04:30 +08:00
parent 40412f6f67
commit 10ef5918fc
94 changed files with 9244 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
package com.timeline.user.service;
import com.timeline.user.dao.CommentMapper;
import com.timeline.user.dto.CommentDto;
import com.timeline.user.dto.CommentEventDto;
import com.timeline.user.dto.CreateCommentRequest;
import com.timeline.user.entity.Comment;
import com.timeline.user.entity.User;
import com.timeline.user.service.impl.CommentServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import java.time.LocalDateTime;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
/**
* WebSocket通知测试
* Tests for WebSocket notifications in CommentService
*/
@ExtendWith(MockitoExtension.class)
class CommentWebSocketTest {
@Mock
private CommentMapper commentMapper;
@Mock
private UserService userService;
@Mock
private SimpMessagingTemplate messagingTemplate;
@InjectMocks
private CommentServiceImpl commentService;
private User testUser;
private Comment testComment;
@BeforeEach
void setUp() {
testUser = new User();
testUser.setUserId("user123");
testUser.setUsername("testuser");
testUser.setNickname("Test User");
testComment = new Comment();
testComment.setId(1L);
testComment.setInstanceId("comment123");
testComment.setEntityType("STORY");
testComment.setEntityId("story456");
testComment.setUserId("user123");
testComment.setContent("Test comment");
testComment.setCreateTime(LocalDateTime.now());
testComment.setUpdateTime(LocalDateTime.now());
testComment.setIsDelete(0);
}
@Test
void testCreateComment_ShouldBroadcastWebSocketEvent() {
// Arrange
CreateCommentRequest request = new CreateCommentRequest();
request.setEntityType("STORY");
request.setEntityId("story456");
request.setContent("Test comment");
when(commentMapper.insert(any(Comment.class))).thenReturn(1);
when(commentMapper.findByInstanceId(anyString())).thenReturn(testComment);
when(userService.getUserByUserId("user123")).thenReturn(testUser);
// Act
CommentDto result = commentService.createComment("user123", request);
// Assert
assertNotNull(result);
assertEquals("Test comment", result.getContent());
// Verify WebSocket message was sent
ArgumentCaptor<String> topicCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<CommentEventDto> eventCaptor = ArgumentCaptor.forClass(CommentEventDto.class);
verify(messagingTemplate, times(1)).convertAndSend(
topicCaptor.capture(),
eventCaptor.capture()
);
// Verify topic format
String expectedTopic = "/topic/comments/STORY/story456";
assertEquals(expectedTopic, topicCaptor.getValue());
// Verify event content
CommentEventDto event = eventCaptor.getValue();
assertEquals(CommentEventDto.CommentEventType.CREATED, event.getEventType());
assertEquals("STORY", event.getEntityType());
assertEquals("story456", event.getEntityId());
assertNotNull(event.getComment());
assertEquals("Test comment", event.getComment().getContent());
}
@Test
void testUpdateComment_ShouldBroadcastWebSocketEvent() {
// Arrange
String updatedContent = "Updated comment";
Comment updatedComment = new Comment();
updatedComment.setInstanceId("comment123");
updatedComment.setEntityType("STORY");
updatedComment.setEntityId("story456");
updatedComment.setUserId("user123");
updatedComment.setContent(updatedContent);
updatedComment.setCreateTime(LocalDateTime.now().minusHours(1));
updatedComment.setUpdateTime(LocalDateTime.now());
updatedComment.setIsDelete(0);
when(commentMapper.findByInstanceId("comment123")).thenReturn(testComment, updatedComment);
when(commentMapper.updateContent("comment123", updatedContent)).thenReturn(1);
when(userService.getUserByUserId("user123")).thenReturn(testUser);
// Act
CommentDto result = commentService.updateComment("comment123", "user123", updatedContent);
// Assert
assertNotNull(result);
// Verify WebSocket message was sent
ArgumentCaptor<String> topicCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<CommentEventDto> eventCaptor = ArgumentCaptor.forClass(CommentEventDto.class);
verify(messagingTemplate, times(1)).convertAndSend(
topicCaptor.capture(),
eventCaptor.capture()
);
// Verify event
CommentEventDto event = eventCaptor.getValue();
assertEquals(CommentEventDto.CommentEventType.UPDATED, event.getEventType());
assertEquals("STORY", event.getEntityType());
assertEquals("story456", event.getEntityId());
}
@Test
void testDeleteComment_ShouldBroadcastWebSocketEvent() {
// Arrange
when(commentMapper.findByInstanceId("comment123")).thenReturn(testComment);
when(commentMapper.softDelete("comment123")).thenReturn(1);
// Act
commentService.deleteComment("comment123", "user123", null);
// Assert
// Verify WebSocket message was sent
ArgumentCaptor<String> topicCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<CommentEventDto> eventCaptor = ArgumentCaptor.forClass(CommentEventDto.class);
verify(messagingTemplate, times(1)).convertAndSend(
topicCaptor.capture(),
eventCaptor.capture()
);
// Verify event
CommentEventDto event = eventCaptor.getValue();
assertEquals(CommentEventDto.CommentEventType.DELETED, event.getEventType());
assertEquals("STORY", event.getEntityType());
assertEquals("story456", event.getEntityId());
assertEquals("comment123", event.getCommentId());
assertNull(event.getComment()); // Comment data should be null for delete events
}
@Test
void testWebSocketBroadcast_ShouldNotFailMainOperation_WhenWebSocketFails() {
// Arrange
CreateCommentRequest request = new CreateCommentRequest();
request.setEntityType("STORY");
request.setEntityId("story456");
request.setContent("Test comment");
when(commentMapper.insert(any(Comment.class))).thenReturn(1);
when(commentMapper.findByInstanceId(anyString())).thenReturn(testComment);
when(userService.getUserByUserId("user123")).thenReturn(testUser);
// Simulate WebSocket failure
doThrow(new RuntimeException("WebSocket error"))
.when(messagingTemplate).convertAndSend(anyString(), any());
// Act - should not throw exception
CommentDto result = commentService.createComment("user123", request);
// Assert - main operation should succeed despite WebSocket failure
assertNotNull(result);
assertEquals("Test comment", result.getContent());
// Verify the WebSocket send was attempted
verify(messagingTemplate, times(1)).convertAndSend(anyString(), any());
}
}