|
|
@@ -1,15 +1,23 @@
|
|
|
package com.fs.live.service.impl;
|
|
|
|
|
|
|
|
|
+import com.fs.common.core.domain.R;
|
|
|
+import com.fs.common.exception.CustomException;
|
|
|
import com.fs.common.utils.DateUtils;
|
|
|
import com.fs.live.domain.LiveMsg;
|
|
|
import com.fs.live.mapper.LiveMsgMapper;
|
|
|
import com.fs.live.service.ILiveMsgService;
|
|
|
+import com.fs.live.vo.LiveMsgExportVO;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
+import org.springframework.data.redis.core.script.DefaultRedisScript;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import java.util.Collections;
|
|
|
import java.util.List;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
/**
|
|
|
* 直播讨论Service业务层处理
|
|
|
@@ -20,10 +28,20 @@ import java.util.List;
|
|
|
@Service
|
|
|
public class LiveMsgServiceImpl implements ILiveMsgService
|
|
|
{
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(LiveMsgServiceImpl.class);
|
|
|
+
|
|
|
@Autowired
|
|
|
private LiveMsgMapper liveMsgMapper;
|
|
|
@Autowired
|
|
|
private LiveDataServiceImpl liveDataService;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private RedisTemplate<String, Object> redisTemplate;
|
|
|
+
|
|
|
+ /** Redis锁前缀 */
|
|
|
+ private static final String LOCK_PREFIX = "live:msg:export:lock:";
|
|
|
+ /** 锁过期时间(秒) */
|
|
|
+ private static final long LOCK_EXPIRE_TIME = 300; // 5分钟
|
|
|
|
|
|
/**
|
|
|
* 查询直播讨论
|
|
|
@@ -108,4 +126,63 @@ public class LiveMsgServiceImpl implements ILiveMsgService
|
|
|
public List<LiveMsg> selectLiveMsgSingleList(LiveMsg liveMsg) {
|
|
|
return liveMsgMapper.selectLiveMsgSingleList(liveMsg);
|
|
|
}
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<LiveMsgExportVO> exportLiveMsgComments(Long liveId, Long userId) {
|
|
|
+ if (liveId == null) {
|
|
|
+ throw new CustomException("直播ID不能为空");
|
|
|
+ }
|
|
|
+ if (userId == null) {
|
|
|
+ throw new CustomException("用户ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // Redis锁的key:用户ID + 直播间ID
|
|
|
+ String lockKey = LOCK_PREFIX + userId + ":" + liveId;
|
|
|
+ String lockValue = String.valueOf(System.currentTimeMillis());
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 尝试获取锁
|
|
|
+ Boolean lockAcquired = false;
|
|
|
+ if (redisTemplate != null) {
|
|
|
+ lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (redisTemplate != null && !lockAcquired) {
|
|
|
+ log.warn("用户{}正在导出直播间{}的评论,请勿重复操作", userId, liveId);
|
|
|
+ throw new CustomException("正在导出中,请勿重复操作");
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 查询评论数据
|
|
|
+ List<LiveMsgExportVO> list = liveMsgMapper.selectLiveMsgForExport(liveId);
|
|
|
+ log.info("用户{}导出直播间{}的评论,共{}条", userId, liveId, list != null ? list.size() : 0);
|
|
|
+ return list != null ? list : Collections.emptyList();
|
|
|
+ } finally {
|
|
|
+ // 释放锁
|
|
|
+ if (redisTemplate != null && lockAcquired) {
|
|
|
+ // 使用Lua脚本确保只删除自己的锁
|
|
|
+ String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
|
|
|
+ "return redis.call('del', KEYS[1]) " +
|
|
|
+ "else return 0 end";
|
|
|
+ DefaultRedisScript<Long> script = new DefaultRedisScript<>();
|
|
|
+ script.setScriptText(luaScript);
|
|
|
+ script.setResultType(Long.class);
|
|
|
+ redisTemplate.execute(script, Collections.singletonList(lockKey), lockValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (CustomException e) {
|
|
|
+ throw e;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("导出直播评论失败,liveId: {}, userId: {}", liveId, userId, e);
|
|
|
+ // 确保异常时也释放锁
|
|
|
+ if (redisTemplate != null) {
|
|
|
+ try {
|
|
|
+ redisTemplate.delete(lockKey);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ log.error("释放Redis锁失败", ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new CustomException("导出失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|