chao пре 3 година
родитељ
комит
c1c2930d4f

+ 19 - 2
src/main/java/com/caimei365/order/mapper/PayOrderMapper.java

@@ -1,5 +1,6 @@
 package com.caimei365.order.mapper;
 
+import com.caimei365.order.model.po.OrderReceiptRelationPo;
 import com.caimei365.order.model.po.PayShopPo;
 import com.caimei365.order.model.po.PayShopRecordPo;
 import com.caimei365.order.model.po.SplitAccountPo;
@@ -92,7 +93,6 @@ public interface PayOrderMapper {
     void updateSplitAccountByPay(String mbOrderId);
     /**
      * 修改收款分账状态
-     * @param mbOrderId
      */
     void updateBySplitStatus(String mbOrderId);
     /**
@@ -123,5 +123,22 @@ public interface PayOrderMapper {
      * 保存付供应商记录
      */
     void insertPayShopRecord(PayShopRecordPo shopRecord);
-
+    /**
+     * 查询未分账已支付收款
+     */
+    List<OrderReceiptRelationPo> getUndividedPaidReceipt(String currentTime);
+    /**
+     * 查询已支付金额
+     * @param orderProductId 订单商品Id
+     */
+    Double getOrderProductPaidAmount(Integer orderProductId);
+    /**
+     * 查询子商户商编
+     * @param shopId 供应商Id
+     */
+    String getShopCommercialCode(Integer shopId);
+    /**
+     * 查询已支付运费
+     */
+    Double getPaidShipping(Integer orderId, Integer shopId);
 }

+ 6 - 1
src/main/java/com/caimei365/order/model/po/OrderReceiptRelationPo.java

@@ -3,7 +3,6 @@ package com.caimei365.order.model.po;
 import lombok.Data;
 
 import java.io.Serializable;
-import java.math.BigDecimal;
 
 /**
  * 收款项和订单关系表
@@ -50,4 +49,10 @@ public class OrderReceiptRelationPo implements Serializable {
      * 分账状态:0待分账,1已分账(只针对线上支付)
      */
     private Integer splitStatus;
+    /**
+     * 付款类型:1建设银行7297、2广发银行0115、3中信银行7172、4中信银行0897、5中信银行0897-财付通、6中信银行0897-支付宝、
+     * 7线上-支付宝、8线上-微信支付、9线上-快钱支付、10口头返佣、11广发银行5461、12PC-B2B网银、13PC-微信支付、14PC-支付宝、
+     * 15小程序-微信支付、16余额抵扣
+     */
+    private Integer payType;
 }

+ 5 - 0
src/main/java/com/caimei365/order/service/PayOrderService.java

@@ -134,6 +134,11 @@ public interface PayOrderService {
      * 二手发布支付回调
      */
     String paymentSecondCallback(String data) throws NoSuchAlgorithmException, InvalidKeySpecException;
+
+    /**
+     * 延时分账
+     */
+    void delayedSplitting(String callUrl);
     /**
      * 延时分账异步通知回调
      */

+ 253 - 12
src/main/java/com/caimei365/order/service/impl/PayOrderServiceImpl.java

@@ -1,5 +1,6 @@
 package com.caimei365.order.service.impl;
 
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.caimei365.order.components.OrderCommonService;
 import com.caimei365.order.components.ProductService;
@@ -36,6 +37,7 @@ import java.security.NoSuchAlgorithmException;
 import java.security.spec.InvalidKeySpecException;
 import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -61,13 +63,13 @@ public class PayOrderServiceImpl implements PayOrderService {
     private WeChatService weChatService;
     @Resource
     private RemoteCallService remoteCallService;
-    @Value("${caimei.redirectLink}")
+    @Value("${pay.redirect-link}")
     private String redirectLink;
-    @Value("${caimei.linkPage}")
+    @Value("${pay.link-page}")
     private String linkPage;
-    @Value("${caimei.notifyUrl}")
+    @Value("${pay.notify-url}")
     private String notifyUrl;
-    @Value("${caimei.secondHandUrl}")
+    @Value("${pay.second-notify-url}")
     private String secondHandUrl;
 
     /**
@@ -231,7 +233,7 @@ public class PayOrderServiceImpl implements PayOrderService {
         List<DiscernReceiptVo> discernReceiptList = orderCommonMapper.getDiscernReceipt(order.getOrderId(), order.getShopOrderIds());
         if (!discernReceiptList.isEmpty()) {
             AtomicDouble receiptAmount = new AtomicDouble(0d);
-            AtomicReference<Boolean> offlineFlag = new AtomicReference<>(false);
+            AtomicBoolean offlineFlag = new AtomicBoolean(false);
             discernReceiptList.forEach(discernReceipt -> {
                 if (2 == discernReceipt.getPayWay()) {
                     offlineFlag.set(true);
@@ -432,7 +434,7 @@ public class PayOrderServiceImpl implements PayOrderService {
      *                payAmount     支付金额,单位分,必须大于2
      *                returnUrl     页面回调地址
      *                }
-     * @param headers
+     * @param headers HttpHeaders
      */
     @Override
     public ResponseJson<JSONObject> payByAlipay(PayDto payDto, HttpHeaders headers) {
@@ -457,7 +459,7 @@ public class PayOrderServiceImpl implements PayOrderService {
      *                bankCode      银行编码(银联支付使用)
      *                userType      用户类型(银联支付使用)企业:ENTERPRISE,个人:USER
      *                }
-     * @param headers
+     * @param headers HttpHeaders
      */
     @Override
     public ResponseJson<JSONObject> payByUnionPay(PayDto payDto, HttpHeaders headers) {
@@ -502,7 +504,7 @@ public class PayOrderServiceImpl implements PayOrderService {
             return ResponseJson.error("付款金额错误!", null);
         }
         // 定义支付请求结果
-        JSONObject result = null;
+        JSONObject result;
         try {
             // 时间戳
             long time = System.currentTimeMillis() / 1000;
@@ -512,7 +514,7 @@ public class PayOrderServiceImpl implements PayOrderService {
             json.put("merAccount", PayUtil.merAccount);
             json.put("merNo", PayUtil.merNo);
             json.put("time", time);
-            //支付金额
+            // 支付金额
             json.put("amount", payParam.getPayAmount());
             json.put("payWay", payParam.getPayWay());
             json.put("payType", payParam.getPayType());
@@ -571,7 +573,7 @@ public class PayOrderServiceImpl implements PayOrderService {
         log.info("******************** 支付异步回调 start *******************");
         // 公钥解密
         JSONObject json = PayUtil.publicKeyDecrypt(data, PayUtil.publicKey);
-        log.info("公钥解密>>>>>>" + json);
+        log.info("【支付异步回调】>>>>>>>>>>>>>>公钥解密:" + json);
         // 公钥验签
         String signaa = json.getString("sign");
         json.remove("sign");
@@ -696,7 +698,7 @@ public class PayOrderServiceImpl implements PayOrderService {
         // 商品数据
         List<OrderProductVo> orderProductList = orderCommonMapper.getOrderProductByOrderId(orderId);
         // 判断是否是充值商品
-        Integer rechargeFlag = 0;
+        int rechargeFlag = 0;
         // 缴纳订金订单
         int[] productId1 = {6060, 6061, 6062, 6063, 6064};
         // 充值余额订单
@@ -1097,6 +1099,244 @@ public class PayOrderServiceImpl implements PayOrderService {
         return "SUCCESS";
     }
 
+    /**
+     * 延时分账
+     *
+     * @param callUrl  回调地址
+     */
+    @Override
+    public void delayedSplitting(String callUrl) {
+        log.info("【延时分账】>>>>>>>>>>延时分账,每一小时执行一次");
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        calendar.add(Calendar.DAY_OF_MONTH, -1);
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String currentTime = format.format(calendar.getTime());
+        // 查询未分账已支付收款
+        List<OrderReceiptRelationPo> orderRelations = payOrderMapper.getUndividedPaidReceipt(currentTime);
+        if (orderRelations != null && orderRelations.size() > 0) {
+            for (OrderReceiptRelationPo orderRelation : orderRelations) {
+                log.info("【延时分账】>>>>>>>>>>订单id:" + orderRelation.getOrderId() + ",进入延时分账");
+                // 订单信息
+                OrderVo order = payOrderMapper.getOrderByOrderId(orderRelation.getOrderId());
+                PayParamBo payParam = new PayParamBo();
+                payParam.setPayAmount(MathUtil.mul(orderRelation.getAssociateAmount(), 100).intValue());
+                if (12 == orderRelation.getPayType()) {
+                    // 网银支付
+                    payParam.setPayWay("UNIONPAY");
+                }
+                List<SplitAccountPo> splitBillDetail = setSplitAccountDetail(order, payParam);
+                List<Map<String, String>> maps = new ArrayList<>();
+                List<ShopOrderVo> shopOrderList = payOrderMapper.getShopOrderListByOrderId(order.getOrderId());
+                //供应商子商户总金额
+                for (ShopOrderVo shopOrder : shopOrderList) {
+                    double shopTotalAmount = 0.00;
+                    String subUserNo = "";
+                    for (SplitAccountPo account : splitBillDetail) {
+                        if (4 == account.getType() && shopOrder.getShopId().equals(account.getShopId())) {
+                            shopTotalAmount = MathUtil.add(shopTotalAmount, account.getSplitAccount()).doubleValue();
+                            subUserNo = account.getSubUserNo();
+                        }
+                    }
+                    addMaps(maps, shopTotalAmount, subUserNo);
+                }
+                //公账-专票总金额,私账-无票总金额,公账-普票总金额
+                double totalAmount1 = 0.00;
+                double totalAmount2 = 0.00;
+                double totalAmount3 = 0.00;
+                for (SplitAccountPo account : splitBillDetail) {
+                    if (1 == account.getType()) {
+                        totalAmount1 = MathUtil.add(totalAmount1, account.getSplitAccount()).doubleValue();
+                    } else if (2 == account.getType()) {
+                        totalAmount2 = MathUtil.add(totalAmount2, account.getSplitAccount()).doubleValue();
+                    } else if (3 == account.getType()) {
+                        totalAmount3 = MathUtil.add(totalAmount3, account.getSplitAccount()).doubleValue();
+                    }
+                }
+                addMaps(maps, totalAmount1, PayUtil.publicAccountNo);
+                addMaps(maps, totalAmount2, PayUtil.privateAccountNo);
+                addMaps(maps, totalAmount3, PayUtil.commonInvoiceNo);
+                String parameters = JSON.toJSONString(maps);
+                log.info("【延时分账】>>>>>>>>>>分账参数: " + parameters);
+
+                //第三方分账接口
+                JSONObject result = null;
+                try {
+                    // 时间戳
+                    long time = System.currentTimeMillis() / 1000;
+                    JSONObject json = new JSONObject();
+                    json.put("merAccount", PayUtil.merAccount);
+                    json.put("orderId", orderRelation.getOrderRequestNo());
+                    json.put("requestNo", orderRelation.getOrderRequestNo());
+                    json.put("mbOrderId", orderRelation.getMbOrderId());
+                    json.put("time", time);
+                    json.put("splitBillDetail", parameters);
+                    json.put("notifyUrl", notifyUrl);
+                    log.info("回调接口>>>" + notifyUrl);
+                    String sign = PayUtil.getPaySign(json, PayUtil.merKey);
+                    json.put("sign", sign);
+                    String data = PayUtil.privateKeyEncrypt(json, PayUtil.merKey);
+                    result = PayUtil.httpGet("https://platform.mhxxkj.com/paygateway/mbpay/splitOrder/v1", PayUtil.merAccount, data);
+                } catch (Exception e) {
+                    log.error("【延时分账】>>>>>>>>>>错误信息", e);
+                }
+                if (result != null) {
+                    String code = result.getString("code");
+                    if (!"000000".equals(code)) {
+                        String msg = result.getString("msg");
+                        log.info("【延时分账】>>>>>>>>>>第三方延迟分账失败>>>>>>>msg:" + msg);
+                    } else {
+                        for (SplitAccountPo splitAccount : splitBillDetail) {
+                            splitAccount.setMbOrderId(orderRelation.getMbOrderId());
+                            splitAccount.setOrderRequestNo(orderRelation.getOrderRequestNo());
+                            splitAccount.setPayStatus(1);
+                            // 保存分账详情
+                            payOrderMapper.insertSplitAccount(splitAccount);
+                        }
+                        log.info("【延时分账】>>>>>>>>>>此订单分账结束");
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 分账参数添加
+     */
+    private void addMaps(List<Map<String, String>> maps, Double shopTotalAmount, String subUserNo) {
+        if (MathUtil.compare(shopTotalAmount, 0) > 0) {
+            Map<String, String> map = new HashMap<>(3);
+            map.put("subUserNo", subUserNo);
+            map.put("splitBillType", "2");
+            map.put("splitBillValue", MathUtil.mul(shopTotalAmount, 100).toString());
+            maps.add(map);
+        }
+    }
+
+    /**
+     * 分账详情
+     */
+    private List<SplitAccountPo> setSplitAccountDetail(OrderVo order, PayParamBo payParam) {
+        List<SplitAccountPo> list = new ArrayList<>();
+        // 本次支付金额,单位/元
+        double payAmount = MathUtil.div(payParam.getPayAmount(), 100).doubleValue();
+        // 待分账总金额
+        double splitAmount = payAmount;
+        // 总手续费
+        double procedureFee = 0.00;
+        if ("UNIONPAY".equals(payParam.getPayWay())) {
+            procedureFee = 8.00;
+        } else {
+            //手续费
+            procedureFee = MathUtil.mul(payAmount, 0.0038, 2).doubleValue();
+            if (MathUtil.compare(procedureFee, 0) == 0) {
+                procedureFee = 0.01;
+            }
+        }
+        splitAmount = MathUtil.sub(splitAmount, procedureFee).doubleValue();
+        // 商品数据
+        List<OrderProductVo> orderProductList = orderCommonMapper.getOrderProductByOrderId(order.getOrderId());
+        for (OrderProductVo orderProduct : orderProductList) {
+            double costPrice = MathUtil.mul(orderProduct.getCostPrice(), orderProduct.getNum()).doubleValue();
+            // 不含税能开发票
+            if (0 == orderProduct.getIncludedTax() && 3 != orderProduct.getInvoiceType()) {
+                //应付总税费
+                Double payableTax = MathUtil.mul(orderProduct.getSingleShouldPayTotalTax(), orderProduct.getNum()).doubleValue();
+                costPrice = MathUtil.add(costPrice, payableTax).doubleValue();
+            }
+            // 判断是否支付过
+            Double paidAmount = payOrderMapper.getOrderProductPaidAmount(orderProduct.getOrderProductId());
+            if (paidAmount != null && MathUtil.compare(paidAmount, 0) > 0) {
+                costPrice = MathUtil.sub(costPrice, paidAmount).doubleValue();
+            }
+            if (paidAmount == null || MathUtil.compare(paidAmount, costPrice) < 0) {
+                // 待分账金额>=本次待分账金额
+                if (MathUtil.compare(splitAmount, costPrice) > -1) {
+                    splitAmount = MathUtil.sub(splitAmount, costPrice).doubleValue();
+                } else {
+                    costPrice = splitAmount;
+                    splitAmount = 0.00;
+                }
+                String commercialCode = payOrderMapper.getShopCommercialCode(orderProduct.getShopId());
+                SplitAccountPo splitAccount = new SplitAccountPo();
+                splitAccount.setOrderId(order.getOrderId());
+                splitAccount.setOrderProductId(orderProduct.getOrderProductId());
+                splitAccount.setShopId(orderProduct.getShopId());
+                splitAccount.setSplitAccount(costPrice);
+                splitAccount.setProductType(1);
+                if (StringUtils.isNotBlank(commercialCode)) {
+                    // 供应商拥有子商户号
+                    splitAccount.setType(4);
+                    splitAccount.setSubUserNo(commercialCode);
+                } else {
+                    if (3 == orderProduct.getInvoiceType()) {
+                        // 不能开票,则分账到私账-无票
+                        splitAccount.setType(2);
+                        splitAccount.setSubUserNo(PayUtil.privateAccountNo);
+                    } else if (1 == orderProduct.getInvoiceType()) {
+                        // 开增值税发票,则分账到公账-专票
+                        splitAccount.setType(1);
+                        splitAccount.setSubUserNo(PayUtil.publicAccountNo);
+                    } else if (2 == orderProduct.getInvoiceType()) {
+                        // 开普通发票,则分账到公账-普票
+                        splitAccount.setType(3);
+                        splitAccount.setSubUserNo(PayUtil.commonInvoiceNo);
+                    }
+                }
+                list.add(splitAccount);
+                if (MathUtil.compare(splitAmount, 0) == 0) {
+                    break;
+                }
+            }
+        }
+        // 付供应商运费,是以供应商为单位的
+        if (MathUtil.compare(splitAmount, 0) > 0) {
+            List<ShopOrderVo> shopOrderList = payOrderMapper.getShopOrderListByOrderId(order.getOrderId());
+            for (ShopOrderVo shopOrder : shopOrderList) {
+                // 运费
+                Double shopPostFee = shopOrder.getShopPostFee();
+                if (MathUtil.compare(shopPostFee, 0) > 0) {
+                    // 查询已支付运费
+                    Double shipping = payOrderMapper.getPaidShipping(order.getOrderId(), shopOrder.getShopId());
+                    shopPostFee = MathUtil.sub(shopPostFee, shipping).doubleValue();
+                    if (MathUtil.compare(splitAmount, shopPostFee) > -1) {
+                        splitAmount = MathUtil.sub(splitAmount, shopPostFee).doubleValue();
+                    } else {
+                        shopPostFee = splitAmount;
+                        splitAmount = 0.00;
+                    }
+                    String commercialCode = payOrderMapper.getShopCommercialCode(shopOrder.getShopId());
+                    SplitAccountPo splitAccount = new SplitAccountPo();
+                    splitAccount.setOrderId(order.getOrderId());
+                    splitAccount.setShopId(shopOrder.getShopId());
+                    splitAccount.setSplitAccount(shopPostFee);
+                    splitAccount.setProductType(2);
+                    if (StringUtils.isNotBlank(commercialCode)) {
+                        //供应商拥有子商户号
+                        splitAccount.setType(4);
+                        splitAccount.setSubUserNo(commercialCode);
+                    } else {
+                        //私账
+                        splitAccount.setType(2);
+                        splitAccount.setSubUserNo(PayUtil.privateAccountNo);
+                    }
+                    list.add(splitAccount);
+                }
+            }
+        }
+        //佣金,公账
+        if (MathUtil.compare(splitAmount, 0) > 0) {
+            SplitAccountPo splitAccount = new SplitAccountPo();
+            splitAccount.setOrderId(order.getOrderId());
+            splitAccount.setSplitAccount(splitAmount);
+            splitAccount.setProductType(3);
+            splitAccount.setType(1);
+            splitAccount.setSubUserNo(PayUtil.publicAccountNo);
+            list.add(splitAccount);
+        }
+        return list;
+    }
+
     /**
      * 延时分账异步通知回调
      */
@@ -1181,11 +1421,12 @@ public class PayOrderServiceImpl implements PayOrderService {
                     shopRecord.setDelFlag(0);
                     payOrderMapper.insertPayShopRecord(shopRecord);
                 }
-                //子订单是否全部付款
+                // 子订单是否全部付款
                 boolean isPay = true;
                 for (ShopOrderVo shopOrder : shopOrderList) {
                     if (3 != shopOrder.getPayStatus()) {
                         isPay = false;
+                        break;
                     }
                 }
                 // 修改主订单付款状态

+ 33 - 0
src/main/java/com/caimei365/order/task/DelayedSplittingTask.java

@@ -0,0 +1,33 @@
+package com.caimei365.order.task;
+
+import com.caimei365.order.service.PayOrderService;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 延时分账定时器
+ *
+ * @author : Charles
+ * @date : 2021/8/3
+ */
+@Component
+@EnableScheduling
+public class DelayedSplittingTask {
+    @Resource
+    private PayOrderService payOrderService;
+
+    @Value("${pay.delayed-splitting-url}")
+    private String delayedSplittingUrl;
+
+    /**
+     * 延时分账,每一小时执行一次
+     */
+    @Scheduled(cron = "0 0 * * * ?")
+    public void delayedSplitting() {
+        payOrderService.delayedSplitting(delayedSplittingUrl);
+    }
+}

+ 1 - 1
src/main/resources/mapper/BaseMapper.xml

@@ -193,7 +193,7 @@
         VALUES (#{payWay}, #{payType}, #{receiptType}, #{receiptStatus}, #{receiptAmount}, #{confirmType},
         #{receiptDate}, #{confirmDate}, #{reviewDate}, #{updateDate}, #{delFlag}, #{rePayFlag}, #{formData})
     </insert>
-    <insert id="insertOrderReceiptRelation" keyColumn="id" keyProperty="id" parameterType="com.caimei365.order.model.po.DiscernReceiptPo" useGeneratedKeys="true">
+    <insert id="insertOrderReceiptRelation" keyColumn="id" keyProperty="id" parameterType="com.caimei365.order.model.po.OrderReceiptRelationPo" useGeneratedKeys="true">
         INSERT INTO cm_receipt_order_relation (relationType, receiptId, associateAmount, orderId, delFlag, mbOrderId, orderRequestNo, splitStatus)
         VALUES (#{relationType}, #{receiptId}, #{associateAmount}, #{orderId}, #{delFlag}, #{mbOrderId}, #{orderRequestNo}, #{splitStatus})
     </insert>

+ 35 - 0
src/main/resources/mapper/PayOrderMapper.xml

@@ -236,5 +236,40 @@
         FROM cm_pay_shop_record
         WHERE STATUS = 1 AND delFlag = 0 AND shopOrderID = #{shopOrderId}
     </select>
+    <select id="getUndividedPaidReceipt" resultType="com.caimei365.order.model.po.OrderReceiptRelationPo">
+        SELECT
+            cror.id,
+            cror.relationType,
+            cror.receiptId,
+            cror.associateAmount,
+            cror.orderId,
+            cror.delFlag,
+            cror.mbOrderId,
+            cror.orderRequestNo,
+            cror.splitStatus,
+            cdr.payType
+        FROM cm_receipt_order_relation cror
+        LEFT JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+        LEFT JOIN cm_order co ON cror.orderID = co.orderID
+        WHERE cror.relationType = 2 AND cror.delFlag = 0 AND cror.mbOrderId IS NOT NULL AND cror.splitStatus = 0
+        AND cdr.payWay = 1 AND cdr.receiptDate <![CDATA[  <=  ]]> #{currentTime}
+        AND co.organizeID = 0 AND co.orderType != 2 AND co.refundType != 2
+    </select>
+    <select id="getOrderProductPaidAmount" resultType="java.lang.Double">
+        SELECT SUM(splitAccount)
+        FROM cm_split_account
+        WHERE orderProductId = #{orderProductId}
+        AND payStatus = 1 AND productType = 1
+    </select>
+    <select id="getShopCommercialCode" resultType="java.lang.String">
+        SELECT commercialCode FROM shop WHERE shopID = #{shopId}
+    </select>
+    <select id="getPaidShipping" resultType="java.lang.Double">
+        SELECT SUM(splitAccount)
+        FROM cm_split_account
+        WHERE orderId = #{orderId}
+        AND shopId = #{shopId}
+        AND productType = 2 AND payStatus = 1
+    </select>
 
 </mapper>