feat(user-service): 实现用户服务核心功能与数据同步
Some checks failed
test/timeline-server/pipeline/head There was a failure building this commit
Some checks failed
test/timeline-server/pipeline/head There was a failure building this commit
- 新增用户资料、偏好设置、自定义字段管理功能 - 实现评论、反应、相册与智能集合的完整业务逻辑 - 添加离线变更记录与数据同步机制支持冲突解决 - 集成 Redis 缓存配置与用户统计数据聚合 - 创建 8 个业务控制器处理用户交互请求 - 新增 Feign 客户端与故事服务集成 - 补充详细的后端实现与 WebSocket 指南文档 - 更新项目依赖配置支持新增功能模块
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
package com.timeline.user.service;
|
||||
|
||||
import com.timeline.user.dao.ReactionMapper;
|
||||
import com.timeline.user.dao.UserMapper;
|
||||
import com.timeline.user.entity.Reaction;
|
||||
import com.timeline.user.entity.User;
|
||||
import com.timeline.user.service.impl.ReactionServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Reaction Service Unit Tests
|
||||
*/
|
||||
class ReactionServiceTest {
|
||||
|
||||
@Mock
|
||||
private ReactionMapper reactionMapper;
|
||||
|
||||
@Mock
|
||||
private UserMapper userMapper;
|
||||
|
||||
@InjectMocks
|
||||
private ReactionServiceImpl reactionService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetReactionSummary_Success() {
|
||||
// Arrange
|
||||
String entityType = "STORY_ITEM";
|
||||
String entityId = "story123";
|
||||
String currentUserId = "user1";
|
||||
|
||||
List<Reaction> reactions = new ArrayList<>();
|
||||
Reaction reaction1 = new Reaction();
|
||||
reaction1.setEntityType(entityType);
|
||||
reaction1.setEntityId(entityId);
|
||||
reaction1.setUserId("user1");
|
||||
reaction1.setReactionType("LIKE");
|
||||
reactions.add(reaction1);
|
||||
|
||||
Reaction reaction2 = new Reaction();
|
||||
reaction2.setEntityType(entityType);
|
||||
reaction2.setEntityId(entityId);
|
||||
reaction2.setUserId("user2");
|
||||
reaction2.setReactionType("LOVE");
|
||||
reactions.add(reaction2);
|
||||
|
||||
when(reactionMapper.findByEntity(entityType, entityId)).thenReturn(reactions);
|
||||
when(reactionMapper.findByEntityAndUser(entityType, entityId, currentUserId)).thenReturn(reaction1);
|
||||
|
||||
User user1 = new User();
|
||||
user1.setUserId("user1");
|
||||
user1.setUsername("User One");
|
||||
when(userMapper.selectByUserId("user1")).thenReturn(user1);
|
||||
|
||||
User user2 = new User();
|
||||
user2.setUserId("user2");
|
||||
user2.setUsername("User Two");
|
||||
when(userMapper.selectByUserId("user2")).thenReturn(user2);
|
||||
|
||||
// Act
|
||||
Map<String, Object> summary = reactionService.getReactionSummary(entityType, entityId, currentUserId);
|
||||
|
||||
// Assert
|
||||
assertNotNull(summary);
|
||||
assertEquals(entityType, summary.get("entityType"));
|
||||
assertEquals(entityId, summary.get("entityId"));
|
||||
assertEquals("LIKE", summary.get("userReaction"));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Integer> counts = (Map<String, Integer>) summary.get("counts");
|
||||
assertEquals(1, counts.get("LIKE"));
|
||||
assertEquals(1, counts.get("LOVE"));
|
||||
assertEquals(0, counts.get("LAUGH"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddOrUpdateReaction_NewReaction() {
|
||||
// Arrange
|
||||
String userId = "user1";
|
||||
String entityType = "PHOTO";
|
||||
String entityId = "photo123";
|
||||
String reactionType = "LOVE";
|
||||
|
||||
when(reactionMapper.findByEntityAndUser(entityType, entityId, userId)).thenReturn(null);
|
||||
when(reactionMapper.insert(any(Reaction.class))).thenReturn(1);
|
||||
|
||||
// Act
|
||||
reactionService.addOrUpdateReaction(userId, entityType, entityId, reactionType);
|
||||
|
||||
// Assert
|
||||
verify(reactionMapper, times(1)).insert(any(Reaction.class));
|
||||
verify(reactionMapper, never()).updateReactionType(anyString(), anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddOrUpdateReaction_UpdateExisting() {
|
||||
// Arrange
|
||||
String userId = "user1";
|
||||
String entityType = "STORY_ITEM";
|
||||
String entityId = "story123";
|
||||
String newReactionType = "WOW";
|
||||
|
||||
Reaction existingReaction = new Reaction();
|
||||
existingReaction.setEntityType(entityType);
|
||||
existingReaction.setEntityId(entityId);
|
||||
existingReaction.setUserId(userId);
|
||||
existingReaction.setReactionType("LIKE");
|
||||
|
||||
when(reactionMapper.findByEntityAndUser(entityType, entityId, userId)).thenReturn(existingReaction);
|
||||
when(reactionMapper.updateReactionType(entityType, entityId, userId, newReactionType)).thenReturn(1);
|
||||
|
||||
// Act
|
||||
reactionService.addOrUpdateReaction(userId, entityType, entityId, newReactionType);
|
||||
|
||||
// Assert
|
||||
verify(reactionMapper, times(1)).updateReactionType(entityType, entityId, userId, newReactionType);
|
||||
verify(reactionMapper, never()).insert(any(Reaction.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveReaction_Success() {
|
||||
// Arrange
|
||||
String userId = "user1";
|
||||
String entityType = "PHOTO";
|
||||
String entityId = "photo123";
|
||||
|
||||
when(reactionMapper.delete(entityType, entityId, userId)).thenReturn(1);
|
||||
|
||||
// Act
|
||||
reactionService.removeReaction(userId, entityType, entityId);
|
||||
|
||||
// Assert
|
||||
verify(reactionMapper, times(1)).delete(entityType, entityId, userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddOrUpdateReaction_InvalidEntityType() {
|
||||
// Arrange
|
||||
String userId = "user1";
|
||||
String entityType = "INVALID_TYPE";
|
||||
String entityId = "entity123";
|
||||
String reactionType = "LIKE";
|
||||
|
||||
// Act & Assert
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
reactionService.addOrUpdateReaction(userId, entityType, entityId, reactionType);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddOrUpdateReaction_InvalidReactionType() {
|
||||
// Arrange
|
||||
String userId = "user1";
|
||||
String entityType = "STORY_ITEM";
|
||||
String entityId = "story123";
|
||||
String reactionType = "INVALID_REACTION";
|
||||
|
||||
// Act & Assert
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
reactionService.addOrUpdateReaction(userId, entityType, entityId, reactionType);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetReactionSummary_OneReactionPerUserConstraint() {
|
||||
// Arrange
|
||||
String entityType = "STORY_ITEM";
|
||||
String entityId = "story123";
|
||||
String userId = "user1";
|
||||
|
||||
// User has one reaction
|
||||
List<Reaction> reactions = new ArrayList<>();
|
||||
Reaction reaction = new Reaction();
|
||||
reaction.setEntityType(entityType);
|
||||
reaction.setEntityId(entityId);
|
||||
reaction.setUserId(userId);
|
||||
reaction.setReactionType("LIKE");
|
||||
reactions.add(reaction);
|
||||
|
||||
when(reactionMapper.findByEntity(entityType, entityId)).thenReturn(reactions);
|
||||
when(reactionMapper.findByEntityAndUser(entityType, entityId, userId)).thenReturn(reaction);
|
||||
|
||||
User user = new User();
|
||||
user.setUserId(userId);
|
||||
user.setUsername("Test User");
|
||||
when(userMapper.selectByUserId(userId)).thenReturn(user);
|
||||
|
||||
// Act
|
||||
Map<String, Object> summary = reactionService.getReactionSummary(entityType, entityId, userId);
|
||||
|
||||
// Assert
|
||||
assertEquals("LIKE", summary.get("userReaction"));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> recentReactions = (List<Map<String, Object>>) summary.get("recentReactions");
|
||||
|
||||
// Verify only one reaction per user
|
||||
long userReactionCount = reactions.stream()
|
||||
.filter(r -> r.getUserId().equals(userId))
|
||||
.count();
|
||||
assertEquals(1, userReactionCount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user