docs: 金蝶单据锁定与解锁业务流程文档

This commit is contained in:
刘泉佳
2026-10-09 19:52:53 +08:00
parent e08be9a297
commit 1063d5875c
@@ -0,0 +1,128 @@
# 金蝶单据锁定与解锁业务流程(lockBusiData 锁定 - unLockNotifyBusiData 回调 - 失败补偿 - 删单还原)
- 日期:2026-10-09
- 范围:正式结算单(应付 A102-2 / 应收 A302-2)、开票申请单(A303-1)推送金蝶成功后的单据锁定、金蝶解锁/删单回调、锁定失败定时补偿
- 设计文档:`docs/superpowers/specs/2026-10-09-kingdee-bill-lock-unlock-design.md`
- 锁记录表:`blade_kingdee_bill_lock`(一行 = 一张金蝶目标单)
## 一、涉及单据与触发链路
| 业务单据 | 推送接口 | 推送入口 | 金蝶目标单 | 锁定触发 |
| --- | --- | --- | --- | --- |
| 正式结算单(应付) | A102-2 财务应付单保存 | 结算单页面「同步金蝶」 | ApFin_other_BT_S | 推送成功后 |
| 正式结算单(应收) | A302-2 财务应收单保存 | 同上 | arfin_other_BT_S | 推送成功后 |
| 开票申请单 | A303-1 开票申请新增 | 开票申请页面「同步金蝶」 | cdm_paybill_bank_elect_BT_S(每张发票张次一张) | 推送成功后 |
- lockBusiData:WLTMS 调金蝶,源系统 WLTMS、目标系统 XH、lockAction=LOCK
- unLockNotifyBusiData:金蝶调 WLTMS,源系统 XH、目标系统 WLTMS、lockAction=UNLOCK
- 批量约定:多张单据编号英文逗号拼接(sourceBillNo 与 targetBillNo 顺序一一对应),同一源单的多张金蝶单合并一次调用
- 出入参同构,应答为 `{updateCount, updateResultList}` 裸结构(逐单 1-成功/0-失败)
## 二、主线流程
### 1. 推送并锁定(WLTMS → 金蝶)
```
用户点「同步金蝶」
├─ doSyncKingdee(单据级事务):幂等预检 → A102-2/A302-2/A303-1 保存 → 回写 kingdeeBillNo/同步状态=synced
│ └─ 事务提交
├─ 锁定(lockAfterPush):
│ ├─ 每张金蝶目标单一行锁记录,先落 failed 占位(保证补偿扫表无遗漏)
│ ├─ 同源单多张目标单合并一次调用:LOCK + SAVE,编号逗号拼接
│ ├─ 应答 updateCount>0 → 全部翻 locked ──→ 单据冻结
│ └─ 失败/超时 → 保持 failed,tryCount+1,失败原因落 fail_reason(主单同步状态不受影响)
└─ 附件联带上传(A902,原有动作,后置不受影响)
```
**锁定后的效果**:
- 开票申请:`作废`入口被 `checkEditable` 拦截(提示"该单据已被金蝶锁定,不可修改")
- 结算单:synced 状态本身已拦截编辑/调整/作废,锁定期被同一组守卫覆盖
- 重复点「同步金蝶」:推送幂等命中直接返回已有单号,已 locked 的锁记录跳过,不会重复锁
### 2. 金蝶侧解锁回调(金蝶 → WLTMS)
金蝶在星瀚侧对单据解锁或删单后,回调 `POST /kingdee-bfi/unLockNotifyBusiData`(`UNLOCK` + 逗号拼接批量):
```
校验:sourceSystem=XH、targetSystem=WLTMS、lockAction=UNLOCK(否则应答失败)
│
├─ operationType=SAVE(仅解锁,没删单)
│ └─ 锁记录 → unlocked,记录回调操作人/时间,解锁动作留痕
│
└─ operationType=DELETE(金蝶把单删了)
├─ 锁记录 → unlocked(同上)
└─ 按 targetBillType 反查 WLTMS 单据类型,分发还原:
├─ SETTLEMENT → 结算单作废(当前节点→已作废),
│ 同步信息还原推送前状态:kingdeeSyncStatus→unsynced、
│ 清 kingdeeBillNo/kingdeeBillId/syncedTime
│ 变更流水记"金蝶删单回调自动废除",操作人=金蝶报文 operator
└─ INVOICE_APPLY → 开票申请作废,kingdeeStatus→unsynced、清单据编号,
刷新关联结算单开票状态,动作流水记 kingdee_delete
应答:{updateCount: 处理张数, updateResultList: [1]}
```
重复回调幂等:单据已作废或锁记录已 unlocked → 该张跳过,不重复还原。
### 3. 锁定失败补偿(定时兜底,不依赖人工)
```
KingdeeBillLockRetryTask(thirdParty.bfi.billLock.retryEnabled,默认每5分钟)
└─ 扫 lock_status=failed 且 try_count<5 的锁记录
├─ 按源单分组,组内合并一次 lockBusiData 重锁
├─ 成功 → 翻 locked,进入正常锁定态
└─ 失败 → try_count+1,超过 5 次停试,留 fail_reason 待人工排查
```
## 三、状态视角
**锁记录状态机**:
```
failed(锁定失败,待补偿)→ locked(金蝶锁定中)→ unlocked(终态)
```
同单重新推送生成新锁记录,走新一轮循环。
**业务单据生命周期**(以结算单为例):
```
草稿 → 提交 → 审批通过 ──同步金蝶──→ synced(金蝶单已生成)
│ │
│ ├─ 锁定成功:locked(冻结,不可动)
│ │ │
│ │ ├─ 金蝶解锁(SAVE):unlocked,流水留痕
│ │ └─ 金蝶删单(DELETE):作废 + 同步信息还原 unsynced
│ │ (结算明细/付款/开票关联的后续动作
│ │ 由各自前置校验自然拦截重推)
│ └─ 锁定失败:failed,定时补偿重锁
└─ 推送失败:failed(可重推)
```
## 四、关键设计点
1. **先占位再锁**:锁记录落库先于远程调用,补偿任务扫 `failed` 就是完整的待重试集合,不漏单
2. **锁失败不回滚推送**:金蝶单已生成,本地回滚只会制造断链;代价是短暂"已推未锁"窗口,由补偿任务闭合
3. **批量同成败**:接口应答只有聚合 updateCount,无逐单成败,整批一起翻状态,最简且不错账
4. **回调幂等**:按 targetBillType+targetBillNo 找最新 locked 记录,找不到/已 unlocked 直接跳过应答成功,防金蝶重试风暴
5. **DELETE 还原口径**:作废源单 + 清金蝶同步信息 + 关联状态刷新;作废是终态,重推需新单重走流程(口径待业务联调确认)
## 五、代码落点速查
| 内容 | 位置 |
| --- | --- |
| 锁单 Feign/DTO/应答 VO | `blade-bfi-api`:`BfiBillLockDTO`、`BfiBillLockResultVO`、`IBfiClient.lockBusiData`、`IBfiService.lockBusiData` |
| 锁类型常量与映射 | `transport/constant/KingdeeLockConstant` |
| 锁记录实体/服务 | `KingdeeBillLock`、`IKingdeeBillLockService` + `KingdeeBillLockServiceImpl` |
| 回调端点 | `KingdeeBfiPublicController`:`POST /kingdee-bfi/unLockNotifyBusiData`(PERMIT_ALL + TenantIgnore) |
| 补偿定时任务 | `KingdeeBillLockRetryTask`(`thirdParty.bfi.billLock.retryEnabled`,默认每5分钟) |
| 推送后锁定触发 | `FormalSettlementServiceImpl.syncKingdee`、`InvoiceApplicationServiceImpl.syncKingdee`(事务提交后) |
| 删单还原 | `FormalSettlementServiceImpl.voidByKingdee`、`InvoiceApplicationServiceImpl.voidByKingdee` |
| 建表脚本 | `doc/sql/transport/blade_kingdee_bill_lock_20261009.sql` |
## 六、联调前配置
1. Nacos 配置 `thirdParty.bfi.billLockUrl`:lockBusiData 真实路径(代码默认占位 `/ierp/kapi/app/billLock/lockBusiData`,以金蝶最终提供为准)
2. Nacos 开启补偿:`thirdParty.bfi.billLock.retryEnabled: true`(按环境)
3. 向金蝶提供回调地址:网关地址 + transport 服务路由 + `/kingdee-bfi/unLockNotifyBusiData`
4. 与金蝶确认 WLTMS 单据类型编码(SETTLEMENT/INVOICE_APPLY)与 DELETE 还原口径