diff --git a/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/enums/GoldOrigin.java b/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/enums/GoldOrigin.java index 1d4027cc..c1d544c3 100644 --- a/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/enums/GoldOrigin.java +++ b/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/enums/GoldOrigin.java @@ -702,6 +702,16 @@ public enum GoldOrigin { * 抽奖奖励 */ LOTTERY_REWARD("Lottery reward"), + + /** + * 抽奖兑换金币 + */ + LOTTERY_EXCHANGE_GOLD("Lottery exchange gold"), + + /** + * 抽奖转账金币 + */ + LOTTERY_TRANSFER_GOLD("Lottery transfer gold"), ; private final String desc; diff --git a/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/activity/LotteryActivityRestController.java b/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/activity/LotteryActivityRestController.java index b2bb6ce9..3756db7c 100644 --- a/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/activity/LotteryActivityRestController.java +++ b/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/activity/LotteryActivityRestController.java @@ -20,6 +20,8 @@ import com.red.circle.other.app.dto.cmd.activity.LotteryRecordQryCmd; import com.red.circle.other.app.dto.cmd.activity.LotteryWinnerHistoryQryCmd; import com.red.circle.other.app.dto.cmd.activity.LotteryWithdrawCmd; import com.red.circle.other.app.dto.cmd.activity.LotteryWithdrawQryCmd; +import com.red.circle.other.app.dto.cmd.activity.LotteryExchangeGoldCmd; +import com.red.circle.other.app.dto.cmd.activity.LotteryTransferCmd; import com.red.circle.other.app.service.activity.LotteryActivityRestService; import java.util.List; @@ -27,12 +29,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; /** *

@@ -219,8 +216,34 @@ public class LotteryActivityRestController extends BaseController { * @eo.request-type formdata */ @GetMapping("/withdraw/amount") - public LotteryWithdrawAmountCO getWithdrawAmount(AppExtCommand cmd, Long activityId) { - return lotteryActivityRestService.getWithdrawAmount(cmd, activityId); + public LotteryWithdrawAmountCO getWithdrawAmount(AppExtCommand cmd, @RequestParam Long activityId) { + return lotteryActivityRestService.getWithdrawAmount(cmd.getReqUserId(), activityId); + } + + /** + * 可提现金额兑换金币. + * + * @eo.name 可提现金额兑换金币. + * @eo.url /exchange + * @eo.method post + * @eo.request-type json + */ + @PostMapping("/exchange") + public void exchange(@RequestBody @Validated LotteryExchangeGoldCmd cmd) { + lotteryActivityRestService.exchangeGold(cmd); + } + + /** + * 可提现金额转账给指定用户. + * + * @eo.name 可提现金额转账给指定用户. + * @eo.url /transfer + * @eo.method post + * @eo.request-type json + */ + @PostMapping("/transfer") + public void transfer(@RequestBody @Validated LotteryTransferCmd cmd) { + lotteryActivityRestService.transfer(cmd); } } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryExchangeGoldExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryExchangeGoldExe.java new file mode 100644 index 00000000..e5812940 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryExchangeGoldExe.java @@ -0,0 +1,142 @@ +package com.red.circle.other.app.command.activity; + +import com.red.circle.component.redis.service.RedisService; +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.framework.core.response.CommonErrorCode; +import com.red.circle.framework.core.response.ResponseErrorCode; +import com.red.circle.other.app.dto.clientobject.activity.LotteryWithdrawAmountCO; +import com.red.circle.other.app.dto.cmd.activity.LotteryExchangeGoldCmd; +import com.red.circle.other.app.service.activity.LotteryActivityRestService; +import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserProgress; +import com.red.circle.other.infra.database.rds.entity.activity.LotteryWithdraw; +import com.red.circle.other.infra.database.rds.service.activity.LotteryUserProgressService; +import com.red.circle.other.infra.database.rds.service.activity.LotteryWithdrawService; +import com.red.circle.other.inner.endpoint.user.region.UserRegionClient; +import com.red.circle.other.inner.model.dto.user.SysExchangeGoldTipDTO; +import com.red.circle.tool.core.date.TimestampUtils; +import com.red.circle.tool.core.num.ArithmeticUtils; +import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; +import com.red.circle.wallet.inner.error.WalletErrorCode; +import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.temporal.WeekFields; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; + +import com.red.circle.wallet.inner.model.enums.GoldOrigin; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 可提现金额兑换金币执行器. + * + * @author system + * @since 2025-10-24 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class LotteryExchangeGoldExe { + + private final RedisService redisService; + private final LotteryWithdrawService lotteryWithdrawService; + private final UserRegionClient userRegionClient; + private final WalletGoldClient walletGoldClient; + private final LotteryWithdrawAmountQryExe lotteryWithdrawAmountQryExe; + + public void execute(LotteryExchangeGoldCmd cmd) { + ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR, + ArithmeticUtils.gtZero(cmd.getAmount())); + + // 上锁 + String key = "LotteryExchangeGold:" + cmd.requiredReqUserId(); + ResponseAssert.isTrue(CommonErrorCode.REQUEST_LIMITING, redisService.lock(key, 60)); + + try { + Long userId = cmd.requiredReqUserId(); + + // 1. 获取可提现金额 + LotteryWithdrawAmountCO withdrawAmount = lotteryWithdrawAmountQryExe.execute(userId, cmd.getActivityId()); + BigDecimal availableAmount = withdrawAmount.getAvailableAmount(); + + // 2. 验证金额是否足够 + ResponseAssert.isTrue(WalletErrorCode.INSUFFICIENT_BALANCE, ArithmeticUtils.gte(availableAmount, cmd.getAmount())); + + // 3. 获取区域兑换金币比例 + Double goldRatio = getExchangeGoldRatio(cmd); + + // 4. 计算金币数量 + BigDecimal goldQuantity = BigDecimal.valueOf(goldRatio) + .multiply(cmd.getAmount()) + .setScale(0, RoundingMode.DOWN); + + // 5. 记录兑换(作为提现记录) + LotteryWithdraw withdraw = new LotteryWithdraw(); + withdraw.setUserId(userId); + withdraw.setWithdrawNo(generateExchangeWithdrawNo()); + withdraw.setActivityId(cmd.getActivityId()); + withdraw.setAmount(cmd.getAmount()); + withdraw.setStatus(3); // 已完成 + withdraw.setDescription("兑换金币: " + goldQuantity + "金币"); + withdraw.setCreateTime(TimestampUtils.now()); + withdraw.setUpdateTime(TimestampUtils.now()); + lotteryWithdrawService.save(withdraw); + + // 6. 充值金币给用户自己 + GoldReceiptCmd build = GoldReceiptCmd.builder() + .appIncome() + .userId(userId) + .amount(goldQuantity) + .eventId(withdraw.getId().toString()) + .remark("抽奖活动兑换金币") + .sysOrigin(cmd.getReqSysOrigin().getOrigin()) + .origin(GoldOrigin.LOTTERY_EXCHANGE_GOLD) + .build(); + ResponseAssert.requiredSuccess(walletGoldClient.changeBalance(build)); + + log.info("用户{}兑换金币成功,金额{},金币{}", userId, cmd.getAmount(), goldQuantity); + } finally { + redisService.unlock(key); + } + } + + /** + * 生成提现单号. + */ + private String generateExchangeWithdrawNo() { + return "EX" + System.currentTimeMillis() + + String.format("%06d", ThreadLocalRandom.current().nextInt(1000000)); + } + + + /** + * 获得1$兑换多少金币比例,基于区域+用户输入的美元. + * + * @return 1美元可获得的金币数量. + */ + private Double getExchangeGoldRatio(LotteryExchangeGoldCmd cmd) { + List exchangeGolds = ResponseAssert.requiredSuccess( + userRegionClient.listRegionExchangeGoldTip(cmd.requiredReqUserId(), false)); + ResponseAssert.notEmpty(CommonErrorCode.CONFIGURATION_ERROR, exchangeGolds); + + exchangeGolds = exchangeGolds.stream().filter(gold -> + cmd.getAmount().compareTo(BigDecimal.valueOf(gold.getStart())) >= 0 && + cmd.getAmount().compareTo(BigDecimal.valueOf(gold.getEnd())) <= 0) + .collect(Collectors.toList()); + ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, exchangeGolds); + + SysExchangeGoldTipDTO exchangeGold = exchangeGolds.get(0); + ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, exchangeGold); + ResponseAssert.isFalse(CommonErrorCode.CONFIGURATION_ERROR, + Objects.isNull(exchangeGold.getValue()) || exchangeGold.getValue() <= 0); + + return exchangeGold.getValue(); + } + +} \ No newline at end of file diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryTransferExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryTransferExe.java new file mode 100644 index 00000000..1be1759f --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryTransferExe.java @@ -0,0 +1,193 @@ +package com.red.circle.other.app.command.activity; + +import com.red.circle.common.business.core.enums.OpUserType; +import com.red.circle.common.business.core.enums.ReceiptType; +import com.red.circle.component.redis.service.RedisService; +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.framework.core.response.CommonErrorCode; +import com.red.circle.framework.core.response.ResponseErrorCode; +import com.red.circle.other.app.dto.clientobject.activity.LotteryWithdrawAmountCO; +import com.red.circle.other.app.dto.cmd.activity.LotteryTransferCmd; +import com.red.circle.other.app.service.activity.LotteryActivityRestService; +import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserProgress; +import com.red.circle.other.infra.database.rds.entity.activity.LotteryWithdraw; +import com.red.circle.other.infra.database.rds.service.activity.LotteryUserProgressService; +import com.red.circle.other.infra.database.rds.service.activity.LotteryWithdrawService; +import com.red.circle.other.inner.endpoint.user.region.UserRegionClient; +import com.red.circle.other.inner.model.dto.user.SysExchangeGoldTipDTO; +import com.red.circle.tool.core.date.TimestampUtils; +import com.red.circle.tool.core.num.ArithmeticUtils; +import com.red.circle.tool.core.tuple.PennyAmount; +import com.red.circle.wallet.inner.endpoint.freight.FreightBalanceClient; +import com.red.circle.wallet.inner.endpoint.freight.FreightBalanceRunningWaterClient; +import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; +import com.red.circle.wallet.inner.error.WalletErrorCode; +import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd; +import com.red.circle.wallet.inner.model.dto.FreightBalanceRunningWaterDTO; +import com.red.circle.wallet.inner.model.enums.FreightBalanceOrigin; +import com.red.circle.wallet.inner.model.enums.GoldOrigin; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.temporal.WeekFields; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 可提现金额转账给指定用户执行器. + * + * @author system + * @since 2025-10-24 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class LotteryTransferExe { + + private final RedisService redisService; + private final LotteryWithdrawService lotteryWithdrawService; + private final UserRegionClient userRegionClient; + private final WalletGoldClient walletGoldClient; + private final FreightBalanceClient freightBalanceClient; + private final FreightBalanceRunningWaterClient freightBalanceRunningWaterClient; + private final LotteryWithdrawAmountQryExe lotteryWithdrawAmountQryExe; + + public void execute(LotteryTransferCmd cmd) { + ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR, + ArithmeticUtils.gtZero(cmd.getAmount())); + ResponseAssert.notNull(ResponseErrorCode.REQUEST_PARAMETER_ERROR, + cmd.getAcceptUserId()); + + // 上锁 + String key = "LotteryTransfer:" + cmd.requiredReqUserId(); + ResponseAssert.isTrue(CommonErrorCode.REQUEST_LIMITING, redisService.lock(key, 60)); + + try { + Long userId = cmd.requiredReqUserId(); + + // 1. 获取可提现金额 + LotteryWithdrawAmountCO withdrawAmount = lotteryWithdrawAmountQryExe.execute(userId, cmd.getActivityId());; + BigDecimal availableAmount = withdrawAmount.getAvailableAmount(); + + // 2. 验证金额是否足够 + ResponseAssert.isTrue(WalletErrorCode.INSUFFICIENT_BALANCE, + ArithmeticUtils.gte(availableAmount, cmd.getAmount())); + + // 3. 获取区域兑换金币比例 + Double goldRatio = getExchangeGoldRatio(cmd); + + // 4. 计算金币数量 + BigDecimal goldQuantity = BigDecimal.valueOf(goldRatio) + .multiply(cmd.getAmount()) + .setScale(0, RoundingMode.DOWN); + + // 5. 记录转账(作为提现记录) + LotteryWithdraw withdraw = new LotteryWithdraw(); + withdraw.setUserId(userId); + withdraw.setWithdrawNo(generateTransferWithdrawNo()); + withdraw.setActivityId(cmd.getActivityId()); + withdraw.setAmount(cmd.getAmount()); + withdraw.setStatus(3); // 已完成 + withdraw.setDescription("转账给用户" + cmd.getAcceptUserId() + ": " + goldQuantity + "金币"); + withdraw.setCreateTime(TimestampUtils.now()); + withdraw.setUpdateTime(TimestampUtils.now()); + lotteryWithdrawService.save(withdraw); + + Long withdrawId = withdraw.getId(); + + // 6. 判断接收用户是否是货运代理 + boolean isFreight = freightBalanceClient.existsBalance(cmd.getAcceptUserId()).getBody(); + + // 7. 发送金币给接收用户 + sendGold(cmd, isFreight, goldQuantity, withdrawId, "抽奖活动转账金币"); + + log.info("用户{}转账给用户{}成功,金额{},金币{}", + userId, cmd.getAcceptUserId(), cmd.getAmount(), goldQuantity); + } finally { + redisService.unlock(key); + } + } + + /** + * 生成提现单号. + */ + private String generateTransferWithdrawNo() { + return "TS" + System.currentTimeMillis() + + String.format("%06d", ThreadLocalRandom.current().nextInt(1000000)); + } + + /** + * 发送金币给接收用户. + */ + private void sendGold(LotteryTransferCmd cmd, boolean isFreight, BigDecimal goldQuantity, + Long withdrawId, String remark) { + if (isFreight) { + // 货运代理入账 + ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE, + freightBalanceClient.incrCandyBalance( + com.red.circle.common.business.core.enums.SysOriginPlatformEnum.valueOf( + cmd.getReqSysOrigin().getOrigin()), + cmd.getAcceptUserId(), goldQuantity).getBody()); + + // 插入流水 + BigDecimal freightBalance = freightBalanceClient.getAvailableBalance(cmd.getAcceptUserId()).getBody(); + freightBalanceRunningWaterClient.save(new FreightBalanceRunningWaterDTO() + .setSysOrigin(cmd.getReqSysOrigin().getOrigin()) + .setUserId(cmd.requiredReqUserId()) + .setAcceptUserId(cmd.getAcceptUserId()) + .setType(ReceiptType.INCOME.getType()) + .setQuantity(goldQuantity) + .setUsdQuantity(cmd.getAmount()) + .setBalance(freightBalance) + .setOrigin(FreightBalanceOrigin.PURCHASE.name()) + .setRemark(remark) + .setCreateUser(cmd.requiredReqUserId())); + return; + } + + // 6. 充值金币给用户自己 + GoldReceiptCmd build = GoldReceiptCmd.builder() + .appIncome() + .userId(cmd.requiredReqUserId()) + .amount(goldQuantity) + .eventId(Objects.toString(withdrawId)) + .sysOrigin(cmd.getReqSysOrigin().getOrigin()) + .origin(GoldOrigin.LOTTERY_TRANSFER_GOLD) + .remark(remark) + .build(); + // 金币入账 + walletGoldClient.changeBalance(build); + } + + + /** + * 获得1$兑换多少金币比例,基于区域+用户输入的美元. + * + * @return 1美元可获得的金币数量. + */ + private Double getExchangeGoldRatio(LotteryTransferCmd cmd) { + List exchangeGolds = ResponseAssert.requiredSuccess( + userRegionClient.listRegionExchangeGoldTip(cmd.requiredReqUserId(), false)); + ResponseAssert.notEmpty(CommonErrorCode.CONFIGURATION_ERROR, exchangeGolds); + + exchangeGolds = exchangeGolds.stream().filter(gold -> + cmd.getAmount().compareTo(BigDecimal.valueOf(gold.getStart())) >= 0 && + cmd.getAmount().compareTo(BigDecimal.valueOf(gold.getEnd())) <= 0) + .collect(Collectors.toList()); + ResponseAssert.notEmpty(CommonErrorCode.CONFIGURATION_ERROR, exchangeGolds); + + SysExchangeGoldTipDTO exchangeGold = exchangeGolds.get(0); + ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, exchangeGold); + ResponseAssert.isFalse(CommonErrorCode.CONFIGURATION_ERROR, + Objects.isNull(exchangeGold.getValue()) || exchangeGold.getValue() <= 0); + + return exchangeGold.getValue(); + } + +} \ No newline at end of file diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryWithdrawAmountQryExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryWithdrawAmountQryExe.java index dd0ed0da..c1024d53 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryWithdrawAmountQryExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryWithdrawAmountQryExe.java @@ -28,14 +28,12 @@ public class LotteryWithdrawAmountQryExe { private final LotteryUserProgressService lotteryUserProgressService; private final LotteryWithdrawService lotteryWithdrawService; - public LotteryWithdrawAmountCO execute(AppExtCommand cmd, Long activityId) { - Long userId = cmd.requiredReqUserId(); - + public LotteryWithdrawAmountCO execute(Long userId, Long activityId) { // 1. 获取用户本周累计金额 String weekKey = getCurrentWeekKey(); LotteryUserProgress progress = lotteryUserProgressService.query() .eq(LotteryUserProgress::getUserId, userId) - .eq(activityId != null, LotteryUserProgress::getActivityId, activityId) + .eq(LotteryUserProgress::getActivityId, activityId) .eq(LotteryUserProgress::getWeekKey, weekKey) .getOne(); @@ -46,7 +44,7 @@ public class LotteryWithdrawAmountQryExe { // 2. 计算已提现金额 BigDecimal withdrawnAmount = lotteryWithdrawService.query() .eq(LotteryWithdraw::getUserId, userId) - .eq(activityId != null, LotteryWithdraw::getActivityId, activityId) + .eq(LotteryWithdraw::getActivityId, activityId) .in(LotteryWithdraw::getStatus, 1, 3) // 已通过、已完成 .list() .stream() @@ -56,7 +54,7 @@ public class LotteryWithdrawAmountQryExe { // 3. 计算待审核金额 BigDecimal pendingAmount = lotteryWithdrawService.query() .eq(LotteryWithdraw::getUserId, userId) - .eq(activityId != null, LotteryWithdraw::getActivityId, activityId) + .eq(LotteryWithdraw::getActivityId, activityId) .eq(LotteryWithdraw::getStatus, 0) // 待审核 .list() .stream() diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryWithdrawApplyExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryWithdrawApplyExe.java index 2d94b8d6..2c7e2728 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryWithdrawApplyExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryWithdrawApplyExe.java @@ -36,12 +36,13 @@ public class LotteryWithdrawApplyExe { public void execute(LotteryWithdrawCmd cmd) { Long userId = cmd.requiredReqUserId(); BigDecimal amount = cmd.getAmount(); + Long activityId = Long.valueOf(cmd.getActivityId()); // 1. 获取用户本周累计金额 String weekKey = getCurrentWeekKey(); LotteryUserProgress progress = lotteryUserProgressService.query() .eq(LotteryUserProgress::getUserId, userId) - .eq(cmd.getActivityId() != null, LotteryUserProgress::getActivityId, cmd.getActivityId()) + .eq(LotteryUserProgress::getActivityId, activityId) .eq(LotteryUserProgress::getWeekKey, weekKey) .getOne(); @@ -50,8 +51,8 @@ public class LotteryWithdrawApplyExe { progress.getTotalAmount() != null && progress.getTotalAmount().compareTo(BigDecimal.ZERO) > 0); // 2. 计算可提现金额(总金额 - 已提现金额 - 待审核金额) - BigDecimal withdrawnAmount = getWithdrawnAmount(userId, cmd.getActivityId()); - BigDecimal pendingAmount = getPendingAmount(userId, cmd.getActivityId()); + BigDecimal withdrawnAmount = getWithdrawnAmount(userId, activityId); + BigDecimal pendingAmount = getPendingAmount(userId, activityId); BigDecimal availableAmount = progress.getTotalAmount().subtract(withdrawnAmount).subtract(pendingAmount); // 3. 校验提现金额 @@ -62,7 +63,7 @@ public class LotteryWithdrawApplyExe { LotteryWithdraw withdraw = new LotteryWithdraw(); withdraw.setWithdrawNo(generateWithdrawNo()); withdraw.setUserId(userId); - withdraw.setActivityId(cmd.getActivityId()); + withdraw.setActivityId(activityId); withdraw.setAmount(amount); withdraw.setContactNumber(cmd.getContactNumber()); withdraw.setDescription(cmd.getDescription()); @@ -83,7 +84,7 @@ public class LotteryWithdrawApplyExe { private BigDecimal getWithdrawnAmount(Long userId, Long activityId) { return lotteryWithdrawService.query() .eq(LotteryWithdraw::getUserId, userId) - .eq(activityId != null, LotteryWithdraw::getActivityId, activityId) + .eq(LotteryWithdraw::getActivityId, activityId) .in(LotteryWithdraw::getStatus, 1, 3) // 已通过、已完成 .list() .stream() @@ -97,7 +98,7 @@ public class LotteryWithdrawApplyExe { private BigDecimal getPendingAmount(Long userId, Long activityId) { return lotteryWithdrawService.query() .eq(LotteryWithdraw::getUserId, userId) - .eq(activityId != null, LotteryWithdraw::getActivityId, activityId) + .eq(LotteryWithdraw::getActivityId, activityId) .eq(LotteryWithdraw::getStatus, 0) // 待审核 .list() .stream() diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestServiceImpl.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestServiceImpl.java index cda2f8a3..501f0b3d 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestServiceImpl.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestServiceImpl.java @@ -14,6 +14,8 @@ import com.red.circle.other.app.command.activity.LotteryWinnerHistoryQryExe; import com.red.circle.other.app.command.activity.LotteryWithdrawAmountQryExe; import com.red.circle.other.app.command.activity.LotteryWithdrawApplyExe; import com.red.circle.other.app.command.activity.LotteryWithdrawQryExe; +import com.red.circle.other.app.command.activity.LotteryExchangeGoldExe; +import com.red.circle.other.app.command.activity.LotteryTransferExe; import com.red.circle.other.app.dto.clientobject.activity.*; import com.red.circle.other.app.dto.cmd.activity.*; @@ -45,6 +47,8 @@ public class LotteryActivityRestServiceImpl implements LotteryActivityRestServic private final LotteryWithdrawApplyExe lotteryWithdrawApplyExe; private final LotteryWithdrawQryExe lotteryWithdrawQryExe; private final LotteryWithdrawAmountQryExe lotteryWithdrawAmountQryExe; + private final LotteryExchangeGoldExe lotteryExchangeGoldExe; + private final LotteryTransferExe lotteryTransferExe; private final DistributedLockUtil distributedLockUtil; private static final String LOCK_KEY_PREFIX = "activity:lottery:draw:lock"; @@ -108,8 +112,18 @@ public class LotteryActivityRestServiceImpl implements LotteryActivityRestServic } @Override - public LotteryWithdrawAmountCO getWithdrawAmount(AppExtCommand cmd, Long activityId) { - return lotteryWithdrawAmountQryExe.execute(cmd, activityId); + public LotteryWithdrawAmountCO getWithdrawAmount(Long userId, Long activityId) { + return lotteryWithdrawAmountQryExe.execute(userId, activityId); + } + + @Override + public void exchangeGold(LotteryExchangeGoldCmd cmd) { + lotteryExchangeGoldExe.execute(cmd); + } + + @Override + public void transfer(LotteryTransferCmd cmd) { + lotteryTransferExe.execute(cmd); } } diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryExchangeGoldCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryExchangeGoldCmd.java new file mode 100644 index 00000000..2df0c17d --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryExchangeGoldCmd.java @@ -0,0 +1,34 @@ +package com.red.circle.other.app.dto.cmd.activity; + +import com.red.circle.common.business.dto.cmd.AppExtCommand; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 可提现金额兑换金币CMD. + * + * @author system + * @since 2025-10-24 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class LotteryExchangeGoldCmd extends AppExtCommand { + + /** + * 活动ID(可选). + */ + private Long activityId; + + /** + * 兑换金额. + */ + @NotNull(message = "兑换金额不能为空") + @DecimalMin(value = "0.01", message = "兑换金额必须大于0") + private BigDecimal amount; + +} \ No newline at end of file diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryTransferCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryTransferCmd.java new file mode 100644 index 00000000..c3cb1f9c --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryTransferCmd.java @@ -0,0 +1,40 @@ +package com.red.circle.other.app.dto.cmd.activity; + +import com.red.circle.common.business.dto.cmd.AppExtCommand; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 可提现金额转账给指定用户CMD. + * + * @author system + * @since 2025-10-24 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class LotteryTransferCmd extends AppExtCommand { + + /** + * 活动ID(可选). + */ + private Long activityId; + + /** + * 接收用户ID. + */ + @NotNull(message = "接收用户ID不能为空") + private Long acceptUserId; + + /** + * 转账金额. + */ + @NotNull(message = "转账金额不能为空") + @DecimalMin(value = "0.01", message = "转账金额必须大于0") + private BigDecimal amount; + +} \ No newline at end of file diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryWithdrawCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryWithdrawCmd.java index 60f08a6c..e9ddd27d 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryWithdrawCmd.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/activity/LotteryWithdrawCmd.java @@ -21,9 +21,10 @@ import lombok.experimental.Accessors; public class LotteryWithdrawCmd extends AppExtCommand { /** - * 活动ID(可选). + * 活动ID */ - private Long activityId; + @NotBlank + private String activityId; /** * 提现金额. diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestService.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestService.java index bc1a630c..f80c24ed 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestService.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/activity/LotteryActivityRestService.java @@ -19,6 +19,8 @@ import com.red.circle.other.app.dto.cmd.activity.LotteryRecordQryCmd; import com.red.circle.other.app.dto.cmd.activity.LotteryWinnerHistoryQryCmd; import com.red.circle.other.app.dto.cmd.activity.LotteryWithdrawCmd; import com.red.circle.other.app.dto.cmd.activity.LotteryWithdrawQryCmd; +import com.red.circle.other.app.dto.cmd.activity.LotteryExchangeGoldCmd; +import com.red.circle.other.app.dto.cmd.activity.LotteryTransferCmd; import java.util.List; /** @@ -87,6 +89,16 @@ public interface LotteryActivityRestService { /** * 查询可提现金额. */ - LotteryWithdrawAmountCO getWithdrawAmount(AppExtCommand cmd, Long activityId); + LotteryWithdrawAmountCO getWithdrawAmount(Long userId, Long activityId); + + /** + * 可提现金额兑换金币. + */ + void exchangeGold(LotteryExchangeGoldCmd cmd); + + /** + * 可提现金额转账给指定用户. + */ + void transfer(LotteryTransferCmd cmd); }