historyviewUpdate

Change-Id: I1a333931b6c999a5b267a51287d745a369ee58a6
diff --git a/src/main/java/com/example/g8backend/controller/PostController.java b/src/main/java/com/example/g8backend/controller/PostController.java
index d53db64..68e0f95 100644
--- a/src/main/java/com/example/g8backend/controller/PostController.java
+++ b/src/main/java/com/example/g8backend/controller/PostController.java
@@ -2,6 +2,7 @@
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.example.g8backend.dto.PostCreateDTO;
+import com.example.g8backend.dto.PostHistoryDTO;
 import com.example.g8backend.entity.PostView;
 import com.example.g8backend.mapper.PostViewMapper;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -115,17 +116,15 @@
             @RequestParam(required = false) String author) {
         return postService.searchPosts(keyword, tags, author);
     }
+
     @GetMapping("/history")
-    public ResponseEntity<List<PostView>> getViewHistory() {
+    public ResponseEntity<List<PostHistoryDTO>> getViewHistory() {
         // 获取当前用户ID
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
         long userId = (long) authentication.getPrincipal();
-        // 查询历史记录(按时间倒序)
-        List<PostView> history = postViewMapper.selectList(
-                new QueryWrapper<PostView>()
-                        .eq("user_id", userId)
-                        .orderByDesc("view_time")
-        );
+
+        // 调用Service层
+        List<PostHistoryDTO> history = postService.getViewHistoryWithTitles(userId);
         return ResponseEntity.ok(history);
     }
     @GetMapping("/recommended")
@@ -139,7 +138,6 @@
         Page<Post> pageResult = postService.getRecommendedPosts(page, size, userId);
         return ResponseEntity.ok(pageResult);
     }
-    // PostController.java - 新增标签推荐接口
     @GetMapping("/recommended-by-tags")
     public ResponseEntity<Page<Post>> getRecommendedByTags(
             @RequestParam(defaultValue = "1") int page,
diff --git a/src/main/java/com/example/g8backend/dto/PostHistoryDTO.java b/src/main/java/com/example/g8backend/dto/PostHistoryDTO.java
new file mode 100644
index 0000000..2afc7b3
--- /dev/null
+++ b/src/main/java/com/example/g8backend/dto/PostHistoryDTO.java
@@ -0,0 +1,13 @@
+// com.example.g8backend.dto.PostHistoryDTO.java
+package com.example.g8backend.dto;
+
+import lombok.Data;
+import java.time.LocalDateTime;
+
+@Data
+public class PostHistoryDTO {
+    private Long viewId;
+    private Long postId;
+    private String postTitle; // 新增标题字段
+    private LocalDateTime viewTime;
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/g8backend/service/IPostService.java b/src/main/java/com/example/g8backend/service/IPostService.java
index 855952e..cd82ef4 100644
--- a/src/main/java/com/example/g8backend/service/IPostService.java
+++ b/src/main/java/com/example/g8backend/service/IPostService.java
@@ -1,6 +1,7 @@
 package com.example.g8backend.service;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.example.g8backend.dto.PostHistoryDTO;
 import com.example.g8backend.entity.Post;
 import com.baomidou.mybatisplus.extension.service.IService;
 import org.springframework.scheduling.annotation.Scheduled;
@@ -30,4 +31,6 @@
     Page<Post> getRecommendedPosts(int page, int size, Long userId);
 
     Page<Post> getRecommendedByTags(int page, int size, Long userId);
+
+    List<PostHistoryDTO> getViewHistoryWithTitles(Long userId);
 }
diff --git a/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java b/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java
index 5faa4ac..1fda1e2 100644
--- a/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java
+++ b/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java
@@ -2,6 +2,7 @@
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.example.g8backend.dto.PostHistoryDTO;
 import com.example.g8backend.entity.*;
 import com.example.g8backend.mapper.*;
 import com.example.g8backend.service.IPostService;
@@ -177,4 +178,30 @@
                 .orderByDesc("hot_score"); // 确保排序条件正确
         return postMapper.selectPage(new Page<>(page, size), queryWrapper);
     }
+
+    @Override
+    public List<PostHistoryDTO> getViewHistoryWithTitles(Long userId) {
+        // 1. 查询浏览记录(按时间倒序)
+        List<PostView> history = postViewMapper.selectList(
+                new QueryWrapper<PostView>()
+                        .eq("user_id", userId)
+                        .orderByDesc("view_time")
+        );
+
+
+        // 2. 转换为DTO并填充标题
+        return history.stream().map(view -> {
+            PostHistoryDTO dto = new PostHistoryDTO();
+            dto.setViewId(view.getViewId());
+            dto.setPostId(view.getPostId());
+            dto.setViewTime(view.getViewTime());
+
+            // 3. 查询帖子标题
+            Post post = postMapper.selectById(view.getPostId());
+            if (post != null) {
+                dto.setPostTitle(post.getPostTitle());
+            }
+            return dto;
+        }).collect(Collectors.toList());
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/com/example/g8backend/service/PostHistoryServiceTest.java b/src/test/java/com/example/g8backend/service/PostHistoryServiceTest.java
index 38b1c87..c4a674d 100644
--- a/src/test/java/com/example/g8backend/service/PostHistoryServiceTest.java
+++ b/src/test/java/com/example/g8backend/service/PostHistoryServiceTest.java
@@ -1,8 +1,7 @@
 package com.example.g8backend.service;
+
 import com.example.g8backend.entity.PostView;
-import com.example.g8backend.mapper.PostMapper;
-import com.example.g8backend.mapper.PostTagMapper;
-import com.example.g8backend.mapper.PostViewMapper;
+import com.example.g8backend.mapper.*;
 import com.example.g8backend.service.impl.PostServiceImpl;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -11,37 +10,56 @@
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.springframework.transaction.annotation.Transactional;
+
 import java.time.LocalDateTime;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
+
 @ExtendWith(MockitoExtension.class)
 @Transactional
 public class PostHistoryServiceTest {
+
     @Mock
     private PostViewMapper postViewMapper;
+
     @Mock
     private PostMapper postMapper;
+
     @Mock
     private PostTagMapper postTagMapper;
+
+    @Mock
+    private UserTagPreferenceMapper userTagPreferenceMapper;
+
     @InjectMocks
     private PostServiceImpl postService;
+
+    // ------------------------ 已有测试用例 ------------------------
     @Test
     public void testRecordViewHistory_NormalCase() {
         // 测试数据
         Long userId = 1L;
         Long postId = 100L;
+
         // 调用方法
         postService.recordViewHistory(userId, postId);
+
         // 验证行为
         verify(postViewMapper, times(1)).insert(any(PostView.class));
         verify(postMapper, times(1)).incrementViewCount(eq(postId));
     }
+
     @Test
     public void testRecordViewHistory_CheckDataIntegrity() {
         Long userId = 2L;
         Long postId = 200L;
+
         postService.recordViewHistory(userId, postId);
+
         // 显式指定参数类型为 PostView
         verify(postViewMapper).insert(argThat(new ArgumentMatcher<PostView>() {
             @Override
@@ -52,13 +70,72 @@
             }
         }));
     }
+
     @Test
     public void testRecordViewHistory_MultipleCalls() {
         // 模拟多次调用
         Long postId = 300L;
         postService.recordViewHistory(1L, postId);
         postService.recordViewHistory(2L, postId);
+
         // 验证浏览数更新次数
         verify(postMapper, times(2)).incrementViewCount(postId);
     }
+
+    // ------------------------ 新增测试用例 ------------------------
+    @Test
+    public void testRecordViewHistory_UpdateUserPreferenceWithTags() {
+        // 测试数据
+        Long userId = 3L;
+        Long postId = 400L;
+        List<Long> tagIds = Arrays.asList(10L, 20L);
+
+        // 模拟标签查询
+        when(postTagMapper.findTagIdsByPostId(postId)).thenReturn(tagIds);
+
+        // 调用方法
+        postService.recordViewHistory(userId, postId);
+
+        // 验证标签偏好更新
+        verify(userTagPreferenceMapper, times(2)).insertOrUpdateWeight(
+                eq(userId),
+                anyLong(),
+                eq(0.1) // 假设每次浏览权重增加0.1
+        );
+    }
+
+    @Test
+    public void testRecordViewHistory_NoTagsShouldNotUpdatePreference() {
+        // 测试数据
+        Long userId = 4L;
+        Long postId = 500L;
+
+        // 模拟无关联标签
+        when(postTagMapper.findTagIdsByPostId(postId)).thenReturn(Collections.emptyList());
+
+        // 调用方法
+        postService.recordViewHistory(userId, postId);
+
+        // 验证未调用偏好更新
+        verify(userTagPreferenceMapper, never()).insertOrUpdateWeight(
+                anyLong(),
+                anyLong(),
+                anyDouble()
+        );
+    }
+
+    @Test
+    public void testRecordViewHistory_ConcurrentUpdateSafety() {
+        // 测试数据
+        Long postId = 600L;
+
+        // 模拟多次并发调用
+        postService.recordViewHistory(1L, postId);
+        postService.recordViewHistory(2L, postId);
+        postService.recordViewHistory(3L, postId);
+
+        // 验证原子操作是否被正确调用
+        verify(postMapper, times(3)).incrementViewCount(postId);
+        verify(postViewMapper, times(3)).insert(any(PostView.class));
+    }
 }
\ No newline at end of file