From b206158d06720c0d109b6930420df10a56350f17 Mon Sep 17 00:00:00 2001 From: tianfeng <769204422@qq.com> Date: Wed, 22 Oct 2025 15:09:06 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=86=E5=8F=96=E5=A5=96=E5=8A=B1=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/SpinsTaskReceiveRewardExe.java | 245 ++++++++++-------- 1 file changed, 141 insertions(+), 104 deletions(-) diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/SpinsTaskReceiveRewardExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/SpinsTaskReceiveRewardExe.java index 4b38d25a..14a7cf6f 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/SpinsTaskReceiveRewardExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/SpinsTaskReceiveRewardExe.java @@ -1,6 +1,7 @@ package com.red.circle.other.app.command; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.red.circle.component.redis.service.RedisService; import com.red.circle.other.app.dto.clientobject.SpinsTaskRewardCO; import com.red.circle.other.app.dto.cmd.SpinsTaskReceiveRewardCmd; import com.red.circle.other.infra.database.rds.entity.task.SpinsTaskConfig; @@ -13,6 +14,7 @@ import com.red.circle.other.inner.endpoint.activity.LotteryTicketClient; import com.red.circle.tool.core.date.TimestampUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -20,6 +22,7 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; /** * 领取任务奖励执行器 @@ -32,115 +35,149 @@ import java.util.List; @RequiredArgsConstructor public class SpinsTaskReceiveRewardExe { - private final SpinsTaskConfigDAO spinsTaskConfigDAO; - private final SpinsUserTaskProgressDAO spinsUserTaskProgressDAO; - private final SpinsUserTaskRecordDAO spinsUserTaskRecordDAO; - private final LotteryTicketClient lotteryTicketClient; + private final SpinsTaskConfigDAO spinsTaskConfigDAO; + private final SpinsUserTaskProgressDAO spinsUserTaskProgressDAO; + private final SpinsUserTaskRecordDAO spinsUserTaskRecordDAO; + private final LotteryTicketClient lotteryTicketClient; + private final RedisService redisService; - /** - * 执行领取任务奖励 - * - * @param cmd 领取奖励命令 - * @return 奖励信息 - */ - @Transactional(rollbackFor = Exception.class) - public SpinsTaskRewardCO execute(SpinsTaskReceiveRewardCmd cmd) { - Long userId = cmd.getReqUserId(); - String taskCode = cmd.getTaskCode(); + private static final String LOCK_KEY_PREFIX = "spins:task:receive:lock:"; + private static final long LOCK_EXPIRE_TIME = 10; // 锁过期时间10秒 - // 1. 查询任务配置 - List spinsTaskConfigs = spinsTaskConfigDAO.selectList( - new LambdaQueryWrapper() - .eq(SpinsTaskConfig::getTaskCode, taskCode) - .eq(SpinsTaskConfig::getStatus, 1) - ); + /** + * 执行领取任务奖励 + * + * @param cmd 领取奖励命令 + * @return 奖励信息 + */ + @Transactional(rollbackFor = Exception.class) + public SpinsTaskRewardCO execute(SpinsTaskReceiveRewardCmd cmd) { + Long userId = cmd.getReqUserId(); + String taskCode = cmd.getTaskCode(); - if (spinsTaskConfigs.isEmpty()) { - throw new RuntimeException("The task does not exist or has been taken offline"); - } - SpinsTaskConfig taskConfig = spinsTaskConfigs.get(0); + // 构建分布式锁key + String lockKey = LOCK_KEY_PREFIX + userId + ":" + taskCode; - // 2. 查询用户任务进度 - String cycleKey = getCurrentCycleKey(); - List progressList = spinsUserTaskProgressDAO.selectList( - new LambdaQueryWrapper() - .eq(SpinsUserTaskProgress::getUserId, userId) - .eq(SpinsUserTaskProgress::getTaskCode, taskCode) - .eq(SpinsUserTaskProgress::getCycleKey, cycleKey) - ); + // 尝试获取锁 + boolean lockSuccess = redisService.setIfAbsent(lockKey, String.valueOf(System.currentTimeMillis()), + LOCK_EXPIRE_TIME, TimeUnit.SECONDS); - if (progressList.isEmpty()) { - throw new RuntimeException("The task has not started yet."); - } - SpinsUserTaskProgress progress = progressList.get(0); - - // 3. 校验任务状态 - if (progress.getTaskStatus() == 0) { - throw new RuntimeException("The task is not completed and the reward cannot be claimed"); - } - - if (progress.getTaskStatus() == 2) { - throw new RuntimeException("The reward has been claimed. Please do not claim it again"); - } - - // 4. 发放抽奖券 - List ticketIds = new ArrayList<>(); - try { - for (int i = 0; i < taskConfig.getRewardValue(); i++) { - Long ticketId = lotteryTicketClient.addTicket( - userId, - 1, - "SPINS_TASK", - taskCode - ).getBody(); - ticketIds.add(ticketId); - } - } catch (Exception e) { - log.error("发放抽奖券失败, userId={}, taskCode={}", userId, taskCode, e); - throw new RuntimeException("The reward distribution failed. Please try again later"); - } - - // 5. 更新任务进度状态 - progress.setTaskStatus(2); - progress.setReceiveTime(TimestampUtils.now()); - progress.setLotteryTicketIds(String.join(",", ticketIds.stream().map(String::valueOf).toArray(String[]::new))); - progress.setUpdateTime(TimestampUtils.now()); - spinsUserTaskProgressDAO.updateById(progress); - - // 6. 记录任务完成记录 - SpinsUserTaskRecord record = new SpinsUserTaskRecord(); - record.setUserId(userId); - record.setTaskId(taskConfig.getId()); - record.setTaskCode(taskCode); - record.setTaskName(taskConfig.getTaskName()); - record.setCompleteValue(progress.getCurrentValue()); - record.setCompleteTime(progress.getCompleteTime()); - record.setCycleKey(cycleKey); - record.setRewardType(taskConfig.getRewardType()); - record.setRewardValue(taskConfig.getRewardValue()); - record.setLotteryActivityId(taskConfig.getLotteryActivityId()); - record.setLotteryTicketIds(progress.getLotteryTicketIds()); - record.setReceiveStatus(1); - record.setCreateTime(TimestampUtils.now()); - spinsUserTaskRecordDAO.insert(record); - - // 7. 组装返回结果 - SpinsTaskRewardCO rewardCO = new SpinsTaskRewardCO(); - rewardCO.setTaskCode(taskCode); - rewardCO.setTaskName(taskConfig.getTaskName()); - rewardCO.setRewardType(taskConfig.getRewardType()); - rewardCO.setRewardValue(taskConfig.getRewardValue()); - rewardCO.setLotteryTicketIds(ticketIds); - rewardCO.setReceiveTime(TimestampUtils.now()); - - log.info("任务奖励领取成功, userId={}, taskCode={}, ticketIds={}", userId, taskCode, ticketIds); - return rewardCO; + if (!lockSuccess) { + log.warn("领取任务奖励请求过于频繁,请稍后再试, userId={}, taskCode={}", userId, taskCode); + throw new RuntimeException("The request is too frequent. Please try again later"); } - /** - * 获取当前周期key(每日任务格式:yyyyMMdd) - */ - private String getCurrentCycleKey() { - return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + try { + return executeReceiveReward(userId, taskCode); + } finally { + // 释放锁 + redisService.delete(lockKey); } -} + } + + /** + * 执行领取任务奖励的核心逻辑 + */ + private SpinsTaskRewardCO executeReceiveReward(Long userId, String taskCode) { + try { + Thread.sleep(1000L); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + // 1. 查询任务配置 + List spinsTaskConfigs = spinsTaskConfigDAO.selectList( + new LambdaQueryWrapper() + .eq(SpinsTaskConfig::getTaskCode, taskCode) + .eq(SpinsTaskConfig::getStatus, 1) + ); + + if (spinsTaskConfigs.isEmpty()) { + throw new RuntimeException("The task does not exist or has been taken offline"); + } + SpinsTaskConfig taskConfig = spinsTaskConfigs.get(0); + + // 2. 查询用户任务进度 + String cycleKey = getCurrentCycleKey(); + List progressList = spinsUserTaskProgressDAO.selectList( + new LambdaQueryWrapper() + .eq(SpinsUserTaskProgress::getUserId, userId) + .eq(SpinsUserTaskProgress::getTaskCode, taskCode) + .eq(SpinsUserTaskProgress::getCycleKey, cycleKey) + ); + + if (progressList.isEmpty()) { + throw new RuntimeException("The task has not started yet."); + } + SpinsUserTaskProgress progress = progressList.get(0); + + // 3. 校验任务状态 + if (progress.getTaskStatus() == 0) { + throw new RuntimeException("The task is not completed and the reward cannot be claimed"); + } + + if (progress.getTaskStatus() == 2) { + throw new RuntimeException("The reward has been claimed. Please do not claim it again"); + } + + // 4. 发放抽奖券 + List ticketIds = new ArrayList<>(); + try { + for (int i = 0; i < taskConfig.getRewardValue(); i++) { + Long ticketId = lotteryTicketClient.addTicket( + userId, + 1, + "SPINS_TASK", + taskCode + ).getBody(); + ticketIds.add(ticketId); + } + } catch (Exception e) { + log.error("发放抽奖券失败, userId={}, taskCode={}", userId, taskCode, e); + throw new RuntimeException("The reward distribution failed. Please try again later"); + } + + // 5. 更新任务进度状态 + progress.setTaskStatus(2); + progress.setReceiveTime(TimestampUtils.now()); + progress.setLotteryTicketIds(String.join(",", ticketIds.stream().map(String::valueOf).toArray(String[]::new))); + progress.setUpdateTime(TimestampUtils.now()); + spinsUserTaskProgressDAO.updateById(progress); + + // 6. 记录任务完成记录 + SpinsUserTaskRecord record = new SpinsUserTaskRecord(); + record.setUserId(userId); + record.setTaskId(taskConfig.getId()); + record.setTaskCode(taskCode); + record.setTaskName(taskConfig.getTaskName()); + record.setCompleteValue(progress.getCurrentValue()); + record.setCompleteTime(progress.getCompleteTime()); + record.setCycleKey(cycleKey); + record.setRewardType(taskConfig.getRewardType()); + record.setRewardValue(taskConfig.getRewardValue()); + record.setLotteryActivityId(taskConfig.getLotteryActivityId()); + record.setLotteryTicketIds(progress.getLotteryTicketIds()); + record.setReceiveStatus(1); + record.setCreateTime(TimestampUtils.now()); + spinsUserTaskRecordDAO.insert(record); + + // 7. 组装返回结果 + SpinsTaskRewardCO rewardCO = new SpinsTaskRewardCO(); + rewardCO.setTaskCode(taskCode); + rewardCO.setTaskName(taskConfig.getTaskName()); + rewardCO.setRewardType(taskConfig.getRewardType()); + rewardCO.setRewardValue(taskConfig.getRewardValue()); + rewardCO.setLotteryTicketIds(ticketIds); + rewardCO.setReceiveTime(TimestampUtils.now()); + + log.info("任务奖励领取成功, userId={}, taskCode={}, ticketIds={}", userId, taskCode, ticketIds); + return rewardCO; + } + + /** + * 获取当前周期key(每日任务格式:yyyyMMdd) + */ + private String getCurrentCycleKey() { + return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + } +} \ No newline at end of file