Browse Source

feat(order): 调整订单支付逻辑与商户信息字段类型- 注释掉订单支付参数获取逻辑,暂不调用支付接口- 模拟微信支付查询结果为成功状态,跳过实际查询
- 注释掉支付参数设置逻辑,延迟支付处理
- 修改 sys_depart 表中 system_type 字段类型为 Integer
-为 sys_depart 实体类增加商户相关字段:mchName、mchId、appid
-为 nm_order 表增加微信分账单号字段 profit_sharing_order_id
- 更新 sys_depart 表 system_type 字段类型为 int 类型- 增加商户全称、商户号、appid 等微信支付相关字段定义

wzq 3 tuần trước cách đây
mục cha
commit
1a74937e85
12 tập tin đã thay đổi với 728 bổ sung176 xóa
  1. 107 19
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/service/WeChatPayService.java
  2. 3 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/service/impl/OrderServiceImpl.java
  3. 101 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/RatiosUtil.java
  4. 348 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WeChatProfitSharingService.java
  5. 4 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatConstants.java
  6. 21 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatReceiver.java
  7. 1 10
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/paytest/payController.java
  8. 0 134
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/routing/WeChatProfitSharingService.java
  9. 108 11
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/unionPay/SM3Util.java
  10. 13 1
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/entity/AppOrder.java
  11. 11 1
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/entity/AppProfitSharingInfo.java
  12. 11 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/entity/AppProfitSharingRecords.java

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

@@ -11,16 +11,13 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.jeecg.common.constant.CommonConstant;
-import org.jeecg.modules.pay.config.WechatConstants;
-import org.jeecg.modules.pay.config.WechatPayV3Utils;
-import org.jeecg.modules.pay.config.WechatUrlConstants;
+import org.jeecg.modules.pay.config.*;
 import org.jeecg.modules.pay.serverPay.WXPayUtility;
-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.entity.*;
+import org.jeecg.modules.system.app.mapper.*;
+import org.jeecg.modules.system.entity.SysDepart;
+import org.jeecg.modules.system.mapper.SysDepartMapper;
+import org.jetbrains.annotations.NotNull;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
@@ -31,7 +28,7 @@ import javax.crypto.spec.SecretKeySpec;
 import javax.servlet.http.HttpServletRequest;
 import java.io.IOException;
 import java.math.BigDecimal;
-import java.math.BigInteger;
+import java.math.RoundingMode;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
 import java.security.InvalidAlgorithmParameterException;
@@ -64,6 +61,18 @@ public class WeChatPayService {
     @Resource
     private AppOrderRefundsInfoMapper appOrderRefundsInfoMapper;
 
+    @Resource
+    private SeparateAccountsMapper separateAccountsMapper;
+
+    @Resource
+    private SysDepartMapper sysDepartMapper;
+
+    @Resource
+    private AppProfitSharingInfoMapper appProfitSharingInfoMapper;
+
+    @Resource
+    private WeChatProfitSharingService weChatProfitSharingService;
+
 
     /**
      * 小程序支付拉起
@@ -163,7 +172,7 @@ public class WeChatPayService {
                 if (ObjectUtil.isNotEmpty(jsonObject.getString("amount"))) {
                     discounts =
                             discounts.add(new BigDecimal(jsonObject.getString("amount")).divide(new BigDecimal("100")).setScale(2,
-                                    BigDecimal.ROUND_HALF_UP));
+                                    RoundingMode.HALF_UP));
                 }
 
                 if (ObjectUtil.isNotEmpty(jsonObject.getString("name"))) {
@@ -223,6 +232,10 @@ public class WeChatPayService {
                 }
                 appOrder.setCallbackStatus(CommonConstant.STATUS_1_INT);
                 appOrderMapper.updateById(appOrder);
+                //创建预分账详情
+                if (appOrder.getOrProfitSharing() == 1) {
+                    addProfitSharingInfos(appOrder);
+                }
             }
             result.put("code", "SUCCESS");
             result.put("message", "OK");
@@ -235,6 +248,83 @@ public class WeChatPayService {
         }
     }
 
+    public void addProfitSharingInfos(AppOrder appOrder) {
+        //创建预分账详情
+        String orgCode = appOrder.getOrgCode();
+        SysDepart depart = sysDepartMapper.selectOne(Wrappers.lambdaQuery(SysDepart.class).eq(SysDepart::getOrgCode, orgCode).last("limit 1"));
+
+        //平台(不进行分账)
+        if (depart.getSystemType() != 0) {
+            SeparateAccounts separateAccounts = separateAccountsMapper.selectOne(Wrappers.lambdaQuery(SeparateAccounts.class).eq(SeparateAccounts::getDeptId, depart.getId()));
+            //获取分账比例
+            BigDecimal PT = separateAccounts.getPtSeparateAccounts();
+            BigDecimal SH = separateAccounts.getShSeparateAccounts();
+            BigDecimal MD = separateAccounts.getMdSeparateAccounts();
+            //微信手续费,不足1分按1分算
+            Integer FEE = RatiosUtil.amount_fee(appOrder.getPrice().multiply(new BigDecimal("0.06")).setScale(0, RoundingMode.UP));
+            //商户(分账给平台)
+            if (depart.getSystemType() == 1) {
+                BigDecimal[] allocate = RatiosUtil.allocate(appOrder.getPrice(), new BigDecimal[]{SH, PT});
+                //商户所得金额
+                AppProfitSharingInfo SHAppProfitSharingInfo = new AppProfitSharingInfo();
+                SHAppProfitSharingInfo.setOrderId(appOrder.getId());
+                SHAppProfitSharingInfo.setOrgCode(orgCode);
+                SHAppProfitSharingInfo.setMchId(depart.getMchId());
+                SHAppProfitSharingInfo.setMchName(depart.getMchName());
+                SHAppProfitSharingInfo.setType(1);
+                SHAppProfitSharingInfo.setRatio(SH);
+                SHAppProfitSharingInfo.setPreAmount(RatiosUtil.amount_fee(allocate[0]));
+                appProfitSharingInfoMapper.insert(SHAppProfitSharingInfo);
+                //平台所得金额(金额*比例-微信手续费,)
+                Integer procedureFee = RatiosUtil.amount_fee(allocate[1]) - FEE;
+                AppProfitSharingInfo PTAppProfitSharingInfo = getPtAppProfitSharingInfo(appOrder, PT, procedureFee);
+                appProfitSharingInfoMapper.insert(PTAppProfitSharingInfo);
+
+            }
+            //门店(分账给平台及商户)
+            if (depart.getSystemType() == 2) {
+                BigDecimal[] allocate = RatiosUtil.allocate(appOrder.getPrice(), MD, SH, PT);
+                //门店所得金额
+                AppProfitSharingInfo MDAppProfitSharingInfo = new AppProfitSharingInfo();
+                MDAppProfitSharingInfo.setOrderId(appOrder.getId());
+                MDAppProfitSharingInfo.setOrgCode(orgCode);
+                MDAppProfitSharingInfo.setMchId(depart.getMchId());
+                MDAppProfitSharingInfo.setMchName(depart.getMchName());
+                MDAppProfitSharingInfo.setType(1);
+                MDAppProfitSharingInfo.setRatio(MD);
+                MDAppProfitSharingInfo.setPreAmount(RatiosUtil.amount_fee(allocate[0]));
+                appProfitSharingInfoMapper.insert(MDAppProfitSharingInfo);
+                //商户所得金额
+                AppProfitSharingInfo SHAppProfitSharingInfo = new AppProfitSharingInfo();
+                SHAppProfitSharingInfo.setOrderId(appOrder.getId());
+                SHAppProfitSharingInfo.setOrgCode(orgCode);
+                SHAppProfitSharingInfo.setMchId(depart.getMchId());
+                SHAppProfitSharingInfo.setMchName(depart.getMchName());
+                SHAppProfitSharingInfo.setType(2);
+                SHAppProfitSharingInfo.setRatio(SH);
+                SHAppProfitSharingInfo.setPreAmount(RatiosUtil.amount_fee(allocate[0]));
+                appProfitSharingInfoMapper.insert(SHAppProfitSharingInfo);
+                //平台所得金额(金额*比例-微信手续费)
+                Integer procedureFee = RatiosUtil.amount_fee(allocate[1]) - FEE;
+                AppProfitSharingInfo PTAppProfitSharingInfo = getPtAppProfitSharingInfo(appOrder, PT, procedureFee);
+                appProfitSharingInfoMapper.insert(PTAppProfitSharingInfo);
+            }
+        }
+    }
+
+    @NotNull
+    private static AppProfitSharingInfo getPtAppProfitSharingInfo(AppOrder appOrder, BigDecimal PT, Integer procedureFee) {
+        AppProfitSharingInfo PTAppProfitSharingInfo = new AppProfitSharingInfo();
+        PTAppProfitSharingInfo.setOrderId(appOrder.getId());
+        PTAppProfitSharingInfo.setOrgCode(WechatConstants.SUB_ORG_CODE);
+        PTAppProfitSharingInfo.setMchId(WechatConstants.WECHAT_SUB_MCH_ID);
+        PTAppProfitSharingInfo.setMchName(WechatConstants.WECHAT_SUB_MCH_NAME);
+        PTAppProfitSharingInfo.setType(2);
+        PTAppProfitSharingInfo.setRatio(PT);
+        PTAppProfitSharingInfo.setPreAmount(procedureFee);
+        return PTAppProfitSharingInfo;
+    }
+
     /**
      * 通过商户订单号查询订单在微信侧支付状态
      *
@@ -283,18 +373,13 @@ public class WeChatPayService {
         appOrderRefundsInfo.setOrderId(appOrder.getId());
         appOrderRefundsInfo.setOrderCode(orderCode);
         appOrderRefundsInfo.setOutRefundNo(out_refund_no);
-//        appOrderRefundsInfo.setRefundId();
         appOrderRefundsInfo.setTransactionId(appOrder.getTransactionId());
         appOrderRefundsInfo.setReason(reason);
-//        appOrderRefundsInfo.setSuccessTime();
         appOrderRefundsInfo.setAmount(total);
-//        appOrderRefundsInfo.setNotifyRequest();
         appOrderRefundsInfo.setCreateTime(new Date());
 
-        appOrderRefundsInfoMapper.insert(appOrderRefundsInfo);
-
-
-        //todo 异步发起分账回退
+        //异步发起分账回退
+        weChatProfitSharingService.returnProfitSharing(orderCode);
 
         try {
             JSONObject params = new JSONObject();
@@ -324,12 +409,15 @@ public class WeChatPayService {
                     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());

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

@@ -1717,6 +1717,9 @@ public class OrderServiceImpl implements IOrderService {
                         appOrderProInfoMapper.updateById(appOrderProInfo);
                     }
                 }
+                if (appOrder.getOrProfitSharing() == 1) {
+                    weChatPayService.addProfitSharingInfos(appOrder);
+                }
             }
             return "100001";//支付成功
         }

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

@@ -0,0 +1,101 @@
+package org.jeecg.modules.pay.config;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Arrays;
+
+/**
+ * 金额分配工具类
+ * @author wzq
+ * @date 2025/09/28
+ */
+public class RatiosUtil {
+
+    private static final int DEFAULT_SCALE = 10; // 默认计算精度
+    private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_UP;
+
+    /**
+     * 金额元转分字符串
+     *
+     * @param cny 元
+     * @return
+     */
+    public static Integer amount_fee(BigDecimal cny) {
+        BigDecimal b2 = new BigDecimal("100");
+        return cny.multiply(b2).setScale(0, RoundingMode.DOWN).intValue();
+    }
+
+    /**
+     * 按比例分配金额,自动标准化比例为总和100
+     *
+     * @param totalAmount 总金额(单位:元)
+     * @param ratios      原始比例数组(支持任意总和)
+     * @return 分配结果数组(保留两位小数)
+     * @throws IllegalArgumentException 输入包含负数或零元素时抛出
+     */
+    public static BigDecimal[] allocate(BigDecimal totalAmount, BigDecimal... ratios) {
+        validateRatios(ratios);
+        BigDecimal[] normalizedRatios = normalizeRatios(ratios); // 标准化为总和100
+        return allocateWithNormalizedRatios(totalAmount, normalizedRatios);
+    }
+
+    private static void validateRatios(BigDecimal[] ratios) {
+        if (ratios == null || ratios.length == 0) {
+            throw new IllegalArgumentException("比例数组不能为空");
+        }
+        for (BigDecimal ratio : ratios) {
+            if (ratio.compareTo(BigDecimal.ZERO) < 0) {
+                throw new IllegalArgumentException("比例不能为负数: " + ratio);
+            }
+        }
+    }
+
+    private static BigDecimal[] normalizeRatios(BigDecimal[] ratios) {
+        BigDecimal sum = Arrays.stream(ratios)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        if (sum.compareTo(new BigDecimal("100")) != 0) {
+            throw new ArithmeticException("标准化后比例总和必须为100");
+        }
+
+        BigDecimal[] normalized = new BigDecimal[ratios.length];
+        BigDecimal remainder = BigDecimal.ZERO;
+        for (int i = 0; i < ratios.length; i++) {
+            // 精确计算标准化值(保留10位小数)
+            normalized[i] = ratios[i].divide(sum, DEFAULT_SCALE, DEFAULT_ROUNDING);
+            remainder = remainder.add(normalized[i]);
+        }
+
+        // 处理余数误差(精度补偿)
+        BigDecimal expectedSum = BigDecimal.ONE;
+        BigDecimal actualSum = remainder.stripTrailingZeros();
+        if (!expectedSum.equals(actualSum)) {
+            BigDecimal compensation = expectedSum.subtract(actualSum);
+            normalized[0] = normalized[0].add(compensation);
+        }
+        return normalized;
+    }
+
+    private static BigDecimal[] allocateWithNormalizedRatios(BigDecimal totalAmount, BigDecimal[] ratios) {
+        BigDecimal amountInCents = totalAmount.movePointRight(2);
+        BigDecimal[] results = new BigDecimal[ratios.length];
+        Arrays.fill(results, BigDecimal.ZERO);
+
+        // 精确分配
+        for (int i = 0; i < ratios.length; i++) {
+            results[i] = amountInCents.multiply(ratios[i])
+                    .setScale(0, RoundingMode.DOWN);
+        }
+
+        // 处理余数分配(原子操作保证线程安全)
+        BigDecimal totalAllocated = Arrays.stream(results)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal remainder = amountInCents.subtract(totalAllocated);
+        for (int i = 0; i < remainder.intValue(); i++) {
+            results[i] = results[i].add(BigDecimal.ONE);
+        }
+
+        return Arrays.stream(results)
+                .map(r -> r.movePointLeft(2).setScale(2, DEFAULT_ROUNDING))
+                .toArray(BigDecimal[]::new);
+    }
+}

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

@@ -0,0 +1,348 @@
+package org.jeecg.modules.pay.config;
+
+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.jeecg.modules.pay.entity.ProfitSharingRequest;
+import org.jeecg.modules.pay.entity.Receiver;
+import org.jeecg.modules.pay.entity.ReceiverAddForm;
+import org.jeecg.modules.system.app.entity.AppOrder;
+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.AppProfitSharingInfoMapper;
+import org.jeecg.modules.system.app.mapper.AppProfitSharingRecordsMapper;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.*;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.lang.Thread.sleep;
+
+@Slf4j
+@Service
+public class WeChatProfitSharingService {
+
+    @Resource
+    private WechatPayV3Utils wechatPayV3Utils;
+
+    @Resource
+    private AppOrderMapper appOrderMapper;
+
+    @Resource
+    private AppProfitSharingRecordsMapper appProfitSharingRecordsMapper;
+
+    @Resource
+    private AppProfitSharingInfoMapper appProfitSharingInfoMapper;
+
+    /**
+     * 添加分账接收方
+     *
+     * @param receiverAddForm
+     * @return JSONObject
+     * @throws Exception
+     */
+    public JSONObject receiversAdd(ReceiverAddForm receiverAddForm) throws Exception {
+        ClassPathResource classPathResource = new ClassPathResource("cert/platform_cert.pem");
+        InputStream certStream = classPathResource.getInputStream();
+        X509Certificate x509Certificate = getCertificate(certStream);
+
+        String name = rsaEncryptOAEP(receiverAddForm.getName(), x509Certificate);
+        receiverAddForm.setName(name);
+        JSONObject params = JSONObject.from(receiverAddForm);
+        log.info("分账接收方:{}", receiverAddForm);
+        JSONObject body = wechatPayV3Utils.profitSharingSendPost(WechatUrlConstants.PAY_V3_RECEIVERS_ADD, params);
+        log.info("添加分账接收方结果:{}", body);
+        return body;
+    }
+
+    /**
+     * 分账
+     *
+     * @param orderCode 订单编号
+     * @return JSONObject
+     * @throws Exception
+     */
+    public JSONObject profitSharing(String orderCode) throws Exception {
+        AppOrder appOrder = appOrderMapper.selectOne(Wrappers.lambdaQuery(AppOrder.class).eq(AppOrder::getOrderCode, orderCode).last(
+                "limit 1"));
+        //订单预分账详情列表
+        List<AppProfitSharingInfo> appProfitSharingInfos = appProfitSharingInfoMapper.selectList(Wrappers.lambdaQuery(AppProfitSharingInfo.class).eq(AppProfitSharingInfo::getOrderId,
+                appOrder.getId()));
+
+        Integer orderAmount = RatiosUtil.amount_fee(appOrder.getPrice());
+        int amount = 0;
+        for (AppProfitSharingInfo appProfitSharingInfo : appProfitSharingInfos) {
+            amount = amount + appProfitSharingInfo.getPreAmount();
+        }
+
+        //创建分账记录
+        AppProfitSharingRecords appProfitSharingRecords = new AppProfitSharingRecords();
+        appProfitSharingRecords.setProfitSharingNo("P" + orderCode);
+        appProfitSharingRecords.setOrderId(appOrder.getId());
+        appProfitSharingRecords.setOrderAmount(orderAmount);
+        appProfitSharingRecords.setAmount(amount);
+        appProfitSharingRecords.setProcedureFee(orderAmount - amount);
+        appProfitSharingRecords.setProfitSharingTime(new Date());
+
+        List<AppProfitSharingInfo> receiverRecords = appProfitSharingInfos.stream().filter(e -> e.getType() == 1).collect(Collectors.toList());
+        List<AppProfitSharingInfo> partyFunding = appProfitSharingInfos.stream().filter(e -> e.getType() == 0).collect(Collectors.toList());
+        List<Receiver> receivers = new ArrayList<>();
+        //查询当前订单的分账接收方列表
+        for (AppProfitSharingInfo receiverRecord : receiverRecords) {
+            Receiver receiver = new Receiver();
+            receiver.setType("MERCHANT_ID")
+                    .setAccount(receiverRecord.getMchId())
+                    .setAmount(receiverRecord.getPreAmount())
+                    .setDescription("商户:" + receiverRecord.getMchName() + "于订单" + receiverRecord.getOrgCode() + "分账所获金额:" + receiverRecord.getPreAmount());
+            receivers.add(receiver);
+
+            //添加分账接收方
+            ReceiverAddForm receiverAddForm = new ReceiverAddForm();
+            receiverAddForm.setSub_mchid(partyFunding.get(0).getMchId());
+            receiverAddForm.setSub_appid(WechatConstants.WECHAT_SUB_APPID);
+            receiverAddForm.setAccount(receiver.getAccount());
+            receiverAddForm.setName(receiverRecord.getMchName());
+
+            receiversAdd(receiverAddForm);
+            sleep(1000);
+        }
+        ProfitSharingRequest profitSharingRequest = new ProfitSharingRequest();
+        profitSharingRequest
+                .setAppid(WechatConstants.WECHAT_SP_APPID)
+                .setSub_mchid(partyFunding.get(0).getMchId())
+                .setTransaction_id(appOrder.getTransactionId())
+                .setOut_order_no(appProfitSharingRecords.getProfitSharingNo())
+                .setReceivers(receivers)
+                .setUnfreeze_unsplit(Boolean.TRUE);
+        try {
+            JSONObject res = wechatPayV3Utils.profitSharingSendPost(WechatUrlConstants.PAY_V3_PROFIT_SHARING, JSONObject.from(profitSharingRequest));
+            log.info("微信服务商分账--------------------------------------------------wechatPay res:{}", res.toString());
+            if (ObjectUtil.isNotEmpty(res)) {
+                if ("PROCESSING".equals(res.getString("state"))) {
+                    appProfitSharingRecords.setStatus(0);
+                    appOrder.setProfitSharingStatus(1);
+                }
+                if ("FINISHED".equals(res.getString("state"))) {
+                    appProfitSharingRecords.setStatus(1);
+                    appOrder.setProfitSharingStatus(2);
+                }
+                String wechatOrderId = res.getString("order_id");
+                String transactionId = res.getString("transaction_id");
+                appProfitSharingRecords.setTransactionId(transactionId);
+                appProfitSharingRecords.setProfitSharingOrderId(wechatOrderId);
+                appProfitSharingRecordsMapper.insert(appProfitSharingRecords);
+
+                //订单
+                appOrder.setOutOrderNo(appProfitSharingRecords.getProfitSharingNo());
+                appOrder.setProfitSharingOrderId(wechatOrderId);
+                appOrderMapper.updateById(appOrder);
+
+                if (ObjectUtil.isNotEmpty(res.get("receivers"))) {
+                    List<WechatReceiver> wechatReceivers = (List<WechatReceiver>) res.get("receivers");
+                    for (WechatReceiver wechatReceiver : wechatReceivers) {
+                        //关联订单分账详情
+                        for (AppProfitSharingInfo appProfitSharingInfo : appProfitSharingInfos) {
+                            if (Objects.equals(wechatReceiver.getAccount(), appProfitSharingInfo.getMchId())) {
+                                appProfitSharingInfo.setProfitSharingRecordsId(appProfitSharingRecords.getId());
+                                appProfitSharingInfo.setAmount(wechatReceiver.getAmount());
+                                appProfitSharingInfo.setStatus(wechatReceiver.getResult());
+                                appProfitSharingInfo.setDescription(wechatReceiver.getDescription());
+                                appProfitSharingInfoMapper.updateById(appProfitSharingInfo);
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 查询分账结果并修改订单分账详情
+     *
+     * @param orderCode 订单编号
+     * @return JSONObject
+     */
+    public JSONObject getProfitSharingResult(String orderCode) {
+        AppOrder appOrder = appOrderMapper.selectOne(Wrappers.lambdaQuery(AppOrder.class).eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
+
+        AppProfitSharingRecords appProfitSharingRecords = appProfitSharingRecordsMapper.selectOne(Wrappers.lambdaQuery(AppProfitSharingRecords.class).eq(AppProfitSharingRecords::getOrderId,
+                appOrder.getId()).last("limit 1"));
+
+        List<AppProfitSharingInfo> appProfitSharingInfos = appProfitSharingInfoMapper.selectList(Wrappers.lambdaQuery(AppProfitSharingInfo.class).eq(AppProfitSharingInfo::getOrderId,
+                appOrder.getId()));
+
+        List<AppProfitSharingInfo> partyFunding = appProfitSharingInfos.stream().filter(e -> e.getType() == 0).collect(Collectors.toList());
+        HashMap<String, String> map = new HashMap<>();
+        map.put("sub_mchid", partyFunding.get(0).getMchId());
+        map.put("transaction_id", appOrder.getTransactionId());
+
+        String params = WechatPayV3Utils.formatUrlParam(map);
+        try {
+            String url = WechatUrlConstants.PAY_V3_GET_PROFIT_SHARING + "P" + orderCode + "?" + params;
+            JSONObject res = wechatPayV3Utils.sendGet(url);
+            log.info("微信服务商分账结果查询--------------------------------------------------wechatPay res:{}", res.toString());
+
+            if (ObjectUtil.isNotEmpty(res)) {
+                if ("PROCESSING".equals(res.getString("state"))) {
+                    appProfitSharingRecords.setStatus(0);
+                    appOrder.setProfitSharingStatus(1);
+                }
+                if ("FINISHED".equals(res.getString("state"))) {
+                    appProfitSharingRecords.setStatus(1);
+                    appOrder.setProfitSharingStatus(2);
+                }
+                String wechatOrderId = res.getString("order_id");
+                String transactionId = res.getString("transaction_id");
+                appProfitSharingRecords.setTransactionId(transactionId);
+                appProfitSharingRecords.setProfitSharingOrderId(wechatOrderId);
+                appProfitSharingRecordsMapper.insert(appProfitSharingRecords);
+
+                //订单
+                appOrder.setOutOrderNo(appProfitSharingRecords.getProfitSharingNo());
+                appOrder.setProfitSharingOrderId(wechatOrderId);
+                appOrderMapper.updateById(appOrder);
+
+                if (ObjectUtil.isNotEmpty(res.get("receivers"))) {
+                    List<WechatReceiver> wechatReceivers = (List<WechatReceiver>) res.get("receivers");
+                    for (WechatReceiver wechatReceiver : wechatReceivers) {
+                        //关联订单分账详情
+                        for (AppProfitSharingInfo appProfitSharingInfo : appProfitSharingInfos) {
+                            if (Objects.equals(wechatReceiver.getAccount(), appProfitSharingInfo.getMchId())) {
+                                appProfitSharingInfo.setProfitSharingRecordsId(appProfitSharingRecords.getId());
+                                appProfitSharingInfo.setAmount(wechatReceiver.getAmount());
+                                appProfitSharingInfo.setStatus(wechatReceiver.getResult());
+                                appProfitSharingInfo.setDescription(wechatReceiver.getDescription());
+                                appProfitSharingInfoMapper.updateById(appProfitSharingInfo);
+                            }
+                        }
+                    }
+                }
+            }
+            return res;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 分账回退
+     *
+     * @param orderCode 订单编号
+     * @return JSONObject
+     */
+    public JSONObject returnProfitSharing(String orderCode) {
+        AppOrder appOrder = appOrderMapper.selectOne(Wrappers.lambdaQuery(AppOrder.class).eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
+
+        List<AppProfitSharingInfo> appProfitSharingInfos = appProfitSharingInfoMapper.selectList(Wrappers.lambdaQuery(AppProfitSharingInfo.class).eq(AppProfitSharingInfo::getOrderId,
+                appOrder.getId()));
+        List<AppProfitSharingInfo> receiverRecords = appProfitSharingInfos.stream().filter(e -> e.getType() == 1).collect(Collectors.toList());
+        List<AppProfitSharingInfo> partyFunding = appProfitSharingInfos.stream().filter(e -> e.getType() == 0).collect(Collectors.toList());
+
+        for (AppProfitSharingInfo receiverRecord : receiverRecords) {
+            Map<String, Object> map = new HashMap<>();
+            map.put("sub_mchid", partyFunding.get(0).getMchId());
+            map.put("out_order_no ", "P" + orderCode);
+            map.put("order_id", appOrder.getProfitSharingOrderId());
+            map.put("out_return_no", "T" + orderCode);
+            map.put("return_mchid", receiverRecord.getMchId());
+            map.put("amount", receiverRecord.getAmount());
+            map.put("description", receiverRecord.getMchName() +"分账订单:"+ orderCode + "退还金额:" + receiverRecord.getAmount());
+
+            try {
+                JSONObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_RETURN_ORDER, JSONObject.from(map));
+                log.info("微信服务商分账回退--------------------------------------------------wechatPay res:{}", res.toString());
+                return res;
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 获取分账回退结果
+     *
+     * @param orderCode 订单编号
+     * @return JSONObject
+     */
+    public JSONObject getReturnProfitSharing(String orderCode) {
+        HashMap<String, String> map = new HashMap<>();
+        map.put("sub_mchid", WechatConstants.WECHAT_SUB_MCH_ID);
+        map.put("out_order_no", "P" + orderCode);
+
+        String params = WechatPayV3Utils.formatUrlParam(map);
+        try {
+            String url = WechatUrlConstants.PAY_V3_GET_RETURN_ORDER + "T" + orderCode + "?" + params;
+            JSONObject res = wechatPayV3Utils.sendGet(url);
+            log.info("微信服务商分账结果查询--------------------------------------------------wechatPay res:{}", res.toString());
+            return res;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取证书
+     *
+     * @param inputStream 证书文件
+     * @return {@link X509Certificate} 获取证书
+     */
+    public static X509Certificate getCertificate(InputStream inputStream) {
+        try {
+            CertificateFactory cf = CertificateFactory.getInstance("X509");
+            X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
+            cert.checkValidity();
+            return cert;
+        } catch (CertificateExpiredException e) {
+            throw new RuntimeException("证书已过期", e);
+        } catch (CertificateNotYetValidException e) {
+            throw new RuntimeException("证书尚未生效", e);
+        } catch (CertificateException e) {
+            throw new RuntimeException("无效的证书", e);
+        }
+    }
+
+    /**
+     * 公钥加密   加密隐私信息数据
+     *
+     * @param certificate 平台公钥证书
+     * @return 加密后的数据
+     * @throws Exception 异常信息
+     */
+    public static String rsaEncryptOAEP(String message, X509Certificate certificate)
+            throws IllegalBlockSizeException, IOException {
+        try {
+            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
+            cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
+            byte[] data = message.getBytes(StandardCharsets.UTF_8);
+            byte[] cipherdata = cipher.doFinal(data);
+            return Base64.getEncoder().encodeToString(cipherdata);
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
+        } catch (InvalidKeyException e) {
+            throw new IllegalArgumentException("无效的证书", e);
+        } catch (IllegalBlockSizeException | BadPaddingException e) {
+            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
+        }
+    }
+
+}

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

@@ -16,6 +16,10 @@ public class WechatConstants {
 
     public static final String WECHAT_SUB_APPID = "wxc032a09413289004";
 
+    public static final String WECHAT_SUB_MCH_NAME = "中数海南(广州)";
+
+    public static final String SUB_ORG_CODE = "A01";
+
     //微信商户平台v3密钥
     public static final String WECHAT_MCH_SECRET_V3 = "4b64e17419689527b256f07cdf6bd60c";
 

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

@@ -0,0 +1,21 @@
+package org.jeecg.modules.pay.config;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+public class WechatReceiver implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private String account;
+    private Integer amount;
+    private String create_time;
+    private String description;
+    private String detail_id;
+    private String finish_time;
+    private String result;
+    private String type;
+}

+ 1 - 10
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/paytest/payController.java

@@ -6,32 +6,23 @@ import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.wechat.pay.java.core.certificate.CertificateDownloader;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang.StringUtils;
-import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.util.EntityUtils;
 import org.jeecg.modules.pay.config.WechatConstants;
 import org.jeecg.modules.pay.config.WechatPayV3Utils;
 import org.jeecg.modules.pay.config.WechatUrlConstants;
 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.routing.WeChatProfitSharingService;
+import org.jeecg.modules.pay.config.WeChatProfitSharingService;
 import org.jeecg.modules.pay.serverPay.HttpClientUtil;
 import org.jeecg.modules.pay.serverPay.PayKit;
 import org.jeecg.modules.pay.serverPay.RsaKit;
-import org.jeecg.modules.pay.unionPay.UnionPayUtils;
 import org.jeecg.modules.system.app.entity.AppOrder;
 import org.jeecg.modules.system.app.service.IAppOrderService;
 import org.springframework.web.bind.annotation.*;
 
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;

+ 0 - 134
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/routing/WeChatProfitSharingService.java

@@ -1,134 +0,0 @@
-package org.jeecg.modules.pay.routing;
-
-import com.alibaba.fastjson2.JSONObject;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import lombok.extern.slf4j.Slf4j;
-import org.jeecg.modules.pay.config.WechatPayV3Utils;
-import org.jeecg.modules.pay.config.WechatUrlConstants;
-import org.jeecg.modules.pay.entity.ReceiverAddForm;
-import org.jeecg.modules.system.app.entity.AppOrder;
-import org.jeecg.modules.system.app.entity.AppProfitSharingRecords;
-import org.jeecg.modules.system.app.entity.SeparateAccounts;
-import org.jeecg.modules.system.app.mapper.AppOrderMapper;
-import org.jeecg.modules.system.app.mapper.AppProfitSharingRecordsMapper;
-import org.jeecg.modules.system.app.mapper.SeparateAccountsMapper;
-import org.jeecg.modules.system.entity.SysDepart;
-import org.jeecg.modules.system.mapper.SysDepartMapper;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-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;
-import java.security.cert.*;
-import java.util.Base64;
-
-@Slf4j
-@Service
-public class WeChatProfitSharingService {
-
-    @Resource
-    private WechatPayV3Utils wechatPayV3Utils;
-
-    @Resource
-    private AppOrderMapper appOrderMapper;
-
-    @Resource
-    private AppProfitSharingRecordsMapper appProfitSharingRecordsMapper;
-
-    @Resource
-    private SysDepartMapper sysDepartMapper;
-
-    @Resource
-    private SeparateAccountsMapper separateAccountsMapper;
-
-    /**
-     *  添加分账接收方
-     */
-    public JSONObject receiversAdd(ReceiverAddForm receiverAddForm) throws Exception {
-        ClassPathResource classPathResource = new ClassPathResource("cert/platform_cert.pem");
-        InputStream certStream = classPathResource.getInputStream();
-        X509Certificate x509Certificate = getCertificate(certStream);
-
-        String name = rsaEncryptOAEP(receiverAddForm.getName(),x509Certificate);
-        receiverAddForm.setName(name);
-        JSONObject params = JSONObject.from(receiverAddForm);
-        log.info("分账接收方:{}",receiverAddForm);
-        JSONObject body = wechatPayV3Utils.profitSharingSendPost(WechatUrlConstants.PAY_V3_RECEIVERS_ADD,params);
-        log.info("添加分账接收方结果:{}",body);
-        return body;
-    }
-
-    public JSONObject profitSharing(String orderCode){
-        AppOrder appOrder = appOrderMapper.selectOne(Wrappers.lambdaQuery(AppOrder.class).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"));
-        SeparateAccounts separateAccounts = separateAccountsMapper.selectOne(Wrappers.lambdaQuery(SeparateAccounts.class).eq(SeparateAccounts::getDeptId, depart.getId()));
-        //获取分账比例
-        BigDecimal PT = separateAccounts.getPtSeparateAccounts();
-        BigDecimal SH = separateAccounts.getShSeparateAccounts();
-        BigDecimal MD = separateAccounts.getMdSeparateAccounts();
-        //创建分账记录
-        AppProfitSharingRecords appProfitSharingRecords = new AppProfitSharingRecords();
-        appProfitSharingRecordsMapper.insert(appProfitSharingRecords);
-
-        //调用之后创建分账详情
-        return null;
-    }
-
-    /**
-     * 获取证书
-     *
-     * @param inputStream 证书文件
-     * @return {@link X509Certificate} 获取证书
-     */
-    public static X509Certificate getCertificate(InputStream inputStream) {
-        try {
-            CertificateFactory cf = CertificateFactory.getInstance("X509");
-            X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
-            cert.checkValidity();
-            return cert;
-        } catch (CertificateExpiredException e) {
-            throw new RuntimeException("证书已过期", e);
-        } catch (CertificateNotYetValidException e) {
-            throw new RuntimeException("证书尚未生效", e);
-        } catch (CertificateException e) {
-            throw new RuntimeException("无效的证书", e);
-        }
-    }
-
-    /**
-     * 公钥加密   加密隐私信息数据
-     *
-
-     * @param certificate 平台公钥证书
-     * @return 加密后的数据
-     * @throws Exception 异常信息
-     */
-    public static String rsaEncryptOAEP(String message, X509Certificate certificate)
-            throws IllegalBlockSizeException, IOException {
-        try {
-            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
-            cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
-            byte[] data = message.getBytes(StandardCharsets.UTF_8);
-            byte[] cipherdata = cipher.doFinal(data);
-            return Base64.getEncoder().encodeToString(cipherdata);
-        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
-            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
-        } catch (InvalidKeyException e) {
-            throw new IllegalArgumentException("无效的证书", e);
-        } catch (IllegalBlockSizeException | BadPaddingException e) {
-            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
-        }
-    }
-
-}

+ 108 - 11
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/unionPay/SM3Util.java

@@ -5,26 +5,123 @@ import org.bouncycastle.crypto.macs.HMac;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.Arrays;
 
 public class SM3Util {
     private static final String ENCODING = "UTF-8";
 
-    public static void main(String[] args) throws Exception {
-        String srcStr = "今天天气很晴朗";
-        String key = "zjqzjq";
-        // ******************************自定义密钥加密及校验*****************************************
-        String hexStrByKey = SM3Util.encrypt(srcStr, key);
-        System.out.println("带密钥加密后的密文:" + hexStrByKey);
+//    public static void main(String[] args) throws Exception {
+//        String srcStr = "今天天气很晴朗";
+//        String key = "zjqzjq";
+//        // ******************************自定义密钥加密及校验*****************************************
+//        String hexStrByKey = SM3Util.encrypt(srcStr, key);
+//        System.out.println("带密钥加密后的密文:" + hexStrByKey);
+//
+//        System.out.println("明文(带密钥)与密文校验结果:" + SM3Util.verify(srcStr, key, hexStrByKey));
+//
+//        // ******************************无密钥的加密及校验******************************************
+//        String hexStrNoKey = SM3Util.encrypt(srcStr);
+//        System.out.println("不带密钥加密后的密文:" + hexStrNoKey);
+//
+//        System.out.println("明文(不带密钥)与密文校验结果:" + SM3Util.verify(srcStr, hexStrNoKey));
+//
+//    }
 
-        System.out.println("明文(带密钥)与密文校验结果:" + SM3Util.verify(srcStr, key, hexStrByKey));
+    public static void main(String[] args) {
+        BigDecimal total1 = new BigDecimal("99");
+        BigDecimal[] ratios1 = {BigDecimal.ONE, new BigDecimal("1.5"), new BigDecimal("97.50")}; // 总和100 → 直接分配
+        System.out.println("示例1结果:" + Arrays.toString(allocate(total1, ratios1)));
+        System.out.println(amount_fee(new BigDecimal(0.01)).multiply(new BigDecimal(0.06)).setScale(0, RoundingMode.UP));
+    }
+
+    /**
+     * 金额元转分字符串
+     *
+     * @param cny 元
+     * @return
+     */
+    public static BigDecimal amount_fee(BigDecimal cny) {
+        BigDecimal b2 = new BigDecimal("100");
+        return cny.multiply(b2).setScale(0, RoundingMode.DOWN);
+    }
+
+    /**
+     * 按比例分配金额,自动标准化比例为总和100
+     *
+     * @param totalAmount 总金额(单位:元)
+     * @param ratios      原始比例数组(支持任意总和)
+     * @return 分配结果数组(保留两位小数)
+     * @throws IllegalArgumentException 输入包含负数或零元素时抛出
+     */
+    public static BigDecimal[] allocate(BigDecimal totalAmount, BigDecimal... ratios) {
+        validateRatios(ratios);
+        BigDecimal[] normalizedRatios = normalizeRatios(ratios); // 标准化为总和100
+        return allocateWithNormalizedRatios(totalAmount, normalizedRatios);
+    }
+
+    private static void validateRatios(BigDecimal[] ratios) {
+        if (ratios == null || ratios.length == 0) {
+            throw new IllegalArgumentException("比例数组不能为空");
+        }
+        for (BigDecimal ratio : ratios) {
+            if (ratio.compareTo(BigDecimal.ZERO) < 0) {
+                throw new IllegalArgumentException("比例不能为负数: " + ratio);
+            }
+        }
+    }
+
+    private static BigDecimal[] normalizeRatios(BigDecimal[] ratios) {
+        BigDecimal sum = Arrays.stream(ratios)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        if (sum.compareTo(new BigDecimal("100")) != 0) {
+            throw new ArithmeticException("标准化后比例总和必须为100");
+        }
+
+        BigDecimal[] normalized = new BigDecimal[ratios.length];
+        BigDecimal remainder = BigDecimal.ZERO;
+        for (int i = 0; i < ratios.length; i++) {
+            // 精确计算标准化值(保留10位小数)
+            normalized[i] = ratios[i].divide(sum, DEFAULT_SCALE, DEFAULT_ROUNDING);
+            remainder = remainder.add(normalized[i]);
+        }
+
+        // 处理余数误差(精度补偿)
+        BigDecimal expectedSum = BigDecimal.ONE;
+        BigDecimal actualSum = remainder.stripTrailingZeros();
+        if (!expectedSum.equals(actualSum)) {
+            BigDecimal compensation = expectedSum.subtract(actualSum);
+            normalized[0] = normalized[0].add(compensation);
+        }
+        return normalized;
+    }
+
+    private static final int DEFAULT_SCALE = 10; // 默认计算精度
+    private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.UP;
+
+    private static BigDecimal[] allocateWithNormalizedRatios(BigDecimal totalAmount, BigDecimal[] ratios) {
+        BigDecimal amountInCents = totalAmount.movePointRight(2);
+        BigDecimal[] results = new BigDecimal[ratios.length];
+        Arrays.fill(results, BigDecimal.ZERO);
 
-        // ******************************无密钥的加密及校验******************************************
-        String hexStrNoKey = SM3Util.encrypt(srcStr);
-        System.out.println("不带密钥加密后的密文:" + hexStrNoKey);
+        // 精确分配
+        for (int i = 0; i < ratios.length; i++) {
+            results[i] = amountInCents.multiply(ratios[i])
+                    .setScale(0, RoundingMode.DOWN);
+        }
 
-        System.out.println("明文(不带密钥)与密文校验结果:" + SM3Util.verify(srcStr, hexStrNoKey));
+        // 处理余数分配(原子操作保证线程安全)
+        BigDecimal totalAllocated = Arrays.stream(results)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal remainder = amountInCents.subtract(totalAllocated);
+        for (int i = 0; i < remainder.intValue(); i++) {
+            results[i] = results[i].add(BigDecimal.ONE);
+        }
 
+        return Arrays.stream(results)
+                .map(r -> r.movePointLeft(2).setScale(2, DEFAULT_ROUNDING))
+                .toArray(BigDecimal[]::new);
     }
 
     /**

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

@@ -113,7 +113,19 @@ public class AppOrder implements Serializable {
     private Integer callbackStatus;
 
     /**
-     * 微信支付订单号
+     * 是否分账(0-否 1-是)
+     */
+    @Schema(description = "是否分账(0-否 1-是)")
+    private Integer orProfitSharing;
+
+    /**
+     * 分账状态
+     */
+    @Schema(description = "分账状态 0-未分账 1-分账中 2-已分账")
+    private Integer profitSharingStatus;
+
+    /**
+     * 微信交易流水号
      */
     @Schema(description = "transactionId")
     private String transactionId;

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

@@ -59,6 +59,10 @@ public class AppProfitSharingInfo implements Serializable {
     @Schema(description = "商户全称")
     private String mchName;
 
+    /**
+     * 账户类型(1-出资方 2-接收方)
+     */
+    private Integer type;
 
     /**
      * 分账比例
@@ -82,7 +86,13 @@ public class AppProfitSharingInfo implements Serializable {
      * 分账状态
      */
     @Schema(description = "分账状态")
-    private Integer status;
+    private String status;
+
+    /**
+     * 分账结果描述
+     */
+    @Schema(description = "分账结果描述")
+    private String description;
 
     /**
      * 创建时间

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

@@ -40,6 +40,17 @@ public class AppProfitSharingRecords implements Serializable {
     @Schema(description = "订单ID")
     private String orderId;
 
+    /**
+     * 微信交易流水号
+     */
+    @Schema(description = "transactionId")
+    private String transactionId;
+
+    /**
+     * 微信分账单号
+     */
+    @Schema(description = "微信分账单号")
+    private String profitSharingOrderId;
 
     /**
      * 订单原金额(分)