From c59d1a241f913094b1b266b531e5ad4183dc57fd Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Mon, 10 Nov 2025 16:34:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E9=80=81=E7=A4=BC=E7=89=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/gift/dto/ProductGiftRespDTO.java | 2 + .../transfer/PayUserTransferTypeEnum.java | 19 ++++++++ yudao-module-pay/yudao-module-pay-biz/pom.xml | 11 +++++ .../AppPayWalletRechargeController.java | 10 +++-- .../wallet/PayWalletRechargeService.java | 2 +- .../wallet/PayWalletRechargeServiceImpl.java | 45 ++++++++++++++++--- .../service/wallet/PayWalletServiceImpl.java | 4 +- 7 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayUserTransferTypeEnum.java diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/gift/dto/ProductGiftRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/gift/dto/ProductGiftRespDTO.java index e6bfade..1e5c347 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/gift/dto/ProductGiftRespDTO.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/gift/dto/ProductGiftRespDTO.java @@ -7,6 +7,8 @@ import java.time.LocalDateTime; @Data public class ProductGiftRespDTO { + private Long id; + /** * 礼物标题 */ diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayUserTransferTypeEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayUserTransferTypeEnum.java new file mode 100644 index 0000000..fd5df99 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayUserTransferTypeEnum.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.pay.enums.transfer; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 用户转账类型枚举 + */ +@AllArgsConstructor +@Getter +public enum PayUserTransferTypeEnum { + + GIFT_TRANSFER("1", "礼物转账"), + ; + + private final String type; + private final String name; + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/pom.xml b/yudao-module-pay/yudao-module-pay-biz/pom.xml index f36dea6..e5b3495 100644 --- a/yudao-module-pay/yudao-module-pay-biz/pom.xml +++ b/yudao-module-pay/yudao-module-pay-biz/pom.xml @@ -30,6 +30,17 @@ ${revision} + + cn.iocoder.boot + yudao-module-member-api + ${revision} + + + + cn.iocoder.boot + yudao-spring-boot-starter-protection + + cn.iocoder.boot diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppPayWalletRechargeController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppPayWalletRechargeController.java index a9555f0..50316e1 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppPayWalletRechargeController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppPayWalletRechargeController.java @@ -5,6 +5,8 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent; +import cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletGiftSendReqVo; import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; @@ -29,6 +31,7 @@ import javax.validation.Valid; import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -59,9 +62,10 @@ public class AppPayWalletRechargeController { @PostMapping("/send-gift") @Operation(summary = "钱包送出礼物") - public CommonResult sendGift(@Valid @RequestBody AppPayWalletGiftSendReqVo dto) { - dto.setOperationMemberId(getLoginUserId()); - dto.setOperationUserType(getLoginUserType()); + @Idempotent(timeout = 3, timeUnit = TimeUnit.SECONDS, keyResolver = UserIdempotentKeyResolver.class, message = "正在送出礼物中,请稍后重试") + public CommonResult sendGift(@Valid @RequestBody AppPayWalletGiftSendReqVo dto) { + dto.setOperationMemberId(getLoginUserId()); + dto.setOperationUserType(getLoginUserType()); return success(walletRechargeService.sendGift(dto)); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeService.java index 11ec0aa..3c2cc54 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeService.java @@ -31,7 +31,7 @@ public interface PayWalletRechargeService { /** * 钱包送出礼物 */ - Boolean sendGift(@Valid AppPayWalletGiftSendReqVo dto); + Long sendGift(@Valid AppPayWalletGiftSendReqVo dto); /** * 获得钱包充值记录分页 diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java index f1c4e02..5fec2f2 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java @@ -7,6 +7,8 @@ import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletGiftSendReqVo; @@ -16,9 +18,12 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO; import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletUserTransitionDO; import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletRechargeMapper; +import cn.iocoder.yudao.module.pay.dal.mysql.wallet.WalletUserTransitionMapper; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; +import cn.iocoder.yudao.module.pay.enums.transfer.PayUserTransferTypeEnum; import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; import cn.iocoder.yudao.module.pay.service.order.PayOrderService; @@ -62,6 +67,8 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @Resource private PayWalletRechargeMapper walletRechargeMapper; @Resource + private WalletUserTransitionMapper walletUserTransitionMapper; + @Resource private PayWalletService payWalletService; @Resource private PayOrderService payOrderService; @@ -74,6 +81,8 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { public SocialClientApi socialClientApi; @Resource private ProductGiftApi productGiftApi; + @Resource + private MemberUserApi memberUserApi; @Resource private PayProperties payProperties; @@ -115,29 +124,51 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { */ @Override @Transactional(rollbackFor = Exception.class) - public Boolean sendGift(AppPayWalletGiftSendReqVo dto) { + public Long sendGift(AppPayWalletGiftSendReqVo dto) { + // 自己不能给自己送出礼物 + if (ObjectUtil.equals(dto.getTargetMemberId(), dto.getOperationMemberId())) { + throw new RuntimeException("自己不能给自己送出礼物"); + } + // 查询目标用户钱包 PayWalletDO targetWallet = payWalletService.getOrCreateWallet(dto.getTargetMemberId(), dto.getOperationUserType()); if (ObjectUtil.isEmpty(targetWallet)) throw exception(WALLET_TARGET_NOT_FOUND); + MemberUserRespDTO targetUser = memberUserApi.getUser(dto.getTargetMemberId()); + // 查询当前用户钱包 - PayWalletDO wallet = payWalletService.getOrCreateWallet(dto.getOperationMemberId(), dto.getOperationUserType()); - if (ObjectUtil.isEmpty(wallet)) throw exception(WALLET_NOT_FOUND); + PayWalletDO currentWallet = payWalletService.getOrCreateWallet(dto.getOperationMemberId(), dto.getOperationUserType()); + if (ObjectUtil.isEmpty(currentWallet)) throw exception(WALLET_NOT_FOUND); + MemberUserRespDTO currentUser = memberUserApi.getUser(dto.getOperationMemberId()); + // 查询礼物 ProductGiftRespDTO gift = productGiftApi.getGift(dto.getGiftId()); if (ObjectUtil.isEmpty(gift)) throw exception(GIFT_NOT_EXISTS); // 判断当前用于余额是否够 - if (wallet.getBalance() < gift.getPrice()) throw exception(WALLET_RECHARGE_GIFT_BALANCE_NOT_ENOUGH); + if (currentWallet.getBalance() < gift.getPrice()) throw exception(WALLET_RECHARGE_GIFT_BALANCE_NOT_ENOUGH); + String title = String.format("用户[%s]赠送给用户[%s]价值[%s]元的礼物", currentUser.getNickname(), targetUser.getNickname(), fenToYuanStr(gift.getPrice())); // 添加转账记录 + WalletUserTransitionDO userTransitionObj = new WalletUserTransitionDO(); + userTransitionObj.setBizType(PayUserTransferTypeEnum.GIFT_TRANSFER.getType()); + userTransitionObj.setBizId(gift.getId()); + userTransitionObj.setSendUserId(dto.getTargetMemberId()); + userTransitionObj.setSendBeforeBalance(currentWallet.getBalance()); + userTransitionObj.setSendAfterBalance(currentWallet.getBalance() - gift.getPrice()); + userTransitionObj.setTargetUserId(dto.getTargetMemberId()); + userTransitionObj.setTargetBeforeBalance(targetWallet.getBalance()); + userTransitionObj.setTargetAfterBalance(targetWallet.getBalance() + gift.getPrice()); + userTransitionObj.setPrice(gift.getPrice()); + userTransitionObj.setTitle(title); + walletUserTransitionMapper.insert(userTransitionObj); // 扣除用户余额 - payWalletService.reduceWalletBalance(wallet.getId(), 1L, PayWalletBizTypeEnum.GIFT, gift.getPrice()); + payWalletService.reduceWalletBalance(currentWallet.getId(), userTransitionObj.getBizId(), PayWalletBizTypeEnum.GIFT, gift.getPrice()); // 添加目标用户余额 - payWalletService.addWalletBalance(targetWallet.getId(), String.valueOf(1L), PayWalletBizTypeEnum.GIFT, gift.getPrice()); + payWalletService.addWalletBalance(targetWallet.getId(), String.valueOf(userTransitionObj.getId()), PayWalletBizTypeEnum.GIFT, gift.getPrice()); - return true; + return userTransitionObj.getId(); } @Override diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java index be8af43..eb41710 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java @@ -112,7 +112,7 @@ public class PayWalletServiceImpl implements PayWalletService { /** * 校验是否能退款 * - * @param refundId 支付退款单 id + * @param refundId 支付退款单 id * @param walletPayNo 钱包支付 no */ private Long validateWalletCanRefund(Long refundId, String walletPayNo) { @@ -147,6 +147,7 @@ public class PayWalletServiceImpl implements PayWalletService { // 2. 扣除余额 int updateCounts; switch (bizType) { + case GIFT: // 送礼物 case PAYMENT: { updateCounts = walletMapper.updateWhenConsumption(payWallet.getId(), price); break; @@ -197,6 +198,7 @@ public class PayWalletServiceImpl implements PayWalletService { walletMapper.updateWhenRecharge(payWallet.getId(), price); break; } + case GIFT: // 赠送 case UPDATE_BALANCE: // 更新余额 case BROKERAGE_WITHDRAW: // 分佣提现 walletMapper.updateWhenAdd(payWallet.getId(), price);