Commit 3b9548aa2dad920740b70d1e8e15404d98ad9a2c

Authored by 痴货
1 parent 2c4d8298

微信转账未完成

yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageWithdrawController.java
... ... @@ -24,6 +24,7 @@ import java.util.Map;
24 24  
25 25 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
26 26 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  27 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
27 28  
28 29 @Tag(name = "管理后台 - 佣金提现")
29 30 @RestController
... ... @@ -41,7 +42,7 @@ public class BrokerageWithdrawController {
41 42 @Operation(summary = "通过申请")
42 43 @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')")
43 44 public CommonResult<Boolean> approveBrokerageWithdraw(@RequestParam("id") Integer id) {
44   - brokerageWithdrawService.auditBrokerageWithdraw(id, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, "");
  45 + brokerageWithdrawService.auditBrokerageWithdraw(id, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, "", getClientIP());
45 46 return success(true);
46 47 }
47 48  
... ... @@ -49,7 +50,7 @@ public class BrokerageWithdrawController {
49 50 @Operation(summary = "驳回申请")
50 51 @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')")
51 52 public CommonResult<Boolean> rejectBrokerageWithdraw(@Valid @RequestBody BrokerageWithdrawRejectReqVO reqVO) {
52   - brokerageWithdrawService.auditBrokerageWithdraw(reqVO.getId(), BrokerageWithdrawStatusEnum.AUDIT_FAIL, reqVO.getAuditReason());
  53 + brokerageWithdrawService.auditBrokerageWithdraw(reqVO.getId(), BrokerageWithdrawStatusEnum.AUDIT_FAIL, reqVO.getAuditReason(), getClientIP());
53 54 return success(true);
54 55 }
55 56  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java
... ... @@ -28,7 +28,7 @@ public interface BrokerageWithdrawService {
28 28 * @param status 审核状态
29 29 * @param auditReason 驳回原因
30 30 */
31   - void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason);
  31 + void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp);
32 32  
33 33 /**
34 34 * 获得佣金提现
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java
... ... @@ -4,20 +4,29 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.date.LocalDateTimeUtil;
5 5 import cn.hutool.core.map.MapUtil;
6 6 import cn.hutool.core.util.ObjectUtil;
  7 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
7 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 9 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
  10 +import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApi;
  11 +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO;
  12 +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum;
9 13 import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
10 14 import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
  15 +import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
  16 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
  17 +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
11 18 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO;
12 19 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;
13 20 import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageWithdrawConvert;
14 21 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
15 22 import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
16 23 import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageWithdrawMapper;
  24 +import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO;
17 25 import cn.iocoder.yudao.module.trade.enums.MessageTemplateConstants;
18 26 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
19 27 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
20 28 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
  29 +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
21 30 import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
22 31 import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
23 32 import org.springframework.stereotype.Service;
... ... @@ -27,12 +36,10 @@ import org.springframework.validation.annotation.Validated;
27 36 import jakarta.annotation.Resource;
28 37 import jakarta.validation.Validator;
29 38 import java.time.LocalDateTime;
30   -import java.util.Collection;
31   -import java.util.Collections;
32   -import java.util.List;
33   -import java.util.Map;
  39 +import java.util.*;
34 40  
35 41 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  42 +import static cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO.TRADE_ORDER_NO_PREFIX;
36 43 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
37 44  
38 45 /**
... ... @@ -46,6 +53,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
46 53  
47 54 @Resource
48 55 private BrokerageWithdrawMapper brokerageWithdrawMapper;
  56 + @Resource
  57 + private TradeNoRedisDAO tradeNoRedisDAO;
49 58  
50 59 @Resource
51 60 private BrokerageRecordService brokerageRecordService;
... ... @@ -54,15 +63,23 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
54 63  
55 64 @Resource
56 65 private NotifyMessageSendApi notifyMessageSendApi;
  66 + @Resource
  67 + private PayTransferApi payTransferApi;
  68 + @Resource
  69 + private SocialUserApi socialUserApi;
57 70  
58 71 @Resource
59 72 private Validator validator;
60 73  
  74 + @Resource
  75 + private TradeOrderProperties tradeOrderProperties;
  76 +
61 77 @Override
62 78 @Transactional(rollbackFor = Exception.class)
63   - public void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason) {
  79 + public void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp) {
64 80 // 1.1 校验存在
65 81 BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id);
  82 +
66 83 // 1.2 校验状态为审核中
67 84 if (ObjectUtil.notEqual(BrokerageWithdrawStatusEnum.AUDITING.getStatus(), withdraw.getStatus())) {
68 85 throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING);
... ... @@ -81,6 +98,12 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
81 98 // 3.1 通过时佣金转余额
82 99 if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) {
83 100 // todo 疯狂:
  101 + }else if (BrokerageWithdrawTypeEnum.WECHAT.getType().equals(withdraw.getType())){
  102 + //获取openid
  103 + SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(UserTypeEnum.MEMBER.getValue(), withdraw.getUserId(), SocialTypeEnum.WECHAT_MINI_APP.getType());
  104 + // 微信提现
  105 + PayTransferCreateReqDTO payTransferCreateReqDTO = getPayTransferCreateReqDTO(userIp, withdraw, socialUser);
  106 + payTransferApi.createTransfer(payTransferCreateReqDTO);
84 107 }
85 108 // TODO 疯狂:调用转账接口
86 109 } else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) {
... ... @@ -102,6 +125,19 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
102 125 .setUserId(withdraw.getUserId()).setTemplateCode(templateCode).setTemplateParams(templateParams));
103 126 }
104 127  
  128 + private PayTransferCreateReqDTO getPayTransferCreateReqDTO(String userIp, BrokerageWithdrawDO withdraw, SocialUserRespDTO socialUser) {
  129 + PayTransferCreateReqDTO payTransferCreateReqDTO = new PayTransferCreateReqDTO();
  130 + payTransferCreateReqDTO.setAppKey(tradeOrderProperties.getPayAppKey());
  131 + payTransferCreateReqDTO.setChannelCode("wx_lite");
  132 + payTransferCreateReqDTO.setUserIp(userIp);
  133 + payTransferCreateReqDTO.setType(PayTransferTypeEnum.WX_BALANCE.getType());
  134 + payTransferCreateReqDTO.setMerchantTransferId(withdraw.getId().toString());
  135 + payTransferCreateReqDTO.setPrice(withdraw.getPrice());
  136 + payTransferCreateReqDTO.setSubject("佣金提现");
  137 + payTransferCreateReqDTO.setOpenid(socialUser.getOpenid());
  138 + return payTransferCreateReqDTO;
  139 + }
  140 +
105 141 private BrokerageWithdrawDO validateBrokerageWithdrawExists(Integer id) {
106 142 BrokerageWithdrawDO withdraw = brokerageWithdrawMapper.selectById(id);
107 143 if (withdraw == null) {
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java
... ... @@ -96,13 +96,15 @@ public class PayTransferServiceImpl implements PayTransferService {
96 96 transfer = INSTANCE.convert(reqDTO)
97 97 .setChannelId(channel.getId())
98 98 .setNo(no).setStatus(WAITING.getStatus())
99   - .setNotifyUrl(payApp.getTransferNotifyUrl());
  99 + .setNotifyUrl(payApp.getTransferNotifyUrl())
  100 + .setAppId(channel.getAppId());
100 101 transferMapper.insert(transfer);
101 102 }
102 103 try {
103 104 // 3. 调用三方渠道发起转账
104 105 PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(transfer)
105 106 .setOutTransferNo(transfer.getNo());
  107 + transferUnifiedReq.setNotifyUrl(payApp.getTransferNotifyUrl());
106 108 PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq);
107 109 // 4. 通知转账结果
108 110 getSelf().notifyTransfer(channel, unifiedTransferResp);
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/pom.xml
... ... @@ -64,6 +64,10 @@
64 64 <groupId>com.github.binarywang</groupId>
65 65 <artifactId>weixin-java-pay</artifactId>
66 66 </dependency>
  67 + <dependency>
  68 + <groupId>com.github.binarywang</groupId>
  69 + <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
  70 + </dependency>
67 71  
68 72 <!-- Test 测试相关 -->
69 73 <dependency>
... ... @@ -71,6 +75,7 @@
71 75 <artifactId>yudao-spring-boot-starter-test</artifactId>
72 76 <scope>test</scope>
73 77 </dependency>
  78 +
74 79 </dependencies>
75 80  
76 81 </project>
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java
... ... @@ -9,6 +9,8 @@ import jakarta.validation.constraints.Min;
9 9 import jakarta.validation.constraints.NotBlank;
10 10 import jakarta.validation.constraints.NotEmpty;
11 11 import jakarta.validation.constraints.NotNull;
  12 +import org.hibernate.validator.constraints.URL;
  13 +
12 14 import java.util.Map;
13 15  
14 16 import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.*;
... ... @@ -75,4 +77,11 @@ public class PayTransferUnifiedReqDTO {
75 77 * 支付渠道的额外参数
76 78 */
77 79 private Map<String, String> channelExtras;
  80 +
  81 + /**
  82 + * 转账结果的 notify 回调地址
  83 + */
  84 + @NotEmpty(message = "转账结果的回调地址不能为空")
  85 + @URL(message = "转账结果的 notify 回调地址必须是 URL 格式")
  86 + private String notifyUrl;
78 87 }
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
... ... @@ -17,20 +17,27 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifie
17 17 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
18 18 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
19 19 import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
  20 +import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
20 21 import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
21 22 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
22 23 import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
23 24 import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
24 25 import com.github.binarywang.wxpay.bean.request.*;
25 26 import com.github.binarywang.wxpay.bean.result.*;
  27 +import com.github.binarywang.wxpay.bean.transfer.TransferBatchesRequest;
  28 +import com.github.binarywang.wxpay.bean.transfer.TransferBatchesResult;
26 29 import com.github.binarywang.wxpay.config.WxPayConfig;
27 30 import com.github.binarywang.wxpay.exception.WxPayException;
  31 +import com.github.binarywang.wxpay.service.TransferService;
28 32 import com.github.binarywang.wxpay.service.WxPayService;
29 33 import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
  34 +import jakarta.annotation.Resource;
30 35 import lombok.extern.slf4j.Slf4j;
31 36  
32 37 import java.time.LocalDateTime;
33 38 import java.time.ZoneId;
  39 +import java.util.ArrayList;
  40 +import java.util.List;
34 41 import java.util.Map;
35 42 import java.util.Objects;
36 43  
... ... @@ -426,8 +433,26 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
426 433 }
427 434  
428 435 @Override
429   - protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
430   - throw new UnsupportedOperationException("待实现");
  436 + protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws WxPayException {
  437 + TransferService transferService = client.getTransferService();
  438 + List<TransferBatchesRequest.TransferDetail> transferDetailList = new ArrayList<>();
  439 + transferDetailList.add(TransferBatchesRequest.TransferDetail.newBuilder()
  440 + .outDetailNo(reqDTO.getOutTransferNo())
  441 + .transferAmount(reqDTO.getPrice())
  442 + .transferRemark(reqDTO.getSubject())
  443 + .openid(reqDTO.getOpenid())
  444 + .build());
  445 + TransferBatchesRequest transferBatches = TransferBatchesRequest.newBuilder()
  446 + .appid(this.config.getAppId())
  447 + .outBatchNo(reqDTO.getOutTransferNo())
  448 + .batchName(reqDTO.getSubject())
  449 + .batchRemark(reqDTO.getSubject())
  450 + .totalAmount(reqDTO.getPrice())
  451 + .totalNum(1)
  452 + .transferDetailList(transferDetailList).build();
  453 + transferBatches.setNotifyUrl(reqDTO.getNotifyUrl());
  454 + TransferBatchesResult transferBatchesResult = transferService.transferBatches(transferBatches);
  455 + return PayTransferRespDTO.waitingOf(reqDTO.getOutTransferNo(), transferBatchesResult.getBatchId() ,transferBatchesResult);
431 456 }
432 457  
433 458 @Override
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java 0 → 100644
  1 +package com.github.binarywang.wxpay.bean.transfer;
  2 +
  3 +import com.github.binarywang.wxpay.v3.SpecEncrypt;
  4 +import com.google.gson.annotations.SerializedName;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Builder;
  7 +import lombok.Data;
  8 +import lombok.NoArgsConstructor;
  9 +
  10 +import java.io.Serializable;
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * 发起商家转账API参数
  15 + *
  16 + * @author zhongjun
  17 + * created on 2022/6/17
  18 + **/
  19 +@Data
  20 +@Builder(builderMethodName = "newBuilder")
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class TransferBatchesRequest implements Serializable {
  24 + private static final long serialVersionUID = -2175582517588397426L;
  25 +
  26 + /**
  27 + * 直连商户的appid
  28 + */
  29 + @SerializedName("appid")
  30 + private String appid;
  31 +
  32 + /**
  33 + * 商家批次单号
  34 + */
  35 + @SerializedName("out_batch_no")
  36 + private String outBatchNo;
  37 +
  38 + /**
  39 + * 批次名称
  40 + */
  41 + @SerializedName("batch_name")
  42 + private String batchName;
  43 +
  44 + /**
  45 + * 批次备注
  46 + */
  47 + @SerializedName("batch_remark")
  48 + private String batchRemark;
  49 +
  50 + /**
  51 + * 转账总金额
  52 + */
  53 + @SerializedName("total_amount")
  54 + private Integer totalAmount;
  55 +
  56 + /**
  57 + * 转账总笔数
  58 + */
  59 + @SerializedName("total_num")
  60 + private Integer totalNum;
  61 +
  62 + /**
  63 + * 转账明细列表
  64 + */
  65 + @SpecEncrypt
  66 + @SerializedName("transfer_detail_list")
  67 + private List<TransferDetail> transferDetailList;
  68 +
  69 + /**
  70 + * 转账场景ID
  71 + */
  72 + @SerializedName("transfer_scene_id")
  73 + private String transferSceneId;
  74 +
  75 + /**
  76 + * 通知地址 说明:异步接收微信支付结果通知的回调地址,通知url必须为公网可访问的url,必须为https,不能携带参数。
  77 + */
  78 + @SerializedName("notify_url")
  79 + private String notifyUrl;
  80 +
  81 + @Data
  82 + @Builder(builderMethodName = "newBuilder")
  83 + @AllArgsConstructor
  84 + @NoArgsConstructor
  85 + public static class TransferDetail {
  86 +
  87 + /**
  88 + * 商家明细单号
  89 + */
  90 + @SerializedName("out_detail_no")
  91 + private String outDetailNo;
  92 +
  93 + /**
  94 + * 转账金额
  95 + */
  96 + @SerializedName("transfer_amount")
  97 + private Integer transferAmount;
  98 +
  99 + /**
  100 + * 转账备注
  101 + */
  102 + @SerializedName("transfer_remark")
  103 + private String transferRemark;
  104 +
  105 + /**
  106 + * 用户在直连商户应用下的用户标示
  107 + */
  108 + @SerializedName("openid")
  109 + private String openid;
  110 +
  111 + /**
  112 + * 收款用户姓名
  113 + */
  114 + @SpecEncrypt
  115 + @SerializedName("user_name")
  116 + private String userName;
  117 + }
  118 +}
... ...