feat: 审批通过事件触发自动同步金蝶(AFTER_COMMIT+4域开关)
- 新增 KingdeeBillApprovedEvent(bizType复用MK处理器取值)与 KingdeeApprovalSyncListener:事务提交后同线程复用手动按钮同一Service方法推送,fallbackExecution兜底无事务场景,全量try/catch吞异常,审批结果不受推送影响
- 4域 approve 置approved成功后发布事件(重复审批被状态校验拒绝天然不重复推送);正式结算单 approve 补 @Transactional(原缺失会致AFTER_COMMIT事件被丢弃,顺带使更新与变更记录原子化)
- 域开关 thirdParty.bfi.autoSync.{settlement,payment,invoiceApply,receipt} 默认全关(Nacos显式打开),关闭即回到现状;手动同步按钮全部保留,三入口并存:自动+定时+手动
- 推送失败由上一独立事务落failed+定时重推兜底
This commit is contained in:
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments from this software for such purposes.
|
||||
* Copyright of this software remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.event;
|
||||
|
||||
/**
|
||||
* 单据审批通过事件:审批事务提交后由 {@code KingdeeApprovalSyncListener} 消费,
|
||||
* 自动推送对应单据至金蝶(星瀚),省掉页面手动点「同步金蝶」。
|
||||
* <p>
|
||||
* 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";
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments from this software for such purposes.
|
||||
* Copyright of this software remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* 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 方法推送,
|
||||
* 与客商档案审批后自动推送同线程模式。
|
||||
* <p>
|
||||
* 不变式:审批结果不受推送影响——监听器整体 try/catch 吞异常,推送异常只落 failed + 日志;
|
||||
* 失败单据由 {@code KingdeeAutoSyncRetryTask} 定时重推、手动按钮兜底。
|
||||
* <p>
|
||||
* ponytail: 同步推送占用审批请求线程,若 BFI 网关大面积变慢导致审批接口可感知变慢,
|
||||
* 再把路由调用提交到守护线程池(MkProcessMessageService 已有同款裸 ScheduledExecutorService 模式)。
|
||||
* <p>
|
||||
* 域开关(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);
|
||||
}
|
||||
}
|
||||
+6
@@ -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<FormalSettlemen
|
||||
private final KingdeeArBillSyncSupport kingdeeArBillSyncSupport;
|
||||
private final KingdeeAttachmentSyncSupport kingdeeAttachmentSyncSupport;
|
||||
private final KingdeeSyncLockSupport kingdeeSyncLockSupport;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* 金蝶应收单勾稽 single-flight 锁租约秒数,须大于最坏一轮耗时(批量远程查询 + 分批纠偏回写),
|
||||
@@ -540,6 +543,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
||||
@Override public void returnBill(FormalSettlementStatusRequest request) { changeStatus(request.getId(), TransportBusiConstant.REVIEWING, TransportBusiConstant.RETURNED, "已驳回", request.getReason()); }
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void approve(FormalSettlementStatusRequest request) {
|
||||
FormalSettlement settlement = existing(request.getId());
|
||||
if (!TransportBusiConstant.REVIEWING.equals(settlement.getApprovalStatus())) throw new ServiceException("仅审批中的正式结算单允许审核");
|
||||
@@ -549,6 +553,8 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
||||
settlement.setApprovedTime(LocalDateTime.now());
|
||||
updateById(settlement);
|
||||
saveChange(settlement.getId(), "结算单基本信息", null, "调整", "正式结算审批通过", request.getReason());
|
||||
// 审批事务提交后自动推送金蝶(AFTER_COMMIT 监听器消费,推送失败不影响审批结果)
|
||||
eventPublisher.publishEvent(new KingdeeBillApprovedEvent(KingdeeBillApprovedEvent.BIZ_FORMAL_SETTLEMENT, settlement.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
@@ -56,6 +56,7 @@ import org.springblade.transport.config.BfiProperties;
|
||||
import org.springblade.transport.constant.KingdeeAttachmentConstant;
|
||||
import org.springblade.transport.constant.KingdeeBfiConstant;
|
||||
import org.springblade.transport.constant.TransportBusiConstant;
|
||||
import org.springblade.transport.event.KingdeeBillApprovedEvent;
|
||||
import org.springblade.transport.mapper.ContractManageMapper;
|
||||
import org.springblade.transport.mapper.CustomerArchiveMapper;
|
||||
import org.springblade.transport.mapper.CustomerContactMapper;
|
||||
@@ -101,6 +102,7 @@ import org.springblade.transport.wrapper.InvoiceApplicationWrapper;
|
||||
import org.springblade.transport.wrapper.WaybillWrapper;
|
||||
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;
|
||||
|
||||
@@ -156,6 +158,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
private final KingdeeArBillSyncSupport kingdeeArBillSyncSupport;
|
||||
private final KingdeeAttachmentSyncSupport kingdeeAttachmentSyncSupport;
|
||||
private final KingdeeSyncLockSupport kingdeeSyncLockSupport;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* 金蝶开票结果回写 single-flight 锁租约秒数,须大于最坏一轮耗时(分批远程查询 + 逐单回写),
|
||||
@@ -484,6 +487,8 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void approve(InvoiceApplicationStatusRequest request) {
|
||||
changeStatus(request.getId(), TransportBusiConstant.REVIEWING, TransportBusiConstant.APPROVED, "approve", "审批通过", null);
|
||||
// 审批事务提交后自动推送金蝶(AFTER_COMMIT 监听器消费,推送失败不影响审批结果)
|
||||
eventPublisher.publishEvent(new KingdeeBillApprovedEvent(KingdeeBillApprovedEvent.BIZ_INVOICE_APPLICATION, request.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
@@ -53,6 +53,7 @@ import org.springblade.thirdparty.bfi.service.IBfiService;
|
||||
import org.springblade.transport.constant.KingdeeAttachmentConstant;
|
||||
import org.springblade.transport.constant.KingdeeBfiConstant;
|
||||
import org.springblade.transport.constant.TransportBusiConstant;
|
||||
import org.springblade.transport.event.KingdeeBillApprovedEvent;
|
||||
import org.springblade.transport.mapper.CustomerArchiveMapper;
|
||||
import org.springblade.transport.mapper.CustomerContactMapper;
|
||||
import org.springblade.transport.mapper.FormalSettlementDetailMapper;
|
||||
@@ -84,6 +85,7 @@ import org.springblade.transport.support.KingdeeReceivedInvoiceSyncSupport;
|
||||
import org.springblade.transport.support.KingdeeSyncLockSupport;
|
||||
import org.springblade.transport.wrapper.InvoiceReceiptWrapper;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -149,6 +151,7 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
|
||||
private final KingdeeReceivedInvoiceSyncSupport kingdeeReceivedInvoiceSyncSupport;
|
||||
private final KingdeeAttachmentSyncSupport kingdeeAttachmentSyncSupport;
|
||||
private final KingdeeSyncLockSupport kingdeeSyncLockSupport;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
/** 金蝶收票单结果回写 single-flight 锁键前缀(多副本定时任务与手动触发共用) */
|
||||
private static final String RECEIVED_RESULT_SYNC_LOCK_PREFIX = "transport:invoice-receipt:kingdee-received-result-sync:";
|
||||
@@ -491,6 +494,8 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
|
||||
}
|
||||
validateStoredAllocation(entity);
|
||||
changeStatus(entity, TransportBusiConstant.APPROVED, "approve", "审批通过", null);
|
||||
// 审批事务提交后自动推送金蝶收票单(AFTER_COMMIT 监听器消费,推送失败不影响审批结果)
|
||||
eventPublisher.publishEvent(new KingdeeBillApprovedEvent(KingdeeBillApprovedEvent.BIZ_INVOICE_RECEIPT, entity.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
@@ -52,6 +52,7 @@ import org.springblade.transport.config.BfiProperties;
|
||||
import org.springblade.transport.constant.KingdeeAttachmentConstant;
|
||||
import org.springblade.transport.constant.KingdeeBfiConstant;
|
||||
import org.springblade.transport.constant.TransportBusiConstant;
|
||||
import org.springblade.transport.event.KingdeeBillApprovedEvent;
|
||||
import org.springblade.transport.mapper.BillLedgerMapper;
|
||||
import org.springblade.transport.mapper.BillLedgerUsageMapper;
|
||||
import org.springblade.transport.mapper.ContractManageMapper;
|
||||
@@ -100,6 +101,7 @@ import org.springblade.transport.support.KingdeeAttachmentSyncSupport;
|
||||
import org.springblade.transport.support.KingdeeSyncLockSupport;
|
||||
import org.springblade.transport.wrapper.PaymentApplicationWrapper;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -161,6 +163,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
private final BfiProperties bfiProperties;
|
||||
private final KingdeeSyncLockSupport kingdeeSyncLockSupport;
|
||||
private final BladeRedis bladeRedis;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* 金蝶付款结果回写 single-flight 锁租约秒数,须大于最坏一轮耗时(分批远程查询 + 逐单回写),
|
||||
@@ -404,6 +407,8 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
entity.setApprovalStatus(TransportBusiConstant.APPROVED); entity.setCurrentNode("审批通过"); entity.setCurrentProcessor(AuthUtil.getUserName());
|
||||
updateById(entity);
|
||||
refreshFormalSettlement(entity);
|
||||
// 审批事务提交后自动推送金蝶(AFTER_COMMIT 监听器消费,推送失败不影响审批结果)
|
||||
eventPublisher.publishEvent(new KingdeeBillApprovedEvent(KingdeeBillApprovedEvent.BIZ_PAYMENT_APPLICATION, entity.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user