Browse Source

feat(order): 调整订单支付逻辑

wzq 2 weeks ago
parent
commit
6363d5e26c
23 changed files with 702 additions and 149 deletions
  1. 1 1
      national-motion-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java
  2. 103 5
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/controller/OrderController.java
  3. 74 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/dto/RefundsInfoDTO.java
  4. 3 1
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/form/PageOrdersForm.java
  5. 27 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/form/RefundOrderForm.java
  6. 5 2
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/service/IOrderService.java
  7. 104 85
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/service/WeChatPayService.java
  8. 68 27
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/service/impl/OrderServiceImpl.java
  9. 79 1
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WeChatProfitSharingService.java
  10. 74 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatCallbackRefundData.java
  11. 1 1
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatConstants.java
  12. 1 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatPayV3Utils.java
  13. 20 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatRefundCallback.java
  14. 2 2
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatUrlConstants.java
  15. 1 1
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/entity/AppOrderProInfo.java
  16. 18 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/entity/AppOrderRefundsInfo.java
  17. 8 2
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/entity/AppProfitSharingRecords.java
  18. 2 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/mapper/AppOrderMapper.java
  19. 21 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/mapper/AppOrderRefundsInfoMapper.java
  20. 2 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/service/IAppOrderRefundsInfoService.java
  21. 5 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/service/impl/AppOrderRefundsInfoServiceimpl.java
  22. 21 21
      national-motion-module-system/national-motion-system-start/src/main/resources/application-dev.yml
  23. 62 0
      updateSql.md

+ 1 - 1
national-motion-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java

@@ -109,7 +109,7 @@ public class ShiroConfig {
         filterChainDefinitionMap.put("/com/AppDevice/notice", "anon");//APP首页接口
 
         filterChainDefinitionMap.put("/app/order/wechatPayNotify", "anon");//支付回调
-        filterChainDefinitionMap.put("/app/order/refundOrderNotify", "anon");//退款回调
+        filterChainDefinitionMap.put("/app/order/callback/refundOrderNotify", "anon");//退款回调
         filterChainDefinitionMap.put("/app/common/getDictItems", "anon");
         filterChainDefinitionMap.put("/app/home/getPlaceList", "anon");
         filterChainDefinitionMap.put("/app/my/evaluate/findByOrderPage", "anon");

+ 103 - 5
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/controller/OrderController.java

@@ -1,5 +1,6 @@
 package org.jeecg.modules.app.controller;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fasterxml.jackson.annotation.JsonFormat;
@@ -9,9 +10,13 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.extern.slf4j.Slf4j;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.RepeatSubmit;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.util.SpringContextUtils;
 import org.jeecg.modules.app.dto.AppOrderInfoDTO;
+import org.jeecg.modules.app.dto.RefundsInfoDTO;
 import org.jeecg.modules.app.form.CreateOrderForm;
 import org.jeecg.modules.app.form.PageOrdersForm;
+import org.jeecg.modules.app.form.RefundOrderForm;
 import org.jeecg.modules.app.form.UserPayForm;
 import org.jeecg.modules.app.service.IAppIsinService;
 import org.jeecg.modules.app.service.IOrderService;
@@ -20,7 +25,20 @@ import org.jeecg.modules.app.vo.AppOrderUseRecordsVO;
 import org.jeecg.modules.app.vo.OrderVO;
 import org.jeecg.modules.app.vo.PageOrdersVO;
 import org.jeecg.modules.app.vo.QueryOrderVerifyRecordsVO;
-import org.jeecg.modules.system.app.entity.AppIsin;
+import org.jeecg.modules.pay.config.WeChatProfitSharingService;
+import org.jeecg.modules.pay.config.WechatCallbackRefundData;
+import org.jeecg.modules.pay.config.WechatRefundCallback;
+import org.jeecg.modules.pay.entity.WxchatCallbackRefundData;
+import org.jeecg.modules.system.app.entity.AppOrder;
+import org.jeecg.modules.system.app.entity.AppOrderProInfo;
+import org.jeecg.modules.system.app.entity.AppOrderRefundsInfo;
+import org.jeecg.modules.system.app.mapper.AppOrderMapper;
+import org.jeecg.modules.system.app.mapper.AppOrderProInfoMapper;
+import org.jeecg.modules.system.app.mapper.AppOrderRefundsInfoMapper;
+import org.jeecg.modules.system.app.service.IAppOrderProInfoService;
+import org.jeecg.modules.system.app.service.IAppOrderRefundsInfoService;
+import org.jeecg.modules.system.app.service.impl.AppOrderProInfoServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -42,9 +60,15 @@ public class OrderController {
     @Resource
     private IOrderService appOrderService;
     @Resource
+    private IAppOrderProInfoService appOrderProInfoService;
+    @Resource
     private WeChatPayService weChatPayService;
     @Resource
     private IAppIsinService appIsinService;
+    @Resource
+    private WeChatProfitSharingService weChatProfitSharingService;
+    @Resource
+    private IAppOrderRefundsInfoService appOrderRefundsInfoService;
 
     /**
      * @return Result<OrderVO.PreviewOrderPlaceSchool>
@@ -165,6 +189,7 @@ public class OrderController {
     @RepeatSubmit(serviceId = "createOrder", limitType = RepeatSubmit.Type.PARAM, lockTime = 10)
     @RequestMapping("/wechatPayNotify")
     public Map<String, String> wechatPayNotify(HttpServletRequest request, HttpServletResponse response) throws InterruptedException {
+        log.info("--------------------------------------------支付回调");
         return appOrderService.wechatPayNotify(request, response);
     }
 
@@ -207,13 +232,13 @@ public class OrderController {
     /**
      * 订单退款
      *
-     * @param orderCode
+     * @param refundOrderForm
      * @return
      */
     @Operation(summary = "订单退款")
-    @PutMapping("/refundOrder")
-    public Result<String> refundOrder(@RequestParam(name = "orderCode") String orderCode,@RequestParam(name = "reason") String reason) {
-        return Result.OK(appOrderService.refundOrder(orderCode,reason));
+    @PostMapping("/refundOrder")
+    public Result<String> refundOrder(@RequestBody RefundOrderForm refundOrderForm) {
+        return Result.OK(appOrderService.refundOrder(refundOrderForm));
     }
 
     /**
@@ -228,6 +253,79 @@ public class OrderController {
         return weChatPayService.refundOrderNotify(jsonData);
     }
 
+    /**
+     * 订单退款回调
+     * @param request
+     * @param response
+     * @return
+     * @throws GeneralSecurityException
+     * @throws IOException
+     */
+    @Operation(summary = "订单退款回调")
+    @PostMapping("/callback/refundOrderNotify")
+    public Map<String, Object> refundOrderNotify(HttpServletRequest request, HttpServletResponse response) {
+        return weChatProfitSharingService.wxPayRefundCallback(request, response, new WechatRefundCallback() {
+            @Override
+            public void success(WechatCallbackRefundData refundData) {
+                log.info("---------------------------------------------------微信退款成功回调");
+                // TODO 退款成功的业务逻辑,例如更改订单状态为退款成功等
+                AppOrder appOrder = appOrderService.getOne(Wrappers.lambdaQuery(AppOrder.class).eq(AppOrder::getOrderCode, refundData.getOrderNo()));
+                appOrder.setOrderStatus(CommonConstant.ORDER_STATUS_6).setAfterSaleStatus(CommonConstant.NUMBER_2);
+                appOrderService.updateById(appOrder);
+                List<AppOrderProInfo> orderProInfoList = appOrderProInfoService.list(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId,
+                        appOrder.getId()));
+
+                for (AppOrderProInfo appOrderProInfo : orderProInfoList) {
+                    appOrderProInfo.setOrderStatus(CommonConstant.ORDER_PRO_INFO_TYPE_6).setAfterSaleStatus(CommonConstant.NUMBER_2);
+                    appOrderProInfoService.updateById(appOrderProInfo);
+                }
+                AppOrderRefundsInfo appOrderRefundsInfo = appOrderRefundsInfoService.getOne(Wrappers.<AppOrderRefundsInfo>lambdaQuery().eq(AppOrderRefundsInfo::getOrderId,
+                        appOrder.getId()));
+                appOrderRefundsInfo.setStatus(refundData.getStatus());
+                appOrderRefundsInfo.setSuccessTime(refundData.getSuccessTime());
+                appOrderRefundsInfoService.updateById(appOrderRefundsInfo);
+            }
+
+            @Override
+            public void fail(WechatCallbackRefundData refundData) {
+                log.info("---------------------------------------------------微信退款失败回调");
+                // TODO 特殊情况下退款失败业务处理,例如银行卡冻结需要人工退款,此时可以邮件或短信提醒管理员,并携带退款单号等关键信息
+                AppOrder appOrder = appOrderService.getOne(Wrappers.lambdaQuery(AppOrder.class).eq(AppOrder::getOrderCode, refundData.getOrderNo()));
+                appOrder.setOrderStatus(CommonConstant.ORDER_STATUS_6).setAfterSaleStatus(CommonConstant.NUMBER_2);
+                appOrderService.updateById(appOrder);
+
+                List<AppOrderProInfo> orderProInfoList = appOrderProInfoService.list(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId,
+                        appOrder.getId()));
+
+                for (AppOrderProInfo appOrderProInfo : orderProInfoList) {
+                    appOrderProInfo.setOrderStatus(CommonConstant.ORDER_PRO_INFO_TYPE_6).setAfterSaleStatus(CommonConstant.NUMBER_2);
+                    appOrderProInfoService.updateById(appOrderProInfo);
+                }
+                AppOrderRefundsInfo appOrderRefundsInfo =
+                        appOrderRefundsInfoService.getOne(Wrappers.<AppOrderRefundsInfo>lambdaQuery().eq(AppOrderRefundsInfo::getOrderId,
+                                appOrder.getId()));
+                appOrderRefundsInfo.setStatus(refundData.getStatus());
+                appOrderRefundsInfo.setSuccessTime(refundData.getSuccessTime());
+                appOrderRefundsInfoService.updateById(appOrderRefundsInfo);
+            }
+        });
+
+    }
+
+    /**
+     * 订单-小程序退款详情
+     *
+     * @param orderId
+     * @param orderProInfoId
+     * @return
+     */
+    @Operation(summary = "订单-小程序退款详情")
+    @GetMapping("/getRefundsInfo")
+    public Result<RefundsInfoDTO> getRefundsInfo(@RequestParam(name = "orderId") String orderId,
+                                                 @RequestParam(name = "orderProInfoId") String orderProInfoId) {
+        return Result.ok(appOrderRefundsInfoService.getRefundsInfo(orderId, orderProInfoId));
+    }
+
     /**
      * 订单-小程序支付后回显信息
      *

+ 74 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/dto/RefundsInfoDTO.java

@@ -0,0 +1,74 @@
+package org.jeecg.modules.app.dto;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@AllArgsConstructor
+@NoArgsConstructor
+@Schema(description = "订单详情响应对象")
+public class RefundsInfoDTO implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    @Schema(description = "id")
+    private String id;
+
+    /**
+     * 订单商品数量
+     */
+    @Schema(description = "orderNum")
+    private Integer orderNum;
+
+    /**
+     * 订单金额(元)
+     */
+    @Schema(description = "price")
+    private BigDecimal price;
+
+    /**
+     * 退款金额(元)
+     */
+    @Schema(description = "amount")
+    private BigDecimal amount;
+
+    /**
+     * 退款受理时间
+     */
+    @Schema(description = "acceptedTime")
+    private Date acceptedTime;
+
+    /**
+     * 退款成功时间
+     */
+    @Schema(description = "successTime")
+    private Date successTime;
+
+    /**
+     * 子订单退款状态
+     */
+    @Schema(description = "refundStatus")
+    private Integer refundStatus;
+
+    /**
+     * 创建时间
+     */
+    @Schema(description = "createTime")
+    private Date createTime;
+}

+ 3 - 1
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/form/PageOrdersForm.java

@@ -6,6 +6,7 @@ import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
 import org.jeecg.modules.app.dto.PageDTO;
 
+import java.io.Serial;
 import java.io.Serializable;
 
 @Data
@@ -13,12 +14,13 @@ import java.io.Serializable;
 @EqualsAndHashCode(callSuper = false)
 @Schema(description = "我的订单分页表单对象")
 public class PageOrdersForm extends PageDTO implements Serializable {
+    @Serial
     private static final long serialVersionUID = 1L;
 
     @Schema(description = "订单状态:0-待付款 1-待使用 2-已使用 3-已到期 4-已取消 5-退款中 6已退款")
     private Integer orderStatus;
 
-    @Schema(description = "是否售后 0-否 1-是",required = true)
+    @Schema(description = "是否售后 0-否 1-是")
     private Integer orAfterSale;
 
     @Schema(description = "当前用户userId",hidden = true)

+ 27 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/form/RefundOrderForm.java

@@ -0,0 +1,27 @@
+package org.jeecg.modules.app.form;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+public class RefundOrderForm implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 订单编号
+     */
+    private String orderCode;
+
+    /**
+     * 子订单ID(逗号分隔)
+     */
+    private String orderProInfoIds;
+
+    /**
+     * 退款原因
+     */
+    private String reason;
+}

+ 5 - 2
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/service/IOrderService.java

@@ -1,13 +1,16 @@
 package org.jeecg.modules.app.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
 import org.jeecg.modules.app.dto.AppOrderInfoDTO;
 import org.jeecg.modules.app.form.CreateOrderForm;
 import org.jeecg.modules.app.form.PageOrdersForm;
+import org.jeecg.modules.app.form.RefundOrderForm;
 import org.jeecg.modules.app.form.UserPayForm;
 import org.jeecg.modules.app.vo.OrderVO;
 import org.jeecg.modules.app.vo.PageOrdersVO;
 import org.jeecg.modules.app.vo.QueryOrderVerifyRecordsVO;
+import org.jeecg.modules.system.app.entity.AppOrder;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -16,7 +19,7 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
-public interface IOrderService {
+public interface IOrderService extends IService<AppOrder>{
 
     /**
      * @Author SheepHy
@@ -93,7 +96,7 @@ public interface IOrderService {
 
     IPage<PageOrdersVO> pageOrders(PageOrdersForm pageOrdersForm);
 
-    String refundOrder(String orderCode, String reason);
+    String refundOrder(RefundOrderForm refundOrderForm);
 
     Map<String, String> wechatPayNotify(HttpServletRequest request, HttpServletResponse response);
 

+ 104 - 85
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/service/WeChatPayService.java

@@ -1,5 +1,6 @@
 package org.jeecg.modules.app.service;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
@@ -7,6 +8,9 @@ import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.aliyun.oss.ServiceException;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.wechat.pay.java.core.notification.Notification;
+import com.wechat.pay.java.core.notification.RequestParam;
+import com.yomahub.liteflow.exception.IfTypeErrorException;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -26,14 +30,12 @@ import javax.crypto.NoSuchPaddingException;
 import javax.crypto.spec.GCMParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
+import java.security.*;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -214,7 +216,7 @@ public class WeChatPayService {
             //查询订单,判断是否已修改为已支付状态
             AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
             if (ObjectUtil.isNotEmpty(appOrder)) {
-                if (Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_0)){
+                if (Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_0)) {
                     appOrder.setOrderStatus(CommonConstant.ORDER_STATUS_1);
                     appOrder.setPayStatus(CommonConstant.ORDER_STATUS_1);
                     appOrder.setPayTime(new Date());
@@ -223,7 +225,7 @@ public class WeChatPayService {
                     appOrder.setPayTime(dealDateFormat(res.getString("success_time")));
 
                     List<AppOrderProInfo> proInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, appOrder.getId()));
-                    if (ObjectUtil.isNotEmpty(proInfoList)){
+                    if (ObjectUtil.isNotEmpty(proInfoList)) {
                         for (AppOrderProInfo appOrderProInfo : proInfoList) {
                             appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_1);
                             appOrderProInfoMapper.updateById(appOrderProInfo);
@@ -239,7 +241,7 @@ public class WeChatPayService {
             }
             result.put("code", "SUCCESS");
             result.put("message", "OK");
-            result.put("orderCode",orderCode);
+            result.put("orderCode", orderCode);
             return result;
         } catch (Exception e) {
             result.put("code", "FAIL");
@@ -249,6 +251,12 @@ public class WeChatPayService {
     }
 
     public void addProfitSharingInfos(AppOrder appOrder) {
+        List<AppOrderProInfo> orderProInfoList = appOrderProInfoMapper.selectList(Wrappers.lambdaQuery(AppOrderProInfo.class).eq(AppOrderProInfo::getOrderId, appOrder.getId()).eq(AppOrderProInfo::getType, CommonConstant.ORDER_PRO_INFO_TYPE_6));
+        BigDecimal insurePrice = BigDecimal.ZERO;
+        if (CollUtil.isNotEmpty(orderProInfoList)) {
+            BigDecimal reduce = orderProInfoList.stream().map(AppOrderProInfo::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
+            insurePrice = insurePrice.add(reduce);
+        }
         //创建预分账详情
         String orgCode = appOrder.getOrgCode();
         SysDepart depart = sysDepartMapper.selectOne(Wrappers.lambdaQuery(SysDepart.class).eq(SysDepart::getOrgCode, orgCode).last("limit 1"));
@@ -260,11 +268,13 @@ public class WeChatPayService {
             BigDecimal PT = separateAccounts.getPtSeparateAccounts();
             BigDecimal SH = separateAccounts.getShSeparateAccounts();
             BigDecimal MD = separateAccounts.getMdSeparateAccounts();
+            //分账金额
+            BigDecimal price = appOrder.getPrice().subtract(insurePrice);
             //微信手续费,不足1分按1分算
-            Integer FEE = RatiosUtil.amount_fee(appOrder.getPrice().multiply(new BigDecimal("0.06")).setScale(0, RoundingMode.UP));
+            Integer FEE = RatiosUtil.amount_fee(price.multiply(new BigDecimal("0.06")).setScale(0, RoundingMode.UP));
             //商户(分账给平台)
             if (depart.getSystemType() == 1) {
-                BigDecimal[] allocate = RatiosUtil.allocate(appOrder.getPrice(), new BigDecimal[]{SH, PT});
+                BigDecimal[] allocate = RatiosUtil.allocate(price, new BigDecimal[]{SH, PT});
                 //商户所得金额
                 AppProfitSharingInfo SHAppProfitSharingInfo = new AppProfitSharingInfo();
                 SHAppProfitSharingInfo.setOrderId(appOrder.getId());
@@ -283,7 +293,7 @@ public class WeChatPayService {
             }
             //门店(分账给平台及商户)
             if (depart.getSystemType() == 2) {
-                BigDecimal[] allocate = RatiosUtil.allocate(appOrder.getPrice(), MD, SH, PT);
+                BigDecimal[] allocate = RatiosUtil.allocate(price, MD, SH, PT);
                 //门店所得金额
                 AppProfitSharingInfo MDAppProfitSharingInfo = new AppProfitSharingInfo();
                 MDAppProfitSharingInfo.setOrderId(appOrder.getId());
@@ -331,98 +341,109 @@ public class WeChatPayService {
      * @param out_trade_no 发起支付时创建的商户订单号
      * @return null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败
      */
-    public JSONObject orderQueryByOutTradeNo(String out_trade_no) {
+    public JSONObject orderQueryByOutTradeNo(String out_trade_no,String subMchId) {
         String url = WechatUrlConstants.PAY_V3_QUERY_OUT;
         url = url.replace("{out_trade_no}", WXPayUtility.urlEncode(out_trade_no));
         Map<String, Object> args = new HashMap<>();
         args.put("sp_mchid", WechatConstants.WECHAT_SP_MCH_ID);
-        args.put("sub_mchid", WechatConstants.WECHAT_SUB_MCH_ID);
+        args.put("sub_mchid", subMchId);
         url = url + "?" + WXPayUtility.urlEncode(args);
         JSONObject res = wechatPayV3Utils.sendGet(url);
-        return res ;
+        return res;
     }
 
     /**
      * 申请退款
-     * @param orderCode 原订单号
+     *
+     * @param appOrder
+     * @param refundAmount
+     * @param reason
      */
-    public void refundOrder(String orderCode,String reason){
+    public void refundOrder(AppOrder appOrder, List<AppOrderProInfo> appOrderProInfoList, BigDecimal refundAmount, String reason) {
 
         log.info("进入退款接口------>");
-        log.info("执行操作的 原支付交易对应的商户订单号:{}", orderCode);
-
-        BigDecimal totalFee = BigDecimal.ZERO;
-        BigDecimal total = BigDecimal.ZERO;
-        AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
-        if (ObjectUtil.isNotEmpty(appOrder)) {
-            total = appOrder.getPrice();
-            totalFee = total;
-            List<AppOrderProInfo> proInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, appOrder.getId()).eq(AppOrderProInfo::getType, CommonConstant.ORDER_PRO_INFO_TYPE_6));
-            if (ObjectUtil.isNotEmpty(proInfoList)) {
-                for (AppOrderProInfo appOrderProInfo : proInfoList) {
-                    totalFee = totalFee.subtract(appOrderProInfo.getPrice());
-                }
-            }
-        }
+        log.info("执行操作的 原支付交易对应的商户订单号:{}", appOrder.getOrderCode());
+
+        SysDepart depart = sysDepartMapper.selectOne(Wrappers.lambdaQuery(SysDepart.class).eq(SysDepart::getOrgCode, appOrder.getOrgCode()).last("limit 1"));
+
         //退款单号
         String out_refund_no = generateOrderNumber(1);
-
         // 创建退款订单
         AppOrderRefundsInfo appOrderRefundsInfo = new AppOrderRefundsInfo();
-
         appOrderRefundsInfo.setOrderId(appOrder.getId());
-        appOrderRefundsInfo.setOrderCode(orderCode);
+        //项目只有单商品退款
+        appOrderRefundsInfo.setOrderProInfoId(appOrderProInfoList.get(0).getId());
+        appOrderRefundsInfo.setOrderCode(appOrder.getOrderCode());
         appOrderRefundsInfo.setOutRefundNo(out_refund_no);
         appOrderRefundsInfo.setTransactionId(appOrder.getTransactionId());
         appOrderRefundsInfo.setReason(reason);
-        appOrderRefundsInfo.setAmount(total);
+        appOrderRefundsInfo.setAmount(refundAmount);
         appOrderRefundsInfo.setCreateTime(new Date());
 
-        //异步发起分账回退
-        weChatProfitSharingService.returnProfitSharing(orderCode);
+        JSONObject params = new JSONObject();
+        params.put("sub_mchid",depart.getMchId());
+        params.put("out_trade_no", appOrder.getOrderCode());//商户订单号
+        params.put("out_refund_no", out_refund_no);//商户退款单号
+        params.put("reason", reason);//退款原因
+        params.put("notify_url", WechatUrlConstants.PAY_V3_REFUND_NOTIFY);//退款通知
+
+        JSONObject amount = new JSONObject();
+        amount.put("refund", RatiosUtil.amount_fee(refundAmount));//退款金额
+        amount.put("currency", "CNY");
+        amount.put("total", RatiosUtil.amount_fee(appOrder.getPrice()));//原订单金额
+        params.put("amount", amount);
+        // 执行请求POST 请求发送到微信退款接口
+        JSONObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_REFUND, params);
 
-        try {
-            JSONObject params = new JSONObject();
-            params.put("sub_mchid", WechatConstants.WECHAT_SUB_MCH_ID);
-            params.put("out_trade_no", orderCode);//商户订单号
-            params.put("out_refund_no", out_refund_no);//商户退款单号
-            params.put("reason", reason);//退款原因
-            params.put("notify_url", WechatUrlConstants.PAY_V3_REFUND_NOTIFY);//退款通知
-
-            JSONObject amount = new JSONObject();
-            amount.put("refund", (totalFee.multiply(BigDecimal.valueOf(100))).longValue());//退款金额
-            amount.put("currency", "CNY");
-            amount.put("total", (total.multiply(BigDecimal.valueOf(100))).longValue());//原订单金额
-            params.put("amount", amount);
-            // 执行请求POST 请求发送到微信退款接口
-            JSONObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_REFUND, params);
-
-            log.info("最终拿到的微信支付通知数据:" + res);
-
-            final String status = res.getString("status");
-            switch (status) {
-                case "SUCCESS":
-                    log.info("订单:{},退款成功!原因:{}",orderCode,reason);
-                    break;
-                case "CLOSED":
-                    log.info("退款关闭");
-                    break;
-                case "PROCESSING":
-                    log.info("退款处理中");
-                    break;
-                case "ABNORMAL":
-                    log.info("订单:{},退款异常",orderCode);
-                    break;
-            }
-            appOrderRefundsInfo.setRefundId(res.getString("refund_id"));
-            appOrderRefundsInfo.setSuccessTime(DateUtil.parse(res.getString("success_time"), "yyyy-MM-dd HH:mm:ss"));
-            appOrderRefundsInfo.setNotifyRequest(res.toString());
-            appOrderRefundsInfoMapper.insert(appOrderRefundsInfo);
-        } catch (Exception e) {
-            // TODO Auto-generated catch block
-            log.info(e.toString());
-            e.printStackTrace();
+        log.info("最终拿到的微信支付通知数据:" + res);
+
+        final String status = res.getString("status");
+        switch (status) {
+            case "SUCCESS":
+                log.info("订单:{},退款成功!原因:{}", appOrder.getOrderCode(), reason);
+                //修改订单状态
+                for (AppOrderProInfo appOrderProInfo : appOrderProInfoList) {
+                    appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_6);
+                    appOrderProInfo.setAfterSaleStatus(CommonConstant.NUMBER_2);
+                    appOrderProInfoMapper.updateById(appOrderProInfo);
+                }
+                break;
+            case "CLOSED":
+                log.info("退款关闭");
+                break;
+            case "PROCESSING":
+                log.info("退款处理中");
+                //修改订单状态
+                for (AppOrderProInfo appOrderProInfo : appOrderProInfoList) {
+                    appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_5);
+                    appOrderProInfo.setAfterSaleStatus(CommonConstant.NUMBER_1);
+                    appOrderProInfoMapper.updateById(appOrderProInfo);
+                }
+
+                List<AppOrderProInfo> orderProInfoList =
+                        appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, appOrder.getId()));
+                boolean isAllRefund = true;
+                for (AppOrderProInfo appOrderProInfo : orderProInfoList) {
+                    if (appOrderProInfo.getAfterSaleStatus() != 1) {
+                        isAllRefund = false;
+                        break;
+                    }
+                }
+                if (isAllRefund) {
+                    log.info("修改订单:{},支付状态为退款中", appOrder.getOrderCode());
+                    appOrder.setOrderStatus(CommonConstant.ORDER_STATUS_5);
+                    appOrderMapper.updateById(appOrder);
+                }
+                break;
+            case "ABNORMAL":
+                log.info("订单:{},退款异常", appOrder.getOrderCode());
+                break;
         }
+        appOrderRefundsInfo.setRefundId(res.getString("refund_id"));
+        appOrderRefundsInfo.setNotifyRequest(res.toString());
+        appOrderRefundsInfo.setTransactionId(res.getString("transaction_id"));
+        appOrderRefundsInfo.setAcceptedTime(new Date());
+        appOrderRefundsInfoMapper.insert(appOrderRefundsInfo);
     }
 
     /**
@@ -444,14 +465,11 @@ public class WeChatPayService {
     }
 
     public Map<String, Object> refundOrderNotify(String jsonData) throws IOException, GeneralSecurityException {
-
         //转为map格式
         Map jsonMap = JSONObject.parseObject(jsonData, Map.class);
 
         //退款成功后返回一个加密字段resource,以下为解密
-        /**
-         * 解密需要从resource参数中,获取到ciphertext,nonce,associated_data这三个参数进行解密
-         */
+        //解密需要从resource参数中,获取到ciphertext,nonce,associated_data这三个参数进行解密
         String resource = JSONObject.toJSONString(jsonMap.get("resource"));
         JSONObject object = JSONObject.parseObject(resource);
 
@@ -462,13 +480,13 @@ public class WeChatPayService {
         String resultStr = decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
         Map<String, String> reqInfo = JSONObject.parseObject(resultStr, Map.class);
 
-        log.info("微信返回的退款通知数据:" + reqInfo);
+        log.info("微信返回的退款通知数据:{}", reqInfo);
 
         String refund_status = reqInfo.get("refund_status");//退款状态
         String out_trade_no = reqInfo.get("out_trade_no"); //订单号
 
         Map<String, Object> parm = new HashMap<>();
-        if (!StringUtils.isEmpty(refund_status) && "SUCCESS".equals(refund_status))  {
+        if (!StringUtils.isEmpty(refund_status) && "SUCCESS".equals(refund_status)) {
 
             //查询订单
             AppOrder order = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, out_trade_no).last("limit 1"));
@@ -493,7 +511,7 @@ public class WeChatPayService {
             // 创建退款订单
             AppOrderRefundsInfo appOrderRefundsInfo = appOrderRefundsInfoMapper.selectOne(Wrappers.lambdaQuery(AppOrderRefundsInfo.class).eq(AppOrderRefundsInfo::getOrderId, order.getId()));
 
-            if (ObjectUtil.isNotEmpty(appOrderRefundsInfo)){
+            if (ObjectUtil.isNotEmpty(appOrderRefundsInfo)) {
                 //        appOrderRefundsInfo.setRefundId();
                 appOrderRefundsInfo.setSuccessTime(new Date());
 //        appOrderRefundsInfo.setNotifyRequest();
@@ -514,7 +532,8 @@ public class WeChatPayService {
     public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) throws GeneralSecurityException, IOException {
         try {
             Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
-            SecretKeySpec key = new SecretKeySpec(WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(), "AES");// 这里的apiV3key是你的商户APIV3密钥
+            // 这里的apiV3key是你的商户ApiV3密钥
+            SecretKeySpec key = new SecretKeySpec(WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(), "AES");
             GCMParameterSpec spec = new GCMParameterSpec(128, nonce);//规定为128
             cipher.init(Cipher.DECRYPT_MODE, key, spec);
             cipher.updateAAD(associatedData);

+ 68 - 27
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/service/impl/OrderServiceImpl.java

@@ -13,6 +13,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.gson.JsonObject;
@@ -26,10 +27,7 @@ import org.jeecg.common.exception.JeecgBootException;
 import org.jeecg.common.system.vo.LoginUser;
 import org.jeecg.common.util.DictAnnotationUtil;
 import org.jeecg.modules.app.dto.AppOrderInfoDTO;
-import org.jeecg.modules.app.form.CreateOrderForm;
-import org.jeecg.modules.app.form.InsureOrderInfoForm;
-import org.jeecg.modules.app.form.PageOrdersForm;
-import org.jeecg.modules.app.form.UserPayForm;
+import org.jeecg.modules.app.form.*;
 import org.jeecg.modules.app.service.IOrderService;
 import org.jeecg.modules.app.service.WeChatPayService;
 import org.jeecg.modules.app.vo.AppGameScheduleVO;
@@ -59,6 +57,7 @@ import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalTime;
 import java.time.ZoneId;
@@ -75,7 +74,7 @@ import static org.jeecg.modules.hikiot.HikiotTool.addUser;
 
 @Service
 @Log4j2
-public class OrderServiceImpl implements IOrderService {
+public class OrderServiceImpl extends ServiceImpl<AppOrderMapper, AppOrder> implements IOrderService {
     @Resource
     private AppSitePlaceMapper appSitePlaceMapper;
     @Resource
@@ -580,6 +579,7 @@ public class OrderServiceImpl implements IOrderService {
                             .setFamilyIds(createOrderForm.getFamilyIds())
                             .setOriginalPrice(priceRule.getSellingPrice())
                             .setAddressSiteId(appSite.getId())
+                            .setOrProfitSharing(0)
                             .setCreateTime(new Date())
                             .setUpdateTime(new Date())
                             .setCreateBy(user.getId())
@@ -1000,10 +1000,12 @@ public class OrderServiceImpl implements IOrderService {
                             //试听优惠
                             appOrder.setSDiscounts(sDiscounts);
                             appOrder.setOrderOrFree(CommonConstant.STATUS_1_INT);
+                            appOrder.setOrProfitSharing(0);
                         }
                     }
                     proInfoList.add(appOrderProInfo);
 
+
                     tDiscounts = tDiscounts.add(appCourse.getOriginalPrice().subtract(appCourse.getSellingPrice()));
                     if (createOrderForm.getOrFreeOrder() == 1) {
                         tDiscounts = BigDecimal.ZERO;
@@ -1195,8 +1197,8 @@ public class OrderServiceImpl implements IOrderService {
         if ((ObjectUtil.isNotEmpty(appOrder.getOrderOrFree()) && appOrder.getOrderOrFree() == 1) || appOrder.getPrice().compareTo(BigDecimal.ZERO)== 0) {
             payForm.setOrPayOrder(0);
         } else {
-//            Map<String, String> result = payment(appOrder.getId());
-//            payForm.setParams(result);
+            Map<String, String> result = payment(appOrder.getId());
+            payForm.setParams(result);
 
             //发布任务到redission延迟队列(16分钟)
             String task = CommonConstant.ORDER_TIME_OUT_TASK_PREFIX + appOrder.getId();
@@ -1207,7 +1209,9 @@ public class OrderServiceImpl implements IOrderService {
 
     public Map<String, String> payment(String appOrderId){
         AppOrder appOrder = appOrderMapper.selectById(appOrderId);
-
+        //查询商户信息
+        String orgCode = appOrder.getOrgCode();
+        SysDepart depart = sysDepartMapper.selectOne(Wrappers.lambdaQuery(SysDepart.class).eq(SysDepart::getOrgCode, orgCode).last("limit 1"));
         if (Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_4)){
             throw new JeecgBootException("当前订单已过期,请重新下单!");
         }
@@ -1226,7 +1230,7 @@ public class OrderServiceImpl implements IOrderService {
         params.put("sp_appid", WechatConstants.WECHAT_SP_APPID); //服务商appid
         params.put("sp_mchid", WechatConstants.WECHAT_SP_MCH_ID);//服务商商户号
         params.put("sub_appid", WechatConstants.WECHAT_SUB_APPID); //小程序appid
-        params.put("sub_mchid", WechatConstants.WECHAT_SUB_MCH_ID); //特约商户商户号
+        params.put("sub_mchid", depart.getMchId()); //特约商户商户号
 
         params.put("description", "全龄运动"); //商品描述
         params.put("out_trade_no", appOrder.getOrderCode()); //商户订单号
@@ -1697,11 +1701,14 @@ public class OrderServiceImpl implements IOrderService {
 
         //查询订单
         AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
+        //查询订单子商户号
+        String orgCode = appOrder.getOrgCode();
+        SysDepart depart = sysDepartMapper.selectOne(Wrappers.lambdaQuery(SysDepart.class).eq(SysDepart::getOrgCode, orgCode).last("limit 1"));
 
         //null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败
-//        JSONObject res = weChatPayService.orderQueryByOutTradeNo(orderCode);
-//        String s = res == null ? null : res.getString("trade_state");
-        String s = "SUCCESS";
+        JSONObject res = weChatPayService.orderQueryByOutTradeNo(orderCode,depart.getMchId());
+        String s = res == null ? null : res.getString("trade_state");
+//        String s = "SUCCESS";
         if ("SUCCESS".equals(s) || appOrder.getOriginalPrice().compareTo(BigDecimal.ZERO)==0) {
 
             if (ObjectUtil.isNotEmpty(appOrder) && Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_0)) {
@@ -1744,7 +1751,7 @@ public class OrderServiceImpl implements IOrderService {
                 appOrderMapper.updateById(appOrder);
                 List<AppOrderProInfo> appOrderProInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, orderId));
                 for (AppOrderProInfo appOrderProInfo : appOrderProInfoList) {
-                    appOrderProInfo.setOrderStatus(2);
+                    appOrderProInfo.setOrderStatus(4);
                     appOrderProInfoMapper.updateById(appOrderProInfo);
                 }
             }
@@ -1818,23 +1825,57 @@ public class OrderServiceImpl implements IOrderService {
     }
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public String refundOrder(String orderCode, String reason){
-        AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
+    public String refundOrder(RefundOrderForm refundOrderForm){
+        AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, refundOrderForm.getOrderCode()).last("limit 1"));
+        //退款金额
+        BigDecimal refundAmount = BigDecimal.ZERO;
+        List<String> refundOrderProInfoIds = new ArrayList<>(Arrays.asList(refundOrderForm.getOrderProInfoIds().split(",")));
+        List<AppOrderProInfo> appOrderProInfoList = new ArrayList<>();
         if (ObjectUtil.isNotEmpty(appOrder)) {
-            if (appOrder.getOrderStatus() == 0) {
-                log.info("修改订单:{},支付状态为退款中", orderCode);
-                appOrder.setOrderStatus(5);
-                appOrderMapper.updateById(appOrder);
-                List<AppOrderProInfo> appOrderProInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, appOrder.getId()));
-                for (AppOrderProInfo appOrderProInfo : appOrderProInfoList) {
-                    appOrderProInfo.setOrderStatus(3);
-                    appOrderProInfo.setAfterSaleStatus(1);
-                    appOrderProInfoMapper.updateById(appOrderProInfo);
+            if (appOrder.getProfitSharingStatus() > CommonConstant.NUMBER_0) {
+                throw new JeecgBootException("当前订单无法进行退款,请联系客服处理!");
+            }
+            if(!Objects.equals(appOrder.getOrderType(), CommonConstant.ORDER_PRO_INFO_TYPE_1) && !appOrder.getOrderType().equals(CommonConstant.ORDER_PRO_INFO_TYPE_2)){
+                throw new JeecgBootException("当前订单类型不支持退款!");
+            }
+            if (Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_2)) {
+                throw new JeecgBootException("订单商品已使用,无法进行退款!");
+            }
+            //通过订单查询门店退款规则,判断是否可以进行退款
+            String s = appOrder.getProductIds().split(",")[0];
+            String priceRuleId = s.split("\\|")[0];
+            AppSitePriceRules appSitePriceRules =
+                    appSitePriceRulesMapper.selectOne(Wrappers.<AppSitePriceRules>lambdaQuery().eq(AppSitePriceRules::getId, priceRuleId).last("limit 1"));
+            String sitePlaceId = appSitePriceRules.getSitePlaceId();
+            AppSitePlace appSitePlace = appSitePlaceMapper.selectById(sitePlaceId);
+            if (Objects.equals(appOrder.getOrderType(), CommonConstant.ORDER_PRO_INFO_TYPE_2) && Objects.equals(appSitePlace.getRefundType(),
+                    CommonConstant.NUMBER_2)) {
+                throw new JeecgBootException("当前门店不支持退款!");
+            }
+            if (Objects.equals(appSitePlace.getRefundType(), CommonConstant.NUMBER_1)) {
+
+                for (String refundOrderProInfoId : refundOrderProInfoIds) {
+                    AppOrderProInfo appOrderProInfo = appOrderProInfoMapper.selectById(refundOrderProInfoId);
+                    String productId = appOrderProInfo.getProductId();
+                    AppSitePriceRules sitePriceRules = appSitePriceRulesMapper.selectById(productId);
+                    Instant originalInstant  = sitePriceRules.getStartTime().toInstant();
+                    Instant time = originalInstant.minusSeconds(appSitePlace.getEarlyRefundTime() * 60);
+                    Instant now = Instant.now();
+                    if (!now.isBefore(time)) {
+                        throw new JeecgBootException("商品:"+ appOrderProInfo.getProductName() +" 已超过可退时间,无法进行退款!");
+                    }
                 }
             }
+
+            //退款金额统计(不为保险的子订单)
+            for (String refundOrderProInfoId : refundOrderProInfoIds) {
+                AppOrderProInfo appOrderProInfo = appOrderProInfoMapper.selectById(refundOrderProInfoId);
+                refundAmount = refundAmount.add(appOrderProInfo.getPrice());
+                appOrderProInfoList.add(appOrderProInfo);
+            }
         }
         //调用退款逻辑
-        weChatPayService.refundOrder(orderCode, reason);
+        weChatPayService.refundOrder(appOrder,appOrderProInfoList,refundAmount, refundOrderForm.getReason());
 
         return "申请成功,预计在3个工作日内处理!";
     }
@@ -1939,10 +1980,10 @@ public class OrderServiceImpl implements IOrderService {
                 throw new JeecgBootException("当前订单已过期取消,请重新下单!");
             }
         }
-//        Map<String, String> payment = payment(appOrderId);
+        Map<String, String> payment = payment(appOrderId);
         payForm.setOrderId(appOrderId)
                 .setOrderCode(appOrder.getOrderCode())
-//                .setParams(payment)
+                .setParams(payment)
         ;
         return payForm;
     }

+ 79 - 1
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WeChatProfitSharingService.java

@@ -1,16 +1,22 @@
 package org.jeecg.modules.pay.config;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.common.constant.CommonConstant;
 import org.jeecg.modules.pay.entity.ProfitSharingRequest;
 import org.jeecg.modules.pay.entity.Receiver;
 import org.jeecg.modules.pay.entity.ReceiverAddForm;
+import org.jeecg.modules.pay.entity.WxchatCallbackRefundData;
 import org.jeecg.modules.system.app.entity.AppOrder;
+import org.jeecg.modules.system.app.entity.AppOrderProInfo;
 import org.jeecg.modules.system.app.entity.AppProfitSharingInfo;
 import org.jeecg.modules.system.app.entity.AppProfitSharingRecords;
 import org.jeecg.modules.system.app.mapper.AppOrderMapper;
+import org.jeecg.modules.system.app.mapper.AppOrderProInfoMapper;
 import org.jeecg.modules.system.app.mapper.AppProfitSharingInfoMapper;
 import org.jeecg.modules.system.app.mapper.AppProfitSharingRecordsMapper;
 import org.springframework.core.io.ClassPathResource;
@@ -21,8 +27,11 @@ import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.InputStream;
+import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
@@ -42,6 +51,9 @@ public class WeChatProfitSharingService {
     @Resource
     private AppOrderMapper appOrderMapper;
 
+    @Resource
+    private AppOrderProInfoMapper appOrderProInfoMapper;
+
     @Resource
     private AppProfitSharingRecordsMapper appProfitSharingRecordsMapper;
 
@@ -79,11 +91,17 @@ public class WeChatProfitSharingService {
     public JSONObject profitSharing(String orderCode) throws Exception {
         AppOrder appOrder = appOrderMapper.selectOne(Wrappers.lambdaQuery(AppOrder.class).eq(AppOrder::getOrderCode, orderCode).last(
                 "limit 1"));
+        List<AppOrderProInfo> orderProInfoList = appOrderProInfoMapper.selectList(Wrappers.lambdaQuery(AppOrderProInfo.class).eq(AppOrderProInfo::getOrderId, appOrder.getId()).eq(AppOrderProInfo::getType, CommonConstant.ORDER_PRO_INFO_TYPE_6));
+        BigDecimal insurePrice = BigDecimal.ZERO;
+        if (CollUtil.isNotEmpty(orderProInfoList)){
+            BigDecimal reduce = orderProInfoList.stream().map(AppOrderProInfo::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
+            insurePrice = insurePrice.add(reduce);
+        }
         //订单预分账详情列表
         List<AppProfitSharingInfo> appProfitSharingInfos = appProfitSharingInfoMapper.selectList(Wrappers.lambdaQuery(AppProfitSharingInfo.class).eq(AppProfitSharingInfo::getOrderId,
                 appOrder.getId()));
 
-        Integer orderAmount = RatiosUtil.amount_fee(appOrder.getPrice());
+        Integer orderAmount = RatiosUtil.amount_fee(appOrder.getPrice().subtract(insurePrice));
         int amount = 0;
         for (AppProfitSharingInfo appProfitSharingInfo : appProfitSharingInfos) {
             amount = amount + appProfitSharingInfo.getPreAmount();
@@ -93,6 +111,7 @@ public class WeChatProfitSharingService {
         AppProfitSharingRecords appProfitSharingRecords = new AppProfitSharingRecords();
         appProfitSharingRecords.setProfitSharingNo("P" + orderCode);
         appProfitSharingRecords.setOrderId(appOrder.getId());
+        appProfitSharingRecords.setPrice(RatiosUtil.amount_fee(appOrder.getPrice()));
         appProfitSharingRecords.setOrderAmount(orderAmount);
         appProfitSharingRecords.setAmount(amount);
         appProfitSharingRecords.setProcedureFee(orderAmount - amount);
@@ -120,6 +139,7 @@ public class WeChatProfitSharingService {
             receiversAdd(receiverAddForm);
             sleep(1000);
         }
+        //构建分账参数
         ProfitSharingRequest profitSharingRequest = new ProfitSharingRequest();
         profitSharingRequest
                 .setAppid(WechatConstants.WECHAT_SP_APPID)
@@ -300,6 +320,64 @@ public class WeChatProfitSharingService {
         return null;
     }
 
+    /**
+     * 微信支付申请退款回调方法
+     *
+     * @param refundCallback 回调方法,用于处理业务逻辑,包含退款成功处理于退款失败处理
+     * @return json格式的string数据,直接返回给微信
+     */
+    public Map<String, Object> wxPayRefundCallback(HttpServletRequest request, HttpServletResponse response, WechatRefundCallback refundCallback) {
+
+        Map<String, Object> result = new HashMap<>();
+        // 2.签名验证
+        //验签及解析返回数据
+        JSONObject res = wechatPayV3Utils.getCallbackData(request);
+        if (res == null) {
+            result.put("code", "FAIL");
+            result.put("message", "失败");
+            return result;
+        }
+        log.info("最终拿到的微信支付通知数据:" + res);
+
+        // 4.封装微信返回的数据
+        WechatCallbackRefundData refundData = getRefundCallbackData(res);
+        if ("SUCCESS".equals(refundData.getStatus())) {
+            // 执行业务逻辑
+            refundCallback.success(refundData);
+        } else {
+            // 特殊情况退款失败业务处理,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款
+            refundCallback.fail(refundData);
+        }
+
+        // 5.成功应答
+        response.setStatus(200);
+        result.put("code", "SUCCESS");
+        result.put("message", "成功");
+        return result;
+    }
+
+    private static WechatCallbackRefundData getRefundCallbackData(JSONObject res) {
+        WechatCallbackRefundData refundData = new WechatCallbackRefundData();
+        String successTime = String.valueOf(res.get("success_time"));
+        if (StringUtils.isNoneBlank(successTime)) {
+            refundData.setSuccessTime(successTime);
+        }
+        refundData.setOrderNo(String.valueOf(res.get("out_trade_no")));
+        refundData.setRefundId(String.valueOf(res.get("out_refund_no")));
+        refundData.setTransactionId(String.valueOf(res.get("transaction_id")));
+        refundData.setTransactionRefundId(String.valueOf(res.get("refund_id")));
+        refundData.setChannel(String.valueOf(res.get("channel")));
+        final String status = String.valueOf(res.get("refund_status"));
+        refundData.setStatus(status);
+        String amount = String.valueOf(res.get("amount"));
+
+        Map<String, Object> amountMap = JSONObject.parseObject(amount, HashMap.class);
+        String refundMoney = String.valueOf(amountMap.get("refund"));
+        refundData.setRefundMoney(new BigDecimal(refundMoney).movePointLeft(2));
+        log.info("refundData:{}", refundData);
+        return refundData;
+    }
+
     /**
      * 获取证书
      *

+ 74 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatCallbackRefundData.java

@@ -0,0 +1,74 @@
+package org.jeecg.modules.pay.config;
+
+import cn.hutool.core.date.DateUtil;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@Slf4j
+public class WechatCallbackRefundData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 商户订单号
+     */
+    private String orderNo;
+
+
+    /**
+     * 商户退款单号,out_refund_no
+     */
+    private String refundId;
+
+    /**
+     * 微信支付系统生成的订单号
+     */
+    private String transactionId;
+
+    /**
+     * 微信支付系统生成的退款订单号
+     */
+    private String transactionRefundId;
+
+    /**
+     * 退款渠道
+     * ORIGINAL:原路退款
+     * BALANCE:退回到余额
+     * OTHER_BALANCE:原账户异常退到其他余额账户
+     * OTHER_BANKCARD:原银行卡异常退到其他银行卡
+     */
+    private String 	channel;
+
+    /**
+     * 退款成功时间
+     * 当前退款成功时才有此返回值
+     */
+    private Date successTime;
+
+    /**
+     * 退款状态
+     * 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。
+     * SUCCESS:退款成功
+     * CLOSED:退款关闭
+     * PROCESSING:退款处理中
+     * ABNORMAL:退款异常
+     */
+    private String status;
+
+    /**
+     * 退款金额
+     */
+    private BigDecimal refundMoney;
+
+    public void setSuccessTime(String successTime) {
+        // Hutool工具包的方法,自动识别一些常用格式的日期字符串
+        this.successTime = DateUtil.parse(successTime);
+    }
+}

+ 1 - 1
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatConstants.java

@@ -16,7 +16,7 @@ public class WechatConstants {
 
     public static final String WECHAT_SUB_APPID = "wxc032a09413289004";
 
-    public static final String WECHAT_SUB_MCH_NAME = "中数海南(广州)";
+    public static final String WECHAT_SUB_MCH_NAME = "中数未来(广州)信息技术有限公司";
 
     public static final String SUB_ORG_CODE = "A01";
 

+ 1 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatPayV3Utils.java

@@ -20,6 +20,7 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.util.EntityUtils;
 import org.jeecg.modules.pay.paytest.SignUtils;
 import org.springframework.core.io.ClassPathResource;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
 import javax.servlet.http.HttpServletRequest;

+ 20 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatRefundCallback.java

@@ -0,0 +1,20 @@
+package org.jeecg.modules.pay.config;
+
+import org.jeecg.modules.pay.entity.WxchatCallbackRefundData;
+
+/**
+ * 退款处理接口,为了防止项目开发人员,不手动判断退款失败的情况
+ * 退款失败:退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款
+ */
+public interface WechatRefundCallback {
+
+    /**
+     * 退款成功处理情况
+     */
+    void success(WechatCallbackRefundData refundData);
+
+    /**
+     * 退款失败处理情况
+     */
+    void fail(WechatCallbackRefundData refundData);
+}

+ 2 - 2
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatUrlConstants.java

@@ -17,10 +17,10 @@ public class WechatUrlConstants {
     public final static String PAY_V3_QUERY_OUT = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/{out_trade_no}";
 
     //微信支付v3 支付通知接口地址
-    public final static String PAY_V3_NOTIFY = "https://37bfd286.r28.cpolar.top/jeecg-boot/app/order/wechatPayNotify";
+    public final static String PAY_V3_NOTIFY = "https://51fdfdc4.r28.cpolar.top/jeecg-boot/app/order/wechatPayNotify";
 
     //微信支付v3 退款通知接口地址
-    public final static String PAY_V3_REFUND_NOTIFY = "https://37bfd286.r28.cpolar.top/jeecg-boot/app/order/refundOrderNotify";
+    public final static String PAY_V3_REFUND_NOTIFY = "https://51fdfdc4.r28.cpolar.top/jeecg-boot/app/order/callback/refundOrderNotify";
 
     //服务商 添加分账接收方
     public final static String PAY_V3_RECEIVERS_ADD = "https://api.mch.weixin.qq.com/v3/profitsharing/receivers/add";

+ 1 - 1
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/entity/AppOrderProInfo.java

@@ -77,7 +77,7 @@ public class AppOrderProInfo implements Serializable{
     private BigDecimal price;
     @Schema(description = "0-待付款 1-待使用 2-已使用 3-已到期 4-已取消 5-退款中 6已退款")
     private Integer orderStatus;
-    @Schema(description = "售后状态")
+    @Schema(description = "售后状态:0-暂无售后 1-待退款 2-已退款")
     private Integer afterSaleStatus;
     @Schema(description = "数量")
     private Integer quantity;

+ 18 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/entity/AppOrderRefundsInfo.java

@@ -33,6 +33,12 @@ public class AppOrderRefundsInfo implements Serializable {
     @Schema(description = "orderId")
     private String orderId;
 
+    /**
+     * 子订单ID
+     */
+    @Schema(description = "orderProInfoId")
+    private String orderProInfoId;
+
     /**
      * 商户订单号
      */
@@ -65,6 +71,12 @@ public class AppOrderRefundsInfo implements Serializable {
     @Schema(description = "reason")
     private String reason;
 
+    /**
+     * 退款受理时间
+     */
+    @Schema(description = "acceptedTime")
+    private Date acceptedTime;
+
     /**
      * 退款成功时间
      */
@@ -101,4 +113,10 @@ public class AppOrderRefundsInfo implements Serializable {
     @Schema(description = "updateTime")
     private Date updateTime;
 
+    /**
+     * 逻辑删除标识
+     */
+    @Schema(description = "delFlag")
+    private Integer delFlag;
+
 }

+ 8 - 2
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/entity/AppProfitSharingRecords.java

@@ -53,9 +53,15 @@ public class AppProfitSharingRecords implements Serializable {
     private String profitSharingOrderId;
 
     /**
-     * 订单金额(分)
+     * 订单支付金额(分)
      */
-    @Schema(description = "订单原金额(分)")
+    @Schema(description = "订单支付金额(分)")
+    private Integer price;
+
+    /**
+     * 订单预分账金额(分)
+     */
+    @Schema(description = "订单预分账金额(分)")
     private Integer orderAmount;
 
     /**

+ 2 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/mapper/AppOrderMapper.java

@@ -3,6 +3,7 @@ package org.jeecg.modules.system.app.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 import org.jeecg.modules.app.form.PageOrdersForm;
 import org.jeecg.modules.app.vo.PageOrdersVO;
@@ -27,6 +28,7 @@ import java.util.List;
  * @Date:   2025-07-03
  * @Version: V1.0
  */
+@Mapper
 public interface AppOrderMapper extends BaseMapper<AppOrder> {
 
     Page<OrderPageVO> queryPage(Page<OrderPageVO> page, @Param("appOrderPageForm") AppOrderPageForm appOrderPageForm);

+ 21 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/mapper/AppOrderRefundsInfoMapper.java

@@ -2,9 +2,30 @@ package org.jeecg.modules.system.app.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.jeecg.modules.app.dto.RefundsInfoDTO;
 import org.jeecg.modules.system.app.entity.AppOrderProInfo;
 import org.jeecg.modules.system.app.entity.AppOrderRefundsInfo;
 
 @Mapper
 public interface AppOrderRefundsInfoMapper extends BaseMapper<AppOrderRefundsInfo> {
+    @Select("SELECT\n" +
+            "\tori.id,\n" +
+            "\to.amount as orderNum,\n" +
+            "\to.price,\n" +
+            "\tori.amount,\n" +
+            "\topi.after_sale_status as refundStatus,\n" +
+            "\tori.create_time,\n" +
+            "\tori.accepted_time,\n" +
+            "\tori.success_time\n" +
+            "\t\n" +
+            "FROM\n" +
+            "\t`nm_order_refunds_info` ori\n" +
+            "\tLEFT JOIN nm_order o ON o.id  = ori.order_id \n" +
+            "\tLEFT JOIN nm_order_pro_info opi ON opi.id = ori.order_pro_info_id \n" +
+            "WHERE \n" +
+            "\tori.del_flag = 0 AND o.del_flag = 0 AND opi.del_flag = 0\n" +
+            "\tAND ori.order_id = #{orderId} AND ori.order_pro_info_id = #{orderProInfoId}")
+    RefundsInfoDTO getRefundsInfo(@Param("orderId") String orderId, @Param("orderProInfoId") String orderProInfoId);
 }

+ 2 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/service/IAppOrderRefundsInfoService.java

@@ -1,8 +1,10 @@
 package org.jeecg.modules.system.app.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.jeecg.modules.app.dto.RefundsInfoDTO;
 import org.jeecg.modules.system.app.entity.AppOrderProInfo;
 import org.jeecg.modules.system.app.entity.AppOrderRefundsInfo;
 
 public interface IAppOrderRefundsInfoService extends IService<AppOrderRefundsInfo> {
+    RefundsInfoDTO getRefundsInfo(String orderId, String orderProInfoId);
 }

+ 5 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/service/impl/AppOrderRefundsInfoServiceimpl.java

@@ -2,6 +2,7 @@ package org.jeecg.modules.system.app.service.impl;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.apache.ibatis.annotations.Mapper;
+import org.jeecg.modules.app.dto.RefundsInfoDTO;
 import org.jeecg.modules.system.app.entity.AppOrderProInfo;
 import org.jeecg.modules.system.app.entity.AppOrderRefundsInfo;
 import org.jeecg.modules.system.app.mapper.AppOrderProInfoMapper;
@@ -12,4 +13,8 @@ import org.springframework.stereotype.Service;
 
 @Service
 public class AppOrderRefundsInfoServiceimpl extends ServiceImpl<AppOrderRefundsInfoMapper, AppOrderRefundsInfo> implements IAppOrderRefundsInfoService {
+    @Override
+    public RefundsInfoDTO getRefundsInfo(String orderId, String orderProInfoId) {
+        return baseMapper.getRefundsInfo(orderId, orderProInfoId);
+    }
 }

+ 21 - 21
national-motion-module-system/national-motion-system-start/src/main/resources/application-dev.yml

@@ -352,27 +352,27 @@ wx:
         aesKey: #微信小程序消息服务器配置的EncodingAESKey
         msgDataFormat: JSON
 # 微信支付配置
-  pay:
-    #服务商微信支付商户号
-    mchId: 1649629687
-    #证书
-    keyPath: classpath:cert/apiclient_cert.p12
-    #服务商微信公众号或者小程序等的appid
-    appId: wx43b5b906cc30ed0b
-    #支付回调通知地址
-    notifyUrl:
-    #服务商key的密钥
-    mchKey:
-    #特约商户微信公众号或者小程序等的appid
-    subAppId:
-    #apiV3秘钥
-    apiV3Key: 4b64e17419689527b256f07cdf6bd60c
-    #证书号
-    certSerialNo: 65E9559D81ADA0BDA0CD3CF484A59A8DFB5610BE
-    #apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径
-    privateKeyPath: classpath:cert/apiclient_key.pem
-    #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
-    privateCertPath: classpath:cert/apiclient_cert.pem
+#  pay:
+#    #服务商微信支付商户号
+#    mchId: 1649629687
+#    #证书
+#    keyPath: classpath:cert/apiclient_cert.p12
+#    #服务商微信公众号或者小程序等的appid
+#    appId: wx43b5b906cc30ed0b
+#    #支付回调通知地址
+#    notifyUrl:
+#    #服务商key的密钥
+#    mchKey:
+#    #特约商户微信公众号或者小程序等的appid
+#    subAppId:
+#    #apiV3秘钥
+#    apiV3Key: 4b64e17419689527b256f07cdf6bd60c
+#    #证书号
+#    certSerialNo: 65E9559D81ADA0BDA0CD3CF484A59A8DFB5610BE
+#    #apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径
+#    privateKeyPath: classpath:cert/apiclient_key.pem
+#    #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
+#    privateCertPath: classpath:cert/apiclient_cert.pem
 
 baidu:
   map:

+ 62 - 0
updateSql.md

@@ -44,3 +44,65 @@ nm_order增加字段:
 ALTER TABLE nm_order ADD profit_sharing_status tinyint(1) COMMENT '分账状态(0-待分账 1-已分账 2-分账回退)' AFTER or_profit_sharing;
 ALTER TABLE nm_order ADD profit_sharing_order_id varchar(64) COMMENT '微信分账单号' AFTER transaction_id;
 
+
+-----------------------------------------------------------------------------
+2025-9-30
+通过建表语句直接修改表结构:(utf8mb4_unicode_ci ---> utf8mb4_general_ci)
+
+CREATE TABLE `nm_order_refunds_info` (
+`id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键ID',
+`order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '订单ID',
+`order_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '商户订单号',
+`order_pro_info_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '退款子订单ID',
+`out_refund_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '商户退款单号',
+`refund_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信支付退款单号',
+`transaction_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信支付订单号',
+`reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '退款原因',
+`accepted_time` datetime DEFAULT NULL COMMENT '退款受理时间',
+`success_time` datetime DEFAULT NULL COMMENT '退款成功时间',
+`amount` decimal(10,2) DEFAULT NULL COMMENT '退款金额(元)',
+`notify_request` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '微信退款回调参数',
+`status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '退款状态',
+`create_time` datetime DEFAULT NULL COMMENT '创建时间',
+`update_time` datetime DEFAULT NULL COMMENT '修改时间',
+`del_flag` int DEFAULT '0' COMMENT '逻辑删除标识',
+PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='退款订单表';
+
+CREATE TABLE `nm_profit_sharing_info` (
+`id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键ID',
+`profit_sharing_records_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分账记录ID',
+`order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '订单ID',
+`org_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '分账接收方所属组织',
+`mch_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '商户号',
+`mch_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '商户全称',
+`type` int DEFAULT NULL COMMENT '账户类型(1-出资方 2-接收方)',
+`ratio` decimal(10,2) DEFAULT '0.00' COMMENT '分账比例',
+`pre_amount` int NOT NULL COMMENT '分账金额(分)',
+`amount` int DEFAULT NULL COMMENT '实际分账金额(分)',
+`status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分账状态',
+`description` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '描述',
+`create_time` datetime DEFAULT NULL COMMENT '创建时间',
+`update_time` datetime DEFAULT NULL COMMENT '修改时间',
+`del_flag` int DEFAULT '0' COMMENT '逻辑删除标识(0-正常,1-已删除)',
+PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='分账详情表';
+
+CREATE TABLE `nm_profit_sharing_records` (
+`id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键ID',
+`profit_sharing_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '分账单号',
+`order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '订单ID',
+`transaction_id` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信交易流水号',
+`profit_sharing_order_id` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信分账单号',
+`price` int DEFAULT NULL COMMENT '订单支付金额',
+`order_amount` int NOT NULL COMMENT '订单预分账金额(分)',
+`amount` int DEFAULT NULL COMMENT '分账金额(分)',
+`procedure_fee` int DEFAULT NULL COMMENT '微信分账手续费(分)0.06%',
+`status` int DEFAULT NULL COMMENT '分账状态(0-处理中 1-分账完成 2-分账失败)',
+`fail_reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分账失败原因',
+`profit_sharing_time` datetime DEFAULT NULL COMMENT '分账时间',
+`create_time` datetime DEFAULT NULL COMMENT '创建时间',
+`update_time` datetime DEFAULT NULL COMMENT '修改时间',
+`del_flag` int DEFAULT '0' COMMENT '逻辑删除标识(0-正常,1-已删除)',
+PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='分账记录表';