RecommendByHot_scores

Change-Id: Icfef76f6ce21a60288c61d60ad1cd2d70045f953
diff --git a/src/main/java/com/example/g8backend/service/IPostService.java b/src/main/java/com/example/g8backend/service/IPostService.java
index a349b59..ca5376b 100644
--- a/src/main/java/com/example/g8backend/service/IPostService.java
+++ b/src/main/java/com/example/g8backend/service/IPostService.java
@@ -1,7 +1,9 @@
 package com.example.g8backend.service;
 
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.example.g8backend.entity.Post;
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
@@ -20,4 +22,10 @@
 
     @Transactional
     void recordViewHistory(Long userId, Long postId);
+
+    @Scheduled(cron = "0 */10 * * * *") // 每10分钟执行一次
+    @Transactional
+    void calculateHotScores();
+
+    Page<Post> getRecommendedPosts(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 f2bef5f..ae47d8a 100644
--- a/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java
+++ b/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java
@@ -1,20 +1,25 @@
 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.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;
 
 @Service
@@ -24,12 +29,15 @@
 
     private final PostViewMapper postViewMapper;
 
+    private final CommentMapper commentMapper;
+
     @Autowired
     private IPostTagService postTagService;
 
-    public PostServiceImpl(PostMapper postMapper, PostViewMapper postViewMapper) {
+    public PostServiceImpl(PostMapper postMapper, PostViewMapper postViewMapper, CommentMapper commentMapper) {
         this.postMapper = postMapper;
         this.postViewMapper = postViewMapper;
+        this.commentMapper = commentMapper;
         this.baseMapper = postMapper; // 重要:设置 baseMapper
     }
 
@@ -38,14 +46,15 @@
         return postMapper.getPostsByUserId(userId);
     }
 
-    @Override
     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); // 初始热度
         post.setCreatedAt(new Timestamp(System.currentTimeMillis()));
         save(post);
         for (long tagId : tagIds) {
@@ -110,4 +119,55 @@
         postMapper.incrementViewCount(postId); // 直接调用原子操作
     }
 
+    @Override
+    @Scheduled(cron = "0 */10 * * * *") // 每10分钟执行一次
+    @Transactional
+    public void calculateHotScores() {
+        // 1. 获取所有帖子
+        List<Post> posts = postMapper.selectList(new QueryWrapper<>());
+        Instant now = Instant.now();
+
+        // 2. 计算每个帖子的热度
+        posts.forEach(post -> {
+            // 计算时间衰减因子(以小时为单位)
+            long hoursSinceCreation = ChronoUnit.HOURS.between(
+                    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);
+    }
+
 }
\ No newline at end of file