diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/task/RoomDailyTaskClaimExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/task/RoomDailyTaskClaimExe.java index 5bc80fc7..271623b6 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/task/RoomDailyTaskClaimExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/task/RoomDailyTaskClaimExe.java @@ -10,7 +10,6 @@ import com.red.circle.other.infra.database.rds.entity.task.RoomDailyTaskTier; import com.red.circle.other.infra.database.rds.service.RoomDailyTaskDatabaseService; import com.red.circle.other.inner.asserts.user.UserErrorCode; import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils; -import com.red.circle.tool.core.sequence.IdWorkerUtils; import com.red.circle.tool.core.tuple.PennyAmount; import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd; @@ -22,9 +21,12 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** - * 领取任务奖励 + * 领取任务奖励(支持同一任务多档位批量领取) */ @Slf4j @Component @@ -38,68 +40,77 @@ public class RoomDailyTaskClaimExe { public RoomDailyTaskClaimCO execute(RoomDailyTaskClaimCmd cmd) { Long userId = cmd.getReqUserId(); String taskCode = cmd.getTaskCode(); - Integer tierIndex = cmd.getTierIndex(); - LocalDate today = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate(); - String bizNo = userId + ":" + taskCode + ":" + today + ":" + tierIndex; - - if (taskDatabaseService.existsClaimRecord(bizNo)) { - ResponseAssert.failure(UserErrorCode.AWARD_RECEIVED); - } RoomDailyTaskConfig config = taskDatabaseService.getConfigByCode(taskCode); - if (config == null) { - ResponseAssert.failure(UserErrorCode.TASK_NOT_FOUND); - } + ResponseAssert.notNull(UserErrorCode.TASK_NOT_FOUND, config); - RoomDailyTaskTier tier = taskDatabaseService.getTier(taskCode, tierIndex); - if (tier == null) { - ResponseAssert.failure(UserErrorCode.TASK_TIER_NOT_FOUND); - } + List tiers = taskDatabaseService.listTiersByTaskCodes(Collections.singletonList(taskCode)); + ResponseAssert.isTrue(UserErrorCode.TASK_TIER_NOT_FOUND, !tiers.isEmpty()); RoomDailyTaskProgress progress = taskDatabaseService.getProgress(userId, taskCode, today); - if (progress == null || progress.getMaxTierCompleted() < tierIndex) { - ResponseAssert.failure(UserErrorCode.INCOMPLETE_TASK); - } - if (progress.getMaxTierClaimed() >= tierIndex) { + ResponseAssert.notNull(UserErrorCode.INCOMPLETE_TASK, progress); + + // 找出所有已完成但未领取的档位 + int maxTierCompleted = progress.getMaxTierCompleted(); + int maxTierClaimed = progress.getMaxTierClaimed(); + if (maxTierCompleted <= maxTierClaimed) { ResponseAssert.failure(UserErrorCode.AWARD_RECEIVED); } - // 更新领取状态 - progress.setMaxTierClaimed(tierIndex); + List claimableTiers = tiers.stream() + .filter(t -> t.getTierIndex() > maxTierClaimed && t.getTierIndex() <= maxTierCompleted) + .toList(); + + long totalReward = 0; + List records = new ArrayList<>(); + for (RoomDailyTaskTier tier : claimableTiers) { + String bizNo = userId + ":" + taskCode + ":" + today + ":" + tier.getTierIndex(); + if (taskDatabaseService.existsClaimRecord(bizNo)) { + continue; + } + totalReward += tier.getRewardValue(); + + RoomDailyTaskClaimRecord record = new RoomDailyTaskClaimRecord(); + record.setUserId(userId); + record.setTaskId(config.getId()); + record.setTaskCode(taskCode); + record.setTierIndex(tier.getTierIndex()); + record.setRewardType(tier.getRewardType()); + record.setRewardValue(tier.getRewardValue()); + record.setCycleDate(today); + record.setBizNo(bizNo); + record.setClaimTime(LocalDateTime.now()); + record.setCreateTime(LocalDateTime.now()); + records.add(record); + } + + if (records.isEmpty()) { + ResponseAssert.failure(UserErrorCode.AWARD_RECEIVED); + } + + // 更新最高已领取档位 + progress.setMaxTierClaimed(maxTierCompleted); progress.setUpdateTime(LocalDateTime.now()); taskDatabaseService.saveOrUpdateProgress(progress); + taskDatabaseService.saveClaimRecords(records); - // 保存领取记录 - RoomDailyTaskClaimRecord record = new RoomDailyTaskClaimRecord(); - record.setUserId(userId); - record.setTaskId(config.getId()); - record.setTaskCode(taskCode); - record.setTierIndex(tierIndex); - record.setRewardType(tier.getRewardType()); - record.setRewardValue(tier.getRewardValue()); - record.setCycleDate(today); - record.setBizNo(bizNo); - record.setClaimTime(LocalDateTime.now()); - record.setCreateTime(LocalDateTime.now()); - taskDatabaseService.saveClaimRecord(record); - + // 发放奖励 + String bizNo = userId + ":" + taskCode + ":" + today + ":batch:" + maxTierClaimed + "-" + maxTierCompleted; walletGoldClient.changeBalance(GoldReceiptCmd.builder() .appIncome() .userId(userId) - .amount(PennyAmount.ofDollar(tier.getRewardValue().longValue())) + .amount(PennyAmount.ofDollar(totalReward)) .eventId(bizNo) .sysOrigin(cmd.requireReqSysOrigin()) .origin(GoldOrigin.ROOM_DAILY_TASK) .build()); - log.info("任务奖励领取成功, userId={}, taskCode={}, tierIndex={}, rewardValue={}", userId, taskCode, tierIndex, tier.getRewardValue()); - RoomDailyTaskClaimCO co = new RoomDailyTaskClaimCO(); co.setTaskCode(taskCode); co.setTaskName(config.getTaskName()); - co.setTierIndex(tierIndex); - co.setRewardValue(tier.getRewardValue()); + co.setClaimedCount(records.size()); + co.setTotalRewardValue(totalReward); co.setClaimTime(LocalDateTime.now()); return co; } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/InviteUserActivityServiceImpl.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/InviteUserActivityServiceImpl.java index 67c18fa6..ebfd10c9 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/InviteUserActivityServiceImpl.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/InviteUserActivityServiceImpl.java @@ -20,6 +20,8 @@ import com.red.circle.other.app.command.activity.GetValidInviteUsersExe; import com.red.circle.other.app.dto.cmd.activity.BindInviteCodeCmd; import com.red.circle.other.app.dto.cmd.activity.InviteUserActivityLimitCmd; import java.util.List; + +import com.red.circle.other.app.util.DistributedLockUtil; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -48,6 +50,7 @@ public class InviteUserActivityServiceImpl implements InviteUserActivityService private final GetIncomeStatisticsExe getIncomeStatisticsExe; private final GetValidInviteUsersExe getValidInviteUsersExe; private final MyInviterInfoQryExe myInviterInfoQryExe; + private final DistributedLockUtil distributedLockUtil; @Override public InviteUserActivitySummaryCO getSummary(AppExtCommand cmd) { @@ -97,7 +100,10 @@ public class InviteUserActivityServiceImpl implements InviteUserActivityService @Override public void bindInviteCode(BindInviteCodeCmd cmd) { - bindInviteCodeExe.execute(cmd); + String key = "bind:inviteCode:" + cmd.getReqUserId(); + distributedLockUtil.executeWithLock(key, () -> { + bindInviteCodeExe.execute(cmd); + }); } @Override diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/task/RoomDailyTaskClaimCO.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/task/RoomDailyTaskClaimCO.java index ac9a4ba3..1b27ca97 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/task/RoomDailyTaskClaimCO.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/task/RoomDailyTaskClaimCO.java @@ -16,11 +16,11 @@ public class RoomDailyTaskClaimCO { /** 任务名称 */ private String taskName; - /** 领取的档位序号 */ - private Integer tierIndex; + /** 本次领取的档位数量 */ + private Integer claimedCount; - /** 奖励金币数 */ - private Integer rewardValue; + /** 本次领取的总奖励金币数 */ + private Long totalRewardValue; /** 领取时间 */ private LocalDateTime claimTime; diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/task/RoomDailyTaskClaimCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/task/RoomDailyTaskClaimCmd.java index 88485295..2523bcb1 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/task/RoomDailyTaskClaimCmd.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/task/RoomDailyTaskClaimCmd.java @@ -2,7 +2,6 @@ package com.red.circle.other.app.dto.cmd.task; import com.red.circle.common.business.dto.cmd.AppExtCommand; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,7 +14,4 @@ public class RoomDailyTaskClaimCmd extends AppExtCommand { @NotBlank(message = "任务编码不能为空") private String taskCode; - - @NotNull(message = "档位序号不能为空") - private Integer tierIndex; } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/RoomDailyTaskDatabaseService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/RoomDailyTaskDatabaseService.java index e46dd275..67c5f1cd 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/RoomDailyTaskDatabaseService.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/RoomDailyTaskDatabaseService.java @@ -78,6 +78,10 @@ public class RoomDailyTaskDatabaseService { claimRecordDAO.insert(record); } + public void saveClaimRecords(List records) { + claimRecordDAO.insertBatchSomeColumn(records); + } + public RoomDailyTaskConfig getConfigByCode(String taskCode) { return configDAO.selectList( new LambdaQueryWrapper()