debug

Change-Id: I5c4de18f786f8cc336d8ad66ae9b424d02ed3674
diff --git a/src/main/java/com/example/g8backend/controller/PostController.java b/src/main/java/com/example/g8backend/controller/PostController.java
index 03687c3..d53db64 100644
--- a/src/main/java/com/example/g8backend/controller/PostController.java
+++ b/src/main/java/com/example/g8backend/controller/PostController.java
@@ -1,5 +1,4 @@
 package com.example.g8backend.controller;
-
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.example.g8backend.dto.PostCreateDTO;
@@ -12,9 +11,7 @@
 import org.springframework.web.bind.annotation.*;
 import com.example.g8backend.entity.Post;
 import com.example.g8backend.service.IPostService;
-
 import java.util.List;
-
 @RestController
 @RequestMapping("/post")
 public class PostController {
@@ -22,14 +19,12 @@
     private IPostService postService;
     @Autowired  // ✅ 新增注入
     private PostViewMapper postViewMapper;
-
     @PostMapping("")
     public ResponseEntity<?> createPost(@RequestBody PostCreateDTO postCreateDTO) {
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
         long userId = (long) authentication.getPrincipal();
         Post post = postCreateDTO.getPost();
         Long[] tagIds = postCreateDTO.getTagIds();
-
         post.setUserId(userId);
         if (tagIds.length > 0){
             postService.createPost(post, tagIds);
@@ -38,20 +33,16 @@
         }
         return ResponseEntity.ok().build();
     }
-
     @GetMapping("/{postId}")
     public Post getPost(@PathVariable Long postId) {
         // 获取当前用户ID
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
         long userId = (long) authentication.getPrincipal();
-
         // 记录浏览行为
         postService.recordViewHistory(userId, postId);
-
         // 返回帖子详情
         return postService.getById(postId);
     }
-
     @DeleteMapping("/{postId}")
     public ResponseEntity<?> deletePost(@PathVariable("postId") Long postId) {
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
@@ -66,42 +57,37 @@
         postService.removeById(postId);
         return ResponseEntity.ok().body("Post deleted successfully.");
     }
-
     @GetMapping("/getAll")
     public List<Post> getAllPosts() {
         return postService.list();
     }
-
     @GetMapping("/getByUserId/{userId}")
     public List<Post> getPostsByUserId(@PathVariable("userId") Long userId) {
         return postService.getPostsByUserId(userId);
     }
-
     @PutMapping("/{postId}")
     public ResponseEntity<?> updatePost(@PathVariable("postId") Long postId, @RequestBody Post post) {
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
         long userId = (long) authentication.getPrincipal();
         Post existingPost = postService.getById(postId);
-        
+
         if (existingPost == null) {
             return ResponseEntity.status(500).body("Post not found.");
         }
         if (existingPost.getUserId() != userId) {
             return ResponseEntity.status(403).body("You are not authorized to update this post.");
         }
-        
+
         post.setPostId(postId);
         post.setUserId(userId);
         postService.updateById(post);
         return ResponseEntity.ok().body("Post updated successfully.");
     }
-
     @GetMapping("/type/{postType}")
     public ResponseEntity<?> getPostsByType(@PathVariable String postType) {
         List<Post> posts = postService.getPostsByType(postType);
         return ResponseEntity.ok().body(posts);
     }
-
     @PostMapping("/{postId}/like")
     public ResponseEntity<?> likePost(@PathVariable Long postId) {
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
@@ -109,7 +95,6 @@
         postService.likePost(userId, postId);
         return ResponseEntity.ok().body("Post liked successfully.");
     }
-
     @DeleteMapping("/{postId}/like")
     public ResponseEntity<?> unlikePost(@PathVariable Long postId) {
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
@@ -117,51 +102,52 @@
         postService.unlikePost(userId, postId);
         return ResponseEntity.ok().body("Post unliked successfully.");
     }
-
     @GetMapping("/{postId}/likes")
     public ResponseEntity<?> getPostLikeCount(@PathVariable Long postId) {
         Long likeCount = postService.getPostLikeCount(postId);
         return ResponseEntity.ok().body(likeCount);
     }
-
     // 搜索帖子
     @GetMapping("/search")
     public List<Post> searchPosts(
             @RequestParam(required = false) String keyword,
             @RequestParam(required = false) List<Long> tags,  // 修改为接收多个标签
             @RequestParam(required = false) String author) {
-
         return postService.searchPosts(keyword, tags, author);
     }
-
     @GetMapping("/history")
     public ResponseEntity<List<PostView>> 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")
         );
-
         return ResponseEntity.ok(history);
     }
-
     @GetMapping("/recommended")
     public ResponseEntity<Page<Post>> getRecommendedPosts(
             @RequestParam(defaultValue = "1") int page,
             @RequestParam(defaultValue = "10") int size) {
-
         // 获取当前用户ID
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
         long userId = (long) authentication.getPrincipal();
-
         // 调用 Service 层方法
         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,
+            @RequestParam(defaultValue = "10") int size) {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        long userId = (long) authentication.getPrincipal();
+        // 调用标签推荐方法
+        Page<Post> result = postService.getRecommendedByTags(page, size, userId);
+        return ResponseEntity.ok(result);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/g8backend/entity/UserTagPreference.java b/src/main/java/com/example/g8backend/entity/UserTagPreference.java
new file mode 100644
index 0000000..42ac4fd
--- /dev/null
+++ b/src/main/java/com/example/g8backend/entity/UserTagPreference.java
@@ -0,0 +1,15 @@
+
+package com.example.g8backend.entity;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import java.sql.Timestamp;
+@Data
+@Accessors(chain = true)
+@TableName("user_tag_preference") // 映射数据库表名
+public class UserTagPreference {
+    private Long userId;      // 用户ID
+    private Long tagId;       // 标签ID
+    private Double weight;    // 偏好权重
+    private Timestamp lastUpdated; // 最后更新时间
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/g8backend/mapper/PostMapper.java b/src/main/java/com/example/g8backend/mapper/PostMapper.java
index b5a5280..baebb17 100644
--- a/src/main/java/com/example/g8backend/mapper/PostMapper.java
+++ b/src/main/java/com/example/g8backend/mapper/PostMapper.java
@@ -1,19 +1,14 @@
 package com.example.g8backend.mapper;
-
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.example.g8backend.entity.Post;
 import com.example.g8backend.entity.PostLike;
 import org.apache.ibatis.annotations.*;
-
 import java.util.List;
-
 @Mapper
 public interface PostMapper extends BaseMapper<Post> {
-
     // 获取用户的帖子
     List<Post> getPostsByUserId(@Param("userId") Long userId);
-
     // 搜索帖子
     @Select("<script>" +
             "SELECT p.* " +
@@ -35,32 +30,24 @@
     List<Post> searchPosts(@Param("keyword") String keyword,
                            @Param("tagIds") List<Long> tagIds,
                            @Param("author") String author);
-
     //  检查用户是否已经点赞该帖子
     @Select("SELECT EXISTS (SELECT 1 FROM post_likes WHERE user_id = #{userId} AND post_id = #{postId})")
     boolean existsByUserIdAndPostId(@Param("userId") Long userId, @Param("postId") Long postId);
-
     // 插入一条点赞记录
     @Insert("INSERT INTO post_likes (user_id, post_id) VALUES (#{userId}, #{postId})")
     void insert(PostLike postLike);
-
     // 删除用户对帖子的点赞记录
     @Delete("DELETE FROM post_likes WHERE user_id = #{userId} AND post_id = #{postId}")
     void deleteLikeByUserIdAndPostId(@Param("userId") Long userId, @Param("postId") Long postId);
-
     // 获取某个帖子点赞数
     @Select("SELECT COUNT(*) FROM post_likes WHERE post_id = #{postId}")
     Long selectCount(@Param("postId") Long postId);
-
     @Update("UPDATE posts SET view_count = view_count + 1 WHERE post_id = #{postId}")
     void incrementViewCount(Long postId);
-
     @Select("SELECT COUNT(*) FROM post_likes WHERE post_id = #{postId}")
     Long selectLikeCount(Long postId);
-
     @Select("SELECT post_id FROM post_views WHERE user_id = #{userId}")
     List<Long> findViewedPostIds(Long userId);
-
     @Update({
             "<script>",
             "UPDATE posts",
@@ -77,4 +64,4 @@
             "</script>"
     })
     int batchUpdateHotScore(@Param("posts") List<Post> posts);
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/g8backend/mapper/PostTagMapper.java b/src/main/java/com/example/g8backend/mapper/PostTagMapper.java
index 184feb2..4c8cc1e 100644
--- a/src/main/java/com/example/g8backend/mapper/PostTagMapper.java
+++ b/src/main/java/com/example/g8backend/mapper/PostTagMapper.java
@@ -1,17 +1,27 @@
 package com.example.g8backend.mapper;
-
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.example.g8backend.entity.Post;
 import com.example.g8backend.entity.PostTag;
 import com.example.g8backend.entity.Tag;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
-
+import org.apache.ibatis.annotations.Select;
 import java.util.List;
-
 @Mapper
 public interface PostTagMapper extends BaseMapper<PostTag> {
     List<Post> getPostsByTagIds(@Param("tagIds") Long[] tagIds);
     List<Tag> getTagsByPostId(@Param("postId") Long postId);
     int deleteByIds(@Param("postId") Long postId, @Param("tagId") Long tagId);
-}
+    @Select("SELECT tag_id FROM post_tag WHERE post_id = #{postId}")
+    List<Long> findTagIdsByPostId(Long postId);
+    @Select({
+            "<script>",
+            "SELECT post_id FROM post_tag",
+            "WHERE tag_id IN",
+            "<foreach item='tagId' collection='tagIds' open='(' separator=',' close=')'>",
+            "#{tagId}",
+            "</foreach>",
+            "</script>"
+    })
+    List<Long> findPostIdsByTagIds(@Param("tagIds") List<Long> tagIds);
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/g8backend/mapper/UserTagPreferenceMapper.java b/src/main/java/com/example/g8backend/mapper/UserTagPreferenceMapper.java
new file mode 100644
index 0000000..c59ea8e
--- /dev/null
+++ b/src/main/java/com/example/g8backend/mapper/UserTagPreferenceMapper.java
@@ -0,0 +1,22 @@
+package com.example.g8backend.mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.example.g8backend.entity.UserTagPreference;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+import java.util.List;
+public interface UserTagPreferenceMapper extends BaseMapper<UserTagPreference> {
+    /**
+     * 插入或更新用户标签偏好权重
+     */
+    @Update("INSERT INTO user_tag_preference (user_id, tag_id, weight) " +
+            "VALUES (#{userId}, #{tagId}, #{increment}) " +
+            "ON DUPLICATE KEY UPDATE weight = weight + #{increment}")
+    void insertOrUpdateWeight(
+            @Param("userId") Long userId,
+            @Param("tagId") Long tagId,
+            @Param("increment") Double increment
+    );
+    @Select("SELECT * FROM user_tag_preference WHERE user_id = #{userId}")
+    List<UserTagPreference> selectByUserId(@Param("userId") Long userId);
+}
\ 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 ca5376b..855952e 100644
--- a/src/main/java/com/example/g8backend/service/IPostService.java
+++ b/src/main/java/com/example/g8backend/service/IPostService.java
@@ -28,4 +28,6 @@
     void calculateHotScores();
 
     Page<Post> getRecommendedPosts(int page, int size, Long userId);
+
+    Page<Post> getRecommendedByTags(int page, int size, 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 ae47d8a..5faa4ac 100644
--- a/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java
+++ b/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java
@@ -1,57 +1,47 @@
 package com.example.g8backend.service.impl;
-
 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.entity.Post;
-import com.example.g8backend.entity.PostLike;
-import com.example.g8backend.entity.PostTag;
-import com.example.g8backend.entity.PostView;
-import com.example.g8backend.mapper.CommentMapper;
-import com.example.g8backend.mapper.PostMapper;
-import com.example.g8backend.mapper.PostViewMapper;
+import com.example.g8backend.entity.*;
+import com.example.g8backend.mapper.*;
 import com.example.g8backend.service.IPostService;
 import com.example.g8backend.service.IPostTagService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-
 import java.sql.Timestamp;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.util.List;
-
+import java.util.stream.Collectors;
 @Service
 public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements IPostService {
-
     private final PostMapper postMapper;
-
     private final PostViewMapper postViewMapper;
-
     private final CommentMapper commentMapper;
-
+    private final UserTagPreferenceMapper userTagPreferenceMapper;
+    private final PostTagMapper postTagMapper;
     @Autowired
     private IPostTagService postTagService;
-
-    public PostServiceImpl(PostMapper postMapper, PostViewMapper postViewMapper, CommentMapper commentMapper) {
+    public PostServiceImpl(PostMapper postMapper, PostViewMapper postViewMapper, CommentMapper commentMapper,
+                           UserTagPreferenceMapper userTagPreferenceMapper, PostTagMapper postTagMapper) {
         this.postMapper = postMapper;
         this.postViewMapper = postViewMapper;
         this.commentMapper = commentMapper;
+        this.userTagPreferenceMapper = userTagPreferenceMapper;
+        this.postTagMapper = postTagMapper;
         this.baseMapper = postMapper; // 重要:设置 baseMapper
     }
-
     @Override
     public List<Post> getPostsByUserId(Long userId) {
         return postMapper.getPostsByUserId(userId);
     }
-
     public void createPost(Post post) {
         post.setHotScore(5.0); // 初始热度
         post.setCreatedAt(new Timestamp(System.currentTimeMillis()));
         save(post);
     }
-
     @Override
     public void createPost(Post post, Long[] tagIds) {
         post.setHotScore(5.0); // 初始热度
@@ -61,20 +51,17 @@
             postTagService.save(new PostTag(post.getPostId(), tagId));
         }
     }
-
     @Override
     public Post updatePost(Post post) {
         updateById(post);
         return getById(post.getPostId());
     }
-
     @Override
     public List<Post> getPostsByType(String postType) {
         QueryWrapper<Post> wrapper = new QueryWrapper<>();
         wrapper.eq("post_type", postType);
         return list(wrapper);
     }
-
     @Override
     public void likePost(Long userId, Long postId) {
         // 检查用户是否已经点赞该帖子
@@ -85,14 +72,12 @@
             postMapper.insert(postLike);  // 执行插入点赞记录
         }
     }
-
     // 取消点赞功能
     @Override
     public void unlikePost(Long userId, Long postId) {
         // 删除用户对帖子的点赞记录
         postMapper.deleteLikeByUserIdAndPostId(userId, postId);  // 使用新的方法删除记录
     }
-
     // 获取帖子点赞数
     @Override
     public Long getPostLikeCount(Long postId) {
@@ -103,8 +88,6 @@
     public List<Post> searchPosts(String keyword, List<Long> tagIds, String author) {
         return postMapper.searchPosts(keyword, tagIds, author); // 调用mapper的搜索方法
     }
-
-
     @Override
     @Transactional
     public void recordViewHistory(Long userId, Long postId) {
@@ -114,11 +97,11 @@
                 .setPostId(postId)
                 .setViewTime(new Timestamp(System.currentTimeMillis()).toLocalDateTime());
         postViewMapper.insert(view);
-
         // 2. 原子更新浏览数
         postMapper.incrementViewCount(postId); // 直接调用原子操作
+        // 3. 新增:更新用户标签偏好
+        updateUserTagPreference(userId, postId);
     }
-
     @Override
     @Scheduled(cron = "0 */10 * * * *") // 每10分钟执行一次
     @Transactional
@@ -126,7 +109,6 @@
         // 1. 获取所有帖子
         List<Post> posts = postMapper.selectList(new QueryWrapper<>());
         Instant now = Instant.now();
-
         // 2. 计算每个帖子的热度
         posts.forEach(post -> {
             // 计算时间衰减因子(以小时为单位)
@@ -134,40 +116,65 @@
                     post.getCreatedAt().toInstant(),
                     now
             );
-
             // 获取互动数据(点赞数、评论数)
             Long likeCount = postMapper.selectLikeCount(post.getPostId());
             Long commentCount = commentMapper.selectCountByPostId(post.getPostId());
-
             // 热度计算公式
             double hotScore = (
                     Math.log(post.getViewCount() + 1) * 0.2 +
                             likeCount * 0.5 +
                             commentCount * 0.3
             ) / Math.pow(hoursSinceCreation + 2, 1.5);
-
             post.setHotScore(hotScore);
             post.setLastCalculated(new Timestamp(System.currentTimeMillis()));
         });
-
         // 3. 批量更新热度(自定义SQL实现)
         postMapper.batchUpdateHotScore(posts);
     }
-
     @Override
     public Page<Post> getRecommendedPosts(int page, int size, Long userId) {
         // 1. 获取用户已浏览的帖子ID列表
         List<Long> viewedPostIds = postViewMapper.findViewedPostIds(userId);
-
         // 2. 构建查询条件:排除已浏览帖子,按热度降序
         QueryWrapper<Post> queryWrapper = new QueryWrapper<>();
         if (!viewedPostIds.isEmpty()) {
             queryWrapper.notIn("post_id", viewedPostIds);
         }
         queryWrapper.orderByDesc("hot_score");
-
         // 3. 分页查询
         return postMapper.selectPage(new Page<>(page, size), queryWrapper);
     }
-
+    private void updateUserTagPreference(Long userId, Long postId) {
+        // 获取帖子关联的标签ID列表
+        List<Long> tagIds = postTagMapper.findTagIdsByPostId(postId);
+        // 对每个标签增加权重(示例:每次浏览 +0.1)
+        tagIds.forEach(tagId -> {
+            userTagPreferenceMapper.insertOrUpdateWeight(
+                    userId,
+                    tagId,
+                    0.1 // 权重增量
+            );
+        });
+    }
+    @Override
+    public Page<Post> getRecommendedByTags(int page, int size, Long userId) {
+        // 获取用户偏好标签
+        List<UserTagPreference> preferences = userTagPreferenceMapper.selectByUserId(userId);
+        if (preferences.isEmpty()) {
+            return new Page<>(page, size);
+        }
+        // 获取标签关联的帖子ID
+        List<Long> tagIds = preferences.stream()
+                .map(UserTagPreference::getTagId)
+                .collect(Collectors.toList());
+        List<Long> postIds = postTagMapper.findPostIdsByTagIds(tagIds);
+        if (postIds.isEmpty()) {
+            return new Page<>(page, size);
+        }
+        // 构建查询条件
+        QueryWrapper<Post> queryWrapper = new QueryWrapper<>();
+        queryWrapper.in("post_id", postIds) // 确保正确添加 IN 条件
+                .orderByDesc("hot_score"); // 确保排序条件正确
+        return postMapper.selectPage(new Page<>(page, size), queryWrapper);
+    }
 }
\ No newline at end of file