|
@@ -2,12 +2,14 @@ package com.fs.activity.service.impl;
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import com.fs.activity.domain.AccVoteRecord;
|
|
import com.fs.activity.domain.AccVoteRecord;
|
|
|
import com.fs.activity.domain.AccWork;
|
|
import com.fs.activity.domain.AccWork;
|
|
|
import com.fs.activity.mapper.AccVoteRecordMapper;
|
|
import com.fs.activity.mapper.AccVoteRecordMapper;
|
|
|
import com.fs.activity.param.AccVoteRecordRequest;
|
|
import com.fs.activity.param.AccVoteRecordRequest;
|
|
|
import com.fs.activity.param.AccVoteRecordResponse;
|
|
import com.fs.activity.param.AccVoteRecordResponse;
|
|
|
|
|
+import com.fs.activity.param.AccWorkRequest;
|
|
|
import com.fs.activity.service.AccActivityService;
|
|
import com.fs.activity.service.AccActivityService;
|
|
|
import com.fs.activity.service.AccWorkService;
|
|
import com.fs.activity.service.AccWorkService;
|
|
|
import com.fs.activity.mapper.AccWorkMapper;
|
|
import com.fs.activity.mapper.AccWorkMapper;
|
|
@@ -16,6 +18,8 @@ import com.fs.common.core.domain.R;
|
|
|
import com.fs.common.core.redis.RedisCache;
|
|
import com.fs.common.core.redis.RedisCache;
|
|
|
import com.fs.common.exception.ServiceException;
|
|
import com.fs.common.exception.ServiceException;
|
|
|
import com.fs.common.exception.base.BusinessException;
|
|
import com.fs.common.exception.base.BusinessException;
|
|
|
|
|
+import com.github.pagehelper.PageHelper;
|
|
|
|
|
+import com.github.pagehelper.PageInfo;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.redisson.api.RLock;
|
|
import org.redisson.api.RLock;
|
|
|
import org.redisson.api.RedissonClient;
|
|
import org.redisson.api.RedissonClient;
|
|
@@ -39,13 +43,17 @@ import java.util.concurrent.TimeUnit;
|
|
|
public class AccWorkServiceImpl extends ServiceImpl<AccWorkMapper, AccWork>
|
|
public class AccWorkServiceImpl extends ServiceImpl<AccWorkMapper, AccWork>
|
|
|
implements AccWorkService{
|
|
implements AccWorkService{
|
|
|
|
|
|
|
|
|
|
+ // 今日投票数
|
|
|
private static final String DAILY_VOTE_LIMIT = "vote:daily:limit";
|
|
private static final String DAILY_VOTE_LIMIT = "vote:daily:limit";
|
|
|
|
|
|
|
|
|
|
+ // 锁
|
|
|
private static final String VOTE_LOCK_KEY = "vote:lock:";
|
|
private static final String VOTE_LOCK_KEY = "vote:lock:";
|
|
|
|
|
|
|
|
|
|
+ // 作品数量
|
|
|
private static final String VOTE_COUNT_KEY = "vote:count:";
|
|
private static final String VOTE_COUNT_KEY = "vote:count:";
|
|
|
|
|
|
|
|
- private static final String VOTE_RANKING_KEY = "vote:ranking";
|
|
|
|
|
|
|
+ // 今日是否已经投给该作品
|
|
|
|
|
+ private static final String VOTE_WORK_KEY = "vote:work:";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -153,9 +161,7 @@ public class AccWorkServiceImpl extends ServiceImpl<AccWorkMapper, AccWork>
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 查询是否上传了视频,已上传更新
|
|
// 查询是否上传了视频,已上传更新
|
|
|
- AccWork work = baseMapper.selectOne(new LambdaQueryWrapper<AccWork>()
|
|
|
|
|
- .eq(AccWork::getActivityId, accWork.getActivityId())
|
|
|
|
|
- .eq(AccWork::getTeamId, accWork.getTeamId()));
|
|
|
|
|
|
|
+ AccWork work=baseMapper.selectOneByTeamIdAndActivityId(accWork.getActivityId(),accWork.getTeamId());
|
|
|
if(work!=null){
|
|
if(work!=null){
|
|
|
accWork.setId(work.getId());
|
|
accWork.setId(work.getId());
|
|
|
if (baseMapper.updateById(accWork)<1){
|
|
if (baseMapper.updateById(accWork)<1){
|
|
@@ -200,11 +206,6 @@ public class AccWorkServiceImpl extends ServiceImpl<AccWorkMapper, AccWork>
|
|
|
|
|
|
|
|
// 4. 使用Redis分布式锁防止重复提交
|
|
// 4. 使用Redis分布式锁防止重复提交
|
|
|
String lockKey = VOTE_LOCK_KEY + userId + ":" + workId + ":" + todayStr;
|
|
String lockKey = VOTE_LOCK_KEY + userId + ":" + workId + ":" + todayStr;
|
|
|
- Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
|
|
|
|
|
- if (Boolean.FALSE.equals(locked)) {
|
|
|
|
|
- throw new ServiceException("请勿重复提交");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
// 引入 Redisson
|
|
// 引入 Redisson
|
|
|
RLock lock = redissonClient.getLock(lockKey);
|
|
RLock lock = redissonClient.getLock(lockKey);
|
|
|
boolean lockAcquired = false;
|
|
boolean lockAcquired = false;
|
|
@@ -212,15 +213,14 @@ public class AccWorkServiceImpl extends ServiceImpl<AccWorkMapper, AccWork>
|
|
|
lockAcquired = lock.tryLock(3, 10, TimeUnit.SECONDS);
|
|
lockAcquired = lock.tryLock(3, 10, TimeUnit.SECONDS);
|
|
|
if (lockAcquired) {
|
|
if (lockAcquired) {
|
|
|
|
|
|
|
|
- // 5. 检查今日是否已给该作品投票
|
|
|
|
|
- if (accVoteRecordMapper.selectCount(new LambdaQueryWrapper<AccVoteRecord>()
|
|
|
|
|
- .eq(AccVoteRecord::getUserId, userId)
|
|
|
|
|
- .eq(AccVoteRecord::getWorkId, workId)
|
|
|
|
|
- .eq(AccVoteRecord::getVoteDate, today)) > 0) {
|
|
|
|
|
|
|
+ // 5. 检查今日是否已给该作品投票 (从 Redis 检查)
|
|
|
|
|
+ String userVoteKey = VOTE_WORK_KEY + userId + ":" + workId + ":" + todayStr;
|
|
|
|
|
+ Boolean hasVoted = redisTemplate.hasKey(userVoteKey);
|
|
|
|
|
+ if (hasVoted != null && hasVoted) {
|
|
|
throw new ServiceException("今日已给该作品投票");
|
|
throw new ServiceException("今日已给该作品投票");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 6. 创建投票记录
|
|
|
|
|
|
|
+ // 6. 创建投票记录 不加事务,影响速度 数据不对后期家定时任务修复
|
|
|
AccVoteRecord record = new AccVoteRecord();
|
|
AccVoteRecord record = new AccVoteRecord();
|
|
|
record.setWorkId(workId);
|
|
record.setWorkId(workId);
|
|
|
record.setUserId(userId);
|
|
record.setUserId(userId);
|
|
@@ -230,34 +230,36 @@ public class AccWorkServiceImpl extends ServiceImpl<AccWorkMapper, AccWork>
|
|
|
throw new ServiceException("投票失败");
|
|
throw new ServiceException("投票失败");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- AccWork work = new AccWork();
|
|
|
|
|
- work.setId(workId);
|
|
|
|
|
- // 7. 更新作品票数
|
|
|
|
|
- work.setVoteCount(work.getVoteCount() + 1);
|
|
|
|
|
- baseMapper.updateById(work);
|
|
|
|
|
|
|
+ // 7. 标记用户今日已投票到 Redis
|
|
|
|
|
+ redisTemplate.opsForValue().set(userVoteKey, true, 2, TimeUnit.DAYS);
|
|
|
|
|
|
|
|
// 8. 更新Redis缓存
|
|
// 8. 更新Redis缓存
|
|
|
// 8.1 更新作品票数缓存
|
|
// 8.1 更新作品票数缓存
|
|
|
String countKey = VOTE_COUNT_KEY + workId;
|
|
String countKey = VOTE_COUNT_KEY + workId;
|
|
|
- redisTemplate.opsForValue().increment(countKey);
|
|
|
|
|
|
|
+ Long currentVotes=redisTemplate.opsForValue().increment(countKey);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ UpdateWrapper<AccWork> updateWrapper = new UpdateWrapper<>();
|
|
|
|
|
+ updateWrapper.eq("id", workId).setSql("vote_count = vote_count + 1");
|
|
|
|
|
+ if(baseMapper.update(null, updateWrapper)<1){
|
|
|
|
|
+ throw new ServiceException("更新失败");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
|
|
|
// 8.2 更新排行榜(使用ZSet)
|
|
// 8.2 更新排行榜(使用ZSet)
|
|
|
- redisTemplate.opsForZSet().incrementScore(VOTE_RANKING_KEY, String.valueOf(workId), 1);
|
|
|
|
|
|
|
+// redisTemplate.opsForZSet().incrementScore(VOTE_RANKING_KEY, String.valueOf(workId), 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
// 8.4 更新用户今日投票计数
|
|
// 8.4 更新用户今日投票计数
|
|
|
String dailyLimitKey = DAILY_VOTE_LIMIT + activityId +":"+ userId + ":" + todayStr;
|
|
String dailyLimitKey = DAILY_VOTE_LIMIT + activityId +":"+ userId + ":" + todayStr;
|
|
|
- Long currentVotes = redisTemplate.opsForValue().increment(dailyLimitKey);
|
|
|
|
|
- redisTemplate.expire(dailyLimitKey, 2, TimeUnit.DAYS);
|
|
|
|
|
|
|
+ redisTemplate.opsForValue().increment(dailyLimitKey);
|
|
|
|
|
+ redisTemplate.expire(dailyLimitKey, 1, TimeUnit.DAYS);
|
|
|
|
|
|
|
|
- log.info("用户{}给作品{}投票成功,今日第{}票,当前作品总票数:{}",
|
|
|
|
|
- userId, workId, currentVotes, work.getVoteCount());
|
|
|
|
|
|
|
|
|
|
// 9. 返回结果
|
|
// 9. 返回结果
|
|
|
AccVoteRecordResponse response = new AccVoteRecordResponse();
|
|
AccVoteRecordResponse response = new AccVoteRecordResponse();
|
|
|
response.setWorkId(workId);
|
|
response.setWorkId(workId);
|
|
|
- response.setWorkName(work.getWorkName());
|
|
|
|
|
- response.setCurrentVotes(work.getVoteCount());
|
|
|
|
|
|
|
+ response.setCurrentVotes(currentVotes);
|
|
|
return R.ok().put("data", response);
|
|
return R.ok().put("data", response);
|
|
|
}else {
|
|
}else {
|
|
|
throw new ServiceException("服务繁忙,请稍后再试");
|
|
throw new ServiceException("服务繁忙,请稍后再试");
|
|
@@ -293,6 +295,32 @@ public class AccWorkServiceImpl extends ServiceImpl<AccWorkMapper, AccWork>
|
|
|
return accVoteRecordMapper.selectCount(new LambdaQueryWrapper<AccVoteRecord>()
|
|
return accVoteRecordMapper.selectCount(new LambdaQueryWrapper<AccVoteRecord>()
|
|
|
.eq(AccVoteRecord::getUserId, userId).eq(AccVoteRecord::getVoteDate, todayStr));
|
|
.eq(AccVoteRecord::getUserId, userId).eq(AccVoteRecord::getVoteDate, todayStr));
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * @Description: 作品查询
|
|
|
|
|
+ * @Param:
|
|
|
|
|
+ * @Return:
|
|
|
|
|
+ * @Author xgb
|
|
|
|
|
+ * @Date 2026/3/10 16:03
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public R getWorkList(AccWorkRequest request) {
|
|
|
|
|
+
|
|
|
|
|
+ LocalDate today = LocalDate.now();
|
|
|
|
|
+ String todayStr = today.format(DateTimeFormatter.ISO_DATE);
|
|
|
|
|
+
|
|
|
|
|
+ PageHelper.startPage(request.getPageNum(), request.getPageSize());
|
|
|
|
|
+ List<AccWork> accWorks=baseMapper.selectListForApp(request);
|
|
|
|
|
+ for (AccWork work : accWorks) {
|
|
|
|
|
+ String userVoteKey = VOTE_WORK_KEY + request.getUserId() + ":" + work.getId() + ":" + todayStr;
|
|
|
|
|
+ Boolean hasVoted = redisTemplate.hasKey(userVoteKey);
|
|
|
|
|
+ // 假设 AccWork 有 isVoted 字段
|
|
|
|
|
+ work.setIsVoted(hasVoted != null && hasVoted);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return R.ok().put("data",new PageInfo<>(accWorks));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|