diff --git a/_doc/Task.md b/_doc/Task.md index 6fd38ed09..542a0162a 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -14,22 +14,26 @@ - [x] 分账查询 - [x] 分账完结 - [x] 分账同步 +- [ ] 保存分账同步记录 - [ ] SDK支持分账相关接口 - [ ] DEMO增加获取微信OpenID和支付宝OpenId功能 - [ ] 分账回调处理 - [ ] 分账通知发送功能 -- [ ] 保存分账同步记录 - [x] 分账支持手动和自动分账两种 - [ ] 金额过小不进行分账, 增加新状态, 金额小于0.01元直接忽略 - [ ] 增加收单收银台功能 +- [x] 优化签名注解和上下文初始化注解切面 - [x] 对账差异单, 数据不一致处理异常, 本地待对账订单类型记录错误 -- [x] 订单超时任务 -- 2.0.7: 对账完善和系统优化 +- [x] 订单超时任务注册任务错误,id改为订单号 +- [x] 系统中金额分转元精度异常问题 + +2.0.7: 对账完善和系统优化 - [ ] 对账提供外部接口调用 - [ ] 下载系统账单 - [ ] 增加资金对账单功能 - [ ] 支付通道两个独立的配置进行合并为一个 - [ ] 撤销接口 + 2.1.x 版本内容 - [ ] 差错单据处理 - [ ] 特殊退款接口 diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/util/PayUtil.java b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/util/PayUtil.java index e8d4d5553..bdf9a87cb 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/util/PayUtil.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/util/PayUtil.java @@ -6,6 +6,8 @@ import cn.daxpay.single.param.payment.pay.PayParam; import cn.hutool.core.date.DatePattern; import lombok.experimental.UtilityClass; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; @@ -16,6 +18,7 @@ import java.time.temporal.ChronoUnit; */ @UtilityClass public class PayUtil { + private static final BigDecimal HUNDRED = new BigDecimal(100); /** * 校验参数 @@ -62,4 +65,23 @@ public class PayUtil { public LocalDateTime getPaymentExpiredTime(Integer minute) { return LocalDateTimeUtil.offset(LocalDateTime.now(), minute, ChronoUnit.MINUTES); } + + /** + * 元转分 + * @param amount 元的金额 + * @return 分的金额 + */ + public static int convertCentAmount(BigDecimal amount) { + return amount.multiply(HUNDRED).setScale(0, RoundingMode.HALF_UP).intValue(); + } + + /** + * 分转元,保留两位小数 + * + * @param amount 元的金额 + * @return 元的金额 两位小数 + */ + public static BigDecimal conversionAmount(int amount) { + return BigDecimal.valueOf(amount).setScale(2, RoundingMode.HALF_UP); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/code/PaymentTypeEnum.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/code/PaymentTypeEnum.java index 6a72152c9..4602f3797 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/code/PaymentTypeEnum.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/code/PaymentTypeEnum.java @@ -18,7 +18,8 @@ public enum PaymentTypeEnum { PAY("pay","支付"), REFUND("refund","退款"), - TRANSFER("transfer","转账"); + TRANSFER("transfer","转账"), + ALLOCATION("allocation","分账"); private final String code; private final String name; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayAllocationService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayAllocationService.java index 3cf70ceea..a6316f8fa 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayAllocationService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayAllocationService.java @@ -8,6 +8,7 @@ import cn.daxpay.single.service.code.AliPayCode; import cn.daxpay.single.service.common.local.PaymentContextLocal; import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrder; import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderDetail; +import cn.daxpay.single.util.PayUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.StrUtil; import com.alipay.api.AlipayResponse; @@ -55,7 +56,7 @@ public class AliPayAllocationService { List royaltyParameters = orderDetails.stream() .map(o -> { OpenApiRoyaltyDetailInfoPojo infoPojo = new OpenApiRoyaltyDetailInfoPojo(); - infoPojo.setAmount(String.valueOf(o.getAmount() / 100.0)); + infoPojo.setAmount(PayUtil.conversionAmount(o.getAmount()).toString()); infoPojo.setTransIn(o.getReceiverAccount()); return infoPojo; }) @@ -89,7 +90,7 @@ public class AliPayAllocationService { List royaltyParameters = orderDetails.stream() .map(o -> { OpenApiRoyaltyDetailInfoPojo infoPojo = new OpenApiRoyaltyDetailInfoPojo(); - infoPojo.setAmount(String.valueOf(o.getAmount() / 100.0)); + infoPojo.setAmount(PayUtil.conversionAmount(o.getAmount()).toString()); infoPojo.setTransIn(o.getReceiverAccount()); return infoPojo; }) diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayReconcileService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayReconcileService.java index 35f03d4b0..777a41a3f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayReconcileService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayReconcileService.java @@ -12,9 +12,10 @@ import cn.daxpay.single.service.core.channel.alipay.dao.AliReconcileBillTotalMan import cn.daxpay.single.service.core.channel.alipay.entity.AliReconcileBillDetail; import cn.daxpay.single.service.core.channel.alipay.entity.AliReconcileBillTotal; import cn.daxpay.single.service.core.order.reconcile.dao.ReconcileFileManager; -import cn.daxpay.single.service.core.order.reconcile.entity.ReconcileTradeDetail; import cn.daxpay.single.service.core.order.reconcile.entity.ReconcileFile; import cn.daxpay.single.service.core.order.reconcile.entity.ReconcileOrder; +import cn.daxpay.single.service.core.order.reconcile.entity.ReconcileTradeDetail; +import cn.daxpay.single.util.PayUtil; import cn.hutool.core.date.DatePattern; import cn.hutool.core.io.IoUtil; import cn.hutool.core.text.csv.CsvReader; @@ -39,6 +40,7 @@ import org.springframework.transaction.annotation.Transactional; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.InputStreamReader; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -174,9 +176,7 @@ public class AliPayReconcileService { */ private ReconcileTradeDetail convert(AliReconcileBillDetail billDetail){ // 金额 - String orderAmount = billDetail.getOrderAmount(); - double v = Double.parseDouble(orderAmount) * 100; - int amount = Math.abs(((int) v)); + int amount = PayUtil.convertCentAmount(new BigDecimal(billDetail.getOrderAmount())); // 默认为支付对账记录 ReconcileTradeDetail reconcileTradeDetail = new ReconcileTradeDetail() diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayRefundService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayRefundService.java index 9d9b1b27e..6ff8c8266 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayRefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayRefundService.java @@ -6,6 +6,7 @@ import cn.daxpay.single.service.code.AliPayCode; import cn.daxpay.single.service.common.context.RefundLocal; import cn.daxpay.single.service.common.local.PaymentContextLocal; import cn.daxpay.single.service.core.order.refund.entity.RefundOrder; +import cn.daxpay.single.util.PayUtil; import com.alipay.api.AlipayApiException; import com.alipay.api.domain.AlipayTradeRefundModel; import com.alipay.api.response.AlipayTradeRefundResponse; @@ -36,7 +37,7 @@ public class AliPayRefundService { refundModel.setOutTradeNo(refundOrder.getOrderNo()); refundModel.setOutRequestNo(refundOrder.getRefundNo()); // 金额转换 - String refundAmount = String.valueOf(refundOrder.getAmount()*0.01); + String refundAmount = PayUtil.conversionAmount(refundOrder.getAmount()).toString(); refundModel.setRefundAmount(refundAmount); // 设置退款信息 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayService.java index 59b6247f8..abeeeee91 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayService.java @@ -6,8 +6,8 @@ import cn.daxpay.single.param.channel.AliPayParam; import cn.daxpay.single.param.payment.pay.PayParam; import cn.daxpay.single.service.code.AliPayCode; import cn.daxpay.single.service.code.AliPayWay; -import cn.daxpay.single.service.common.context.PayLocal; import cn.daxpay.single.service.common.context.NoticeLocal; +import cn.daxpay.single.service.common.context.PayLocal; import cn.daxpay.single.service.common.local.PaymentContextLocal; import cn.daxpay.single.service.core.channel.alipay.entity.AliPayConfig; import cn.daxpay.single.service.core.order.pay.entity.PayOrder; @@ -71,7 +71,7 @@ public class AliPayService { * 调起支付 */ public void pay(PayOrder payOrder, AliPayParam aliPayParam, AliPayConfig alipayConfig) { - Integer amount = payOrder.getAmount(); + String amount = PayUtil.conversionAmount(payOrder.getAmount()).toString(); String payBody = null; // 异步线程存储 PayLocal payInfo = PaymentContextLocal.get().getPayInfo(); @@ -102,12 +102,12 @@ public class AliPayService { /** * wap支付 */ - public String wapPay(int amount, PayOrder payOrder, AliPayConfig alipayConfig) { + public String wapPay(String amount, PayOrder payOrder, AliPayConfig alipayConfig) { NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo(); AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); model.setSubject(payOrder.getTitle()); model.setOutTradeNo(payOrder.getOrderNo()); - model.setTotalAmount(String.valueOf(amount*0.01)); + model.setTotalAmount(amount); // 过期时间 model.setTimeExpire(PayUtil.getAliTimeExpire(payOrder.getExpiredTime())); model.setProductCode(AliPayCode.QUICK_WAP_PAY); @@ -141,7 +141,7 @@ public class AliPayService { /** * app支付 */ - public String appPay(int amount, PayOrder payOrder, AliPayConfig alipayConfig) { + public String appPay(String amount, PayOrder payOrder, AliPayConfig alipayConfig) { AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); model.setSubject(payOrder.getTitle()); @@ -149,7 +149,7 @@ public class AliPayService { model.setOutTradeNo(payOrder.getOrderNo()); // 过期时间 model.setTimeExpire(PayUtil.getAliTimeExpire(payOrder.getExpiredTime())); - model.setTotalAmount(String.valueOf(amount*0.01)); + model.setTotalAmount(amount); // 是否分账 if (payOrder.getAllocation()){ ExtendParams extendParams = new ExtendParams(); @@ -171,14 +171,14 @@ public class AliPayService { /** * PC支付 */ - public String webPay(int amount, PayOrder payOrder, AliPayConfig alipayConfig) { + public String webPay(String amount, PayOrder payOrder, AliPayConfig alipayConfig) { AlipayTradePagePayModel model = new AlipayTradePagePayModel(); model.setSubject(payOrder.getTitle()); model.setOutTradeNo(payOrder.getOrderNo()); // 过期时间 model.setTimeExpire(PayUtil.getAliTimeExpire(payOrder.getExpiredTime())); - model.setTotalAmount(String.valueOf(amount*0.01)); + model.setTotalAmount(amount); // 目前仅支持FAST_INSTANT_TRADE_PAY model.setProductCode(AliPayCode.FAST_INSTANT_TRADE_PAY); @@ -209,11 +209,11 @@ public class AliPayService { /** * 二维码支付(扫码支付) */ - public String qrCodePay(int amount, PayOrder payOrder, AliPayConfig alipayConfig) { + public String qrCodePay(String amount, PayOrder payOrder, AliPayConfig alipayConfig) { AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); model.setSubject(payOrder.getTitle()); model.setOutTradeNo(payOrder.getOrderNo()); - model.setTotalAmount(String.valueOf(amount*0.01)); + model.setTotalAmount(amount); // 是否分账 if (payOrder.getAllocation()){ ExtendParams extendParams = new ExtendParams(); @@ -237,7 +237,7 @@ public class AliPayService { /** * 付款码支付 */ - public void barCode(int amount, PayOrder payOrder, AliPayParam aliPayParam, AliPayConfig alipayConfig) { + public void barCode(String amount, PayOrder payOrder, AliPayParam aliPayParam, AliPayConfig alipayConfig) { PayLocal payInfo = PaymentContextLocal.get().getPayInfo(); AlipayTradePayModel model = new AlipayTradePayModel(); @@ -253,7 +253,7 @@ public class AliPayService { } // 过期时间 model.setTimeExpire(PayUtil.getAliTimeExpire(payOrder.getExpiredTime())); - model.setTotalAmount(String.valueOf(amount*0.01)); + model.setTotalAmount(amount); try { AlipayTradePayResponse response = AliPayApi.tradePayToResponse(model, alipayConfig.getNotifyUrl()); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayRefundService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayRefundService.java index 586d8248d..31a56541e 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayRefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayRefundService.java @@ -7,6 +7,7 @@ import cn.daxpay.single.service.common.local.PaymentContextLocal; import cn.daxpay.single.service.core.order.refund.entity.RefundOrder; import cn.daxpay.single.service.sdk.union.api.UnionPayKit; import cn.daxpay.single.service.sdk.union.bean.UnionRefundOrder; +import cn.daxpay.single.util.PayUtil; import com.egzosn.pay.union.bean.UnionRefundResult; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -31,8 +32,8 @@ public class UnionPayRefundService { public void refund(RefundOrder refundOrder, UnionPayKit unionPayKit) { // 金额转换 - BigDecimal refundAmount = BigDecimal.valueOf(refundOrder.getAmount() * 0.01); - BigDecimal orderAmount = BigDecimal.valueOf(refundOrder.getOrderAmount() * 0.01); + BigDecimal refundAmount = PayUtil.conversionAmount(refundOrder.getAmount()); + BigDecimal orderAmount =PayUtil.conversionAmount(refundOrder.getOrderAmount()); UnionRefundOrder unionRefundOrder = new UnionRefundOrder(); unionRefundOrder.setRefundNo(refundOrder.getRefundNo()); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayService.java index 5ba8f1f05..a9ac01023 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayService.java @@ -12,6 +12,7 @@ import cn.daxpay.single.service.core.channel.union.entity.UnionPayConfig; import cn.daxpay.single.service.core.order.pay.entity.PayOrder; import cn.daxpay.single.service.sdk.union.api.UnionPayKit; import cn.daxpay.single.service.sdk.union.bean.UnionPayOrder; +import cn.daxpay.single.util.PayUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.map.MapUtil; @@ -66,9 +67,8 @@ public class UnionPayService { * 支付接口 */ public void pay(PayOrder payOrder, UnionPayParam unionPayParam, UnionPayKit unionPayKit){ - Integer amount = payOrder.getAmount(); - BigDecimal totalFee = BigDecimal.valueOf(amount * 0.01); - PayLocal payInfo = PaymentContextLocal.get().getPayInfo();; + BigDecimal totalFee = PayUtil.conversionAmount(payOrder.getAmount()); + PayLocal payInfo = PaymentContextLocal.get().getPayInfo(); String payBody = null; PayMethodEnum payMethodEnum = PayMethodEnum.findByCode(payOrder.getMethod()); @@ -198,6 +198,5 @@ public class UnionPayService { String errMsg = MapUtil.getStr(result, UnionPayCode.RESP_MSG); throw new PayFailureException(errMsg); } - } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WechatPayReconcileService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WechatPayReconcileService.java index 03dde25df..ba83153ba 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WechatPayReconcileService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WechatPayReconcileService.java @@ -14,9 +14,10 @@ import cn.daxpay.single.service.core.channel.wechat.entity.WxReconcileBillDetail import cn.daxpay.single.service.core.channel.wechat.entity.WxReconcileBillTotal; import cn.daxpay.single.service.core.channel.wechat.entity.WxReconcileFundFlowDetail; import cn.daxpay.single.service.core.order.reconcile.dao.ReconcileFileManager; -import cn.daxpay.single.service.core.order.reconcile.entity.ReconcileTradeDetail; import cn.daxpay.single.service.core.order.reconcile.entity.ReconcileFile; import cn.daxpay.single.service.core.order.reconcile.entity.ReconcileOrder; +import cn.daxpay.single.service.core.order.reconcile.entity.ReconcileTradeDetail; +import cn.daxpay.single.util.PayUtil; import cn.hutool.core.codec.Base64; import cn.hutool.core.date.DatePattern; import cn.hutool.core.io.IoUtil; @@ -41,6 +42,7 @@ import org.springframework.transaction.annotation.Transactional; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.InputStreamReader; +import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; @@ -195,9 +197,7 @@ public class WechatPayReconcileService{ // 支付 if (Objects.equals(billDetail.getStatus(), "SUCCESS")){ // 金额 - String orderAmount = billDetail.getOrderAmount(); - double v = Double.parseDouble(orderAmount) * 100; - int amount = Math.abs(((int) v)); + int amount = PayUtil.convertCentAmount(new BigDecimal( billDetail.getOrderAmount())); reconcileTradeDetail.setType(ReconcileTradeEnum.PAY.getCode()) .setAmount(amount); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/allocation/service/AllocationService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/allocation/service/AllocationService.java index 42104edcc..b5ef14fd3 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/allocation/service/AllocationService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/allocation/service/AllocationService.java @@ -3,19 +3,15 @@ package cn.daxpay.single.service.core.payment.allocation.service; import cn.bootx.platform.common.core.exception.DataNotExistException; import cn.bootx.platform.common.core.exception.RepetitiveOperationException; import cn.bootx.platform.common.core.util.CollUtil; -import cn.daxpay.single.code.AllocDetailResultEnum; -import cn.daxpay.single.code.AllocOrderResultEnum; import cn.daxpay.single.code.AllocOrderStatusEnum; import cn.daxpay.single.code.PayOrderAllocStatusEnum; import cn.daxpay.single.exception.pay.PayFailureException; import cn.daxpay.single.param.payment.allocation.AllocFinishParam; -import cn.daxpay.single.param.payment.allocation.AllocSyncParam; import cn.daxpay.single.param.payment.allocation.AllocationParam; import cn.daxpay.single.param.payment.allocation.QueryAllocOrderParam; import cn.daxpay.single.result.allocation.AllocOrderDetailResult; import cn.daxpay.single.result.allocation.AllocOrderResult; import cn.daxpay.single.result.allocation.AllocationResult; -import cn.daxpay.single.result.allocation.AllocationSyncResult; import cn.daxpay.single.service.common.local.PaymentContextLocal; import cn.daxpay.single.service.core.order.allocation.convert.AllocationConvert; import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderDetailManager; @@ -31,7 +27,6 @@ import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService; import cn.daxpay.single.service.core.payment.allocation.dao.AllocationGroupManager; import cn.daxpay.single.service.core.payment.allocation.entity.AllocationGroup; import cn.daxpay.single.service.core.payment.allocation.factory.AllocationFactory; -import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService; import cn.daxpay.single.service.dto.allocation.AllocationGroupReceiverResult; import cn.daxpay.single.service.func.AbsAllocationStrategy; import com.baomidou.lock.LockInfo; @@ -39,8 +34,6 @@ import com.baomidou.lock.LockTemplate; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.List; @@ -71,9 +64,9 @@ public class AllocationService { private final PayOrderQueryService payOrderQueryService; - private final LockTemplate lockTemplate; private final AllocationOrderExtraManager allocationOrderExtraManager; - private final ClientNoticeService clientNoticeService; + + private final LockTemplate lockTemplate; /** @@ -249,101 +242,6 @@ public class AllocationService { .setStatus(allocationOrder.getStatus()); } - /** - * 分账同步, 开启一个新的事务, 不受外部抛出异常的影响 - */ - @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) - public AllocationSyncResult sync(AllocSyncParam param) { - // 获取分账订单 - AllocationOrder allocationOrder = null; - if (Objects.nonNull(param.getAllocationNo())){ - allocationOrder = allocationOrderManager.findByAllocationNo(param.getAllocationNo()) - .orElseThrow(() -> new DataNotExistException("分账单不存在")); - } - if (Objects.isNull(allocationOrder)){ - allocationOrder = allocationOrderManager.findByAllocationNo(param.getBizAllocationNo()) - .orElseThrow(() -> new DataNotExistException("分账单不存在")); - } - this.sync(allocationOrder); - return new AllocationSyncResult(); - } - - /** - * 分账同步 - */ - public void sync(AllocationOrder allocationOrder){ - LockInfo lock = lockTemplate.lock("payment:allocation:" + allocationOrder.getOrderId(),10000,200); - if (Objects.isNull(lock)){ - throw new RepetitiveOperationException("分账同步中,请勿重复操作"); - } - try { - List detailList = allocationOrderDetailManager.findAllByOrderId(allocationOrder.getId()); - // 获取分账策略 - AbsAllocationStrategy allocationStrategy = AllocationFactory.create(allocationOrder.getChannel()); - allocationStrategy.initParam(allocationOrder, detailList); - // 分账完结预处理 - allocationStrategy.doBeforeHandler(); - allocationStrategy.doSync(); - // TODO 保存分账同步记录 - - // 根据订单明细更新订单的状态和处理结果 - this.updateOrderStatus(allocationOrder, detailList); - } finally { - lockTemplate.releaseLock(lock); - } - } - - /** - * 根据订单明细更新订单的状态和处理结果, 如果订单是分账结束或失败, 不更新状态 - * TODO 是否多次同步会产生多次变动, 注意处理多次推送通知的问题, 目前是 - */ - private void updateOrderStatus(AllocationOrder allocationOrder, List details){ - // 如果是分账结束或失败, 不更新状态 - String status = allocationOrder.getStatus(); - - // 如果是分账结束或失败, 不进行对订单进行处理 - List list = Arrays.asList(AllocOrderStatusEnum.FINISH.getCode(), AllocOrderStatusEnum.FINISH_FAILED.getCode()); - if (!list.contains(status)){ - // 判断明细状态. 获取成功和失败的 - long successCount = details.stream() - .map(AllocationOrderDetail::getResult) - .filter(AllocDetailResultEnum.SUCCESS.getCode()::equals) - .count(); - long failCount = details.stream() - .map(AllocationOrderDetail::getResult) - .filter(AllocDetailResultEnum.FAIL.getCode()::equals) - .count(); - - // 成功和失败都为0 表示进行中 - if (successCount == 0 && failCount == 0){ - allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_PROCESSING.getCode()) - .setResult(AllocOrderResultEnum.ALL_PENDING.getCode()); - } else { - if (failCount == details.size()){ - // 全部失败 - allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode()) - .setResult(AllocOrderResultEnum.ALL_FAILED.getCode()); - } else if (successCount == details.size()){ - // 全部成功 - allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode()) - .setResult(AllocOrderResultEnum.ALL_SUCCESS.getCode()); - } else { - // 部分成功 - allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode()) - .setResult(AllocOrderResultEnum.PART_SUCCESS.getCode()); - } - } - } - allocationOrderDetailManager.updateAllById(details); - allocationOrderManager.updateById(allocationOrder); - - // 如果状态为完成, 发送通知 - if (Objects.equals(AllocOrderStatusEnum.ALLOCATION_END.getCode(), allocationOrder.getStatus())){ - // 发送通知 - clientNoticeService.registerAllocNotice(allocationOrder, null, details); - } - } - /** * 获取并检查支付订单 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/allocation/service/AllocationSyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/allocation/service/AllocationSyncService.java new file mode 100644 index 000000000..285eb6ea2 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/allocation/service/AllocationSyncService.java @@ -0,0 +1,141 @@ +package cn.daxpay.single.service.core.payment.allocation.service; + +import cn.bootx.platform.common.core.exception.DataNotExistException; +import cn.bootx.platform.common.core.exception.RepetitiveOperationException; +import cn.daxpay.single.code.AllocDetailResultEnum; +import cn.daxpay.single.code.AllocOrderResultEnum; +import cn.daxpay.single.code.AllocOrderStatusEnum; +import cn.daxpay.single.param.payment.allocation.AllocSyncParam; +import cn.daxpay.single.result.allocation.AllocationSyncResult; +import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderDetailManager; +import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderManager; +import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrder; +import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderDetail; +import cn.daxpay.single.service.core.payment.allocation.factory.AllocationFactory; +import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService; +import cn.daxpay.single.service.func.AbsAllocationStrategy; +import com.baomidou.lock.LockInfo; +import com.baomidou.lock.LockTemplate; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * 对账同步 + * @author xxm + * @since 2024/5/23 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class AllocationSyncService { + + private final ClientNoticeService clientNoticeService; + private final AllocationOrderManager allocationOrderManager; + + private final LockTemplate lockTemplate; + private final AllocationOrderDetailManager allocationOrderDetailManager; + + + /** + * 分账同步, 开启一个新的事务, 不受外部抛出异常的影响 + */ + @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) + public AllocationSyncResult sync(AllocSyncParam param) { + // 获取分账订单 + AllocationOrder allocationOrder = null; + if (Objects.nonNull(param.getAllocationNo())){ + allocationOrder = allocationOrderManager.findByAllocationNo(param.getAllocationNo()) + .orElseThrow(() -> new DataNotExistException("分账单不存在")); + } + if (Objects.isNull(allocationOrder)){ + allocationOrder = allocationOrderManager.findByAllocationNo(param.getBizAllocationNo()) + .orElseThrow(() -> new DataNotExistException("分账单不存在")); + } + this.sync(allocationOrder); + return new AllocationSyncResult(); + } + + /** + * 分账同步 + */ + public void sync(AllocationOrder allocationOrder){ + LockInfo lock = lockTemplate.lock("payment:allocation:" + allocationOrder.getOrderId(),10000,200); + if (Objects.isNull(lock)){ + throw new RepetitiveOperationException("分账同步中,请勿重复操作"); + } + try { + List detailList = allocationOrderDetailManager.findAllByOrderId(allocationOrder.getId()); + // 获取分账策略 + AbsAllocationStrategy allocationStrategy = AllocationFactory.create(allocationOrder.getChannel()); + allocationStrategy.initParam(allocationOrder, detailList); + // 分账完结预处理 + allocationStrategy.doBeforeHandler(); + allocationStrategy.doSync(); + // TODO 保存分账同步记录 + + // 根据订单明细更新订单的状态和处理结果 + this.updateOrderStatus(allocationOrder, detailList); + } finally { + lockTemplate.releaseLock(lock); + } + } + + /** + * 根据订单明细更新订单的状态和处理结果, 如果订单是分账结束或失败, 不更新状态 + * TODO 是否多次同步会产生多次变动, 注意处理多次推送通知的问题, 目前是 + */ + private void updateOrderStatus(AllocationOrder allocationOrder, List details){ + // 如果是分账结束或失败, 不更新状态 + String status = allocationOrder.getStatus(); + + // 如果是分账结束或失败, 不进行对订单进行处理 + List list = Arrays.asList(AllocOrderStatusEnum.FINISH.getCode(), AllocOrderStatusEnum.FINISH_FAILED.getCode()); + if (!list.contains(status)){ + // 判断明细状态. 获取成功和失败的 + long successCount = details.stream() + .map(AllocationOrderDetail::getResult) + .filter(AllocDetailResultEnum.SUCCESS.getCode()::equals) + .count(); + long failCount = details.stream() + .map(AllocationOrderDetail::getResult) + .filter(AllocDetailResultEnum.FAIL.getCode()::equals) + .count(); + + // 成功和失败都为0 表示进行中 + if (successCount == 0 && failCount == 0){ + allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_PROCESSING.getCode()) + .setResult(AllocOrderResultEnum.ALL_PENDING.getCode()); + } else { + if (failCount == details.size()){ + // 全部失败 + allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode()) + .setResult(AllocOrderResultEnum.ALL_FAILED.getCode()); + } else if (successCount == details.size()){ + // 全部成功 + allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode()) + .setResult(AllocOrderResultEnum.ALL_SUCCESS.getCode()); + } else { + // 部分成功 + allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode()) + .setResult(AllocOrderResultEnum.PART_SUCCESS.getCode()); + } + } + } + allocationOrderDetailManager.updateAllById(details); + allocationOrderManager.updateById(allocationOrder); + + // 如果状态为完成, 发送通知 + if (Objects.equals(AllocOrderStatusEnum.ALLOCATION_END.getCode(), allocationOrder.getStatus())){ + // 发送通知 + clientNoticeService.registerAllocNotice(allocationOrder, null, details); + } + } + +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/record/sync/entity/PaySyncRecord.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/record/sync/entity/PaySyncRecord.java index 1bde88970..504947b61 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/record/sync/entity/PaySyncRecord.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/record/sync/entity/PaySyncRecord.java @@ -2,6 +2,7 @@ package cn.daxpay.single.service.core.record.sync.entity; import cn.bootx.platform.common.core.function.EntityBaseFunction; import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity; +import cn.daxpay.single.code.AllocOrderStatusEnum; import cn.daxpay.single.code.PayChannelEnum; import cn.daxpay.single.code.RefundSyncStatusEnum; import cn.daxpay.single.code.PaySyncStatusEnum; @@ -46,6 +47,7 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction< * 三方支付返回状态 * @see PaySyncStatusEnum * @see RefundSyncStatusEnum + * @see AllocOrderStatusEnum */ @DbColumn(comment = "网关返回状态") private String outTradeStatus; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/handler/excel/AmountConverter.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/handler/excel/AmountConverter.java index 4a62525a1..dbe8600be 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/handler/excel/AmountConverter.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/handler/excel/AmountConverter.java @@ -1,12 +1,12 @@ package cn.daxpay.single.service.handler.excel; +import cn.daxpay.single.util.PayUtil; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.metadata.GlobalConfiguration; import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.property.ExcelContentProperty; import java.math.BigDecimal; -import java.math.RoundingMode; /** * 金额分转元 @@ -23,8 +23,7 @@ public class AmountConverter implements Converter { if (value == null){ return new WriteCellData<>(""); } - BigDecimal divide = BigDecimal.valueOf(value) - .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + BigDecimal divide = PayUtil.conversionAmount(value); return new WriteCellData<>(divide); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/task/AllocationSyncTask.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/task/AllocationSyncTask.java index 555f09d52..9ba89cd2f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/task/AllocationSyncTask.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/task/AllocationSyncTask.java @@ -4,6 +4,7 @@ import cn.daxpay.single.code.AllocOrderStatusEnum; import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderManager; import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrder; import cn.daxpay.single.service.core.payment.allocation.service.AllocationService; +import cn.daxpay.single.service.core.payment.allocation.service.AllocationSyncService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.quartz.*; @@ -23,6 +24,7 @@ public class AllocationSyncTask implements Job { private final AllocationOrderManager allocationOrderManager; + private final AllocationSyncService allocationSyncService; private final AllocationService allocationService; /** @@ -34,7 +36,7 @@ public class AllocationSyncTask implements Job { try { // 分账中走同步逻辑 if (allocationOrder.getStatus().equals(AllocOrderStatusEnum.ALLOCATION_PROCESSING.getCode())) { - allocationService.sync(allocationOrder); + allocationSyncService.sync(allocationOrder); } // 如果分账结束, 调用自动完结逻辑 if (allocationOrder.getStatus().equals(AllocOrderStatusEnum.ALLOCATION_END.getCode())) {