新增活动exchange transfer 功能
This commit is contained in:
parent
79f0c8f15a
commit
ba7a024e03
@ -702,6 +702,16 @@ public enum GoldOrigin {
|
|||||||
* 抽奖奖励
|
* 抽奖奖励
|
||||||
*/
|
*/
|
||||||
LOTTERY_REWARD("Lottery reward"),
|
LOTTERY_REWARD("Lottery reward"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽奖兑换金币
|
||||||
|
*/
|
||||||
|
LOTTERY_EXCHANGE_GOLD("Lottery exchange gold"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽奖转账金币
|
||||||
|
*/
|
||||||
|
LOTTERY_TRANSFER_GOLD("Lottery transfer gold"),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String desc;
|
private final String desc;
|
||||||
|
|||||||
@ -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.LotteryWinnerHistoryQryCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.activity.LotteryWithdrawCmd;
|
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.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 com.red.circle.other.app.service.activity.LotteryActivityRestService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -27,12 +29,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -219,8 +216,34 @@ public class LotteryActivityRestController extends BaseController {
|
|||||||
* @eo.request-type formdata
|
* @eo.request-type formdata
|
||||||
*/
|
*/
|
||||||
@GetMapping("/withdraw/amount")
|
@GetMapping("/withdraw/amount")
|
||||||
public LotteryWithdrawAmountCO getWithdrawAmount(AppExtCommand cmd, Long activityId) {
|
public LotteryWithdrawAmountCO getWithdrawAmount(AppExtCommand cmd, @RequestParam Long activityId) {
|
||||||
return lotteryActivityRestService.getWithdrawAmount(cmd, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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<SysExchangeGoldTipDTO> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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<SysExchangeGoldTipDTO> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -28,14 +28,12 @@ public class LotteryWithdrawAmountQryExe {
|
|||||||
private final LotteryUserProgressService lotteryUserProgressService;
|
private final LotteryUserProgressService lotteryUserProgressService;
|
||||||
private final LotteryWithdrawService lotteryWithdrawService;
|
private final LotteryWithdrawService lotteryWithdrawService;
|
||||||
|
|
||||||
public LotteryWithdrawAmountCO execute(AppExtCommand cmd, Long activityId) {
|
public LotteryWithdrawAmountCO execute(Long userId, Long activityId) {
|
||||||
Long userId = cmd.requiredReqUserId();
|
|
||||||
|
|
||||||
// 1. 获取用户本周累计金额
|
// 1. 获取用户本周累计金额
|
||||||
String weekKey = getCurrentWeekKey();
|
String weekKey = getCurrentWeekKey();
|
||||||
LotteryUserProgress progress = lotteryUserProgressService.query()
|
LotteryUserProgress progress = lotteryUserProgressService.query()
|
||||||
.eq(LotteryUserProgress::getUserId, userId)
|
.eq(LotteryUserProgress::getUserId, userId)
|
||||||
.eq(activityId != null, LotteryUserProgress::getActivityId, activityId)
|
.eq(LotteryUserProgress::getActivityId, activityId)
|
||||||
.eq(LotteryUserProgress::getWeekKey, weekKey)
|
.eq(LotteryUserProgress::getWeekKey, weekKey)
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
@ -46,7 +44,7 @@ public class LotteryWithdrawAmountQryExe {
|
|||||||
// 2. 计算已提现金额
|
// 2. 计算已提现金额
|
||||||
BigDecimal withdrawnAmount = lotteryWithdrawService.query()
|
BigDecimal withdrawnAmount = lotteryWithdrawService.query()
|
||||||
.eq(LotteryWithdraw::getUserId, userId)
|
.eq(LotteryWithdraw::getUserId, userId)
|
||||||
.eq(activityId != null, LotteryWithdraw::getActivityId, activityId)
|
.eq(LotteryWithdraw::getActivityId, activityId)
|
||||||
.in(LotteryWithdraw::getStatus, 1, 3) // 已通过、已完成
|
.in(LotteryWithdraw::getStatus, 1, 3) // 已通过、已完成
|
||||||
.list()
|
.list()
|
||||||
.stream()
|
.stream()
|
||||||
@ -56,7 +54,7 @@ public class LotteryWithdrawAmountQryExe {
|
|||||||
// 3. 计算待审核金额
|
// 3. 计算待审核金额
|
||||||
BigDecimal pendingAmount = lotteryWithdrawService.query()
|
BigDecimal pendingAmount = lotteryWithdrawService.query()
|
||||||
.eq(LotteryWithdraw::getUserId, userId)
|
.eq(LotteryWithdraw::getUserId, userId)
|
||||||
.eq(activityId != null, LotteryWithdraw::getActivityId, activityId)
|
.eq(LotteryWithdraw::getActivityId, activityId)
|
||||||
.eq(LotteryWithdraw::getStatus, 0) // 待审核
|
.eq(LotteryWithdraw::getStatus, 0) // 待审核
|
||||||
.list()
|
.list()
|
||||||
.stream()
|
.stream()
|
||||||
|
|||||||
@ -36,12 +36,13 @@ public class LotteryWithdrawApplyExe {
|
|||||||
public void execute(LotteryWithdrawCmd cmd) {
|
public void execute(LotteryWithdrawCmd cmd) {
|
||||||
Long userId = cmd.requiredReqUserId();
|
Long userId = cmd.requiredReqUserId();
|
||||||
BigDecimal amount = cmd.getAmount();
|
BigDecimal amount = cmd.getAmount();
|
||||||
|
Long activityId = Long.valueOf(cmd.getActivityId());
|
||||||
|
|
||||||
// 1. 获取用户本周累计金额
|
// 1. 获取用户本周累计金额
|
||||||
String weekKey = getCurrentWeekKey();
|
String weekKey = getCurrentWeekKey();
|
||||||
LotteryUserProgress progress = lotteryUserProgressService.query()
|
LotteryUserProgress progress = lotteryUserProgressService.query()
|
||||||
.eq(LotteryUserProgress::getUserId, userId)
|
.eq(LotteryUserProgress::getUserId, userId)
|
||||||
.eq(cmd.getActivityId() != null, LotteryUserProgress::getActivityId, cmd.getActivityId())
|
.eq(LotteryUserProgress::getActivityId, activityId)
|
||||||
.eq(LotteryUserProgress::getWeekKey, weekKey)
|
.eq(LotteryUserProgress::getWeekKey, weekKey)
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
@ -50,8 +51,8 @@ public class LotteryWithdrawApplyExe {
|
|||||||
progress.getTotalAmount() != null && progress.getTotalAmount().compareTo(BigDecimal.ZERO) > 0);
|
progress.getTotalAmount() != null && progress.getTotalAmount().compareTo(BigDecimal.ZERO) > 0);
|
||||||
|
|
||||||
// 2. 计算可提现金额(总金额 - 已提现金额 - 待审核金额)
|
// 2. 计算可提现金额(总金额 - 已提现金额 - 待审核金额)
|
||||||
BigDecimal withdrawnAmount = getWithdrawnAmount(userId, cmd.getActivityId());
|
BigDecimal withdrawnAmount = getWithdrawnAmount(userId, activityId);
|
||||||
BigDecimal pendingAmount = getPendingAmount(userId, cmd.getActivityId());
|
BigDecimal pendingAmount = getPendingAmount(userId, activityId);
|
||||||
BigDecimal availableAmount = progress.getTotalAmount().subtract(withdrawnAmount).subtract(pendingAmount);
|
BigDecimal availableAmount = progress.getTotalAmount().subtract(withdrawnAmount).subtract(pendingAmount);
|
||||||
|
|
||||||
// 3. 校验提现金额
|
// 3. 校验提现金额
|
||||||
@ -62,7 +63,7 @@ public class LotteryWithdrawApplyExe {
|
|||||||
LotteryWithdraw withdraw = new LotteryWithdraw();
|
LotteryWithdraw withdraw = new LotteryWithdraw();
|
||||||
withdraw.setWithdrawNo(generateWithdrawNo());
|
withdraw.setWithdrawNo(generateWithdrawNo());
|
||||||
withdraw.setUserId(userId);
|
withdraw.setUserId(userId);
|
||||||
withdraw.setActivityId(cmd.getActivityId());
|
withdraw.setActivityId(activityId);
|
||||||
withdraw.setAmount(amount);
|
withdraw.setAmount(amount);
|
||||||
withdraw.setContactNumber(cmd.getContactNumber());
|
withdraw.setContactNumber(cmd.getContactNumber());
|
||||||
withdraw.setDescription(cmd.getDescription());
|
withdraw.setDescription(cmd.getDescription());
|
||||||
@ -83,7 +84,7 @@ public class LotteryWithdrawApplyExe {
|
|||||||
private BigDecimal getWithdrawnAmount(Long userId, Long activityId) {
|
private BigDecimal getWithdrawnAmount(Long userId, Long activityId) {
|
||||||
return lotteryWithdrawService.query()
|
return lotteryWithdrawService.query()
|
||||||
.eq(LotteryWithdraw::getUserId, userId)
|
.eq(LotteryWithdraw::getUserId, userId)
|
||||||
.eq(activityId != null, LotteryWithdraw::getActivityId, activityId)
|
.eq(LotteryWithdraw::getActivityId, activityId)
|
||||||
.in(LotteryWithdraw::getStatus, 1, 3) // 已通过、已完成
|
.in(LotteryWithdraw::getStatus, 1, 3) // 已通过、已完成
|
||||||
.list()
|
.list()
|
||||||
.stream()
|
.stream()
|
||||||
@ -97,7 +98,7 @@ public class LotteryWithdrawApplyExe {
|
|||||||
private BigDecimal getPendingAmount(Long userId, Long activityId) {
|
private BigDecimal getPendingAmount(Long userId, Long activityId) {
|
||||||
return lotteryWithdrawService.query()
|
return lotteryWithdrawService.query()
|
||||||
.eq(LotteryWithdraw::getUserId, userId)
|
.eq(LotteryWithdraw::getUserId, userId)
|
||||||
.eq(activityId != null, LotteryWithdraw::getActivityId, activityId)
|
.eq(LotteryWithdraw::getActivityId, activityId)
|
||||||
.eq(LotteryWithdraw::getStatus, 0) // 待审核
|
.eq(LotteryWithdraw::getStatus, 0) // 待审核
|
||||||
.list()
|
.list()
|
||||||
.stream()
|
.stream()
|
||||||
|
|||||||
@ -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.LotteryWithdrawAmountQryExe;
|
||||||
import com.red.circle.other.app.command.activity.LotteryWithdrawApplyExe;
|
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.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.clientobject.activity.*;
|
||||||
import com.red.circle.other.app.dto.cmd.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 LotteryWithdrawApplyExe lotteryWithdrawApplyExe;
|
||||||
private final LotteryWithdrawQryExe lotteryWithdrawQryExe;
|
private final LotteryWithdrawQryExe lotteryWithdrawQryExe;
|
||||||
private final LotteryWithdrawAmountQryExe lotteryWithdrawAmountQryExe;
|
private final LotteryWithdrawAmountQryExe lotteryWithdrawAmountQryExe;
|
||||||
|
private final LotteryExchangeGoldExe lotteryExchangeGoldExe;
|
||||||
|
private final LotteryTransferExe lotteryTransferExe;
|
||||||
private final DistributedLockUtil distributedLockUtil;
|
private final DistributedLockUtil distributedLockUtil;
|
||||||
private static final String LOCK_KEY_PREFIX = "activity:lottery:draw:lock";
|
private static final String LOCK_KEY_PREFIX = "activity:lottery:draw:lock";
|
||||||
|
|
||||||
@ -108,8 +112,18 @@ public class LotteryActivityRestServiceImpl implements LotteryActivityRestServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LotteryWithdrawAmountCO getWithdrawAmount(AppExtCommand cmd, Long activityId) {
|
public LotteryWithdrawAmountCO getWithdrawAmount(Long userId, Long activityId) {
|
||||||
return lotteryWithdrawAmountQryExe.execute(cmd, activityId);
|
return lotteryWithdrawAmountQryExe.execute(userId, activityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exchangeGold(LotteryExchangeGoldCmd cmd) {
|
||||||
|
lotteryExchangeGoldExe.execute(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transfer(LotteryTransferCmd cmd) {
|
||||||
|
lotteryTransferExe.execute(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@ -21,9 +21,10 @@ import lombok.experimental.Accessors;
|
|||||||
public class LotteryWithdrawCmd extends AppExtCommand {
|
public class LotteryWithdrawCmd extends AppExtCommand {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 活动ID(可选).
|
* 活动ID
|
||||||
*/
|
*/
|
||||||
private Long activityId;
|
@NotBlank
|
||||||
|
private String activityId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提现金额.
|
* 提现金额.
|
||||||
|
|||||||
@ -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.LotteryWinnerHistoryQryCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.activity.LotteryWithdrawCmd;
|
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.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;
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user