Bladeren bron

店铺问答讨论优化:点赞用分布式锁。

dujian 3 maanden geleden
bovenliggende
commit
804323f388

+ 0 - 0
alien-store-platform/接口文档/36-店铺问答讨论模块接口.md → alien-store/api_docs/36-店铺问答讨论模块接口.md


+ 7 - 1
alien-store/pom.xml

@@ -11,7 +11,7 @@
     <artifactId>alien-store</artifactId>
     <version>1.0.0</version>
     <name>alien-store</name>
-    <description>爱丽恩二期项目</description>
+    <description>爱丽恩二期项目 用户端</description>
 
     <properties>
         <java.version>1.8</java.version>
@@ -48,6 +48,11 @@
         </dependency>
 
         <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-pool2</artifactId>
         </dependency>
@@ -292,6 +297,7 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-security</artifactId>
         </dependency>
+
     </dependencies>
 
     <build>

+ 9 - 3
alien-store/src/main/java/shop/alien/store/controller/StorePlatformDiscussionController.java

@@ -20,6 +20,7 @@ import java.util.List;
  *
  * @author dujian
  * @since 2025-12-30
+ * Fixme 用户发主贴之后,异步提AI做关键词分析。异步线程得到关键词后,与store_id绑定,注意意向上的历史标签去重。※新建问答标签-店铺关联表
  */
 @Slf4j
 @Api(tags = {"用户端-店铺问答讨论"})
@@ -30,7 +31,7 @@ public class StorePlatformDiscussionController {
 
     private final StorePlatformDiscussionService storePlatformDiscussionService;
 
-    @ApiOperation("发布一级问答讨论 (主贴)")
+    @ApiOperation("发布主贴")
     @PostMapping("/postTopic")
     public R<Boolean> postTopic(@RequestBody StorePlatformDiscussion discussion) {
         log.info("StorePlatformDiscussionController.postTopic?discussion={}", discussion);
@@ -60,8 +61,13 @@ public class StorePlatformDiscussionController {
     @PostMapping("/like/{id}")
     public R<Boolean> like(@PathVariable Integer id) {
         log.info("StorePlatformDiscussionController.like?id={}", id);
-        boolean success = storePlatformDiscussionService.likeDiscussion(id);
-        return success ? R.success("点赞成功") : R.fail("点赞失败");
+        try {
+            boolean success = storePlatformDiscussionService.likeDiscussion(id);
+            return success ? R.success("点赞讨论成功") : R.fail("点赞讨论失败");
+        } catch (Exception e) {
+            log.error("点赞讨论失败", e);
+            return R.fail(e.getMessage());
+        }
     }
 
     @ApiOperation("分页获取店铺一级问答讨论列表 (支持搜索和排序)")

+ 49 - 17
alien-store/src/main/java/shop/alien/store/service/impl/StorePlatformDiscussionServiceImpl.java

@@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.Data;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
 import org.springframework.beans.BeanUtils;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
@@ -27,6 +29,7 @@ import shop.alien.store.util.LoginUserUtil;
 import shop.alien.util.common.safe.DeepseekClient;
 
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 /**
@@ -43,8 +46,11 @@ public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatfor
     private final LifeUserMapper lifeUserMapper;
     private final StorePlatformDiscussionLikeMapper discussionLikeMapper;
     private final StoreDiscussionConfig discussionConfig;
+    private final RedissonClient redissonClient;
     private final DeepseekClient deepseekClient = new DeepseekClient("sk-dd8a6e10972145c9847883791ac9fb41");
 
+    private static final String DISCUSSION_LIKE_LOCK_PREFIX = "lock:discussion:like:";
+
     /**
      * 内部配置类
      */
@@ -84,21 +90,22 @@ public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatfor
 
     @Override
     public StorePlatformDiscussionReplyVo pageRepliesByRootId(Integer rootId, Integer pageNum, Integer pageSize, String tags, Integer sortMode) {
+        // 1. 获取主贴信息
         StorePlatformDiscussion root = this.getById(rootId);
         if (root == null) {
             throw new RuntimeException("主贴讨论不存在");
         }
         StorePlatformDiscussionUserVo topicVo = convertToVoWithUserInfo(root);
 
-        LambdaQueryWrapper<StorePlatformDiscussion> wrapper = new LambdaQueryWrapper<StorePlatformDiscussion>()
+        // 2. 分页获取回复
+        Page<StorePlatformDiscussion> page = new Page<>(pageNum, pageSize);
+        IPage<StorePlatformDiscussion> replyPage = this.page(page, new LambdaQueryWrapper<StorePlatformDiscussion>()
                 .eq(StorePlatformDiscussion::getRootId, rootId)
-                .ne(StorePlatformDiscussion::getId, rootId);
-
-        applySorting(wrapper, sortMode);
+                .ne(StorePlatformDiscussion::getId, rootId) // 排除主贴本身
+                .orderByDesc(StorePlatformDiscussion::getCreatedTime));
+//                .orderByAsc(StorePlatformDiscussion::getCreatedTime));
 
-        Page<StorePlatformDiscussion> page = new Page<>(pageNum, pageSize);
-        IPage<StorePlatformDiscussion> replyPage = this.page(page, wrapper);
-        
+        // 3. 转换为VO并补充用户信息
         IPage<StorePlatformDiscussionUserVo> voPage = replyPage.convert(this::convertToVo);
         fillUserInfoBatch(voPage.getRecords());
 
@@ -237,6 +244,11 @@ public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatfor
         return this.save(discussion);
     }
 
+    /**
+     * 用户点赞
+     * @param id 讨论ID
+     * @return
+     */
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean likeDiscussion(Integer id) {
@@ -244,24 +256,44 @@ public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatfor
         if (currentUserId == null) {
             throw new RuntimeException("请先登录再进行点赞");
         }
-
         // 检查是否已点赞
         LambdaQueryWrapper<StorePlatformDiscussionLike> likeWrapper = new LambdaQueryWrapper<>();
         likeWrapper.eq(StorePlatformDiscussionLike::getDiscussionId, id)
-                   .eq(StorePlatformDiscussionLike::getUserId, currentUserId);
-        
+                .eq(StorePlatformDiscussionLike::getUserId, currentUserId);
+
         if (discussionLikeMapper.selectCount(likeWrapper) > 0) {
             throw new RuntimeException("您已点赞过该内容");
         }
 
-        // 插入点赞记录
-        StorePlatformDiscussionLike like = new StorePlatformDiscussionLike();
-        like.setDiscussionId(id);
-        like.setUserId(currentUserId);
-        discussionLikeMapper.insert(like);
+        // 针对同一个讨论id,同一时间只有一个线程可以执行更新点赞数操作
+        String lockKey = DISCUSSION_LIKE_LOCK_PREFIX + id;
+        RLock lock = redissonClient.getLock(lockKey);
+        try {
+            // 尝试加锁,最多等待5秒,启用看门狗机制自动续锁
+            if (lock.tryLock(5, TimeUnit.SECONDS)) {
+                log.info("加锁成功!");
+
+
+                // 插入点赞记录
+                StorePlatformDiscussionLike like = new StorePlatformDiscussionLike();
+                like.setDiscussionId(id);
+                like.setUserId(currentUserId);
+                discussionLikeMapper.insert(like);
 
-        // 更新点赞数
-        return this.update().setSql("like_count = like_count + 1").eq("id", id).update();
+                // 更新点赞数
+                return this.update().setSql("like_count = like_count + 1").eq("id", id).update();
+            } else {
+                throw new RuntimeException("点赞人数过多,请稍后再试");
+            }
+        } catch (InterruptedException e) {
+            log.error("获取讨论点赞锁失败, id={}", id, e);
+            Thread.currentThread().interrupt();
+            throw new RuntimeException("系统繁忙");
+        } finally {
+            if (lock.isHeldByCurrentThread()) {
+                lock.unlock();
+            }
+        }
     }
 
     @Override