diff --git a/sql/mysql/recent/member_recent.sql b/sql/mysql/recent/member_recent.sql index 2f6a224..7b73811 100644 --- a/sql/mysql/recent/member_recent.sql +++ b/sql/mysql/recent/member_recent.sql @@ -293,4 +293,29 @@ CREATE TABLE `pay_wallet_user_transition` `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '钱包(用户之间的相互转账、赠送)'; \ No newline at end of file +) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '钱包(用户之间的相互转账、赠送)'; + +-- ---------------------------- +-- Table structure for pay_wallet_tx_auth +-- ---------------------------- +DROP TABLE IF EXISTS `pay_wallet_tx_auth`; +CREATE TABLE `pay_wallet_tx_auth` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '钱包提现', + `user_id` bigint NOT NULL COMMENT '用户id', + `wallet_id` bigint NOT NULL COMMENT '钱包id', + `biz_type` varchar(36) NOT NULL COMMENT '提现类型', + `biz_id` bigint DEFAULT NULL COMMENT '提现类型绑定id', + `tx_price` int NOT NULL COMMENT '提现金额', + `title` varchar(256) DEFAULT NULL COMMENT '提现title', + `check_status` varchar(36) NOT NULL COMMENT '提现状态', + `check_time` datetime DEFAULT NULL COMMENT '审核时间', + `tx_refuse` varchar(256) DEFAULT NULL COMMENT '提现拒绝信息', + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '提现金额审核'; \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/TradeBrokerageRecordApi.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/TradeBrokerageRecordApi.java index a78cd40..d38aec2 100644 --- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/TradeBrokerageRecordApi.java +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/TradeBrokerageRecordApi.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.trade.api.brokerage; import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageAddDTO; +import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageWechatTransferDTO; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; import javax.validation.Valid; @@ -13,10 +14,18 @@ public interface TradeBrokerageRecordApi { /** * 增加佣金【多级分佣】 + * * @param userId 会员编号 * @param bizType 业务类型 * @param list 请求参数列表 */ void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, @Valid List list); + /** + * 微信转账 + * @param id 提现单id + * @param userId 会员id + * @param price 金额(分) + */ + BrokerageWechatTransferDTO wechatTransfer(Long id, Long userId, Integer price); } diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/dto/BrokerageWechatTransferDTO.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/dto/BrokerageWechatTransferDTO.java new file mode 100644 index 0000000..9894d03 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/dto/BrokerageWechatTransferDTO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.trade.api.brokerage.dto; + +import lombok.Data; + +@Data +public class BrokerageWechatTransferDTO { + /** + * 编号 + */ + private Long id; + + // ========== 其它字段 ========== + + /** + * 渠道 package 信息 + *

+ * 特殊:目前只有微信转账有这个东西!!! + * + * @see JSAPI 调起用户确认收款 + */ + private String channelPackageInfo; +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/TradeBrokerageRecordApiImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/TradeBrokerageRecordApiImpl.java index b3fcbaf..f2bbc6a 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/TradeBrokerageRecordApiImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/TradeBrokerageRecordApiImpl.java @@ -1,25 +1,89 @@ package cn.iocoder.yudao.module.trade.api.brokerage; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayTransferCreateRespDTO; +import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApi; +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum; +import cn.iocoder.yudao.module.system.api.social.SocialUserApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageAddDTO; +import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageWechatTransferDTO; +import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService; import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; -import java.util.List; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; @Service @Validated public class TradeBrokerageRecordApiImpl implements TradeBrokerageRecordApi { + @Resource + private TradeOrderProperties tradeOrderProperties; + @Resource private BrokerageRecordService brokerageRecordService; + @Resource + private SocialUserApi socialUserApi; + @Resource + private PayTransferApi payTransferApi; + @Override public void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, List list) { brokerageRecordService.addBrokerage(userId, bizType, BeanUtils.toBean(list, BrokerageAddReqBO.class)); } + + /** + * 微信转账 + * + * @param id 提现单id + * @param userId 会员id + * @param price 金额(分) + */ + @Override + public BrokerageWechatTransferDTO wechatTransfer(Long id, Long userId, Integer price) { + // 1.1 获取微信 openid + SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(UserTypeEnum.MEMBER.getValue(), userId, SocialTypeEnum.WECHAT_MINI_APP.getType()); + if (ObjectUtil.isEmpty(socialUser)) return null; + // 1.2 构建请求 + Map channelExtras = new HashMap<>(); + List> sceneReportInfos = new ArrayList<>(); + sceneReportInfos.add(new LinkedHashMap() {{ + put("infoType", "岗位类型"); + put("infoContent", "分销员"); + }}); + sceneReportInfos.add(new LinkedHashMap() {{ + put("infoType", "报酬说明"); + put("infoContent", "邀请新用户有礼,提现" + MoneyUtils.fenToYuanStr(price) + "元"); + }}); + channelExtras.put("sceneId", "1005"); + channelExtras.put("sceneReportInfos", JSONUtil.toJsonStr(sceneReportInfos)); + PayTransferCreateReqDTO payTransferCreateReqDTO = new PayTransferCreateReqDTO() + .setAppKey(tradeOrderProperties.getPayAppKey()) + .setChannelCode("wx_lite") + .setType(PayTransferTypeEnum.WX_BALANCE.getType()) + .setMerchantTransferId(String.valueOf(id)) + .setPrice(price) + .setSubject("佣金报酬") + .setOpenid(socialUser.getOpenid()) + .setUserIp(getClientIP()) + .setChannelExtras(channelExtras); + // 2. 发起请求 + PayTransferCreateRespDTO transfer = payTransferApi.createTransfer(payTransferCreateReqDTO); + return BeanUtils.toBean(transfer, BrokerageWechatTransferDTO.class); + } } diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/config/dto/MemberConfigRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/config/dto/MemberConfigRespDTO.java index 59aab53..bddae95 100644 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/config/dto/MemberConfigRespDTO.java +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/config/dto/MemberConfigRespDTO.java @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.member.api.config.dto; import lombok.Data; +import java.util.List; + /** * 用户信息 Response DTO * @@ -14,19 +16,64 @@ public class MemberConfigRespDTO { * 积分抵扣开关 */ private Boolean pointTradeDeductEnable; + /** * 积分抵扣,单位:分 *

* 1 积分抵扣多少分 */ private Integer pointTradeDeductUnitPrice; + /** * 积分抵扣最大值 */ private Integer pointTradeDeductMaxPrice; + /** * 1 元赠送多少分 */ private Integer pointTradeGivePoint; + /** + * 普通用户每天可以和几个人聊天 + */ + private Integer defaultChatUserCout; + + /** + * 普通用户每天可以和人发几次消息 + */ + private Integer defaultChatUserMsgCout; + + /** + * vip用户每天可以和几个人聊天 + */ + private Integer vipChatUserCout; + + /** + * vip用户每天可以和人发几次消息 + */ + private Integer vipChatUserMsgCout; + + /** + * vip查看微信次数 + */ + private Integer vipLookWechtCout; + + /** + * 礼物提现比例 + * `giftCashRatio`可以兑换1人民币 + */ + private Integer giftCashRatio; + + /** + * 新人入驻天数判断 + * 入驻未满`newPersonDays`都是新人用户 + */ + private Integer newPersonDays; + + /** + * 举报类型 + */ + private List reportTypeList; + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberRecentTagServiceImp.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberRecentTagServiceImp.java index d838d1b..72e014a 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberRecentTagServiceImp.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberRecentTagServiceImp.java @@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.member.service.opentype.BO.OpenTypeExtBO; import cn.iocoder.yudao.module.member.service.opentype.OpenTypeService; import cn.iocoder.yudao.module.member.service.user.BO.MemberTagExtBO; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; import org.springframework.security.core.parameters.P; import org.springframework.stereotype.Service; @@ -39,6 +40,7 @@ public class MemberRecentTagServiceImp implements MemberRecentTagService { private MemberRecentTagMapper memberRecentTagMapper; @Resource + @Lazy private OpenTypeService openTypeService; @Resource private MemberConfigService memberConfigService; diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java index 8c86800..42c637a 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java @@ -9,6 +9,8 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; */ public interface ErrorCodeConstants { + ErrorCode EMPTY = new ErrorCode(1_007_999_999, "{}"); + // ========== APP 模块 1-007-000-000 ========== ErrorCode APP_NOT_FOUND = new ErrorCode(1_007_000_000, "App 不存在"); ErrorCode APP_IS_DISABLE = new ErrorCode(1_007_000_002, "App 已经被禁用"); @@ -79,6 +81,7 @@ public interface ErrorCodeConstants { // ========== 钱包用户转账模块 1-007-010-000 ========== ErrorCode WALLET_USER_TRANSFER_NOT_FOUND = new ErrorCode(1_007_010_000, "用户转账单不存在"); ErrorCode WALLET_USER_TRANSFER_NOT_SELF = new ErrorCode(1_007_010_001, "自己不能给自己送出礼物"); + ErrorCode WALLET_TX_AUTH_NOT_EXISTS = new ErrorCode(1_007_010_002, "用户申请提现记录不存在"); // ========== 示例订单 1-007-900-000 ========== ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1_007_900_000, "示例订单不存在"); diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletBizTypeEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletBizTypeEnum.java index ea04967..197396d 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletBizTypeEnum.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletBizTypeEnum.java @@ -23,6 +23,7 @@ public enum PayWalletBizTypeEnum implements ArrayValuable { BROKERAGE_WITHDRAW(6, "分佣提现"), GIFT(7, "送礼物"), RECEIVE_GIFT(8, "收到礼物"), + RECEIVE_GIFT_TX(9, "礼物提现"), ; /** diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletTxAuthBizTypeEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletTxAuthBizTypeEnum.java new file mode 100644 index 0000000..dcf7c34 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletTxAuthBizTypeEnum.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.pay.enums.wallet; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 钱包提现业务类型 + */ +@AllArgsConstructor +@Getter +public enum PayWalletTxAuthBizTypeEnum implements ArrayValuable { + + GIFT_TX_AUTH("1", "礼物提现审核"), + ; + + /** + * 业务分类 + */ + private final String type; + /** + * 说明 + */ + private final String description; + + public static final String[] ARRAYS = Arrays.stream(values()).map(PayWalletTxAuthBizTypeEnum::getType).toArray(String[]::new); + + @Override + public String[] array() { + return ARRAYS; + } + + public static PayWalletTxAuthBizTypeEnum valueByTypeOf(String type) { + return Arrays.stream(values()).filter(item -> item.getType().equals(type)).findFirst().orElse(null); + } + +} diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletTxAuthStatusEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletTxAuthStatusEnum.java new file mode 100644 index 0000000..9216a4d --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletTxAuthStatusEnum.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.pay.enums.wallet; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 钱包提现审核状态 + */ +@AllArgsConstructor +@Getter +public enum PayWalletTxAuthStatusEnum implements ArrayValuable { + + TX_AUTH_AWAIT("1", "待审核"), + AUTH_SUCCESS("2", "审核通过"), + AUTH_FAIL("3", "审核不通过"), + AUTH_TX_SUCCESS("4", "已提现"), + AUTH_TX_ERROR("5", "提现失败"), + ; + + /** + * 业务分类 + */ + private final String type; + /** + * 说明 + */ + private final String description; + + public static final String[] ARRAYS = Arrays.stream(values()).map(PayWalletTxAuthStatusEnum::getType).toArray(String[]::new); + + @Override + public String[] array() { + return ARRAYS; + } + + public static PayWalletTxAuthStatusEnum valueOfByType(String type) { + return Arrays.stream(values()).filter(item -> item.getType().equals(type)).findFirst().orElse(null); + } +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/WalletTxAuthController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/WalletTxAuthController.java new file mode 100755 index 0000000..932098e --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/WalletTxAuthController.java @@ -0,0 +1,118 @@ +package cn.iocoder.yudao.module.pay.controller.admin.wallet; + +import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthCheckReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthPageReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthRespVO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthSaveReqVO; +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletTxAuthDO; +import cn.iocoder.yudao.module.pay.service.wallet.WalletTxAuthService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.annotation.security.PermitAll; +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; + +@Slf4j +@Tag(name = "管理后台 - 提现金额审核") +@RestController +@RequestMapping("/pay/wallet-tx-auth") +@Validated +public class WalletTxAuthController { + + @Resource + private WalletTxAuthService walletTxAuthService; + + @PostMapping("/check") + @Operation(summary = "审核") + public CommonResult check(@Valid @RequestBody WalletTxAuthCheckReqVO dto) { + return success(walletTxAuthService.check(dto)); + } + + @PostMapping("/create") + @Operation(summary = "创建提现金额审核") + @PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:create')") + public CommonResult createWalletTxAuth(@Valid @RequestBody WalletTxAuthSaveReqVO createReqVO) { + return success(walletTxAuthService.createWalletTxAuth(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新提现金额审核") + @PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:update')") + public CommonResult updateWalletTxAuth(@Valid @RequestBody WalletTxAuthSaveReqVO updateReqVO) { + walletTxAuthService.updateWalletTxAuth(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除提现金额审核") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:delete')") + public CommonResult deleteWalletTxAuth(@RequestParam("id") Long id) { + walletTxAuthService.deleteWalletTxAuth(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得提现金额审核") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:query')") + public CommonResult getWalletTxAuth(@RequestParam("id") Long id) { + WalletTxAuthDO walletTxAuth = walletTxAuthService.getWalletTxAuth(id); + return success(BeanUtils.toBean(walletTxAuth, WalletTxAuthRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得提现金额审核分页") + @PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:query')") + public CommonResult> getWalletTxAuthPage(@Valid WalletTxAuthPageReqVO pageReqVO) { + PageResult pageResult = walletTxAuthService.getWalletTxAuthPage(pageReqVO); + return success(pageResult); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出提现金额审核 Excel") + @PreAuthorize("@ss.hasPermission('pay:wallet-tx-auth:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportWalletTxAuthExcel(@Valid WalletTxAuthPageReqVO pageReqVO, HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = walletTxAuthService.getWalletTxAuthPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "提现金额审核.xls", "数据", WalletTxAuthRespVO.class, list); + } + + @PostMapping("/update-transfer") + @Operation(summary = "更新转账记录(回调)") + @PermitAll + public CommonResult updateTransfer(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) { + log.info("[updateTransfer][notifyReqDTO({})]", notifyReqDTO); + walletTxAuthService.updateTransfer(notifyReqDTO); + return success(true); + } + +} \ No newline at end of file diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthCheckReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthCheckReqVO.java new file mode 100644 index 0000000..c079c19 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthCheckReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 提现金额审核 Request VO") +@Data +@ToString(callSuper = true) +public class WalletTxAuthCheckReqVO { + + @Schema(description = "提现id") + @NotNull(message = "提现id不能为空") + private Long id; + + @Schema(description = "审核状态") + @NotEmpty(message = "审核状态不能为空") + private String checkStatus; + + @Schema(description = "拒绝信息") + private String txRefuse; +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthPageReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthPageReqVO.java new file mode 100755 index 0000000..0955082 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthPageReqVO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 提现金额审核分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WalletTxAuthPageReqVO extends PageParam { + + @Schema(description = "用户id", example = "22189") + private Long userId; + + @Schema(description = "钱包id", example = "25463") + private Long walletId; + + @Schema(description = "提现类型", example = "1") + private String bizType; + + @Schema(description = "提现类型绑定id", example = "25829") + private Long bizId; + + @Schema(description = "提现金额", example = "18586") + private Integer txPrice; + + @Schema(description = "提现title") + private String title; + + @Schema(description = "提现状态", example = "2") + private String checkStatus; + + @Schema(description = "审核时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] checkTime; + + @Schema(description = "提现拒绝信息") + private String txRefuse; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthRespVO.java new file mode 100755 index 0000000..5fc97be --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthRespVO.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.util.*; + +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import com.alibaba.excel.annotation.*; + +@Schema(description = "管理后台 - 提现金额审核 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WalletTxAuthRespVO { + + @Schema(description = "钱包提现", requiredMode = Schema.RequiredMode.REQUIRED, example = "30337") + @ExcelProperty("钱包提现") + private Long id; + + @Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "22189") + @ExcelProperty("用户id") + private Long userId; + + @Schema(description = "钱包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "25463") + @ExcelProperty("钱包id") + private Long walletId; + + @Schema(description = "提现类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("提现类型") + private String bizType; + + @Schema(description = "提现类型绑定id", example = "25829") + @ExcelProperty("提现类型绑定id") + private Long bizId; + + @Schema(description = "提现金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "18586") + @ExcelProperty("提现金额") + private Integer txPrice; + + @Schema(description = "提现title") + @ExcelProperty("提现title") + private String title; + + @Schema(description = "提现状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("提现状态") + private String checkStatus; + + @Schema(description = "审核时间") + @ExcelProperty("审核时间") + private LocalDateTime checkTime; + + @Schema(description = "提现拒绝信息") + @ExcelProperty("提现拒绝信息") + private String txRefuse; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + /** + * 用户相关 + */ + @Schema(description = "用户昵称") + private String memberNickname; + + @Schema(description = "用户头像") + private String memberAvatar; + + @Schema(description = "用户手机") + private String memberMobile; + +} \ No newline at end of file diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthSaveReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthSaveReqVO.java new file mode 100755 index 0000000..7a03b60 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/txauth/WalletTxAuthSaveReqVO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 提现金额审核新增/修改 Request VO") +@Data +public class WalletTxAuthSaveReqVO { + + @Schema(description = "钱包提现", requiredMode = Schema.RequiredMode.REQUIRED, example = "30337") + private Long id; + + @Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "22189") + @NotNull(message = "用户id不能为空") + private Long userId; + + @Schema(description = "钱包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "25463") + @NotNull(message = "钱包id不能为空") + private Long walletId; + + @Schema(description = "提现类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotEmpty(message = "提现类型不能为空") + private String bizType; + + @Schema(description = "提现类型绑定id", example = "25829") + private Long bizId; + + @Schema(description = "提现金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "18586") + @NotNull(message = "提现金额不能为空") + private Integer txPrice; + + @Schema(description = "提现title") + private String title; + + @Schema(description = "提现状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotEmpty(message = "提现状态不能为空") + private String checkStatus; + + @Schema(description = "审核时间") + private LocalDateTime checkTime; + + @Schema(description = "提现拒绝信息") + private String txRefuse; + +} \ No newline at end of file diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppWalletTxAuthController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppWalletTxAuthController.java new file mode 100644 index 0000000..4e4d185 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppWalletTxAuthController.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.pay.controller.app.wallet; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent; +import cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthPageReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthRespVO; +import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.txauth.AppWalletTxAuthGiftApplyReqVo; +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletTxAuthDO; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletTxAuthBizTypeEnum; +import cn.iocoder.yudao.module.pay.service.wallet.WalletTxAuthService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import java.util.concurrent.TimeUnit; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType; + +@Tag(name = "用户 APP - 提现金额审核") +@RestController +@RequestMapping("/pay/wallet-tx-auth") +@Validated +public class AppWalletTxAuthController { + + @Resource + private WalletTxAuthService walletTxAuthService; + + @PostMapping("/gift/apply") + @Operation(summary = "礼物申请提现") + @Idempotent(timeout = 3, timeUnit = TimeUnit.SECONDS, keyResolver = UserIdempotentKeyResolver.class, message = "正在提现中,请稍后重试") + public CommonResult apply(@RequestBody AppWalletTxAuthGiftApplyReqVo dto) { + dto.setUserId(getLoginUserId()); + dto.setUserType(getLoginUserType()); + return success(walletTxAuthService.giftApply(dto)); + } + + @GetMapping("/detail") + @Operation(summary = "获取提现金额审核详情") + public CommonResult detail(@RequestParam("id") Long id) { + return success(walletTxAuthService.getWalletTxAuth(id)); + } + + @GetMapping("/gift/page") + @Operation(summary = "礼物提现记录") + public CommonResult> getWalletTxAuthPage(@Valid WalletTxAuthPageReqVO pageReqVO) { + pageReqVO.setUserId(getLoginUserId()); + pageReqVO.setBizType(PayWalletTxAuthBizTypeEnum.GIFT_TX_AUTH.getType()); + PageResult pageResult = walletTxAuthService.getWalletTxAuthPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, WalletTxAuthRespVO.class)); + } + + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/vo/txauth/AppWalletTxAuthGiftApplyReqVo.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/vo/txauth/AppWalletTxAuthGiftApplyReqVo.java new file mode 100644 index 0000000..6fde595 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/vo/txauth/AppWalletTxAuthGiftApplyReqVo.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.pay.controller.app.wallet.vo.txauth; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "用户 APP - 用户申请礼物提现") +@Data +public class AppWalletTxAuthGiftApplyReqVo { + + @Schema(description = "用户id") + private Long userId; + + @Schema(description = "用户类型") + private Integer userType; + + @Schema(description = "礼物金额") + @NotNull(message = "礼物金额不能为空") + private Integer giftPrice; + + @Schema(description = "微信号") + @NotEmpty(message = "微信号不能为空") + private String wechatNum; + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/WalletTxAuthDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/WalletTxAuthDO.java new file mode 100755 index 0000000..6f4e34e --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/WalletTxAuthDO.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.pay.dal.dataobject.wallet; + +import lombok.*; + +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 提现金额审核 DO + */ +@TableName("pay_wallet_tx_auth") +@KeySequence("pay_wallet_tx_auth_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WalletTxAuthDO extends BaseDO { + + /** + * 钱包提现 + */ + @TableId + private Long id; + + /** + * 用户id + */ + private Long userId; + + /** + * 钱包id + */ + private Long walletId; + + /** + * 提现类型 + */ + private String bizType; + + /** + * 提现类型绑定id + */ + private Long bizId; + + /** + * 提现缘都金额 + */ + private Integer txYdPrice; + + /** + * 提现金额 + */ + private Integer txPrice; + + /** + * 提现title + */ + private String title; + + /** + * 提现状态 + */ + private String checkStatus; + + /** + * 审核时间 + */ + private LocalDateTime checkTime; + + /** + * 提现拒绝信息 + */ + private String txRefuse; + + /** + * 提现转账信息 + */ + private Long payTransferId; + + /** + * 渠道商户编号 + */ + private String channelMchId; + + /** + * 渠道 package 信息 + */ + private String channelPackageInfo; + + /** + * 到账时间 + */ + private LocalDateTime dzTime; + +} \ No newline at end of file diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java index a06662b..fa60d4e 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java @@ -140,6 +140,48 @@ public interface PayWalletMapper extends BaseMapperX { update(null, lambdaUpdateWrapper); } + /** + * 当消费(提现)时候,更新钱包(礼物) + */ + default int updateWhenGiftConsumption(Long id, Integer price) { + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" gift_balance = gift_balance - " + price + + ", gift_total_expense = gift_total_expense + " + price) + .eq(PayWalletDO::getId, id) + .ge(PayWalletDO::getGiftBalance, price); // cas 逻辑 + return update(null, lambdaUpdateWrapper); + } + + /** + * 冻结钱包部分余额(礼物金额) + * + * @param id 钱包 id + * @param price 冻结金额 + */ + default int freezeGiftPrice(Long id, Integer price) { + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" gift_balance = gift_balance - " + price + + ", gift_freeze_price = gift_freeze_price + " + price) + .eq(PayWalletDO::getId, id) + .ge(PayWalletDO::getGiftBalance, price); // cas 逻辑 + return update(null, lambdaUpdateWrapper); + } + + /** + * 解冻钱包余额(礼物金额) + * + * @param id 钱包 id + * @param price 解冻金额 + */ + default int unfreezeGiftPrice(Long id, Integer price) { + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" gift_balance = gift_balance + " + price + + ", gift_freeze_price = gift_freeze_price - " + price) + .eq(PayWalletDO::getId, id) + .ge(PayWalletDO::getGiftFreezePrice, price); // cas 逻辑 + return update(null, lambdaUpdateWrapper); + } + /* *************************************** end 礼物相关 end *************************************** */ diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/WalletTxAuthMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/WalletTxAuthMapper.java new file mode 100755 index 0000000..348a45a --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/WalletTxAuthMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.pay.dal.mysql.wallet; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthPageReqVO; +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletTxAuthDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 提现金额审核 Mapper + */ +@Mapper +public interface WalletTxAuthMapper extends BaseMapperX { + + default PageResult selectPage(WalletTxAuthPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(WalletTxAuthDO::getUserId, reqVO.getUserId()) + .eqIfPresent(WalletTxAuthDO::getWalletId, reqVO.getWalletId()) + .eqIfPresent(WalletTxAuthDO::getBizType, reqVO.getBizType()) + .eqIfPresent(WalletTxAuthDO::getBizId, reqVO.getBizId()) + .eqIfPresent(WalletTxAuthDO::getTxPrice, reqVO.getTxPrice()) + .eqIfPresent(WalletTxAuthDO::getTitle, reqVO.getTitle()) + .eqIfPresent(WalletTxAuthDO::getCheckStatus, reqVO.getCheckStatus()) + .betweenIfPresent(WalletTxAuthDO::getCheckTime, reqVO.getCheckTime()) + .eqIfPresent(WalletTxAuthDO::getTxRefuse, reqVO.getTxRefuse()) + .betweenIfPresent(WalletTxAuthDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(WalletTxAuthDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java index 1f2b638..5b1a77c 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java @@ -66,8 +66,13 @@ public interface PayWalletService { * @param price 扣减金额 * @return 钱包流水 */ - PayWalletTransactionDO reduceWalletBalance(Long walletId, Long bizId, - PayWalletBizTypeEnum bizType, Integer price); + PayWalletTransactionDO reduceWalletBalance(Long walletId, Long bizId, PayWalletBizTypeEnum bizType, Integer price); + + /** + * 减少礼物钱包(提现) + */ + PayWalletTransactionDO reduceWalletGiftBalance(Long walletId, String bizId, PayWalletBizTypeEnum bizType, Integer price); + /** * 增加钱包余额 @@ -94,6 +99,14 @@ public interface PayWalletService { */ void freezePrice(Long id, Integer price); + /** + * 冻结钱包部分余额(送礼物) + * + * @param id 钱包编号 + * @param price 冻结金额 + */ + void freezeGiftPrice(Long id, Integer price); + /** * 解冻钱包余额 * @@ -102,4 +115,12 @@ public interface PayWalletService { */ void unfreezePrice(Long id, Integer price); + /** + * 解冻钱包余额(送礼物) + * + * @param id 钱包编号 + * @param price 解冻金额 + */ + void unfreezeGiftPrice(Long id, Integer price); + } 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 e0027b6..95f2498 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 @@ -249,6 +249,43 @@ public class PayWalletServiceImpl implements PayWalletService { }); } + /** + * 减少礼物钱包(提现) + */ + @Override + @Transactional(rollbackFor = Exception.class) + @SneakyThrows + public PayWalletTransactionDO reduceWalletGiftBalance(Long walletId, String bizId, PayWalletBizTypeEnum bizType, Integer price) { + // 1. 获取钱包 + PayWalletDO payWallet = getWallet(walletId); + if (payWallet == null) { + log.error("[addWalletBalance][用户钱包({})不存在]", walletId); + throw exception(WALLET_NOT_FOUND); + } + // 2. 加锁,更新钱包余额(目的:避免钱包流水的并发更新时,余额变化不连贯) + return lockRedisDAO.lock(walletId, UPDATE_TIMEOUT_MILLIS, () -> { + // 3. 更新钱包金额 + switch (bizType) { + case RECEIVE_GIFT: // 收到礼物 + walletMapper.updateWhenGiftConsumption(payWallet.getId(), price); + break; + default: { + throw new UnsupportedOperationException("待实现:" + bizType); + } + } + // 4. 生成钱包流水 + Integer afterBalance = payWallet.getBalance() - price; + WalletTransactionCreateReqBO transactionCreateReqBO = new WalletTransactionCreateReqBO() + .setWalletId(payWallet.getId()) + .setPrice(-price) + .setBalance(afterBalance) + .setBizId(bizId) + .setBizType(bizType.getType()) + .setTitle(bizType.getDescription()); + return walletTransactionService.createWalletTransaction(transactionCreateReqBO); + }); + } + @Override public void freezePrice(Long id, Integer price) { int updateCounts = walletMapper.freezePrice(id, price); @@ -257,6 +294,14 @@ public class PayWalletServiceImpl implements PayWalletService { } } + @Override + public void freezeGiftPrice(Long id, Integer price) { + int updateCounts = walletMapper.freezeGiftPrice(id, price); + if (updateCounts == 0) { + throw exception(WALLET_BALANCE_NOT_ENOUGH); + } + } + @Override public void unfreezePrice(Long id, Integer price) { int updateCounts = walletMapper.unFreezePrice(id, price); @@ -265,4 +310,12 @@ public class PayWalletServiceImpl implements PayWalletService { } } + @Override + public void unfreezeGiftPrice(Long id, Integer price) { + int updateCounts = walletMapper.unfreezeGiftPrice(id, price); + if (updateCounts == 0) { + throw exception(WALLET_FREEZE_PRICE_NOT_ENOUGH); + } + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/WalletTxAuthService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/WalletTxAuthService.java new file mode 100755 index 0000000..20a4ceb --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/WalletTxAuthService.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.pay.service.wallet; + +import java.util.*; +import javax.validation.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthCheckReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthPageReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthRespVO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthSaveReqVO; +import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.txauth.AppWalletTxAuthGiftApplyReqVo; +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletTxAuthDO; + +/** + * 提现金额审核 Service 接口 + */ +public interface WalletTxAuthService { + + /** + * 更新转账记录(回调) + */ + void updateTransfer(PayRefundNotifyReqDTO notifyReqDTO); + + /** + * 礼物申请提现 + */ + Long giftApply(@Valid AppWalletTxAuthGiftApplyReqVo dto); + + /** + * 审核 + */ + Boolean check(@Valid WalletTxAuthCheckReqVO dto); + + /** + * 创建提现金额审核 + */ + Long createWalletTxAuth(@Valid WalletTxAuthSaveReqVO createReqVO); + + /** + * 更新提现金额审核 + */ + void updateWalletTxAuth(@Valid WalletTxAuthSaveReqVO updateReqVO); + + /** + * 删除提现金额审核 + */ + void deleteWalletTxAuth(Long id); + + /** + * 获得提现金额审核 + */ + WalletTxAuthDO getWalletTxAuth(Long id); + + /** + * 获得提现金额审核分页 + */ + PageResult getWalletTxAuthPage(WalletTxAuthPageReqVO pageReqVO); +} \ No newline at end of file diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/WalletTxAuthServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/WalletTxAuthServiceImpl.java new file mode 100755 index 0000000..eb825ff --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/WalletTxAuthServiceImpl.java @@ -0,0 +1,260 @@ +package cn.iocoder.yudao.module.pay.service.wallet; + +import cn.hutool.core.collection.IterUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.member.api.config.MemberConfigApi; +import cn.iocoder.yudao.module.member.api.config.dto.MemberConfigRespDTO; +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.notify.dto.PayRefundNotifyReqDTO; +import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApiImpl; +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthCheckReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthPageReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthRespVO; +import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.txauth.WalletTxAuthSaveReqVO; +import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.txauth.AppWalletTxAuthGiftApplyReqVo; +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.WalletTxAuthDO; +import cn.iocoder.yudao.module.pay.dal.mysql.wallet.WalletTxAuthMapper; +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletTxAuthBizTypeEnum; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletTxAuthStatusEnum; +import cn.iocoder.yudao.module.trade.api.brokerage.TradeBrokerageRecordApi; +import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageWechatTransferDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; + +/** + * 提现金额审核 Service 实现类 + * + * @author 聊天交友 + */ +@Slf4j +@Service +@Validated +public class WalletTxAuthServiceImpl implements WalletTxAuthService { + + @Resource + private WalletTxAuthMapper walletTxAuthMapper; + @Resource + private PayWalletService walletService; + @Resource + private PayTransferApiImpl payTransferApi; + + @Resource + private MemberConfigApi memberConfigApi; + @Resource + private MemberUserApi memberUserApi; + @Resource + private TradeBrokerageRecordApi tradeBrokerageRecordApi; + + /** + * 更新转账记录(回调) + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void updateTransfer(PayRefundNotifyReqDTO notifyReqDTO) { + Long id = notifyReqDTO.getPayRefundId(); + PayTransferRespDTO transfer = payTransferApi.getTransfer(id); + WalletTxAuthDO detail = walletTxAuthMapper.selectOne(new LambdaQueryWrapperX() + .eq(WalletTxAuthDO::getPayTransferId, id) + ); + if (ObjectUtil.isEmpty(detail)) { + log.error("|更新转账记录(回调)|,提现记录不存在,不进行操作notifyReqDTO:{}", JSONUtil.toJsonStr(notifyReqDTO)); + return; + } + if (PayTransferStatusEnum.isWaiting(transfer.getStatus())) { + log.info("|更新转账记录(回调)|,转账正在等待中,不进行操作notifyReqDTO:{}", JSONUtil.toJsonStr(notifyReqDTO)); + return; + } + if (PayTransferStatusEnum.isInProgress(transfer.getStatus())) { + log.info("|更新转账记录(回调)|,转账正在进行中,不进行操作notifyReqDTO:{}", JSONUtil.toJsonStr(notifyReqDTO)); + return; + } + if (PayTransferStatusEnum.isClosed(transfer.getStatus())) { + // 解冻 + walletService.unfreezeGiftPrice(detail.getWalletId(), detail.getTxYdPrice()); + WalletTxAuthDO updateObj = new WalletTxAuthDO(); + updateObj.setId(detail.getId()); + updateObj.setCheckStatus(PayWalletTxAuthStatusEnum.AUTH_TX_ERROR.getType()); + walletTxAuthMapper.updateById(updateObj); + } + if (PayTransferStatusEnum.isClosed(transfer.getStatus())) { + // 解冻 + walletService.unfreezeGiftPrice(detail.getWalletId(), detail.getTxYdPrice()); + // 进行扣款 + walletService.reduceWalletGiftBalance(detail.getWalletId(), String.valueOf(detail.getId()), PayWalletBizTypeEnum.RECEIVE_GIFT_TX, detail.getTxYdPrice()); + WalletTxAuthDO updateObj = new WalletTxAuthDO(); + updateObj.setId(detail.getId()); + updateObj.setCheckStatus(PayWalletTxAuthStatusEnum.AUTH_TX_SUCCESS.getType()); + updateObj.setDzTime(LocalDateTime.now()); + walletTxAuthMapper.updateById(updateObj); + } + } + + /** + * 礼物申请提现 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long giftApply(AppWalletTxAuthGiftApplyReqVo dto) { + // 礼物配置 + MemberConfigRespDTO config = memberConfigApi.getConfig(); + if (ObjectUtil.isEmpty(config) || ObjectUtil.isEmpty(config.getGiftCashRatio())) { + throw exception(EMPTY, "请先配置礼物提现比例"); + } + // 查询用户 + MemberUserRespDTO user = memberUserApi.getUser(dto.getUserId()); + if (ObjectUtil.isEmpty(user)) { + throw exception(EMPTY, "提现用户不存在"); + } + // 查询用户钱包 + PayWalletDO wallet = walletService.getOrCreateWallet(dto.getUserId(), dto.getUserType()); + int afterGiftBalance = wallet.getGiftBalance() - dto.getGiftPrice(); + if (afterGiftBalance < 0) { + throw exception(EMPTY, "提现金额大于礼物余额"); + } + // 计算提现金额 + int txPrice = BigDecimal.valueOf(dto.getGiftPrice()).divide(BigDecimal.valueOf(config.getGiftCashRatio()), 2, RoundingMode.HALF_UP).intValue(); + // 添加记录 + WalletTxAuthDO walletTxAuth = new WalletTxAuthDO(); + walletTxAuth.setUserId(dto.getUserId()); + walletTxAuth.setWalletId(wallet.getId()); + walletTxAuth.setBizType(PayWalletTxAuthBizTypeEnum.GIFT_TX_AUTH.getType()); + walletTxAuth.setBizId(null); + walletTxAuth.setTxPrice(txPrice); + walletTxAuth.setTxYdPrice(dto.getGiftPrice()); + walletTxAuth.setTitle("用户" + user.getNickname() + "申请礼物提现:" + MoneyUtils.fenToYuanStr(txPrice) + "元"); + walletTxAuth.setCheckStatus(PayWalletTxAuthStatusEnum.TX_AUTH_AWAIT.getType()); + walletTxAuthMapper.insert(walletTxAuth); + return walletTxAuth.getWalletId(); + } + + /** + * 审核 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean check(WalletTxAuthCheckReqVO dto) { + WalletTxAuthDO detail = walletTxAuthMapper.selectById(dto.getId()); + if (ObjectUtil.isEmpty(detail)) { + throw exception(WALLET_TX_AUTH_NOT_EXISTS); + } + if (ObjectUtil.notEqual(PayWalletTxAuthStatusEnum.TX_AUTH_AWAIT.getType(), detail.getCheckStatus())) { + throw exception(EMPTY, "已审核,无需重复审核"); + } + WalletTxAuthDO updateObj = new WalletTxAuthDO(); + updateObj.setId(dto.getId()); + updateObj.setCheckStatus(dto.getCheckStatus()); + updateObj.setTxRefuse(dto.getTxRefuse()); + updateObj.setCheckTime(LocalDateTime.now()); + // 审核通过 + if (ObjectUtil.equals(PayWalletTxAuthStatusEnum.AUTH_SUCCESS.getType(), dto.getCheckStatus())) { + BrokerageWechatTransferDTO wechatTransfer = tradeBrokerageRecordApi.wechatTransfer(detail.getId(), detail.getUserId(), detail.getTxPrice()); + if (ObjectUtil.isEmpty(wechatTransfer)) { + throw exception(EMPTY, "发起微信转账失败"); + } + PayTransferRespDTO transfer = payTransferApi.getTransfer(wechatTransfer.getId()); + if (ObjectUtil.isEmpty(transfer)) { + throw exception(EMPTY, "查询转账记录失败"); + } + updateObj.setPayTransferId(wechatTransfer.getId()); + updateObj.setChannelMchId(transfer.getChannelMchId()); + updateObj.setChannelPackageInfo(wechatTransfer.getChannelPackageInfo()); + // 应该冻结操作金额的数量 + walletService.freezeGiftPrice(detail.getWalletId(), detail.getTxYdPrice()); + } + walletTxAuthMapper.updateById(updateObj); + return true; + } + + @Override + public Long createWalletTxAuth(WalletTxAuthSaveReqVO createReqVO) { + // 插入 + WalletTxAuthDO walletTxAuth = BeanUtils.toBean(createReqVO, WalletTxAuthDO.class); + walletTxAuthMapper.insert(walletTxAuth); + // 返回 + return walletTxAuth.getId(); + } + + @Override + public void updateWalletTxAuth(WalletTxAuthSaveReqVO updateReqVO) { + // 校验存在 + validateWalletTxAuthExists(updateReqVO.getId()); + // 更新 + WalletTxAuthDO updateObj = BeanUtils.toBean(updateReqVO, WalletTxAuthDO.class); + walletTxAuthMapper.updateById(updateObj); + } + + @Override + public void deleteWalletTxAuth(Long id) { + // 校验存在 + validateWalletTxAuthExists(id); + // 删除 + walletTxAuthMapper.deleteById(id); + } + + private void validateWalletTxAuthExists(Long id) { + if (walletTxAuthMapper.selectById(id) == null) { + throw exception(WALLET_TX_AUTH_NOT_EXISTS); + } + } + + @Override + public WalletTxAuthDO getWalletTxAuth(Long id) { + return walletTxAuthMapper.selectById(id); + } + + @Override + public PageResult getWalletTxAuthPage(WalletTxAuthPageReqVO pageReqVO) { + PageResult pageResult = new PageResult<>(); + + PageResult pageList = walletTxAuthMapper.selectPage(pageReqVO); + + List listResult = BeanUtils.toBean(pageList.getList(), WalletTxAuthRespVO.class); + disposePageList(listResult); + pageResult.setTotal(pageList.getTotal()); + pageResult.setList(listResult); + return pageResult; + } + + private void disposePageList(List list) { + if (ObjectUtil.isEmpty(list)) return; + List userIds = list.stream().map(WalletTxAuthRespVO::getUserId).distinct().collect(Collectors.toList()); + if (ObjectUtil.isNotEmpty(userIds)) { + List userList = memberUserApi.getUserList(userIds); + Map userMap = IterUtil.toMap(userList, MemberUserRespDTO::getId); + for (WalletTxAuthRespVO item : list) { + MemberUserRespDTO target = userMap.get(item.getUserId()); + if (ObjectUtil.isEmpty(target)) continue; + item.setMemberAvatar(target.getAvatar()); + item.setMemberNickname(target.getNickname()); + item.setMemberMobile(target.getMobile()); + } + } + } + +} \ No newline at end of file