From 03b343102e495f5a111dae600bfee822074c4584 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:48:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8E=A8=E9=80=81=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E8=90=BDfailed=E5=8F=A3=E5=BE=84=EF=BC=88?= =?UTF-8?q?=E4=BF=AE=E6=80=BB=E7=BA=B211.2-#3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 结算单/开票申请推送失败由整体回滚保持unsynced改为:事务回滚后独立事务落failed+原因(结算写变更记录、开票写操作流水,无新列),与付款/收票口径统一 - 收票新增pushKingdeeReceivedInvoice(ids,autoSync)重载:自动推送路径前置拦截(未绑定镜像/查验未通过/前置应付单未生成等)置failed+原因纳入重推循环,已指定应付的良性跳过不改状态;手动批量路径保持拦截不动状态逐单报因 - 汇率未维护照旧不传不阻断 --- .../service/IFormalSettlementService.java | 9 ++++ .../service/IInvoiceApplicationService.java | 9 ++++ .../service/IInvoiceReceiptService.java | 10 +++++ .../impl/FormalSettlementServiceImpl.java | 35 +++++++++++++++- .../impl/InvoiceApplicationServiceImpl.java | 41 ++++++++++++++++++- .../impl/InvoiceReceiptServiceImpl.java | 39 ++++++++++++++++-- 6 files changed, 136 insertions(+), 7 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java index 2fdd3d55..a341a8eb 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java @@ -51,6 +51,15 @@ public interface IFormalSettlementService extends BaseService */ String doSyncKingdee(Long id); + /** + * 独立事务落"生成失败"状态并记失败原因(在推送事务回滚、行锁释放后执行,与付款/收票口径统一); + * 仅更新审批通过且未同步的单据,未审批/已同步/已删除单据自动跳过,保持原状态 + * + * @param id 正式结算单id + * @param reason 失败原因(写变更记录留档) + */ + void markKingdeeSyncFailed(Long id, String reason); + String syncKingdeeArResult(); List detailFees(Long detailId); void adjustDetail(PreSettlementDetailAdjustRequest request); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IInvoiceApplicationService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IInvoiceApplicationService.java index 92e01890..6c31fd0d 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IInvoiceApplicationService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IInvoiceApplicationService.java @@ -65,5 +65,14 @@ public interface IInvoiceApplicationService extends BaseService { */ String pushKingdeeReceivedInvoice(List ids); + /** + * 自动推送路径批量推送金蝶收票单(审批通过事件 / 定时重推专用): + * 前置拦截(未绑定镜像/查验未通过/前置应付单未生成等)一并置 failed + 原因,纳入定时重推循环 + * + * @param ids 收票登记id列表 + * @param autoSync 自动推送标记(前置拦截置 failed) + * @return 执行摘要(逐单失败原因) + */ + String pushKingdeeReceivedInvoice(List ids, boolean autoSync); + /** * 定时回写金蝶收票单结果(A801-3):金蝶侧删除/红冲/作废 → 回收为 failed 提示人工(不自动作废登记); * pushed(保存成功后中断)→ 指定补偿;assigned 仅补登缺失的收票单号 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 7ebf7728..fd6bd5e8 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 @@ -565,13 +565,44 @@ public class FormalSettlementServiceImpl extends BaseServiceImpllambdaUpdate() + .eq(FormalSettlement::getId, id) + .eq(FormalSettlement::getApprovalStatus, TransportBusiConstant.APPROVED) + .ne(FormalSettlement::getKingdeeSyncStatus, KingdeeBfiConstant.KINGDEE_STATUS_SYNCED) + .set(FormalSettlement::getKingdeeSyncStatus, KingdeeBfiConstant.KINGDEE_STATUS_FAILED)); + if (updated > 0) { + // 失败原因写变更记录留档(推送事务已回滚,此处独立事务);原因过长截断留档,不抛错 + String failReason = Func.toStrWithEmpty(reason, "未知异常"); + saveChange(id, "结算单基本信息", null, "调整", + "同步金蝶失败:" + (failReason.length() > 200 ? failReason.substring(0, 200) : failReason), null); + } + } + /** * 附件联带上传(A902):主单推送成功(含幂等预检命中)后调用,以本地 kingdeeBillId 定位金蝶单据; * 本地缺失(存量数据/幂等跳过路径未带回内码)时经 A102-4/A302-4 按防重键反查回填; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java index 4974787e..679d9547 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java @@ -509,13 +509,50 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpllambdaUpdate() + .eq(InvoiceApplication::getId, id) + .eq(InvoiceApplication::getApprovalStatus, TransportBusiConstant.APPROVED) + .notIn(InvoiceApplication::getKingdeeStatus, KingdeeBfiConstant.KINGDEE_STATUS_SYNCED, + KingdeeBfiConstant.KINGDEE_STATUS_INVOICED) + .set(InvoiceApplication::getKingdeeStatus, KingdeeBfiConstant.KINGDEE_STATUS_FAILED)); + if (updated > 0) { + // 失败原因写操作流水留档(推送事务已回滚,此处独立事务) + record(id, "sync", "同步金蝶失败", TransportBusiConstant.APPROVED, TransportBusiConstant.APPROVED, + truncateReason(reason), null); + } + } + + /** 失败原因截断留档(流水 reason 字段兜底 500 长度) */ + private String truncateReason(String reason) { + String value = Func.toStrWithEmpty(reason, "未知异常"); + return value.length() > 500 ? value.substring(0, 500) : value; + } + /** * 附件联带上传(A902):主单推送成功(含 A303-4 勾稽回填)后调用,以本地 kingdeeBillId 定位金蝶单据; * 本地缺失(存量数据)跳过待重推补传;附件上传异常独立捕获,绝不影响主单同步结果 diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceReceiptServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceReceiptServiceImpl.java index f12665ef..e041053d 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceReceiptServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceReceiptServiceImpl.java @@ -536,6 +536,24 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl ids) { + // 手动批量推送:前置拦截保持现状(blockedReason 不动推送状态,逐单报因) + return pushKingdeeReceivedInvoice(ids, false); + } + + /** + * 批量推送收票登记至金蝶(图05 环节②③:A303-2 收票单批量保存 + A302-6 指定应付)。 + *

+ * 编排流程:参数去重校验 → 批量组装推送计划(分摊/镜像/结算单/结算明细一次取回防 N+1)→ + * A102-4 应付单已生成预检 → A801-3 幂等预检(金蝶已有收票单则跳过保存续推指定)→ + * 逐单推送(单笔失败不中断整批)→ 汇总成功/失败笔数返回 + * + * @param ids 收票登记主键集合 + * @param autoSync 自动推送路径(审批通过事件/定时重推):前置拦截置 failed + 原因纳入重推循环; + * 手动批量路径保持现状(拦截不改状态,逐单报因) + * @return 推送结果汇总(本批笔数、成功/失败笔数及失败原因明细) + */ + @Override + public String pushKingdeeReceivedInvoice(List ids, boolean autoSync) { if (Func.isEmpty(ids)) { throw new ServiceException("请选择需要推送金蝶的收票登记"); } @@ -603,7 +621,7 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl failures = new ArrayList<>(); for (InvoiceReceipt receipt : receipts) { try { - pushOneReceivedInvoice(planByReceipt.get(receipt.getId()), kingdeeRowByInvoiceNo, apBillNoBySrcCode, context); + pushOneReceivedInvoice(planByReceipt.get(receipt.getId()), kingdeeRowByInvoiceNo, apBillNoBySrcCode, context, autoSync); success++; } catch (RuntimeException pushException) { failures.add("发票" + Func.toStrWithEmpty(receipt.getInvoiceNo(), String.valueOf(receipt.getId())) @@ -621,27 +639,42 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl kingdeeRows, - Map apBillNoBySrcCode, KingdeeSyncContext context) { + Map apBillNoBySrcCode, KingdeeSyncContext context, boolean autoSync) { InvoiceReceipt receipt = plan.receipt(); log.info("收票登记[{}]推送金蝶收票单开始:id={},推送状态={},应付单防重键={}", receipt.getInvoiceNo(), receipt.getId(), receipt.getKingdeePushStatus(), plan.apBillSrcCode()); if (Func.isNotBlank(plan.blockedReason())) { log.warn("收票登记[{}]推送金蝶收票单拦截:{}", receipt.getInvoiceNo(), plan.blockedReason()); + // 自动推送路径:前置不满足置 failed + 原因纳入重推循环("已指定应付"的良性跳过不改状态) + if (autoSync && !KingdeeBfiConstant.RECEIVED_PUSH_ASSIGNED.equals( + Func.toStrWithEmpty(receipt.getKingdeePushStatus(), KingdeeBfiConstant.RECEIVED_PUSH_UNSYNCED))) { + applyReceivedInvoicePushResult(receipt, receipt.getKingdeeReceivedInvoiceNo(), receipt.getKingdeeReceivedInvoiceId(), + KingdeeBfiConstant.RECEIVED_PUSH_FAILED, truncatePushMsg(plan.blockedReason()), true); + } throw new ServiceException(plan.blockedReason()); } String apBillSrcCode = plan.apBillSrcCode(); if (!apBillNoBySrcCode.containsKey(apBillSrcCode)) { log.warn("收票登记[{}]推送金蝶收票单拦截:结算单[{}]未生成财务应付单(防重键={})", receipt.getInvoiceNo(), plan.settlement().getFormalSettlementNo(), apBillSrcCode); + // 自动推送路径:前置应付单未生成置 failed + 原因,上游结算单就绪后由重推任务自动续推 + if (autoSync) { + applyReceivedInvoicePushResult(receipt, receipt.getKingdeeReceivedInvoiceNo(), receipt.getKingdeeReceivedInvoiceId(), + KingdeeBfiConstant.RECEIVED_PUSH_FAILED, + truncatePushMsg("请先同步金蝶应付单:结算单" + plan.settlement().getFormalSettlementNo() + "未生成财务应付单"), true); + } throw new ServiceException("请先同步金蝶应付单:结算单" + plan.settlement().getFormalSettlementNo() + "未生成财务应付单"); }