docs: 新增预付款申请A103-3金蝶链路补齐方案

覆盖三项改造:e_bebank_number 与 A103-1 统一取联行号、A103-2 消失解锁兜底(ADR-0005)、
回写任务开关默认策略;含状态机全景、时序图、配置汇总与联调验证要点
This commit is contained in:
刘泉佳
2026-09-29 01:51:07 +08:00
parent 846f080343
commit 4c32cb9046
@@ -0,0 +1,169 @@
# 预付款申请(A103-3)金蝶链路补齐方案:e_bebank_number 统一、A103-2 消失解锁、回写默认开启
> 日期:2026-09-29
> 关联文档:
> - `doc/docs/TMS业财融合-集成流程图文字版与业务流程梳理.md`(图03 财务应付及付款,步骤4 推送金蝶 / 步骤13 异常回收)
> - `doc/adr/0005-B901事件驱动双表锁单模型.md`(UNLOCK 置失败态不自动重推;A103-2 连续两轮查询消失兜底复用 `unlockBill()`)
> - `doc/docs/业务流程/付款申请同步金蝶(星瀚)技术文档.md`(A103-3 接口定义)
> - `doc/docs/业务流程/2026-09-17-付款管理金蝶付款结果定时回写(A103-2付款申请单查询+A103-4付款处理查询)技术文档.md`
> - `doc/docs/业务流程/2026-09-18-付款管理金蝶同步全链路逻辑梳理(推送链路与回写链路连贯性).md`
## 1. 背景与范围
对照方案文档图03 对 A103-3 预付款路径做金蝶侧缺口盘点,本方案补齐三项(合同合规系统相关事项另期处理):
| # | 缺口 | 原状 | 本方案 |
|---|---|---|---|
| 1 | A103-3 分录 `e_bebank_number`(往来银行.编码)取 `bankName`(银行名称字段) | 与 A103-1 取客商收款账户联行号的口径不一致,疑似取错字段 | 统一取联行号,未维护即拦截 |
| 2 | 金蝶侧反审核删除付款申请单后,TMS 无感知、无恢复路径 | A103-2 查不到仅 `continue`,本地停留 synced/paying;状态门禁禁止重推 → 单据永久卡死 | 连续两轮查询消失 → `unlockBill()` 解锁置失败,交人工重推 |
| 3 | 定时回写任务默认关闭 | 代码 `@Value` 默认 false(注释却写"默认开启"),三套 Nacos 配置默认 false,上线必须手工开启 | 代码与 Nacos 默认均改为开启,注释纠正 |
不改动范围:A103-3 推送报文其余字段、三层幂等门禁、A902 附件链路、A103-4 付款记录回写、履约数据联动(均已就绪)。
## 2. 改造一:A103-3 `e_bebank_number` 与 A103-1 统一口径
### 2.1 变更内容
`PaymentApplicationServiceImpl#pushBfiPayApply`:
| 项 | 变更前 | 变更后 |
|---|---|---|
| 取值 | `entity.getBankName()`(付款申请"收款银行名称"文本) | `bfiBebankNumber(entity, context)` |
| 实际含义 | 银行名称(非编码,金蝶档案无法匹配) | 付款申请所选客商收款账户(`blade_customer_receipt_account`)**联行号 `cnapsCode`** |
| 缺失行为 | 不校验,直接传名称 | 收款账户不存在 / 未维护联行号 → `ServiceException` 拦截推送(与 A103-1 同口径) |
```java
// 往来银行.编码:与 A103-1 统一口径,取付款申请所选客商收款账户联行号(未维护即拦截)
entry.setEBebankNumber(this.bfiBebankNumber(entity, context));
```
### 2.2 取值链路(与 A103-1 完全一致)
1. 付款申请选择了收款账户(`receiptAccountId`)→ 按账户 id 直查,批量同步经 `KingdeeSyncContext.receiptAccountCache()` 复用;
2. 未选账户 → 按合同乙方客商 + 收款账号兜底匹配(默认账户优先);
3. `cnapsCode` 为空 → 拦截:"收款账户未维护联行号,请先在客商档案维护收款信息联行号"。
### 2.3 影响
- **新增推送前置条件**:预付款推送前必须维护收款账户联行号。存量已 synced 单据不受影响(状态门禁不会重推);failed 待重推单据需先补联行号。
- 解锁重推(改造二)产生的"新单"同样走该口径,与金蝶侧往来银行档案编码匹配。
## 3. 改造二:A103-2 消失解锁兜底(ADR-0005)
### 3.1 业务问题
图03 异常回收分支:财务共享驳回后,金蝶侧对该付款申请单**反审核 + 删除**,TMS 需解锁并允许重新推送。此前 TMS 只有正向回写:A103-2 查不到该单仅跳过,本地停留 synced/paying,且 `doSyncKingdee` 状态门禁禁止 synced/paying 重推——单据卡死,只能改库解救。
B901 UNLOCK 事件通知属合同系统对接域,本期不建设;按 ADR-0005 约定,A103-2 轮询兜底与 B901 共用同一 `unlockBill()` 方法,B901 后续接入时零改动复用。
### 3.2 判定规则:"连续两轮查询消失"
| 要素 | 规则 |
|---|---|
| 判定对象 | 本轮 A103-2 批量查询中未查得的、本地状态为 **synced / paying** 的单据 |
| 健康前提 | 同批次其余付款申请号正常返回(A103-2 服务整体故障时整轮抛异常,不做消失判定) |
| 排除对象 | failed(金蝶查不到属正常态,等待人工重推)、终态 paid/closed(不参与轮询)、paymentNo 为空(防御) |
| 计数存储 | Redis `transport:payment-application:kingdee-pay-miss:{付款申请id}`,每轮未查得 `INCR`,TTL 2100s(覆盖 3 个 10 分钟回写周期;任务中断超时后计数自然过期,需重新累计,方向保守) |
| 查得即清零 | A103-2 查得该单 → `DEL` 计数键 |
| 解锁阈值 | 连续计数 ≥ 2 轮 → 调用 `unlockBill(id, "A103-2 连续N轮查询消失")` |
| Redis 异常 | 放弃本轮判定(解锁会清空金蝶单号,属破坏性动作,宁可延迟不可误判) |
### 3.3 `unlockBill()` 语义(ADR-0005,接口方法,B901 可直接复用)
前置:仅 synced / paying 生效;failed 与终态单据自动跳过。
动作(单据级事务,条件更新防并发):
1. 清除消失计数键;
2. `kingdee_status`:synced/paying → **failed(生成失败)**;
3. `kingdee_bill_id`、`kingdee_bill_no` **清空**(原值 warn 日志留档;金蝶侧单据已删除,原内码对 A902 附件、A103-4 勾稽均已失效);
4. **不自动重推**——金蝶反审核删除大概率伴随驳回意见,原样重推等于无视驳回(ADR-0005),置 failed 回人工。
### 3.4 重推闭环与误判安全网
解锁后单据进入 failed,与"推送异常"恢复路径完全合并:
```mermaid
sequenceDiagram
participant T as TMS 定时回写(A103-2)
participant R as Redis 计数
participant K as 金蝶(星瀚)
participant U as 人工
T->>K: A103-2 按 paymentNo 批量查询
K-->>T: 某单未查得(同批其余正常)
T->>R: INCR 消失计数(第1轮,仅告警)
T->>K: 下一轮再查仍未见
T->>R: INCR 计数 ≥ 2
T->>T: unlockBill():status=failed、清单号/内码(日志留档)
U->>U: 核实金蝶确已删除(驳回原因)
U->>T: 「同步金蝶」重新推送
T->>K: A103-2 勾稽预检(failed 恢复场景)
alt 金蝶侧单据实际存在(误判)
K-->>T: 命中 → 回填单据id/编号,跳过推送(不产生重复单据)
else 确已删除
K-->>T: 未命中 → A103-3 重新保存,生成新单
T->>K: A902 附件上传到新单
end
```
要点:**即使误判解锁,重推链的 A103-2 勾稽预检也能兜底**——预付款路径 failed 恢复场景必经 `reconcileExistPayApply`,正式路径推送前统一预检,命中即回填跳过,不会产生重复单据。
### 3.5 涉及代码
| 位置 | 内容 |
|---|---|
| `IPaymentApplicationService#unlockBill(Long id, String source)` | 新增接口方法(B901 接入时复用) |
| `PaymentApplicationServiceImpl#unlockBill` | 解锁实现(事务 + 条件更新) |
| `PaymentApplicationServiceImpl#handlePayApplyMissed` | 消失轮次判定(回写主流程接入) |
| `executePaymentResultSync` ① 循环 | `payApply == null` → `handlePayApplyMissed`;查得 → `clearPayApplyMissKey` |
## 4. 改造三:定时回写默认开启
| 位置 | 变更 |
|---|---|
| `PaymentKingdeeResultTask` | 注释纠正为"代码默认关闭(安全兜底),由 Nacos 按环境显式开启"(`@Value` 兜底默认保持 `false`) |
| `doc/nacos/blade-dev.yaml` / `blade-prod.yaml` / `third-party-api.yaml` | `syncEnabled: ${BFI_PAY_RESULT_SYNC_ENABLED:true}`——**环境配置默认开启**,未部署 Nacos 配置时代码兜底关闭 |
- 调度频率不变:`0 */10 * * * ?`(每 10 分钟)。
- 环境变量 `BFI_PAY_RESULT_SYNC_ENABLED=false` 仍可显式关闭(灰度/演练用)。
- 消失解锁依赖该任务运行,开关关闭期间兜底判定同步停摆(预期行为)。
## 5. 状态机全景(变更后)
```
unsynced ─A103-1/A103-3─▶ synced ─Inpayment─▶ paying ─Alreadypay─▶ paid(终)
│ ▲ │
│ └─ A103-2 勾稽纠偏 ──┘
├─ 连续两轮查询消失 / B901 UNLOCK ─▶ failed ─人工重推─▶ synced
推送异常 ─────────────────────────────────────────────────────▶ failed
billstatus=D ──────────────────────────────────────────────────▶ closed(终)
```
- 解锁后 `kingdee_bill_id/no` 清空 → A103-2/A103-4 后续轮询按 paymentNo 重新勾稷新单;
- failed 纳入轮询的既有纠偏逻辑与解锁路径互不冲突(failed 查不到不计数、查到回填 synced)。
## 6. 配置项汇总
| 配置 | 默认 | 说明 |
|---|---|---|
| `thirdParty.bfi.payResult.syncEnabled` | Nacos 配置 `true`;代码兜底 `false` | 回写任务总开关,关闭则状态回写与消失解锁同时停摆 |
| `thirdParty.bfi.payResult.cron` | `0 */10 * * * ?` | 回写周期,同时决定消失解锁的轮次间隔 |
| `thirdParty.bfi.payResult.lockLeaseSeconds` | `1800` | 回写 single-flight 锁租约 |
| Redis 键 `transport:payment-application:kingdee-pay-miss:{id}` | TTL 2100s | 消失轮次计数,无配置项(常量) |
## 7. 验证要点
1. **联行号拦截**:收款账户未维护联行号的预付款点击"同步金蝶" → 报"收款账户未维护联行号"且不产生金蝶单据;维护后重推成功,A103-2 查回的 `e_bebank_number` 为联行号。
2. **消失解锁**:推送成功(synced)→ 金蝶侧反审核并删除该付款申请单 → 等待 ≥2 个回写周期 → 日志出现"第1轮/连续2轮",单据变 failed、`kingdee_bill_id/no` 清空。
3. **误判回填**:解锁后金蝶侧单据实际存在(构造 A103-2 两次查询异常)→ 人工重推 → 勾稽命中回填,金蝶侧不出现重复单据。
4. **开关回归**:`BFI_PAY_RESULT_SYNC_ENABLED=false` 环境变量能覆盖默认值关闭任务。
## 8. 遗留事项(不在本方案范围)
| # | 事项 | 状态 |
|---|---|---|
| 1 | B901 UNLOCK 回调接入(解锁延迟从 ≤2 个回写周期缩短为事件即时) | 待建设,复用 `unlockBill()` 零改动 |
| 2 | closed 终态(金蝶驳回/关闭)票据额度释放与待处理提醒 | 未实现 |
| 3 | A103-3 预付款不传 `awrpSrcConNo`(合同号)是否符合金蝶勾稽要求 | 待联调确认 |
| 4 | A103-2 查询未传 modifytime 时间窗(接口约束 ≤1 个月)是否放行 | 待联调确认 |