领取奖励新增锁
This commit is contained in:
parent
e9a281e8dd
commit
b206158d06
@ -1,6 +1,7 @@
|
|||||||
package com.red.circle.other.app.command;
|
package com.red.circle.other.app.command;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
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.clientobject.SpinsTaskRewardCO;
|
||||||
import com.red.circle.other.app.dto.cmd.SpinsTaskReceiveRewardCmd;
|
import com.red.circle.other.app.dto.cmd.SpinsTaskReceiveRewardCmd;
|
||||||
import com.red.circle.other.infra.database.rds.entity.task.SpinsTaskConfig;
|
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 com.red.circle.tool.core.date.TimestampUtils;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@ -20,6 +22,7 @@ import java.time.LocalDate;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 领取任务奖励执行器
|
* 领取任务奖励执行器
|
||||||
@ -32,115 +35,149 @@ import java.util.List;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SpinsTaskReceiveRewardExe {
|
public class SpinsTaskReceiveRewardExe {
|
||||||
|
|
||||||
private final SpinsTaskConfigDAO spinsTaskConfigDAO;
|
private final SpinsTaskConfigDAO spinsTaskConfigDAO;
|
||||||
private final SpinsUserTaskProgressDAO spinsUserTaskProgressDAO;
|
private final SpinsUserTaskProgressDAO spinsUserTaskProgressDAO;
|
||||||
private final SpinsUserTaskRecordDAO spinsUserTaskRecordDAO;
|
private final SpinsUserTaskRecordDAO spinsUserTaskRecordDAO;
|
||||||
private final LotteryTicketClient lotteryTicketClient;
|
private final LotteryTicketClient lotteryTicketClient;
|
||||||
|
private final RedisService redisService;
|
||||||
|
|
||||||
/**
|
private static final String LOCK_KEY_PREFIX = "spins:task:receive:lock:";
|
||||||
* 执行领取任务奖励
|
private static final long LOCK_EXPIRE_TIME = 10; // 锁过期时间10秒
|
||||||
*
|
|
||||||
* @param cmd 领取奖励命令
|
|
||||||
* @return 奖励信息
|
|
||||||
*/
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public SpinsTaskRewardCO execute(SpinsTaskReceiveRewardCmd cmd) {
|
|
||||||
Long userId = cmd.getReqUserId();
|
|
||||||
String taskCode = cmd.getTaskCode();
|
|
||||||
|
|
||||||
// 1. 查询任务配置
|
/**
|
||||||
List<SpinsTaskConfig> spinsTaskConfigs = spinsTaskConfigDAO.selectList(
|
* 执行领取任务奖励
|
||||||
new LambdaQueryWrapper<SpinsTaskConfig>()
|
*
|
||||||
.eq(SpinsTaskConfig::getTaskCode, taskCode)
|
* @param cmd 领取奖励命令
|
||||||
.eq(SpinsTaskConfig::getStatus, 1)
|
* @return 奖励信息
|
||||||
);
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public SpinsTaskRewardCO execute(SpinsTaskReceiveRewardCmd cmd) {
|
||||||
|
Long userId = cmd.getReqUserId();
|
||||||
|
String taskCode = cmd.getTaskCode();
|
||||||
|
|
||||||
if (spinsTaskConfigs.isEmpty()) {
|
// 构建分布式锁key
|
||||||
throw new RuntimeException("The task does not exist or has been taken offline");
|
String lockKey = LOCK_KEY_PREFIX + userId + ":" + taskCode;
|
||||||
}
|
|
||||||
SpinsTaskConfig taskConfig = spinsTaskConfigs.get(0);
|
|
||||||
|
|
||||||
// 2. 查询用户任务进度
|
// 尝试获取锁
|
||||||
String cycleKey = getCurrentCycleKey();
|
boolean lockSuccess = redisService.setIfAbsent(lockKey, String.valueOf(System.currentTimeMillis()),
|
||||||
List<SpinsUserTaskProgress> progressList = spinsUserTaskProgressDAO.selectList(
|
LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
|
||||||
new LambdaQueryWrapper<SpinsUserTaskProgress>()
|
|
||||||
.eq(SpinsUserTaskProgress::getUserId, userId)
|
|
||||||
.eq(SpinsUserTaskProgress::getTaskCode, taskCode)
|
|
||||||
.eq(SpinsUserTaskProgress::getCycleKey, cycleKey)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (progressList.isEmpty()) {
|
if (!lockSuccess) {
|
||||||
throw new RuntimeException("The task has not started yet.");
|
log.warn("领取任务奖励请求过于频繁,请稍后再试, userId={}, taskCode={}", userId, taskCode);
|
||||||
}
|
throw new RuntimeException("The request is too frequent. Please try again later");
|
||||||
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<Long> 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
try {
|
||||||
* 获取当前周期key(每日任务格式:yyyyMMdd)
|
return executeReceiveReward(userId, taskCode);
|
||||||
*/
|
} finally {
|
||||||
private String getCurrentCycleKey() {
|
// 释放锁
|
||||||
return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
redisService.delete(lockKey);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行领取任务奖励的核心逻辑
|
||||||
|
*/
|
||||||
|
private SpinsTaskRewardCO executeReceiveReward(Long userId, String taskCode) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000L);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 查询任务配置
|
||||||
|
List<SpinsTaskConfig> spinsTaskConfigs = spinsTaskConfigDAO.selectList(
|
||||||
|
new LambdaQueryWrapper<SpinsTaskConfig>()
|
||||||
|
.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<SpinsUserTaskProgress> progressList = spinsUserTaskProgressDAO.selectList(
|
||||||
|
new LambdaQueryWrapper<SpinsUserTaskProgress>()
|
||||||
|
.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<Long> 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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user