Parcourir la source

feat(order): 实现订单分账及过期处理功能- 新增分账结果更新接口,支持PUT请求方式
- 实现订单过期定时任务处理逻辑
- 添加订单分账状态字段及保险金额字段-优化订单退款逻辑,增加退款ID查询条件
- 实现分账金额计算逻辑,支持微信手续费扣除- 添加订单延迟队列处理,支持订单过期自动取消- 修复订单支付回调重复处理问题
-优化分账接收方信息结构,增加姓名字段
- 实现分账金额临界值处理逻辑
- 添加订单分账定时任务调度服务

wzq il y a 1 mois
Parent
commit
0e7942963d
22 fichiers modifiés avec 527 ajouts et 160 suppressions
  1. 1 0
      national-motion-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java
  2. 5 3
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/controller/OrderController.java
  3. 1 7
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/controller/commercial/CommercialController.java
  4. 91 56
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/service/WeChatPayService.java
  5. 53 38
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/service/impl/OrderServiceImpl.java
  6. 163 21
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WeChatProfitSharingService.java
  7. 2 2
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/config/WechatUrlConstants.java
  8. 2 1
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/entity/Receiver.java
  9. 32 19
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/paytest/payController.java
  10. 19 3
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/unionPay/SM3Util.java
  11. 25 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/quartz/job/OrderExpiredJobService.java
  12. 4 1
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/quartz/job/ProfitSharingJonService.java
  13. 1 2
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/redission/RedissonConfig.java
  14. 56 2
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/redission/RedissonDelayQueue.java
  15. 16 1
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/controller/AppOrderController.java
  16. 5 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/entity/AppProfitSharingInfo.java
  17. 4 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/mapper/AppCoursesVerificationRecordMapper.java
  18. 23 4
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/mapper/xml/AppCoursesVerificationRecordMapper.xml
  19. 2 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/mapper/xml/AppOrderMapper.xml
  20. 5 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/service/IAppCoursesVerificationRecordService.java
  21. 5 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/service/impl/AppCoursesVerificationRecordServiceImpl.java
  22. 12 0
      national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/vo/OrderPageVO.java

+ 1 - 0
national-motion-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java

@@ -657,6 +657,7 @@ public interface CommonConstant {
      * 订单超时未支付延迟任务前缀
      */
      String ORDER_TIME_OUT_TASK_PREFIX= "OrderTimeOutTask_";
+     String ORDER_TIME_TASK_PREFIX= "OrderExpireTask_";
 
     /**
      *  券状态 0-待使用 1-已使用 2-已失效

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

@@ -280,7 +280,7 @@ public class OrderController {
                     appOrderProInfoService.updateById(appOrderProInfo);
                 }
                 AppOrderRefundsInfo appOrderRefundsInfo = appOrderRefundsInfoService.getOne(Wrappers.<AppOrderRefundsInfo>lambdaQuery().eq(AppOrderRefundsInfo::getOrderId,
-                        appOrder.getId()));
+                        appOrder.getId()).eq(AppOrderRefundsInfo::getRefundId, refundData.getTransactionRefundId()).last("limit 1"));
                 appOrderRefundsInfo.setStatus(refundData.getStatus());
                 appOrderRefundsInfo.setSuccessTime(refundData.getSuccessTime());
                 appOrderRefundsInfoService.updateById(appOrderRefundsInfo);
@@ -302,8 +302,10 @@ public class OrderController {
                     appOrderProInfoService.updateById(appOrderProInfo);
                 }
                 AppOrderRefundsInfo appOrderRefundsInfo =
-                        appOrderRefundsInfoService.getOne(Wrappers.<AppOrderRefundsInfo>lambdaQuery().eq(AppOrderRefundsInfo::getOrderId,
-                                appOrder.getId()));
+                        appOrderRefundsInfoService.getOne(Wrappers.<AppOrderRefundsInfo>lambdaQuery()
+                                .eq(AppOrderRefundsInfo::getOrderId, appOrder.getId())
+                                .eq(AppOrderRefundsInfo::getRefundId, refundData.getTransactionRefundId())
+                        );
                 appOrderRefundsInfo.setStatus(refundData.getStatus());
                 appOrderRefundsInfoService.updateById(appOrderRefundsInfo);
             }

+ 1 - 7
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/app/controller/commercial/CommercialController.java

@@ -108,14 +108,8 @@ public class CommercialController {
     @Operation(summary = "拍照验课人员查看")
     @PostMapping("/courseQueryUsers")
     public Result<List<AppCoursesVerificationRecord>> courseQueryUsers(@RequestBody CourseQueryUsersForm form) {
-        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
         List<AppCoursesVerificationRecord> verificationRecords =
-                appCoursesVerificationRecordService.list(Wrappers.<AppCoursesVerificationRecord>lambdaQuery()
-                        .eq(AppCoursesVerificationRecord::getCoursesPriceRuleId, form.getCoursePriceRulesId())
-                        .likeRight(AppCoursesVerificationRecord::getOrderCode, loginUser.getOrgCode())
-                        .eq(ObjectUtil.isNotNull(form.getOrPostpone()), AppCoursesVerificationRecord::getOrPostpone, form.getOrPostpone())
-                        .eq(ObjectUtil.isNotNull(form.getVerifyStatus()), AppCoursesVerificationRecord::getVerifyStatus, form.getVerifyStatus())
-                );
+                appCoursesVerificationRecordService.courseQueryUsersList( form.getCoursePriceRulesId(),form.getOrPostpone(), form.getVerifyStatus());
         List<AppCoursesVerificationRecord> list = verificationRecords.stream()
                 .collect(Collectors.collectingAndThen(
                         Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(AppCoursesVerificationRecord::getUseUserId))),

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

@@ -12,6 +12,7 @@ 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.common.exception.JeecgBootException;
 import org.jeecg.modules.pay.config.*;
 import org.jeecg.modules.pay.serverPay.WXPayUtility;
 import org.jeecg.modules.system.app.dto.receiptPaymentDetails.ReceiptPaymentDetailsInfoVo;
@@ -211,6 +212,12 @@ public class WeChatPayService {
             String orderCode = res.getString("out_trade_no");
             //查询订单,判断是否已修改为已支付状态
             AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
+            if (ObjectUtil.isNotEmpty(appOrder) && appOrder.getCallbackStatus() == 1) {
+                result.put("code", "SUCCESS");
+                result.put("message", "OK");
+                result.put("orderCode", orderCode);
+                return result;
+            }
             if (ObjectUtil.isNotEmpty(appOrder)) {
                 if (Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_0)) {
                     appOrder.setOrderStatus(CommonConstant.ORDER_STATUS_1);
@@ -227,25 +234,54 @@ public class WeChatPayService {
                             appOrderProInfoMapper.updateById(appOrderProInfo);
                         }
                     }
+                    appOrder.setCallbackStatus(CommonConstant.STATUS_1_INT);
+                    appOrderMapper.updateById(appOrder);
+                    //创建预分账详情
+                    if (appOrder.getOrProfitSharing() == 1) {
+                        addProfitSharingInfos(appOrder);
+                    }
                 }
-                appOrder.setCallbackStatus(CommonConstant.STATUS_1_INT);
-                appOrderMapper.updateById(appOrder);
-                //创建预分账详情
-                if (appOrder.getOrProfitSharing() == 1) {
-                    addProfitSharingInfos(appOrder);
-                }
+
             }
             result.put("code", "SUCCESS");
             result.put("message", "OK");
             result.put("orderCode", orderCode);
             return result;
         } catch (Exception e) {
+            log.error("微信支付回调异常:" + e.getMessage());
             result.put("code", "FAIL");
             result.put("message", "失败");
             return result;
         }
     }
 
+    private static final BigDecimal MIN_THRESHOLD = new BigDecimal("0.01"); // 最小阈值
+
+    /**
+     * 计算千分之6并处理临界值
+     *
+     * @param input 输入金额(单位:元)
+     * @return 计算结果(保留两位小数,不足0.01按0.01计算)
+     * @throws IllegalArgumentException 输入为null或负数时抛出
+     */
+    public static BigDecimal calculate(BigDecimal input, BigDecimal multiplier) {
+        if (input == null) {
+            throw new IllegalArgumentException("输入金额不能为null");
+        }
+        if (input.compareTo(BigDecimal.ZERO) < 0) {
+            throw new IllegalArgumentException("输入金额不能为负数");
+        }
+
+        // 精确乘法运算(保留4位中间精度)
+        BigDecimal product = input.multiply(multiplier)
+                .setScale(4, RoundingMode.HALF_UP);
+
+        // 临界值判断与补偿
+        return product.compareTo(MIN_THRESHOLD) < 0
+                ? MIN_THRESHOLD
+                : product.setScale(2, RoundingMode.DOWN);
+    }
+
     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;
@@ -264,10 +300,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(price.multiply(new BigDecimal("0.06")).setScale(0, RoundingMode.UP));
+            //微信手续费,不足1分按1分算(当计算值小于0.01时强制提升至0.01)
+            BigDecimal FEE = calculate(price, new BigDecimal("0.006"));
+
+            price = price.subtract(FEE);
+
             //商户(分账给平台)
             if (depart.getSystemType() == 1) {
                 BigDecimal[] allocate = RatiosUtil.allocate(price, new BigDecimal[]{SH, PT});
@@ -281,11 +320,15 @@ public class WeChatPayService {
                 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);
 
+                //平台所得金额(金额*比例-微信手续费)
+                Integer procedureFee = RatiosUtil.amount_fee(allocate[2].add(insurePrice));
+                AppProfitSharingInfo PTAppProfitSharingInfo = getPtAppProfitSharingInfo(appOrder, PT, procedureFee, insurePrice);
+                if (allocate[2] != null && ((allocate[2].add(insurePrice)).compareTo(BigDecimal.ZERO) == 0)) {
+                    PTAppProfitSharingInfo.setAmount(RatiosUtil.amount_fee(BigDecimal.ZERO));
+                    PTAppProfitSharingInfo.setDescription("分账金额为0,请自行处理");
+                }
+                appProfitSharingInfoMapper.insert(PTAppProfitSharingInfo);
             }
             //门店(分账给平台及商户)
             if (depart.getSystemType() == 2) {
@@ -301,6 +344,7 @@ public class WeChatPayService {
                 MDAppProfitSharingInfo.setPreAmount(RatiosUtil.amount_fee(allocate[0]));
                 appProfitSharingInfoMapper.insert(MDAppProfitSharingInfo);
                 //商户所得金额
+
                 AppProfitSharingInfo SHAppProfitSharingInfo = new AppProfitSharingInfo();
                 SHAppProfitSharingInfo.setOrderId(appOrder.getId());
                 SHAppProfitSharingInfo.setOrgCode(orgCode);
@@ -308,18 +352,27 @@ public class WeChatPayService {
                 SHAppProfitSharingInfo.setMchName(depart.getMchName());
                 SHAppProfitSharingInfo.setType(2);
                 SHAppProfitSharingInfo.setRatio(SH);
-                SHAppProfitSharingInfo.setPreAmount(RatiosUtil.amount_fee(allocate[0]));
+                SHAppProfitSharingInfo.setPreAmount(RatiosUtil.amount_fee(allocate[1]));
+                if (allocate[1] != null && allocate[1].compareTo(BigDecimal.ZERO) == 0) {
+                    SHAppProfitSharingInfo.setAmount(RatiosUtil.amount_fee(BigDecimal.ZERO));
+                    SHAppProfitSharingInfo.setDescription("分账金额为0,请自行处理");
+                }
                 appProfitSharingInfoMapper.insert(SHAppProfitSharingInfo);
+
                 //平台所得金额(金额*比例-微信手续费)
-                Integer procedureFee = RatiosUtil.amount_fee(allocate[1]) - FEE;
-                AppProfitSharingInfo PTAppProfitSharingInfo = getPtAppProfitSharingInfo(appOrder, PT, procedureFee);
+                Integer procedureFee = RatiosUtil.amount_fee(allocate[2].add(insurePrice));
+                AppProfitSharingInfo PTAppProfitSharingInfo = getPtAppProfitSharingInfo(appOrder, PT, procedureFee, insurePrice);
+                if (allocate[2] != null && ((allocate[2].add(insurePrice)).compareTo(BigDecimal.ZERO) == 0)) {
+                    PTAppProfitSharingInfo.setAmount(RatiosUtil.amount_fee(BigDecimal.ZERO));
+                    PTAppProfitSharingInfo.setDescription("分账金额为0,请自行处理");
+                }
                 appProfitSharingInfoMapper.insert(PTAppProfitSharingInfo);
             }
         }
     }
 
     @NotNull
-    private static AppProfitSharingInfo getPtAppProfitSharingInfo(AppOrder appOrder, BigDecimal PT, Integer procedureFee) {
+    private static AppProfitSharingInfo getPtAppProfitSharingInfo(AppOrder appOrder, BigDecimal PT, Integer procedureFee, BigDecimal insurePrice) {
         AppProfitSharingInfo PTAppProfitSharingInfo = new AppProfitSharingInfo();
         PTAppProfitSharingInfo.setOrderId(appOrder.getId());
         PTAppProfitSharingInfo.setOrgCode(WechatConstants.SUB_ORG_CODE);
@@ -327,6 +380,7 @@ public class WeChatPayService {
         PTAppProfitSharingInfo.setMchName(WechatConstants.WECHAT_SUB_MCH_NAME);
         PTAppProfitSharingInfo.setType(2);
         PTAppProfitSharingInfo.setRatio(PT);
+        PTAppProfitSharingInfo.setInsureAmount(RatiosUtil.amount_fee(insurePrice));
         PTAppProfitSharingInfo.setPreAmount(procedureFee);
         return PTAppProfitSharingInfo;
     }
@@ -337,7 +391,7 @@ public class WeChatPayService {
      * @param out_trade_no 发起支付时创建的商户订单号
      * @return null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败
      */
-    public JSONObject orderQueryByOutTradeNo(String out_trade_no,String subMchId) {
+    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<>();
@@ -377,7 +431,7 @@ public class WeChatPayService {
         appOrderRefundsInfo.setCreateTime(new Date());
 
         JSONObject params = new JSONObject();
-        params.put("sub_mchid",depart.getMchId());
+        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);//退款原因
@@ -484,6 +538,8 @@ public class WeChatPayService {
         Map<String, Object> parm = new HashMap<>();
         if (!StringUtils.isEmpty(refund_status) && "SUCCESS".equals(refund_status)) {
 
+            List<AppOrderProInfo> appOrderProInfoList = new ArrayList<>();
+
             //查询订单
             AppOrder order = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, out_trade_no).last("limit 1"));
             if (ObjectUtil.isNotEmpty(order)) {
@@ -497,53 +553,32 @@ public class WeChatPayService {
                     List<AppOrderProInfo> proInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, order.getId()));
                     if (ObjectUtil.isNotEmpty(proInfoList)) {
                         for (AppOrderProInfo appOrderProInfo : proInfoList) {
-                            appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_1);
-                            appOrderProInfoMapper.updateById(appOrderProInfo);
+                            if (appOrderProInfo.getOrderStatus() == 5) {
+                                appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_6);
+                                appOrderProInfoMapper.updateById(appOrderProInfo);
+                                appOrderProInfoList.add(appOrderProInfo);
+                            }
                         }
                     }
                 }
             }
 
-            // 创建退款订单
-            AppOrderRefundsInfo appOrderRefundsInfo = appOrderRefundsInfoMapper.selectOne(Wrappers.lambdaQuery(AppOrderRefundsInfo.class).eq(AppOrderRefundsInfo::getOrderId, order.getId()));
+            for (AppOrderProInfo appOrderProInfo : appOrderProInfoList) {
+                // 创建退款订单
+                AppOrderRefundsInfo appOrderRefundsInfo = appOrderRefundsInfoMapper.selectOne(Wrappers.lambdaQuery(AppOrderRefundsInfo.class)
+                        .eq(AppOrderRefundsInfo::getOrderId, order.getId())
+                        .eq(AppOrderRefundsInfo::getOrderProInfoId, appOrderProInfo.getId())
+                );
 
-            if (ObjectUtil.isNotEmpty(appOrderRefundsInfo)) {
-                //        appOrderRefundsInfo.setRefundId();
-                appOrderRefundsInfo.setSuccessTime(new Date());
+                if (ObjectUtil.isNotEmpty(appOrderRefundsInfo)) {
+                    //        appOrderRefundsInfo.setRefundId();
+                    appOrderRefundsInfo.setSuccessTime(new Date());
 //        appOrderRefundsInfo.setNotifyRequest();
-                appOrderRefundsInfoMapper.updateById(appOrderRefundsInfo);
+                    appOrderRefundsInfoMapper.updateById(appOrderRefundsInfo);
+                }
             }
-
             parm.put("code", "SUCCESS");
             parm.put("message", "成功");
-            SysDepart sysDepart = sysDepartMapper.findByOrgCodeAndParentId(order.getOrgCode());
-            SeparateAccounts separateAccounts = separateAccountsMapper.findByDeptIdAndStatus(sysDepart.getId());
-            BigDecimal shBigDecimal = BigDecimal.ZERO;
-            try {
-                shBigDecimal = getBigDecimal(order.getPrice(), separateAccounts.getShSeparateAccounts());
-            } catch (Exception e) {
-                log.error("百分比计算错误", e);
-            }
-            BigDecimal ptBigDecimal = BigDecimal.ZERO;
-            try {
-                ptBigDecimal = getBigDecimal(order.getPrice(), separateAccounts.getPtSeparateAccounts());
-            } catch (Exception e) {
-                log.error("百分比计算错误", e);
-            }
-            SysDepart byOrgCode = sysDepartMapper.findByOrgCode("A01");
-            BigDecimal mdBigDecimal = order.getPrice().subtract(shBigDecimal).subtract(ptBigDecimal).setScale(2, RoundingMode.HALF_UP);
-            log.info("退款成功消息通知,金额:" + mdBigDecimal + ";" + shBigDecimal + ";" + ptBigDecimal);
-            ReceiptPaymentDetailsInfoVo receiptPaymentDetailsInfoVo = new ReceiptPaymentDetailsInfoVo();
-            receiptPaymentDetailsInfoVo.setMoney(order.getPrice());
-            receiptPaymentDetailsInfoVo.setChangeMoney(appOrderRefundsInfo.getAmount());
-            receiptPaymentDetailsInfoVo.setPayType(1);
-            receiptPaymentDetailsInfoVo.setPurseChangeReason(1);
-            receiptPaymentDetailsInfoVo.setCreateTime(new Date());
-            receiptPaymentDetailsInfoVo.setUpdateTime(new Date());
-            receiptPaymentDetailsInfoVo.setOrderId(appOrderRefundsInfo.getOrderId());
-            ReceiptPaymentDetailsInfoVo receiptPaymentDetailsInfoVoMd = getChangeMoney(receiptPaymentDetailsInfoVo, order.getOrgCode(), order.getTenantId(), 2, mdBigDecimal);
-            ReceiptPaymentDetailsInfoVo receiptPaymentDetailsInfoVoSh = getChangeMoney(receiptPaymentDetailsInfoVo, order.getOrgCode(), sysDepart.getId(), 1, shBigDecimal);
-            ReceiptPaymentDetailsInfoVo receiptPaymentDetailsInfoVoPt = getChangeMoney(receiptPaymentDetailsInfoVo, order.getOrgCode(), byOrgCode.getId(), 0, ptBigDecimal);
         } else {
             parm.put("code", "FAIL");
             parm.put("message", "失败");

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

@@ -917,9 +917,15 @@ public class OrderServiceImpl extends ServiceImpl<AppOrderMapper, AppOrder> impl
                     //判断当前课程是否已下过单
                     List<AppOrderProInfo> infos = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery()
                             .eq(AppOrderProInfo::getProductId, appCourse.getId())
-                            .eq(AppOrderProInfo::getFamilyUserId, familyUserId));
-                    List<AppOrderProInfo> infoList = infos.stream().filter(info -> Objects.equals(info.getOrFreePro()
-                            , CommonConstant.STATUS_0_INT) && !Objects.equals(info.getOrderStatus(), CommonConstant.ORDER_PRO_INFO_TYPE_4)).collect(Collectors.toList());
+                            .eq(AppOrderProInfo::getFamilyUserId, familyUserId)
+                    );
+                    List<AppOrderProInfo> infoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery()
+                            .eq(AppOrderProInfo::getProductId, appCourse.getId())
+                            .eq(AppOrderProInfo::getFamilyUserId, familyUserId)
+                            .eq(AppOrderProInfo::getOrFreePro,CommonConstant.STATUS_0_INT)
+                            .notIn(AppOrderProInfo::getOrderStatus, CommonConstant.ORDER_STATUS_4, CommonConstant.ORDER_STATUS_5, CommonConstant.ORDER_STATUS_6)
+                    );
+
                     if (ObjectUtil.isNotEmpty(infoList)) {
                         throw new JeecgBootException("当前课程已下过单,请勿重复下单");
                     }
@@ -1190,6 +1196,7 @@ public class OrderServiceImpl extends ServiceImpl<AppOrderMapper, AppOrder> impl
             for (AppCoursesVerificationRecord appCoursesVerificationRecord : appCoursesVerificationRecordList) {
                 appCoursesVerificationRecord.setOrderId(appOrder.getId());
                 appCoursesVerificationRecord.setOrderCode(appOrder.getOrderCode());
+                appCoursesVerificationRecord.setOrgCode(appOrder.getOrgCode());
                 appCoursesVerificationRecordMapper.insert(appCoursesVerificationRecord);
             }
         }
@@ -1208,6 +1215,10 @@ public class OrderServiceImpl extends ServiceImpl<AppOrderMapper, AppOrder> impl
             //发布任务到redission延迟队列(16分钟)
             String task = CommonConstant.ORDER_TIME_OUT_TASK_PREFIX + appOrder.getId();
             redissonDelayQueue.offerTask(task, 60 * 16);
+
+            //发布任务到redission延迟队列(16分钟)
+            String task2 = CommonConstant.ORDER_TIME_TASK_PREFIX + appOrder.getId();
+            redissonDelayQueue.offerTask(task2, 60 * 16);
         }
         return payForm;
     }
@@ -1721,7 +1732,7 @@ public class OrderServiceImpl extends ServiceImpl<AppOrderMapper, AppOrder> impl
                 appOrder.setOrderStatus(1);
                 appOrder.setPayTime(new Date());
                 appOrder.setPayStatus(1);
-//                appOrder.setTransactionId(res.getString("transaction_id"));
+                appOrder.setTransactionId(res.getString("transaction_id"));
                 appOrderMapper.updateById(appOrder);
                 List<AppOrderProInfo> proInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, appOrder.getId()));
                 if (CollUtil.isNotEmpty(proInfoList)) {
@@ -1734,6 +1745,10 @@ public class OrderServiceImpl extends ServiceImpl<AppOrderMapper, AppOrder> impl
                     weChatPayService.addProfitSharingInfos(appOrder);
                 }
             }
+            if (ObjectUtil.isEmpty(appOrder.getTransactionId())){
+                appOrder.setTransactionId(res.getString("transaction_id"));
+                appOrderMapper.updateById(appOrder);
+            }
             return "100001";//支付成功
         }
         if (s == null) {
@@ -1838,40 +1853,40 @@ public class OrderServiceImpl extends ServiceImpl<AppOrderMapper, AppOrder> impl
         List<String> refundOrderProInfoIds = new ArrayList<>(Arrays.asList(refundOrderForm.getOrderProInfoIds().split(",")));
         List<AppOrderProInfo> appOrderProInfoList = new ArrayList<>();
         if (ObjectUtil.isNotEmpty(appOrder)) {
-            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() +" 已超过可退时间,无法进行退款!");
-                    }
-                }
-            }
+//            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) {

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

@@ -4,6 +4,9 @@ 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 com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.jeecg.common.constant.CommonConstant;
@@ -31,6 +34,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Type;
 import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.security.InvalidKeyException;
@@ -118,25 +122,26 @@ public class WeChatProfitSharingService {
         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<AppProfitSharingInfo> partyFunding = appProfitSharingInfos.stream().filter(e -> e.getType() == 2).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);
+        for (AppProfitSharingInfo receiverRecord : partyFunding) {
+            if (receiverRecord.getPreAmount() != 0){
+                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);
         }
         //构建分账参数
@@ -172,7 +177,137 @@ public class WeChatProfitSharingService {
                 appOrderMapper.updateById(appOrder);
 
                 if (ObjectUtil.isNotEmpty(res.get("receivers"))) {
-                    List<WechatReceiver> wechatReceivers = (List<WechatReceiver>) res.get("receivers");
+                    //List<WechatReceiver> wechatReceivers = (List<WechatReceiver>) res.get("receivers");
+                    Gson gson = new Gson();
+                    // 定义目标类型的TypeToken
+                    Type receiverListType = new TypeToken<List<WechatReceiver>>() {}.getType();
+                    // 转换
+                    List<WechatReceiver> wechatReceivers = gson.fromJson(
+                            gson.toJson(res.get("receivers")), // 将receivers转为JSON字符串
+                            receiverListType
+                    );
+                    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) {
+            log.error("分账操作发生异常,订单号:{}", orderCode, e);
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 分账
+     *
+     * @param orderCode 订单编号
+     * @return JSONObject
+     * @throws Exception
+     */
+    public JSONObject profitSharings(String orderCode,String s) 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().subtract(insurePrice));
+        int amount = 0;
+        for (AppProfitSharingInfo appProfitSharingInfo : appProfitSharingInfos) {
+            amount = amount + appProfitSharingInfo.getPreAmount();
+        }
+
+        //创建分账记录
+        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);
+        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() == 2).collect(Collectors.toList());
+        List<Receiver> receivers = new ArrayList<>();
+        //查询当前订单的分账接收方列表
+        for (AppProfitSharingInfo receiverRecord : partyFunding) {
+            if (receiverRecord.getPreAmount() != 0){
+                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 = JSONObject.parseObject(s);
+            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");
+                    Gson gson = new Gson();
+                    // 定义目标类型的TypeToken
+                    Type receiverListType = new TypeToken<List<WechatReceiver>>() {}.getType();
+                    // 转换
+                    List<WechatReceiver> wechatReceivers = gson.fromJson(
+                            gson.toJson(res.get("receivers")), // 将receivers转为JSON字符串
+                            receiverListType
+                    );
                     for (WechatReceiver wechatReceiver : wechatReceivers) {
                         //关联订单分账详情
                         for (AppProfitSharingInfo appProfitSharingInfo : appProfitSharingInfos) {
@@ -209,7 +344,7 @@ public class WeChatProfitSharingService {
         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());
+        List<AppProfitSharingInfo> partyFunding = appProfitSharingInfos.stream().filter(e -> e.getType() == 2).collect(Collectors.toList());
         HashMap<String, String> map = new HashMap<>();
         map.put("sub_mchid", partyFunding.get(0).getMchId());
         map.put("transaction_id", appOrder.getTransactionId());
@@ -233,7 +368,7 @@ public class WeChatProfitSharingService {
                 String transactionId = res.getString("transaction_id");
                 appProfitSharingRecords.setTransactionId(transactionId);
                 appProfitSharingRecords.setProfitSharingOrderId(wechatOrderId);
-                appProfitSharingRecordsMapper.insert(appProfitSharingRecords);
+                appProfitSharingRecordsMapper.updateById(appProfitSharingRecords);
 
                 //订单
                 appOrder.setOutOrderNo(appProfitSharingRecords.getProfitSharingNo());
@@ -241,7 +376,14 @@ public class WeChatProfitSharingService {
                 appOrderMapper.updateById(appOrder);
 
                 if (ObjectUtil.isNotEmpty(res.get("receivers"))) {
-                    List<WechatReceiver> wechatReceivers = (List<WechatReceiver>) res.get("receivers");
+                    Gson gson = new Gson();
+                    // 定义目标类型的TypeToken
+                    Type receiverListType = new TypeToken<List<WechatReceiver>>() {}.getType();
+                    // 转换
+                    List<WechatReceiver> wechatReceivers = gson.fromJson(
+                            gson.toJson(res.get("receivers")), // 将receivers转为JSON字符串
+                            receiverListType
+                    );
                     for (WechatReceiver wechatReceiver : wechatReceivers) {
                         //关联订单分账详情
                         for (AppProfitSharingInfo appProfitSharingInfo : appProfitSharingInfos) {

+ 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://51fdfdc4.r28.cpolar.top/jeecg-boot/app/order/wechatPayNotify";
+    public final static String PAY_V3_NOTIFY = "https://3adf4b4f.r28.cpolar.top/jeecg-boot/app/order/wechatPayNotify";
 
     //微信支付v3 退款通知接口地址
-    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_REFUND_NOTIFY = "https://3adf4b4f.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";

+ 2 - 1
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/pay/entity/Receiver.java

@@ -12,7 +12,8 @@ public class Receiver implements Serializable {
     private static final long serialVersionUID = 1L;
 
     private String type;          // MERCHANT_ID/PERSONAL_OPENID
-    private String account;       // 接收方账号
+    private String account;   // 接收方账号
+    private String name;
     private Integer amount;       // 分账金额(分)
     private String description;   // 描述
 }

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

@@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.http.client.methods.HttpGet;
+import org.jeecg.common.util.SpringContextUtils;
 import org.jeecg.modules.pay.config.WechatConstants;
 import org.jeecg.modules.pay.config.WechatPayV3Utils;
 import org.jeecg.modules.pay.config.WechatUrlConstants;
@@ -23,6 +24,8 @@ import org.jeecg.modules.system.app.entity.AppOrder;
 import org.jeecg.modules.system.app.service.IAppOrderService;
 import org.springframework.web.bind.annotation.*;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
@@ -263,25 +266,35 @@ public class payController {
     }
 
     @GetMapping(value = "/test2")
-    public void CertificateDownloaderTest() {
-        //用于证书解密的密钥
-        String apiV3key = "4b64e17419689527b256f07cdf6bd60c";
-        // 商户号
-        String mchId = "1725845681";
-        // 商户证书序列号
-        String mchSerialNo = "65E9559D81ADA0BDA0CD3CF484A59A8DFB5610BE";
-        // 商户私钥
-        String mchPrivateKeyFilePath = "E:\\wzq\\sss";
-        // 微信支付平台证书
-        //private static String wechatpayCertificateFilePath = "wxpayplatformcert.pem";
-        //下载成功后保存证书的路径
-        String outputFilePath = "E:\\wzq\\sss";
-
-
-        String[] args = {"-k", apiV3key, "-m", mchId, "-f", mchPrivateKeyFilePath,
-                    "-s", mchSerialNo, "-o", outputFilePath};
-
-//            CommandLine.run(new CertificateDownloader(), args);
+    public JSONObject CertificateDownloaderTest() throws Exception {
+
+        String res = "{\"order_id\":\"30002408162025101097141236049\",\"out_order_no\":\"PD202510101728312802\"," +
+                "\"receivers\":[{\"account\":\"1726971843\",\"amount\":19,\"create_time\":\"2025-10-10T18:08:34+08:00\",\"description\":\"商户:中数未来(广州)信息技术有限公司于订单A01分账所获金额:19\",\"detail_id\":\"36002408162025101037927485682\",\"finish_time\":\"1970-01-01T08:00:00+08:00\",\"result\":\"PENDING\",\"type\":\"MERCHANT_ID\"},{\"account\":\"1726705634\",\"amount\":80,\"create_time\":\"2025-10-10T18:08:34+08:00\",\"description\":\"解冻给分账方\",\"detail_id\":\"36002408162025101037927485683\",\"finish_time\":\"1970-01-01T08:00:00+08:00\",\"result\":\"PENDING\",\"type\":\"MERCHANT_ID\"}],\"state\":\"PROCESSING\",\"sub_mchid\":\"1726705634\",\"transaction_id\":\"4200002834202510107247953928\"}";
+
+        WeChatProfitSharingService weChatProfitSharingService = SpringContextUtils.getBean(WeChatProfitSharingService.class);
+        JSONObject jsonObject = weChatProfitSharingService.profitSharings("D202510101728312802", res);
+
+        return jsonObject;
+
+
+//        //用于证书解密的密钥
+//        String apiV3key = "4b64e17419689527b256f07cdf6bd60c";
+//        // 商户号
+//        String mchId = "1725845681";
+//        // 商户证书序列号
+//        String mchSerialNo = "65E9559D81ADA0BDA0CD3CF484A59A8DFB5610BE";
+//        // 商户私钥
+//        String mchPrivateKeyFilePath = "E:\\wzq\\sss";
+//        // 微信支付平台证书
+//        //private static String wechatpayCertificateFilePath = "wxpayplatformcert.pem";
+//        //下载成功后保存证书的路径
+//        String outputFilePath = "E:\\wzq\\sss";
+//
+//
+//        String[] args = {"-k", apiV3key, "-m", mchId, "-f", mchPrivateKeyFilePath,
+//                    "-s", mchSerialNo, "-o", outputFilePath};
+//
+////            CommandLine.run(new CertificateDownloader(), args);
     }
 
 }

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

@@ -1,10 +1,21 @@
 package org.jeecg.modules.pay.unionPay;
 
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson2.JSONObject;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
 import org.bouncycastle.crypto.digests.SM3Digest;
 import org.bouncycastle.crypto.macs.HMac;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
+import org.jeecg.common.util.SpringContextUtils;
+import org.jeecg.modules.pay.config.WeChatProfitSharingService;
+import org.jeecg.modules.pay.config.WechatReceiver;
+import org.jeecg.modules.system.app.entity.AppProfitSharingInfo;
+import org.jeecg.modules.system.util.SecurityUtil;
 
+import java.lang.reflect.Type;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.LocalDate;
@@ -12,6 +23,8 @@ import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.FormatStyle;
 import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
 
 public class SM3Util {
     private static final String ENCODING = "UTF-8";
@@ -33,9 +46,12 @@ public class SM3Util {
 //
 //    }
 
-    public static void main(String[] args) {
-        BigDecimal total1 = new BigDecimal("99");
-        BigDecimal[] ratios1 = {BigDecimal.ONE, new BigDecimal("1.5"), new BigDecimal("97.50")}; // 总和100 → 直接分配
+    public static void main(String[] args) throws Exception {
+
+
+
+        BigDecimal total1 = new BigDecimal("0.09");
+        BigDecimal[] ratios1 = {new BigDecimal("69.4"), new BigDecimal("20"), new BigDecimal("10.6")}; // 总和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));
     }

+ 25 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/quartz/job/OrderExpiredJobService.java

@@ -0,0 +1,25 @@
+package org.jeecg.modules.quartz.job;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+
+import javax.transaction.Transactional;
+
+/**
+ * 订单过期定时任务
+ */
+@Slf4j
+public class OrderExpiredJobService {
+    @Scheduled(cron = "0 0 * * * ?")
+    @Transactional(rollbackOn = Exception.class)
+    public void profitSharingExecute() {
+        log.info("开始执行分账定时任务");
+        try {
+
+            //订单过期
+
+        } catch (Exception e) {
+            log.error("分账定时任务异常", e);
+        }
+    }
+}

+ 4 - 1
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/quartz/job/ProfitSharingJonService.java

@@ -10,6 +10,7 @@ import org.jeecg.modules.system.app.entity.AppOrder;
 import org.jeecg.modules.system.app.mapper.AppOrderMapper;
 import org.jeecg.modules.system.app.service.IAppOrderService;
 import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.time.Instant;
@@ -27,6 +28,7 @@ import static java.lang.Thread.sleep;
  */
 @Slf4j
 @AllArgsConstructor
+@Component
 public class ProfitSharingJonService {
 
     private final WeChatProfitSharingService weChatProfitSharingService;
@@ -59,7 +61,7 @@ public class ProfitSharingJonService {
                 if (validRange) {
                     //分账
                     JSONObject jsonObject = weChatProfitSharingService.profitSharing(appOrder.getOrderCode());
-
+                    log.info("订单:{}================分账结果:{}",appOrder.getOrderCode(),jsonObject);
                 }
             }
         } catch (Exception e) {
@@ -81,6 +83,7 @@ public class ProfitSharingJonService {
                 sleep(1000);
                 //查询分账结果
                 JSONObject profitSharingResult = weChatProfitSharingService.getProfitSharingResult(appOrder.getOrderCode());
+                log.info("订单:{}================分账结果:{}",appOrder.getOrderCode(),profitSharingResult);
             }
         } catch (Exception e) {
             log.error("分账结果查询定时任务异常", e);

+ 1 - 2
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/redission/RedissonConfig.java

@@ -33,7 +33,6 @@ public class RedissonConfig {
         serverConfig.setPingConnectionInterval(30000);
         config.useSingleServer();
         config.setEventLoopGroup(new NioEventLoopGroup(16));
-        RedissonClient redisson = Redisson.create(config);
-        return redisson;
+        return Redisson.create(config);
     }
 }

+ 56 - 2
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/redission/RedissonDelayQueue.java

@@ -43,10 +43,14 @@ public class RedissonDelayQueue {
     private RDelayedQueue<String> delayQueue;
     private RBlockingQueue<String> blockingQueue;
 
+    private RDelayedQueue<String> expireDelayQueue;
+    private RBlockingQueue<String> expireBlockingQueue;
+
     @PostConstruct
     public void init() {
         initDelayQueue();
         startDelayQueueConsumer();
+        startExpireDelayQueueConsumer();
     }
 
     private void initDelayQueue() {
@@ -54,6 +58,9 @@ public class RedissonDelayQueue {
 
         blockingQueue = redissonClient.getBlockingQueue("OrderTimeOutTask");
         delayQueue = redissonClient.getDelayedQueue(blockingQueue);
+
+        expireBlockingQueue = redissonClient.getBlockingQueue("OrderExpireTask");
+        expireDelayQueue = redissonClient.getDelayedQueue(expireBlockingQueue);
     }
 
     private void startDelayQueueConsumer() {
@@ -69,13 +76,13 @@ public class RedissonDelayQueue {
                     if(ObjectUtil.isNotEmpty(appOrder)){
                         if (Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_0) && appOrder.getRevision() == 0) {
                             log.info("修改订单:{},支付状态为已取消", orderId);
-                            appOrder.setOrderStatus(4);
+                            appOrder.setOrderStatus(CommonConstant.ORDER_STATUS_4);
                             appOrderService.updateById(appOrder);
                             //修改子订单状态
                             List<AppOrderProInfo> appOrderProInfoList = appOrderProInfoService.list(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, orderId));
                             if (CollUtil.isNotEmpty(appOrderProInfoList)){
                                 for (AppOrderProInfo appOrderProInfo : appOrderProInfoList) {
-                                    appOrderProInfo.setOrderStatus(4);
+                                    appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_4);
                                     appOrderProInfoService.updateById(appOrderProInfo);
                                 }
                             }
@@ -89,9 +96,56 @@ public class RedissonDelayQueue {
         }, "OrderTimeOutTask-Consumer").start();
     }
 
+    private void startExpireDelayQueueConsumer() {
+        new Thread(() -> {
+            while (true) {
+                try {
+                    String task = expireBlockingQueue.take();
+                    log.info("接收到延迟任务:{}", task);
+
+                    //执行业务代码
+                    String orderId = Arrays.stream(task.split("_")).collect(Collectors.toList()).get(1);
+                    AppOrder appOrder = appOrderService.getById(orderId);
+                    if(ObjectUtil.isNotEmpty(appOrder)){
+                        if (Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_0) && appOrder.getRevision() == 0) {
+
+                            //修改子订单状态
+                            List<AppOrderProInfo> appOrderProInfoList = appOrderProInfoService.list(Wrappers.<AppOrderProInfo>lambdaQuery()
+                                    .eq(AppOrderProInfo::getOrderId, orderId)
+                            );
+                            if (CollUtil.isNotEmpty(appOrderProInfoList)){
+                                for (AppOrderProInfo appOrderProInfo : appOrderProInfoList) {
+                                    if (Objects.equals(appOrderProInfo.getOrderStatus(), CommonConstant.ORDER_STATUS_1)){
+                                        log.info("修改订单:{},支付状态为已过期", orderId);
+                                        appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_3);
+                                        appOrderProInfoService.updateById(appOrderProInfo);
+                                    }
+                                }
+                            }
+                            if (appOrderProInfoList.stream().filter(appOrderProInfo -> Objects.equals(appOrderProInfo.getOrderStatus(),
+                                    CommonConstant.ORDER_STATUS_3)).count() == appOrderProInfoList.size()){
+                                log.info("修改订单:{},支付状态为已过期", orderId);
+                                appOrder.setOrderStatus(CommonConstant.ORDER_STATUS_4);
+                                appOrderService.updateById(appOrder);
+                            }
+                        }
+
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }, "OrderExpireTask-Consumer").start();
+    }
+
     public void offerTask(String task, long seconds) {
         log.info("添加延迟任务:{} 延迟时间:{}s", task, seconds);
         delayQueue.offer(task, seconds, TimeUnit.SECONDS);
     }
 
+    public void offerExpireTask(String task, long days) {
+        log.info("添加延迟任务:{} 延迟时间:{}天", task, days);
+        expireDelayQueue.offer(task, days, TimeUnit.DAYS);
+    }
+
 }

+ 16 - 1
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/controller/AppOrderController.java

@@ -156,11 +156,26 @@ public class AppOrderController extends JeecgController<AppOrder, IAppOrderServi
     @AutoLog(value = "分账")
     @Operation(summary = "分账")
 //    @RequiresPermissions("org.jeecg.modules.app:nm_order:profitSharing")
-    @DeleteMapping(value = "/profitSharing")
+    @PutMapping(value = "/profitSharing")
     public Result<JSONObject> profitSharing(@RequestParam(name = "orderCode", required = true) String orderCode) throws Exception {
         JSONObject jsonObject = weChatProfitSharingService.profitSharing(orderCode);
         return Result.OK(jsonObject);
     }
+
+    /**
+     * 分账结果更新
+     *
+     * @param orderCode
+     * @return
+     * @throws Exception
+     */
+    @AutoLog(value = "分账结果更新")
+    @Operation(summary = "分账结果更新")
+    @PutMapping(value = "/profitSharingReflush")
+    public Result<JSONObject> profitSharingReflush(@RequestParam(name = "orderCode", required = true) String orderCode) throws Exception {
+        JSONObject jsonObject = weChatProfitSharingService.getProfitSharingResult(orderCode);
+        return Result.OK(jsonObject);
+    }
 	
 	/**
 	 *  批量删除

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

@@ -88,6 +88,11 @@ public class AppProfitSharingInfo implements Serializable {
     @Schema(description = "分账状态")
     private String status;
 
+    /**
+     * 保险金额
+     */
+    private Integer insureAmount;
+
     /**
      * 分账结果描述
      */

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

@@ -2,8 +2,11 @@ package org.jeecg.modules.system.app.mapper;
 
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
 import org.jeecg.modules.system.app.entity.AppCoursesVerificationRecord;
 
+import java.util.List;
+
 /**
  * @Description: nm_courses_verification_record
  * @Author: jeecg-boot
@@ -12,4 +15,5 @@ import org.jeecg.modules.system.app.entity.AppCoursesVerificationRecord;
  */
 public interface AppCoursesVerificationRecordMapper extends BaseMapper<AppCoursesVerificationRecord> {
 
+    List<AppCoursesVerificationRecord> courseQueryUsersList(@Param("coursePriceRulesId") String coursePriceRulesId, @Param("orPostpone") Integer orPostpone, @Param("verifyStatus") Integer verifyStatus);
 }

+ 23 - 4
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/mapper/xml/AppCoursesVerificationRecordMapper.xml

@@ -1,5 +1,24 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="org.jeecg.modules.demo.org.jeecg.modules.system.app.mapper.NmCoursesVerificationRecordMapper">
-
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="org.jeecg.modules.system.app.mapper.AppCoursesVerificationRecordMapper">
+    <!-- 根据课程价格规则ID、机构代码、是否延期、审核状态查询用户课程验证记录 -->
+    <select id="courseQueryUsersList" resultType="org.jeecg.modules.system.app.entity.AppCoursesVerificationRecord">
+        SELECT
+        cvr.*
+        FROM
+        `nm_courses_verification_record` cvr
+        LEFT JOIN nm_order o ON o.id = cvr.order_id
+        <where>
+            o.del_flag = 0 AND o.pay_status = 1
+            <if test="coursePriceRulesId != null and coursePriceRulesId != ''">
+                AND cvr.courses_price_rule_id = #{coursePriceRulesId}
+            </if>
+            <if test="orPostpone != null">
+                AND cvr.or_postpone = #{orPostpone}
+            </if>
+            <if test="verifyStatus != null">
+                AND cvr.verify_status = #{verifyStatus}
+            </if>
+        </where>
+    </select>
 </mapper>

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

@@ -10,6 +10,8 @@
         o.create_time,
         s.`name` siteName,
         f.depart_name departName,
+        o.or_profit_sharing,
+        o.profit_sharing_status,
         o.amount,
         o.price,
         o.org_code,

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

@@ -5,6 +5,9 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import org.jeecg.modules.system.app.entity.AppCoursesVerificationRecord;
 import org.jeecg.modules.system.app.form.VerifyForm;
 
+import javax.validation.constraints.NotBlank;
+import java.util.List;
+
 /**
  * @Description: nm_courses_verification_record
  * @Author: jeecg-boot
@@ -14,4 +17,6 @@ import org.jeecg.modules.system.app.form.VerifyForm;
 public interface IAppCoursesVerificationRecordService extends IService<AppCoursesVerificationRecord> {
 
     Boolean courseUploadImage(VerifyForm verifyForm);
+
+    List<AppCoursesVerificationRecord> courseQueryUsersList(@NotBlank(message = "课时ID不能为空") String coursePriceRulesId, Integer orPostpone, Integer verifyStatus);
 }

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

@@ -118,4 +118,9 @@ public class AppCoursesVerificationRecordServiceImpl extends ServiceImpl<AppCour
         }
         return Boolean.TRUE;
     }
+
+    @Override
+    public List<AppCoursesVerificationRecord> courseQueryUsersList(String coursePriceRulesId, Integer orPostpone, Integer verifyStatus) {
+        return baseMapper.courseQueryUsersList(coursePriceRulesId,orPostpone, verifyStatus);
+    }
 }

+ 12 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/vo/OrderPageVO.java

@@ -56,6 +56,18 @@ public class OrderPageVO implements Serializable {
     @Schema(description = "orgCode")
     private String orgCode;
 
+    /**
+     * 是否分账(0-否 1-是)
+     */
+    @Schema(description = "是否分账(0-否 1-是)")
+    private Integer orProfitSharing;
+
+    /**
+     * 分账状态
+     */
+    @Schema(description = "分账状态 0-未分账 1-分账中 2-已分账")
+    private Integer profitSharingStatus;
+
     @Schema(description = "子订单信息")
     private List<AppOrderProInfo> orderProInfoList;
 }