|
@@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import lombok.Data;
|
|
import lombok.Data;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.redisson.api.RLock;
|
|
|
|
|
+import org.redisson.api.RedissonClient;
|
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
|
import org.springframework.stereotype.Component;
|
|
import org.springframework.stereotype.Component;
|
|
@@ -27,6 +29,7 @@ import shop.alien.store.util.LoginUserUtil;
|
|
|
import shop.alien.util.common.safe.DeepseekClient;
|
|
import shop.alien.util.common.safe.DeepseekClient;
|
|
|
|
|
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -43,8 +46,11 @@ public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatfor
|
|
|
private final LifeUserMapper lifeUserMapper;
|
|
private final LifeUserMapper lifeUserMapper;
|
|
|
private final StorePlatformDiscussionLikeMapper discussionLikeMapper;
|
|
private final StorePlatformDiscussionLikeMapper discussionLikeMapper;
|
|
|
private final StoreDiscussionConfig discussionConfig;
|
|
private final StoreDiscussionConfig discussionConfig;
|
|
|
|
|
+ private final RedissonClient redissonClient;
|
|
|
private final DeepseekClient deepseekClient = new DeepseekClient("sk-dd8a6e10972145c9847883791ac9fb41");
|
|
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
|
|
@Override
|
|
|
public StorePlatformDiscussionReplyVo pageRepliesByRootId(Integer rootId, Integer pageNum, Integer pageSize, String tags, Integer sortMode) {
|
|
public StorePlatformDiscussionReplyVo pageRepliesByRootId(Integer rootId, Integer pageNum, Integer pageSize, String tags, Integer sortMode) {
|
|
|
|
|
+ // 1. 获取主贴信息
|
|
|
StorePlatformDiscussion root = this.getById(rootId);
|
|
StorePlatformDiscussion root = this.getById(rootId);
|
|
|
if (root == null) {
|
|
if (root == null) {
|
|
|
throw new RuntimeException("主贴讨论不存在");
|
|
throw new RuntimeException("主贴讨论不存在");
|
|
|
}
|
|
}
|
|
|
StorePlatformDiscussionUserVo topicVo = convertToVoWithUserInfo(root);
|
|
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)
|
|
.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);
|
|
IPage<StorePlatformDiscussionUserVo> voPage = replyPage.convert(this::convertToVo);
|
|
|
fillUserInfoBatch(voPage.getRecords());
|
|
fillUserInfoBatch(voPage.getRecords());
|
|
|
|
|
|
|
@@ -237,6 +244,11 @@ public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatfor
|
|
|
return this.save(discussion);
|
|
return this.save(discussion);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 用户点赞
|
|
|
|
|
+ * @param id 讨论ID
|
|
|
|
|
+ * @return
|
|
|
|
|
+ */
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public boolean likeDiscussion(Integer id) {
|
|
public boolean likeDiscussion(Integer id) {
|
|
@@ -244,24 +256,44 @@ public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatfor
|
|
|
if (currentUserId == null) {
|
|
if (currentUserId == null) {
|
|
|
throw new RuntimeException("请先登录再进行点赞");
|
|
throw new RuntimeException("请先登录再进行点赞");
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
// 检查是否已点赞
|
|
// 检查是否已点赞
|
|
|
LambdaQueryWrapper<StorePlatformDiscussionLike> likeWrapper = new LambdaQueryWrapper<>();
|
|
LambdaQueryWrapper<StorePlatformDiscussionLike> likeWrapper = new LambdaQueryWrapper<>();
|
|
|
likeWrapper.eq(StorePlatformDiscussionLike::getDiscussionId, id)
|
|
likeWrapper.eq(StorePlatformDiscussionLike::getDiscussionId, id)
|
|
|
- .eq(StorePlatformDiscussionLike::getUserId, currentUserId);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ .eq(StorePlatformDiscussionLike::getUserId, currentUserId);
|
|
|
|
|
+
|
|
|
if (discussionLikeMapper.selectCount(likeWrapper) > 0) {
|
|
if (discussionLikeMapper.selectCount(likeWrapper) > 0) {
|
|
|
throw new RuntimeException("您已点赞过该内容");
|
|
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
|
|
@Override
|