From 276dd813d4d64b5124f339996ff7a8de06fdc203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Sat, 10 Oct 2026 13:51:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=A1=E6=89=B9=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E8=A7=A6=E5=8F=91=E8=87=AA=E5=8A=A8=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E9=87=91=E8=9D=B6=EF=BC=88AFTER=5FCOMMIT+4=E5=9F=9F?= =?UTF-8?q?=E5=BC=80=E5=85=B3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 KingdeeBillApprovedEvent(bizType复用MK处理器取值)与 KingdeeApprovalSyncListener:事务提交后同线程复用手动按钮同一Service方法推送,fallbackExecution兜底无事务场景,全量try/catch吞异常,审批结果不受推送影响 - 4域 approve 置approved成功后发布事件(重复审批被状态校验拒绝天然不重复推送);正式结算单 approve 补 @Transactional(原缺失会致AFTER_COMMIT事件被丢弃,顺带使更新与变更记录原子化) - 域开关 thirdParty.bfi.autoSync.{settlement,payment,invoiceApply,receipt} 默认全关(Nacos显式打开),关闭即回到现状;手动同步按钮全部保留,三入口并存:自动+定时+手动 - 推送失败由上一独立事务落failed+定时重推兜底 --- .../event/KingdeeBillApprovedEvent.java | 44 ++++++ .../listener/KingdeeApprovalSyncListener.java | 133 ++++++++++++++++++ .../impl/FormalSettlementServiceImpl.java | 6 + .../impl/InvoiceApplicationServiceImpl.java | 5 + .../impl/InvoiceReceiptServiceImpl.java | 5 + .../impl/PaymentApplicationServiceImpl.java | 5 + 6 files changed, 198 insertions(+) create mode 100644 blade-service/blade-transport/src/main/java/org/springblade/transport/event/KingdeeBillApprovedEvent.java create mode 100644 blade-service/blade-transport/src/main/java/org/springblade/transport/listener/KingdeeApprovalSyncListener.java diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/event/KingdeeBillApprovedEvent.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/event/KingdeeBillApprovedEvent.java new file mode 100644 index 00000000..9652742f --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/event/KingdeeBillApprovedEvent.java @@ -0,0 +1,44 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments from this software for such purposes. + * Copyright of this software remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.transport.event; + +/** + * 单据审批通过事件:审批事务提交后由 {@code KingdeeApprovalSyncListener} 消费, + * 自动推送对应单据至金蝶(星瀚),省掉页面手动点「同步金蝶」。 + *

+ * bizType 复用 MkProcessBizHandlers 既有取值;只在审批状态真正变为 approved 的那次发布, + * 重复回调被 approve 状态校验拒绝,天然不重复推送。 + * + * @author Chill + */ +public record KingdeeBillApprovedEvent(String bizType, Long id) { + + /** 正式结算单(应付 A102-2 / 应收 A302-2) */ + public static final String BIZ_FORMAL_SETTLEMENT = "formal-settlement"; + + /** 付款申请(A103-1 正式 / A103-3 预付) */ + public static final String BIZ_PAYMENT_APPLICATION = "payment-application"; + + /** 开票申请(A303-1) */ + public static final String BIZ_INVOICE_APPLICATION = "invoice-application"; + + /** 收票登记(A303-2 + A302-6) */ + public static final String BIZ_INVOICE_RECEIPT = "invoice-receipt"; +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/listener/KingdeeApprovalSyncListener.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/listener/KingdeeApprovalSyncListener.java new file mode 100644 index 00000000..57216faf --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/listener/KingdeeApprovalSyncListener.java @@ -0,0 +1,133 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments from this software for such purposes. + * Copyright of this software remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.transport.listener; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.transport.event.KingdeeBillApprovedEvent; +import org.springblade.transport.service.IFormalSettlementService; +import org.springblade.transport.service.IInvoiceApplicationService; +import org.springblade.transport.service.IInvoiceReceiptService; +import org.springblade.transport.service.IPaymentApplicationService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import java.util.List; + +/** + * 单据审批通过自动同步金蝶监听器:审批事务提交(AFTER_COMMIT)后复用手动按钮同一 Service 方法推送, + * 与客商档案审批后自动推送同线程模式。 + *

+ * 不变式:审批结果不受推送影响——监听器整体 try/catch 吞异常,推送异常只落 failed + 日志; + * 失败单据由 {@code KingdeeAutoSyncRetryTask} 定时重推、手动按钮兜底。 + *

+ * ponytail: 同步推送占用审批请求线程,若 BFI 网关大面积变慢导致审批接口可感知变慢, + * 再把路由调用提交到守护线程池(MkProcessMessageService 已有同款裸 ScheduledExecutorService 模式)。 + *

+ * 域开关(Nacos thirdParty.bfi.autoSync.*)默认全部关闭,与既有定时任务开关风格一致; + * 关闭某域 = 该域回到现状(审批不自动推送、手动按钮照用)。 + * + * @author Chill + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class KingdeeApprovalSyncListener { + + private final IFormalSettlementService formalSettlementService; + private final IPaymentApplicationService paymentApplicationService; + private final IInvoiceApplicationService invoiceApplicationService; + private final IInvoiceReceiptService invoiceReceiptService; + + /** 结算单审批后自动推 A102-2/A302-2 开关,默认关闭 */ + @Value("${thirdParty.bfi.autoSync.settlement:false}") + private boolean settlementEnabled; + + /** 付款申请审批后自动推 A103-1/A103-3 开关,默认关闭 */ + @Value("${thirdParty.bfi.autoSync.payment:false}") + private boolean paymentEnabled; + + /** 开票申请审批后自动推 A303-1 开关,默认关闭 */ + @Value("${thirdParty.bfi.autoSync.invoiceApply:false}") + private boolean invoiceApplyEnabled; + + /** 收票登记审批后自动推 A303-2+A302-6 开关,默认关闭 */ + @Value("${thirdParty.bfi.autoSync.receipt:false}") + private boolean receiptEnabled; + + /** + * 事务提交后同步推送。fallbackExecution=true 兜底:approve 未开启事务时事件不再被默认丢弃。 + * + * @param event 审批通过事件(bizType + 单据id) + */ + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true) + public void onBillApproved(KingdeeBillApprovedEvent event) { + try { + switch (event.bizType()) { + case KingdeeBillApprovedEvent.BIZ_FORMAL_SETTLEMENT -> pushSettlement(event.id()); + case KingdeeBillApprovedEvent.BIZ_PAYMENT_APPLICATION -> pushPayment(event.id()); + case KingdeeBillApprovedEvent.BIZ_INVOICE_APPLICATION -> pushInvoiceApply(event.id()); + case KingdeeBillApprovedEvent.BIZ_INVOICE_RECEIPT -> pushReceipt(event.id()); + default -> log.warn("审批通过自动同步金蝶跳过:未知业务类型 bizType={} id={}", event.bizType(), event.id()); + } + } catch (Exception exception) { + // 推送失败只记日志(失败状态已在推送方法内独立事务落库),不影响审批结果,交给重推任务与手动按钮兜底 + log.error("审批通过自动同步金蝶失败 bizType={} id={}", event.bizType(), event.id(), exception); + } + } + + private void pushSettlement(Long id) { + if (!settlementEnabled) { + return; + } + log.info("正式结算单[{}]审批通过自动同步金蝶开始", id); + String billNo = formalSettlementService.syncKingdee(id); + log.info("正式结算单[{}]审批通过自动同步金蝶完成:billNo={}", id, billNo); + } + + private void pushPayment(Long id) { + if (!paymentEnabled) { + return; + } + log.info("付款申请[{}]审批通过自动同步金蝶开始", id); + String kingdeeBillId = paymentApplicationService.syncKingdee(id); + log.info("付款申请[{}]审批通过自动同步金蝶完成:kingdeeBillId={}", id, kingdeeBillId); + } + + private void pushInvoiceApply(Long id) { + if (!invoiceApplyEnabled) { + return; + } + log.info("开票申请[{}]审批通过自动同步金蝶开始", id); + String billNo = invoiceApplicationService.syncKingdee(id); + log.info("开票申请[{}]审批通过自动同步金蝶完成:billNo={}", id, billNo); + } + + private void pushReceipt(Long id) { + if (!receiptEnabled) { + return; + } + log.info("收票登记[{}]审批通过自动推送金蝶收票单开始", id); + String summary = invoiceReceiptService.pushKingdeeReceivedInvoice(List.of(id), true); + log.info("收票登记[{}]审批通过自动推送金蝶收票单完成:{}", id, summary); + } +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java index fdd377ea..b5048ace 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java @@ -92,11 +92,13 @@ import org.springblade.transport.support.KingdeeSyncLockSupport; import org.springblade.transport.support.KingdeeAttachmentSyncSupport; import org.springblade.transport.wrapper.PreSettlementWrapper; import org.springblade.core.tool.jackson.JsonUtil; +import org.springblade.transport.event.KingdeeBillApprovedEvent; import org.springblade.transport.wrapper.FormalSettlementWrapper; import org.springblade.transport.wrapper.PaymentApplicationWrapper; import org.springblade.system.cache.UserCache; import org.springframework.beans.factory.annotation.Value; import org.springblade.core.tool.utils.SpringUtil; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -167,6 +169,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl