🐛 修复付款申请同步金蝶断链后重推被拦截问题,推送与回写全链路支持幂等恢复
- A102-2 应付单已生成时幂等跳过保存并续推 A103-1,A103-2 勾稽已下推成功则直接回填 - 预付款 A103-3 恢复推送前增加 A103-2 预检,避免重复推送 - 定时回写任务将生成失败单据纳入 A103-2 轮询,自动纠偏远端已生成单据 - 抽取公共方法:A103-2 取最近修改、分录标识生成、批量分批、统一日期格式(yyyy-MM-dd HH:mm:ss) - 新增全链路逻辑梳理文档(推送链路与回写链路连贯性)
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
# 付款管理金蝶同步全链路逻辑梳理(推送链路与回写链路连贯性)
|
||||
|
||||
> 关联文档:
|
||||
> - `2026-09-16-付款管理正式付款同步金蝶(财务应付单+付款申请下推)技术文档.md`
|
||||
> - `2026-09-17-付款管理金蝶付款结果定时回写(A103-2付款申请单查询+A103-4付款处理查询)技术文档.md`
|
||||
> - `../TMS业财融合-集成流程图文字版与业务流程梳理.md`(§4.3 流程步骤、§4.4 关键规则)
|
||||
>
|
||||
> 本文为 2026-09-18 幂等恢复改造后的全链路现状梳理,代码落点 `PaymentApplicationServiceImpl`。
|
||||
|
||||
## 1. 背景与范围
|
||||
|
||||
付款申请审批通过后与金蝶(星瀚)的双向同步:
|
||||
|
||||
- **推送链路(TMS → 金蝶)**:正式结算付款两步链路 A102-2 财务应付单保存 → A103-1 应付下推付款申请;预付款一步链路 A103-3 预付款保存。
|
||||
- **回写链路(金蝶 → TMS)**:定时任务经 A103-2 付款申请单查询回写单据与付款状态,经 A103-4 付款处理查询回填付款记录。
|
||||
|
||||
原实现两步推送同处一个本地事务,A102-2 成功后 A103-1 失败会导致"金蝶已生成、本地回滚为生成失败"的断链,重试被"请勿重复推送"拦截而死锁。本次改造确立全链路恢复原则:
|
||||
|
||||
> **以金蝶侧实际生成为准:已生成则幂等跳过/回填勾稽,未生成才续推;推送接口真正报错才算同步失败。**
|
||||
|
||||
## 2. 接口与角色清单
|
||||
|
||||
| 接口 | 名称 | 方向 | 方法(TMS 侧) | 用途 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| A102-2 | 财务应付单保存 | 推送 | `pushBfiApBill` | 正式结算付款第一步 |
|
||||
| A102-4 | 财务应付单查询 | 查询 | `queryApBillExistNos` | 推送前幂等预检(防重键) |
|
||||
| A103-1 | 付款申请保存(应付下推) | 推送 | `pushBfiPayApplyPush` | 正式结算付款第二步 |
|
||||
| A103-2 | 付款申请单查询 | 查询 | `queryPayApplyPages` / `queryLatestPayApply` | 恢复勾稽 + 定时回写 |
|
||||
| A103-3 | 付款申请保存(预付款) | 推送 | `pushBfiPayApply` | 预付款一步链路 |
|
||||
| A103-4 | 付款处理查询 | 查询 | `queryPayBillPages` | 回填付款记录 |
|
||||
|
||||
**防重键体系**(`awrp_src_sys = WLTMS`,常量统一在 `KingdeeBfiConstant`):
|
||||
|
||||
| 键 | 取值 | 使用接口 |
|
||||
| --- | --- | --- |
|
||||
| 付款申请单来源单号 | `awrp_src_code = paymentNo` | A103-1 / A103-2 / A103-3 |
|
||||
| 财务应付单来源单号 | `awrp_src_code = paymentNo_结算明细单据号拼接`(`ExternalSrcCodeSupport.buildSrcCode`) | A102-2 / A102-4 |
|
||||
| 分录标识 | `bfiEntryCode = paymentNo-分录序号(1 起)` | A102-2 `awrp_entry_code`、A103-1 `awrp_finapbill_entryid` / `awrp_entry_code`、A103-3(序号固定 1) |
|
||||
|
||||
公共约定:各接口日期文本统一 `yyyy-MM-dd HH:mm:ss`(`bfiDateTimeText`,申请日期为空取当天);分页查询统一 `BfiUtil.queryAllPages` 自动翻页(页大小 200、最大 50 页保护);批量入参按 50 单/批(`batchForeach`);A103-2 结果按来源单号取最近修改一条(`latestPayApplyBySrcCode`)。
|
||||
|
||||
## 3. 状态机
|
||||
|
||||
本地 `kingdeeStatus` 流转与 A103-2 状态映射(`kingdeeResultStatus`):
|
||||
|
||||
```
|
||||
unsynced ──推送成功──> synced ──paystatus=Inpayment──> paying ──paystatus=Alreadypay──> paid(终态)
|
||||
│ │ │
|
||||
│ └──billstatus=D──> closed(终态) └──billstatus=D──> closed(终态)
|
||||
└──推送异常(事务回滚 + 独立事务落状态)──> failed ──重推成功或回写勾稽纠偏──> synced/paying/paid
|
||||
```
|
||||
|
||||
| A103-2 返回 | 本地状态 | 是否终态 |
|
||||
| --- | --- | --- |
|
||||
| paystatus=Alreadypay | paid | 是(不再轮询) |
|
||||
| billstatus=D(已关闭) | closed | 是(不再轮询) |
|
||||
| paystatus=Inpayment | paying | 否(继续轮询) |
|
||||
| 其余(A暂存/B已提交/C已审核 + Unpaid) | synced | 否(继续轮询) |
|
||||
| 无返回(金蝶未生成) | 状态不变 | — |
|
||||
|
||||
门禁:仅 `approvalStatus = approved` 允许同步;已 synced/paying/paid/closed 在 `doSyncKingdee` 入口幂等跳过并返回既有单据 id。
|
||||
|
||||
## 4. 推送链路完整逻辑(TMS → 金蝶)
|
||||
|
||||
### 4.1 入口与事务边界
|
||||
|
||||
- 单条:`POST /sync-kingdee` → `syncKingdee(id)`;批量:`POST /sync-kingdee-batch` → `syncKingdeeBatch(ids)`。
|
||||
- 经自代理逐单调用 `doSyncKingdee`:**单据级事务**,单条失败只回滚自身,批量不中断。
|
||||
- 失败时在独立事务 `markKingdeeSyncFailed` 落 `failed`(仅 approved 单据生效)。
|
||||
- 批量同步共享 `KingdeeSyncContext`:档案缓存(合同/客商/项目/货物类型/结算明细)+ 应付单防重键缓存,批量 A102-4 预热一次(`preloadApBillExistNos`,仅正式结算单据),消除逐单远程查询 N+1。
|
||||
|
||||
### 4.2 正式结算付款链路(settlement_payment)
|
||||
|
||||
| 步骤 | 动作 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 1 | `requirePayableSettlement` | 结算类型路由:应付放行,应收(A302-2)待业务确认暂拦截 |
|
||||
| 2 | `resolveBfiEntryGroups` | 结算明细按货物编码+计量单位聚合分录组(金额取结算金额含税/不含税) |
|
||||
| 3 | A102-4 幂等预检 | 按防重键查应付单是否已生成(上下文缓存优先,懒加载兜底) |
|
||||
| 3a | **已生成 → 幂等跳过保存**,返回已有单据编号并回填 `kingdeeBillNo` | 说明上次同步已在后续环节中断,应续推而非拦截报错 |
|
||||
| 3b | 未生成 → A102-2 保存 | 仅校验保存成功,应付单 id 不落库;单据编号回填 `kingdeeBillNo` |
|
||||
| 4 | **已存在时先 A103-2 恢复勾稽**(`reconcileExistPayApply`) | 付款申请单已生成 → 回填 `kingdeeBillId/kingdeeBillNo`,直接视为同步成功,跳过下推 |
|
||||
| 5 | 勾稽未命中 → A103-1 应付下推 | 分录经 `bfiEntryCode` 与 A102-2 明细一一关联;返回星瀚单据 id |
|
||||
| 6 | 落库 | `kingdeeBillId = 付款申请单 id`,`kingdeeStatus = synced` |
|
||||
|
||||
### 4.3 预付款链路(project_advance / progress_advance)
|
||||
|
||||
| 步骤 | 动作 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 1 | 同步前状态为 `failed` 时先 A103-2 恢复勾稽 | 金蝶已生成 → 回填并视为成功,避免重推产生重复单据;首推(状态非 failed)不多一次远程查询 |
|
||||
| 2 | 勾稽未命中 → A103-3 保存 | 不绑应付单;返回星瀚单据 id |
|
||||
| 3 | 落库 | 同 4.2 第 6 步 |
|
||||
|
||||
## 5. 回写链路完整逻辑(金蝶 → TMS)
|
||||
|
||||
触发:`PaymentKingdeeResultTask` 定时任务(默认每 10 分钟,`thirdParty.bfi.payResult.syncEnabled/cron`),也可手动 `POST /sync-kingdee-result`;逻辑收敛在 `syncKingdeePaymentResult()`。
|
||||
|
||||
### 5.1 ① A103-2 单据与付款状态回写
|
||||
|
||||
- 轮询范围:`approved` 且 `kingdeeStatus IN (synced, paying, failed)`;paid/closed 终态不再轮询。
|
||||
- **failed 纳入勾稽**:金蝶已生成说明上次推送实际成功(如下推请求超时但远端已落单、本地事务回滚),查到即经 `applyPayApplyResult` 回填 `kingdeeBillNo/billno`、`kingdeeBillId/id`、状态、已付金额(分录 `e_paidamt` 汇总),自动纠偏,防止人工重推重复;未查到保持 failed 等待异常处理后重新推送。**任务只勾稽回写,不主动推送**。
|
||||
- 付款完成(paid)联动:清理 MOCK 付款记录、履约付款数据(预结算预付记录、正式结算付款记录)。
|
||||
|
||||
### 5.2 ② A103-4 付款记录回填
|
||||
|
||||
- 候选:付款中/已付款单据(本轮 A103-2 查到 `paystatus ≠ Unpaid`)+ 已付款但无真实付款记录的单据(补偿轮询)。
|
||||
- 按付款人(公司)编码分组查询(编码优先取 A103-2 `payorg_number`,兜底合同组织;业务日期窗口最近 1 个月,接口限制)。
|
||||
- 勾稽:付款处理单源单 id(= `kingdeeBillId`)优先,源单编号(= `kingdeeBillNo`)兜底(`matchPayBill`)。
|
||||
- 回填(`applyPayBillRecord`):按付款处理单号 upsert 付款记录(金额、日期、凭证留档 JSON)。
|
||||
|
||||
## 6. 两边逻辑连贯性对照
|
||||
|
||||
### 6.1 正常时序(正式结算付款)
|
||||
|
||||
```
|
||||
TMS 审批通过 → A102-2 保存应付单(回填 kingdeeBillNo)→ A103-1 下推付款申请(落 kingdeeBillId,置 synced)
|
||||
→ 任务 A103-2 回写(billno 覆盖 kingdeeBillNo,状态 synced→paying→paid)→ 任务 A103-4 回填付款记录 → 终态
|
||||
```
|
||||
|
||||
### 6.2 异常场景矩阵(两边行为与闭环路径)
|
||||
|
||||
| # | 断点场景 | 金蝶侧实际 | 本地状态 | 恢复路径 | 闭环 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 1 | 推送前校验/档案缺失失败(A102-2 之前) | 无任何单据 | failed | 重新推送走全链路首推 | ✅ |
|
||||
| 2 | A102-2 成功、A103-1 报错(如分录校验失败) | 应付单已生成 | failed(事务回滚,编号回填一并回滚) | 重推:A102-4 预检命中 → 幂等跳过保存 → A103-2 勾稽未命中 → A103-1 续推;下推再报错才真实失败 | ✅ 本次改造 |
|
||||
| 3 | A103-1 远端已落单但响应超时/本地回滚 | 应付单 + 付款申请单均已生成 | failed | 重推:A102-4 命中跳过 → A103-2 勾稽命中 → 回填置 synced;或定时任务自动纠偏 | ✅ 本次改造 |
|
||||
| 4 | 预付款 A103-3 远端成功但本地失败 | 付款申请单已生成 | failed | 重推前 A103-2 预检命中 → 回填跳过;或定时任务纠偏 | ✅ 本次改造 |
|
||||
| 5 | 推送成功但 kingdeeBillNo 尚为应付单编号 | 付款申请单已生成 | synced | 定时任务 A103-2 回写覆盖为付款申请单编号(字段语义随阶段演进,设计如此) | ✅ |
|
||||
| 6 | 手工重推与定时任务并发纠偏 | 单据唯一(防重键唯一) | — | 两侧均为"先查后动":勾稽只读远端按 paymentNo 唯一归并,重复执行幂等无冲突 | ✅ |
|
||||
| 7 | 金蝶反审核删除后重推 | 无单据 | failed | 预检查无 → 重新推送生成新单据(符合"金蝶反审核+删除后 ERP 重推"规则) | ✅ |
|
||||
| 8 | failed 且金蝶确无单据(如档案长期缺失) | 无单据 | failed | 每轮任务空查一次 A103-2(量级 = failed 单据数),状态保持,等待人工处理 | ⚠️ 已知边界 |
|
||||
|
||||
### 6.3 连贯性结论
|
||||
|
||||
1. **推送侧自洽**:三条件分支(应付单已存在 / 付款申请单已存在 / 均不存在)覆盖全部断点组合,任一断点重推均从断点续传而非从头重推,且以远端实际为准。
|
||||
2. **回写侧衔接**:恢复成功后本地状态为 synced(或直接 paid/paying),落入任务轮询范围,付款状态与付款记录由任务持续驱动至终态,无需再次人工干预。
|
||||
3. **双通道兜底**:手工重推(勾稽预检)与定时任务(勾稽纠偏)对同一断点均可闭环,先后到达不冲突。
|
||||
4. **语义一致性**:防重键、分录标识、日期格式、A103-2 归并规则均已收敛为统一生成点(`apBillSrcCode`/`bfiEntryCode`/`bfiDateTimeText`/`latestPayApplyBySrcCode`/`batchForeach`),两侧关联字段不存在漂移可能。
|
||||
|
||||
## 7. 已知边界与注意事项
|
||||
|
||||
1. **failed 空查**:确无金蝶单据的 failed 单据会被任务持续空查(每轮 A103-2 一次),如量大可考虑按申请日期窗口截断或引入重试次数上限(当前不加,保持简单)。
|
||||
2. **`kingdeeBillNo` 阶段语义**:推送瞬间为应付单编号,勾稽/回写后为付款申请单编号;依赖该字段展示时以回写后的值为准。
|
||||
3. **应收结算(A302-2)未接入**:结算类型为应收的单据在 `requirePayableSettlement` 拦截,推送规则待业务确认。
|
||||
4. **A102-2/A103-1 联调临时取值**:物料/计量单位/项目编码仍传金蝶测试档案值(调试块),档案对齐后需拆除,见 2026-09-16 技术文档遗留项。
|
||||
5. **A103-2 勾稽不校验金额**:恢复勾稽以"来源单号存在"为准,不比对金额与分录;若远端单据被人工修改,以远端为准回写(与回写任务原则一致)。
|
||||
Reference in New Issue
Block a user