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