From f8bd66aafb2d3c991c79364a3be31c3d88cda67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Wed, 30 Sep 2026 15:01:57 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20BFI=E6=8E=A5=E5=8F=A3=E7=BC=96=E5=8F=B7?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=90=8D=E7=A7=B0=E6=A0=87=E6=B3=A8=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=85=A8=E6=A2=B3=E7=90=86=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...€š)BFI接口调用全链路梳理(按业务域汇总·更新版).md | 120 +++++----- ...付款回写-应收勾稽-开票回写-收票回写-客商同步-收款认领推送).md | 209 ++++++++++++++++++ 2 files changed, 276 insertions(+), 53 deletions(-) create mode 100644 doc/docs/业务流程/2026-09-30-金蝶(星瀚)BFI定时任务逻辑全梳理(付款回写-应收勾稽-开票回写-收票回写-客商同步-收款认领推送).md diff --git a/doc/docs/业务流程/2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).md b/doc/docs/业务流程/2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).md index c0169b94..5a26cea4 100644 --- a/doc/docs/业务流程/2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).md +++ b/doc/docs/业务流程/2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).md @@ -14,11 +14,11 @@ - [0. 阅读须知与本次更新要点](#0-阅读须知与本次更新要点) - [1. 集成总览与公共机制](#1-集成总览与公共机制) -- [2. 业务域一:客商档案(A902-1 / A902-2)](#2-业务域一客商档案a902-1--a902-2) -- [3. 业务域二:财务应付与付款(A102-4 / A102-2 / A103-1 / A103-3 / A103-2 / A103-4)](#3-业务域二财务应付与付款) -- [4. 业务域三:财务应收与开票(A302-4 / A302-2 / A303-4 / A303-1 / A303-5)](#4-业务域三财务应收与开票) -- [5. 业务域四:收票与金蝶票池(A801-1 / A303-2 / A302-6 / A801-3)](#5-业务域四收票与金蝶票池) -- [6. 业务域五:收款认领(A301-2 / A301-3 / A301-1)★新增](#6-业务域五收款认领a301-2--a301-3--a301-1新增) +- [2. 业务域一:客商档案(A902-1 客户查询 / A902-2 客户保存)](#2-业务域一客商档案a902-1-客户查询--a902-2-客户保存) +- [3. 业务域二:财务应付与付款(A102-4 财务应付单查询 / A102-2 财务应付单保存 / A103-1 应付下推生成付款申请 / A103-3 预付款申请保存 / A103-2 付款申请单查询 / A103-4 付款处理查询)](#3-业务域二财务应付与付款) +- [4. 业务域三:财务应收与开票(A302-4 财务应收单查询 / A302-2 财务应收单保存 / A303-4 开票申请单查询 / A303-1 开票申请单新增 / A303-5 开票发票查询)](#4-业务域三财务应收与开票) +- [5. 业务域四:收票与金蝶票池(A801-1 全票池发票查询 / A303-2 收票单批量保存 / A302-6 收票指定应付 / A801-3 收票单查询)](#5-业务域四收票与金蝶票池) +- [6. 业务域五:收款认领(A301-2 收款认领公告查询 / A301-3 收款认领结果推送 / A301-1 收款单查询)★新增](#6-业务域五收款认领a301-2-收款认领公告查询--a301-3-收款认领结果推送--a301-1-收款单查询新增) - [7. 业务域六:公共取数(A801-4 汇率)★新启用](#7-业务域六公共取数a801-4-汇率新启用) - [8. 全景:接口先后顺序与依赖关系](#8-全景接口先后顺序与依赖关系) - [9. 状态机汇总](#9-状态机汇总) @@ -113,19 +113,19 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) > 与基础业务文档《TMS业财融合-集成流程图文字版与业务流程梳理》第 10 节「接口速查」的差异:文档清单中的 **A901-1 供应商查询、A801-5 黑名单、A913 项目保存、A101-1/2 合同保存、A102-1/A302-1 暂估单、A902 附件上传** 均未在客户端实现(合同/项目同步走合同合规系统,非金蝶 A 类接口;A902 附件上传各推送链路均未接,方案文档中的「审批单据/磅单等作为附件推送金蝶」暂未实现)。 -**查询类接口时间窗约束**(IBfiService Javadoc):A103-2 / A103-4 / A102-4 / A302-4 / A303-4 / **A301-1** 查询区间 **≤ 1 个月**;A902-1 **≤ 6 个月**;A301-2 无时间窗参数(按认领用户+分页)。 +**查询类接口时间窗约束**(IBfiService Javadoc):A103-2 付款申请单查询 / A103-4 付款处理查询 / A102-4 财务应付单查询 / A302-4 财务应收单查询 / A303-4 开票申请单查询 / **A301-1 收款单查询** 查询区间 **≤ 1 个月**;A902-1 客户查询 **≤ 6 个月**;A301-2 收款认领公告查询 无时间窗参数(按认领用户+分页)。 -### 1.3 认证与令牌(A100-1) +### 1.3 认证与令牌(A100-1 获取令牌) - **取令牌**:`BfiServiceImpl.getToken()`。请求体 `BfiTokenDTO`:`client_id / client_secret / username / accountId(数据中心) / language`,另由 `BfiUtil.fillRandomParam` 填充 `nonce`(UUID) 与 `timestamp`(服务端校验 ±5 分钟)。 -- **缓存**:Redis,key=`blade:third:bfi:token`;TTL = `expires_in` − 120 秒缓冲;`getAccessToken()` 先读缓存,未命中才调 A100-1。无 refresh 机制;写入用 `setIfAbsent`,已有未过期缓存时新令牌不会覆盖(排障需知)。 +- **缓存**:Redis,key=`blade:third:bfi:token`;TTL = `expires_in` − 120 秒缓冲;`getAccessToken()` 先读缓存,未命中才调 A100-1 获取令牌。无 refresh 机制;写入用 `setIfAbsent`,已有未过期缓存时新令牌不会覆盖(排障需知)。 - **注入**:双头——业务方法 `@RequestHeader("access_token")` 显式携带;`BfiRequestInterceptor` 统一加 `Authorization` 网关头。`BfiFeignClientConfig` 用空拦截器覆盖全局 BladeFeignRequestInterceptor;超时 10s/60s。 ### 1.4 响应封装、分页与异常处理 -- **统一响应** `BfiResultVO`:`data / errorCode("0"=成功) / message / status(Boolean)`;`isSuccess() = status && errorCode=="0"`。三类封装:`commonCall`(标准)、`commonRimCall`(rim 契约,兼容 "0"/"0000",A801-1 用)、`commonOpenApiCall`(A303-5,只看 `success`;请求/响应 data 均 Base64(JSON))。 +- **统一响应** `BfiResultVO`:`data / errorCode("0"=成功) / message / status(Boolean)`;`isSuccess() = status && errorCode=="0"`。三类封装:`commonCall`(标准)、`commonRimCall`(rim 契约,兼容 "0"/"0000",A801-1 全票池发票查询用)、`commonOpenApiCall`(A303-5 开票发票查询,只看 `success`;请求/响应 data 均 Base64(JSON))。 - **保存类响应** `BfiBillSaveResultVO`:`successCount/failCount`(String)、`result[]`(`billIndex/billStatus/entryIds/errors/id/number/type: Add/Push/Update`)。业务侧统一 `requireBfiSaveSuccess` 校验 `result[0].billStatus`,失败抛异常。 -- **分页**:`BfiPageVO{filter, lastPage, pageNo, pageSize, totalCount, rows}`;通用翻页 `BfiUtil.queryAllPages`(页码递增,终止:页空 / `lastPage=true` / rows 不满页 / 超 50 页)适用于 9 个 BfiPageVO 类查询接口。**不适用**:A801-1(rim 契约无 lastPage/totalCount,游标分页 `PoolScanCursor`,单请求上限 3 次远程调用)与 A801-4(单页固定 pageSize=10)。 +- **分页**:`BfiPageVO{filter, lastPage, pageNo, pageSize, totalCount, rows}`;通用翻页 `BfiUtil.queryAllPages`(页码递增,终止:页空 / `lastPage=true` / rows 不满页 / 超 50 页)适用于 9 个 BfiPageVO 类查询接口。**不适用**:A801-1 全票池发票查询(rim 契约无 lastPage/totalCount,游标分页 `PoolScanCursor`,单请求上限 3 次远程调用)与 A801-4 汇率查询(单页固定 pageSize=10)。 - **异常转换**:`BfiExceptionTranslator`(@RestControllerAdvice)→ `R.fail(500)`。 ### 1.5 批量同步上下文 KingdeeSyncContext(09-24 重构) @@ -153,14 +153,14 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) | 金蝶单据 | awrp_src_code | awrp_src_sys / srcType | 分录 awrp_entry_code | 预检查重接口 | |---|---|---|---|---| -| 财务应付单 A102-2 | `正式结算单号_结算明细单据号1_…` | WLTMS;`TMS财务应付单` | 明细行=**结算明细单据号**;手工合计费用项行=**结算单号-费用类型-费用项**拼接 | A102-4 | -| 财务应收单 A302-2 | 同应付口径 | WLTMS;`TMS财务应收单` | 同应付口径 | A302-4 | -| 付款申请 A103-1 | `paymentNo` | WLTMS;srcType=配置 `0KzXs` | **固定 `paymentNo-1`(单分录)** | A103-2 | -| 预付款申请 A103-3 | 付款申请号口径 | WLTMS;`0KzXs` | `paymentNo-1`(单分录) | A103-2 预检共用 | -| 开票申请 A303-1 | `billno = application_no`(单张)或 `application_no-张次序号`(多张)★变化 | systemsource=WLTMS;srcType=`0KzXs` | 开票商品行 id(blade_invoice_application_line.id) | A303-4(**批量 billno**) | -| 收票单 A303-2 | `收票登记 id` | WLTMS;`TMS收票登记` | 与 A102-2 分录同源同值(明细行/手工合计行) | A801-3 | -| 收款认领 A301-3 | `SKCL+yyyyMMddHHmmss+认领id尾号6位`(blade_receipt_claim.awrp_src_code)★新增 | WLTMS | 结算单分摊行(seq 序号) | A301-1(sourcebillnumber 匹配) | -| 客户 A902-2 | `number = TMS客商编号`(KS+6位自增),`societycreditcode` 为金蝶侧候选键 | awrp_datasourcesys=WLTMS | — | A902-1 | +| 财务应付单 A102-2 | `正式结算单号_结算明细单据号1_…` | WLTMS;`TMS财务应付单` | 明细行=**结算明细单据号**;手工合计费用项行=**结算单号-费用类型-费用项**拼接 | A102-4 财务应付单查询 | +| 财务应收单 A302-2 | 同应付口径 | WLTMS;`TMS财务应收单` | 同应付口径 | A302-4 财务应收单查询 | +| 付款申请 A103-1 | `paymentNo` | WLTMS;srcType=配置 `0KzXs` | **固定 `paymentNo-1`(单分录)** | A103-2 付款申请单查询 | +| 预付款申请 A103-3 | 付款申请号口径 | WLTMS;`0KzXs` | `paymentNo-1`(单分录) | A103-2 付款申请单查询 预检共用 | +| 开票申请 A303-1 | `billno = application_no`(单张)或 `application_no-张次序号`(多张)★变化 | systemsource=WLTMS;srcType=`0KzXs` | 开票商品行 id(blade_invoice_application_line.id) | A303-4 开票申请单查询(**批量 billno**) | +| 收票单 A303-2 | `收票登记 id` | WLTMS;`TMS收票登记` | 与 A102-2 分录同源同值(明细行/手工合计行) | A801-3 收票单查询 | +| 收款认领 A301-3 | `SKCL+yyyyMMddHHmmss+认领id尾号6位`(blade_receipt_claim.awrp_src_code)★新增 | WLTMS | 结算单分摊行(seq 序号) | A301-1 收款单查询(sourcebillnumber 匹配) | +| 客户 A902-2 | `number = TMS客商编号`(KS+6位自增),`societycreditcode` 为金蝶侧候选键 | awrp_datasourcesys=WLTMS | — | A902-1 客户查询 | ### 1.7 公共档案取数(应付/应收/收票三侧复用 `KingdeeApBillSyncSupport`) @@ -168,7 +168,7 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) |---|---|---| | 组织 org/payorg(应收 recorg) | 结算单 `contract_id` → `blade_contract_manage.organization_id` → `SysCache.getDept().getDeptCode()` | 取数主体由合同改为结算单(结算单维度传参) | | 币种 | 合同 `settlement_currency`,`RMB→CNY` | 不变 | -| **汇率 exchangerate / exratedate** | **正式结算单页面维护值**(来源:结算页面经 `GET /bfi/exchange-rate` 查金蝶 A801-4 回填);未维护不送 | ★新增 | +| **汇率 exchangerate / exratedate** | **正式结算单页面维护值**(来源:结算页面经 `GET /bfi/exchange-rate` 调金蝶 A801-4 汇率查询回填);未维护不送 | ★新增 | | 往来户 asstact_number | 名称(合同乙方 / 结算单付款方 / 开票单位)→ 本地 `blade_customer_archive` 按 `full_name/short_name` 匹配取 `customer_code`;查不到即报错拦截 | 不变 | | 往来类型 asstacttype | 合同类别=`客户合同` → `bd_customer`,否则 `bd_supplier` | 不变 | | **物料编码** | 真实映射链路:装卸大类费用行→`装卸`;国际业务模式/项目业务类型→`国际货运代理服务费`;国内→`国内道路货物运输服务-`+运单运输方式;再经 `BFI_MATERIAL_NUMBER_MAP` 映射金蝶编码(`Item-00001081` / `XDWL11010001` 等);⚠️ 三处 `if(true)` 调试组合块仍做最终覆盖(兜底 物流配送 Item-00001080+TON;**收票侧未命中随机取**) | ★重构 | @@ -180,10 +180,12 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) --- -## 2. 业务域一:客商档案(A902-1 / A902-2) +## 2. 业务域一:客商档案(A902-1 客户查询 / A902-2 客户保存) **定位**:全局数据前提。其他链路的往来户编码(`customer_code`)来自 `blade_customer_archive`,与金蝶客户的对应关系由本链路建立(**金蝶客户编码 = TMS 客商编号**,无独立存储)。 +![客商档案页面(2026-09-29):新增弹窗按统一社会信用代码满 18 位防抖触发 A902-1 查询回填;审批通过档案的 A902-2 推送为行内「同步金蝶」链接(仅 approved 且 kingdeeStatus=failed 时显示,人工重推入口),另有定时复查/重推任务](./2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).assets/bfi-01-customer-archive.png) + ### 2.1 A902-1 客户查询(回填 / 复查) **触发场景(3 个入口)** @@ -239,7 +241,9 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) ### 3.1 A102-2 财务应付单保存 -- **触发**:`POST /formal-settlement/sync-kingdee`(FormalSettlementController:209),结算类型=`payable` 路由(`receivable` 路由 A302-2,同一入口)。无定时。 +![正式结算单页面(2026-09-29):应付 / 应收 Tab 同页,勾选一条单据后点工具栏「同步金蝶」——payable 路由 A102-2、receivable 路由 A302-2(4.1),成功后回写结算单 kingdee_bill_no 并置 synced](./2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).assets/bfi-02-formal-settlement.png) + +- **触发**:`POST /formal-settlement/sync-kingdee`(FormalSettlementController:209),结算类型=`payable` 路由(`receivable` 路由 A302-2 财务应收单保存,同一入口)。无定时。 - **顺序**:校验 approved+应付类型 → synced 幂等返回 → 组防重键 → **A102-4 预检** → 未生成才 A102-2 保存(`KingdeeApBillSyncSupport.pushApBill`)→ 回写。单笔单事务。 - **参数来源**: - 防重键 `正式结算单号_结算明细单据号…`;srcType=`TMS财务应付单`;billtype=`服务采购应付`→`ApFin_service_BT_S`; @@ -251,7 +255,7 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) - ⚠️ 调试组合块强制物料/计量单位(见 1.7);⚠️ 项目编码仍传测试值 `999`; - **planentity 仍不传** → 金蝶按分录自动汇总生成付款计划。 - **响应存储**:`result[0].number` → `blade_formal_settlement.kingdee_bill_no`、`kingdee_sync_status='synced'`;id 不落库。失败抛异常整体回滚保持 unsynced(**不落 failed**)。 -- **依赖**:本地数据齐备即可;**是 A103-1 与 A303-2(收票)的硬前置**。 +- **依赖**:本地数据齐备即可;**是 A103-1 应付下推生成付款申请与 A303-2 收票单批量保存的硬前置**。 - **幂等**:A102-4 按防重键预检,已存在直接返回已有 billno(alreadyExist=true)。 ### 3.2 A102-4 财务应付单查询(★用途缩减) @@ -263,19 +267,21 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) ### 3.3 A103-1 付款申请保存(应付下推)/ A103-3 预付款直推(★大改) +![付款管理页面(2026-09-29):「批量同步」= /payment-application/sync-kingdee[-batch](A102-4 硬校验 → A103-2 幂等预检 → A103-1 下推,预付款走 A103-3);「同步付款结果」手动触发 A103-2 / A103-4 状态与付款记录回写](./2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).assets/bfi-03-payment-application.png) + - **触发**:`POST /payment-application/sync-kingdee`(单笔)与 `/sync-kingdee-batch`(批量)。**预付款**(project_advance/progress_advance)走 A103-3 直推。 - **调用顺序**(`doSyncKingdee`,单据级事务): 1. 锁单 + approved 校验 → 状态门禁(synced/paying/paid/closed 幂等跳过); 2. 结算类型路由 → **A102-4 硬校验**(关联的**每张**正式结算单应付单必须已生成,按本地组装的防重键逐单预检); 3. **A103-2 幂等预检**(`reconcileExistPayApply`:正式路径统一先预检;预付款路径仅 failed 恢复时预检——查到即回填跳过,防「远端已落单本地回滚」重推); 4. **A103-1 下推**(单分录)/ **A103-3 保存** → 回写。批处理:整批一次 A102-4 预热 → 逐单自代理独立事务 → 单条失败仅回滚自身、落 `failed`、不中断整批。 -- **A103-1 参数(★新口径)**: +- **A103-1 付款申请保存(应付下推)参数(★新口径)**: - 防重键 `awrp_src_code`=`paymentNo`;`applydate`=申请日期(空取当天);`exratetable_number`=配置 `01`(注释保留,实际不送——按应付单自动带出);`awrp_textfield1`=付款方式字典标签(非必传); - **分录固定一条**:`e_paymenttype_number`=201(正式付款);**`e_settlementtype_number` 按付款方式映射(★新增):汇票付款→`012-02`(应付票据电子银行承兑汇票),其余→`02`(银行转账)**; - **`awrp_finapbill_entryid` = 主结算单应付单防重键(★本地组装 `settlementApBillSrcCode`,不再 A102-4 反查 `awrp_entry_code_p`)**——主结算单=付款申请 `settlementId`,聚合多结算单取关联结算明细首个结算单 id;⚠️ 支撑类注释仍写「A102-4 反查、禁止自行拼接」,**注释与代码不一致,金蝶侧对该值的校验口径需联调确认**(见 11.2-#4); - `awrp_entry_code`=`paymentNo-1`;`e_assacct`=付款申请 `bankAccount`(收款账号);**`e_bebank_number`=客商收款账户联行号(★`blade_customer_receipt_account`,未维护报错拦截)**; - `e_applyamount`=申请付款金额(2 位 HALF_UP);project 仍传 `999`。 -- **A103-3 参数**:单分录 `paymentNo-1`;`e_paymenttype_number` 按付款类型(正式=201/预付);`e_assacct`=bankAccount;**`e_bebank_number`=`entity.getBankName()`**(⚠️ 语义存疑:往来银行.编码取的是名称字段,见 11.2-#5);`awrp_textfield1`=付款方式标签;汇率表/汇率日期=申请日期;**project=`entity.projectId`→项目档案 `projectCode`(真实编码,无则 999)** ★改善。 +- **A103-3 预付款申请保存参数**:单分录 `paymentNo-1`;`e_paymenttype_number` 按付款类型(正式=201/预付);`e_assacct`=bankAccount;**`e_bebank_number`=`entity.getBankName()`**(⚠️ 语义存疑:往来银行.编码取的是名称字段,见 11.2-#5);`awrp_textfield1`=付款方式标签;汇率表/汇率日期=申请日期;**project=`entity.projectId`→项目档案 `projectCode`(真实编码,无则 999)** ★改善。 - **响应存储**:`requireBfiBillIdAndFillNo`:`result[0].id` → `kingdee_bill_id`;`result[0].number` → `kingdee_bill_no`(推送即时值,后续被 A103-2 回写覆盖校准);`kingdee_status='synced'`。异常:事务回滚后独立事务 `markKingdeeSyncFailed` → `failed`。 - **依赖**:硬前置 = A102-2 已生成(A102-4 校验)。失败重推断点续传:A102-4 命中跳过 → A103-2 命中直接回填。 - **幂等**:三层——本地状态门禁、A102-4 应付预检、A103-2 按 paymentNo 查重。 @@ -320,13 +326,13 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) - **参数**:防重键同应付口径;srcType=`TMS财务应收单`;billtype=`服务销售应收`→`arfin_sersal_BT_S`;`bizdate`=同步当日;**exchangerate/exratedate=结算单页面维护值** ★新增;`asstacttype` 固定 `bd_customer`;`asstact_number`=结算单付款方 payer_name→客商编码;paymode=CASH、payproperty=1001; **分录(★新口径,与 A102-2 完全同源)**:结算明细行 + 手工合计费用项各一条,`awrp_entry_code` 同应付口径;调试组合块同在;项目 `999`。 - **响应存储**:`result[0].number` → `blade_formal_settlement.kingdee_bill_no`、`synced`;id 不落库;失败回滚保持 unsynced(**不落 failed**)。 -- **依赖/幂等**:是 A303-1 的硬前置;A302-4 预检防重推。 +- **依赖/幂等**:是 A303-1 开票申请单新增的硬前置;A302-4 预检防重推。 ### 4.2 A302-4 财务应收单查询 - **触发**:① A302-2 推送前幂等预检;② **定时勾稽纠偏**(`syncKingdeeArResult`:轮询 approved+receivable+status ∈ {synced, **failed**},批量 50 键/批:查到补缺失 kingdee_bill_no、failed 纠偏为 synced;查不到不变);③ **A303-1 推送前置硬校验**(任一关联结算单应收单未生成即拦截)。 - **参数/存储**:`awrp_src_code`(多值)+ `awrp_src_sys`;落库仅限勾稽纠偏补号/状态纠正。 -- **依赖/幂等**:A302-2 →(A302-4)→ A303-1 硬顺序。 +- **依赖/幂等**:A302-2 财务应收单保存 →(A302-4 财务应收单查询)→ A303-1 开票申请单新增 硬顺序。 ### 4.3 A303-4 开票申请单查询(★批量幂等预检) @@ -336,6 +342,8 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) ### 4.4 A303-1 开票申请单新增(★按张次拆分重写) +![开票管理页面(2026-09-29):工具栏「批量同步」= /invoice-application/sync-kingdee,A302-4 硬校验 → A303-4 按全部候选 billno 批量幂等预检 → A303-1 按发票张次逐张新增;列表支持按金蝶单据状态筛选](./2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).assets/bfi-04-invoice-application.png) + - **触发**:用户动作 `POST /invoice-application/sync-kingdee` → `syncKingdee`。单申请一次。 - **调用顺序**:校验 approved → synced/invoiced 幂等返回 → 关联结算单非空 → **A302-4 硬校验** → **A303-4 批量预检** → 销/购方取数校验 → **逐张发票张次**组装保存(`requireBfiSaveSuccess` 校验每张)→ 批量回写张次与申请单。 - **参数**: @@ -347,15 +355,15 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) ### 4.5 A303-5 开票发票查询(结果回写) - **触发**:定时(PaymentKingdeeResultTask 第 3 步)+ 手动 `POST /invoice-application/sync-kingdee-invoice-result`。 -- **顺序**:待回写 = approved + synced + 有 kingdee_bill_no → **按 `application.kingdee_bill_no` 逗号拆分逐个 serialNo**(★适配张次多单号)→ 按销方税号分组(开票单位→客商档案税号,解析不到跳过 warn)→ 组内 ≤50/批调 A303-5(票易通 openApi,Base64 包裹)。 +- **顺序**:待回写 = approved + synced + 有 kingdee_bill_no → **按 `application.kingdee_bill_no` 逗号拆分逐个 serialNo**(★适配张次多单号)→ 按销方税号分组(开票单位→客商档案税号,解析不到跳过 warn)→ 组内 ≤50/批调 A303-5 开票发票查询(票易通 openApi,Base64 包裹)。 - **响应存储**:`billNo + invoiceList[]` → `blade_invoice_application_invoice`(serial_no、invoice_no、invoice_date、invoice_amount、raw_json);幂等 = 批量预查已有发票号 + 唯一键 `uk_application_invoice(tenant_id, application_id, invoice_no)`;有新增发票 → `invoiced` 终态。**发票结果不反写结算单**。 -- **依赖/幂等**:必须先有 A303-1 产生的单号;唯一键防重;批次失败仅计数不中断。 +- **依赖/幂等**:必须先有 A303-1 开票申请单新增产生的单号;唯一键防重;批次失败仅计数不中断。 --- ## 5. 业务域四:收票与金蝶票池 -**架构现状**:票池浏览**实时回源 A801-1**(全量定时同步任务已退役),镜像表 `blade_kingdee_invoice_pool` 降级为**登记存根**;推送链 A303-2 → A302-6 人工批量按钮触发;A801-3 承担推送预检 + 定时回写。 +**架构现状**:票池浏览**实时回源 A801-1 全票池发票查询**(全量定时同步任务已退役),镜像表 `blade_kingdee_invoice_pool` 降级为**登记存根**;推送链 A303-2 收票单批量保存 → A302-6 收票指定应付 人工批量按钮触发;A801-3 收票单查询 承担推送预检 + 定时回写。 ### 5.1 A801-1 全票池发票查询(选票 / 保存重查 / 补票种) @@ -368,6 +376,8 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) ### 5.2 A303-2 收票单批量保存 + A302-6 指定应付(推送链) +![收票管理页面(2026-09-29):工具栏「批量同步」= /invoice-receipt/push-kingdee,链路 A102-4 应付预检 → A801-3 幂等预检(防重键=收票登记 id)→ A303-2 收票单保存 → A302-6 指定应付;选票 / 保存重查经 A801-1 实时回源票池](./2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).assets/bfi-05-invoice-receipt.png) + - **触发**:`POST /invoice-receipt/push-kingdee`。单笔失败不中断整批,逐单报因。 - **调用顺序**(`pushOneReceivedInvoice`): 1. 批量取数防 N+1(分摊/镜像/结算单/明细一次取回); @@ -378,8 +388,8 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) 6. **A303-2 保存** → 成功先落单号 + `pushed`; 7. **A302-6 指定应付**(★入参改 receipt.getId())→ 成功 `assigned`(终态)+ 记流水;失败 `failed` + 原因。unassign 不自动执行。 - **参数**: - - A303-2:`awrp_src_code`=收票登记 id;srcType=`TMS收票登记`;`invoiceno`=receipt.invoice_no;`receivedate`=推送当日;`issuedate`=镜像 invoice_date;`invoicetypef7_number`=镜像 invoice_type_code(缺则报错);销购方四要素=镜像票面;org/币种/`receivablessupp_number`(合同乙方→客商编码)复用公共取数;**分录与 A102-2 同源同口径(★结算明细行+手工合计费用项)**;⚠️ 调试组合块**含随机兜底**(未命中组合随机选一个物料,见 11.2-#6); - - A302-6:`ap_invoice` 引用 `awrp_src_code`=收票登记 id(★修复);`ap_finapbill` 引用 `apBillSrcCode`=正式结算单号_明细单据号(与 A102-2 同源)。 + - A303-2 收票单批量保存:`awrp_src_code`=收票登记 id;srcType=`TMS收票登记`;`invoiceno`=receipt.invoice_no;`receivedate`=推送当日;`issuedate`=镜像 invoice_date;`invoicetypef7_number`=镜像 invoice_type_code(缺则报错);销购方四要素=镜像票面;org/币种/`receivablessupp_number`(合同乙方→客商编码)复用公共取数;**分录与 A102-2 同源同口径(★结算明细行+手工合计费用项)**;⚠️ 调试组合块**含随机兜底**(未命中组合随机选一个物料,见 11.2-#6); + - A302-6 收票指定应付:`ap_invoice` 引用 `awrp_src_code`=收票登记 id(★修复);`ap_finapbill` 引用 `apBillSrcCode`=正式结算单号_明细单据号(与 A102-2 同源)。 - **响应存储**:`result[0].number` → `blade_invoice_receipt.kingdee_received_invoice_no`;状态机 `unsynced→pushed→assigned/failed` → `kingdee_push_status`;失败原因→`kingdee_push_msg`(截 500 字);终态/失败写 `blade_invoice_receipt_record` 流水。`blade_invoice_receipt.kingdee_bill_no / kingdee_status` 两列已删除(20260923),金蝶状态唯一来源=`kingdee_push_status`。 - **依赖/幂等**:A302-6 依赖 A303-2 同键收票单已生成、且依赖 A102-2 应付单(硬预检);A801-3 预检命中跳过保存;pushed 断点由定时补偿重发 A302-6;assigned 拒绝重推;pushed/assigned 状态**禁止本地作废**(voidBill)。 @@ -399,7 +409,7 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) --- -## 6. 业务域五:收款认领(A301-2 / A301-3 / A301-1)★新增 +## 6. 业务域五:收款认领(A301-2 收款认领公告查询 / A301-3 收款认领结果推送 / A301-1 收款单查询)★新增 **定位**:图06「回款认领」采用**业务先行认领(场景二)**:金蝶司库收到银行流水完成第一轮自动认领(到客户维度),TMS 二次认领到结算单(应收单)维度后回推金蝶生成收款单。**三个接口本期全部接线,均为手动触发,无定时任务**(定时开关 `thirdParty.bfi.receiptClaimResult.*` 预留未接)。专项文档:`2026-09-24-收款认领金蝶全链路业务流程….md`。 @@ -416,10 +426,12 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) ### 6.1 A301-2 收款认领公告查询(公告同步) +![收款流水页面(2026-09-29):「手动同步流水」= POST /receipt-flow/sync(A301-2 公告查询,claimusers=当前用户真实姓名,全量翻页幂等落库);勾选流水后「批量认领」在认领事务内同步推送 A301-3](./2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).assets/bfi-06-receipt-flow.png) + - **触发**:仅手动 `POST /receipt-flow/sync`(按钮权限 `receipt_flow_sync`)。前端已去 mock,直接调后端。 - **顺序**:确定参与认领用户(`UserCache.getUserRealName`,为空拦截)→ 全量翻页(pageSize 200,50 页保护,时间窗口从 Nacos 配置 `thirdParty.bfi.claimAnnounce.months` 读取,默认 6 个月)→ 幂等落库 → 操作留痕(`blade_receipt_flow_record` 记 sync)。 - **参数**:`claimusers`=当前用户真实姓名(**前提:金蝶公告的 claimusers 须与 TMS 用户姓名一致**,否则查不到公告)。 -- **响应存储**:upsert `blade_kingdee_receipt_flow`,幂等键 `detail_serial_no`(取 detailid,缺省回退 tradedetailno/bankdetailno;重复明细流水号拦截);落库 `bill_id`(**认领公告 id,A301-3 必填**)、billno→receipt_notice_no、reamount、对方户名/账号/开户行、摘要、交易时间、unclaimed_amount、fee、payee_name、claimstatus→kingdee_claim_status、claimme。**公告侧已认领金额/状态与本地 claimed_amount/claim_status 互不覆盖**。 +- **响应存储**:upsert `blade_kingdee_receipt_flow`,幂等键 `detail_serial_no`(取 detailid,缺省回退 tradedetailno/bankdetailno;重复明细流水号拦截);落库 `bill_id`(**认领公告 id,A301-3 收款认领结果推送必填**)、billno→receipt_notice_no、reamount、对方户名/账号/开户行、摘要、交易时间、unclaimed_amount、fee、payee_name、claimstatus→kingdee_claim_status、claimme。**公告侧已认领金额/状态与本地 claimed_amount/claim_status 互不覆盖**。 - **依赖/幂等**:独立;唯一索引 `(tenant_id, detail_serial_no)`。 ### 6.2 A301-3 收款认领结果推送 @@ -432,6 +444,8 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) ### 6.3 A301-1 收款单查询(认领结果回写) +![认领记录页面(2026-09-29):「查询金蝶认领结果」= POST /receipt-claim-record/query-kingdee-result,手动调 A301-1 收款单查询(今天回溯 30 天),按 sourcebillnumber == awrp_src_code(SKCL 码)匹配回写单号与状态](./2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).assets/bfi-07-receipt-claim-record.png) + - **触发**:仅手动 `POST /receipt-claim-record/query-kingdee-result`。**仅查询,不推送**。 - **顺序**:轮询 `claim_status=claimed` 且 `awrp_src_code` 非空且 `kingdee_bill_status ∈ (pushed, approved)` → A301-1 按业务日期**今天回溯 30 天**(≤1 个月约束)全量翻页(pageSize 200)→ 本地匹配 `sourcebillnumber == awrp_src_code` → 逐条回写。 - **响应存储**:`kingdee_bill_no`=收款单 billno(首条命中后仅推进状态);`kingdee_bill_status` 映射:A 暂存/B 已提交→pushed;C 已审核→approved;**D 已收款→received(终态)**;其他/G 退单→failed。窗口内查不到不改状态(金蝶回单延迟兜底)。**不改本地认领金额/分摊**。 @@ -510,16 +524,16 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) | 2 | A102-2(应付单)先于 A103-1(付款下推) | A102-4 硬校验(逐关联结算单);~~A102-4 反查 awrp_entry_code_p~~ **已移除** | | 3 | A102-2(应付单)先于 A303-2/A302-6(收票推送) | A102-4 硬预检 | | 4 | A302-2(应收单)先于 A303-1(开票申请) | A302-4 硬校验 | -| 5 | A303-2 先于 A302-6 | 同一次推送内串行;assigned 依赖 pushed | +| 5 | A303-2 收票单批量保存 先于 A302-6 收票指定应付 | 同一次推送内串行;assigned 依赖 pushed | | 6 | A302-2(应收单)先于 A301-3(认领推送分录携带应收单号) | 分录 e_corebillno 缺失不传(软依赖),联调前提建议先同步应收 | -| 7 | 推送类先于回写类:A103-2/A103-4、A302-4 勾稽、A303-5、A801-3、A301-1 都依赖推送产生的键/单号 | 查询键(paymentNo / srcCode / billNo / serialNo / SKCL码)由推送链写入本地 | -| 8 | A801-4 汇率查询先于 A102-2/A302-2(如需携带汇率) | 页面维护动作,非代码强制(未维护不传) | +| 7 | 推送类先于回写类:A103-2 付款申请单查询/A103-4 付款处理查询、A302-4 财务应收单查询 勾稽、A303-5 开票发票查询、A801-3 收票单查询、A301-1 收款单查询 都依赖推送产生的键/单号 | 查询键(paymentNo / srcCode / billNo / serialNo / SKCL码)由推送链写入本地 | +| 8 | A801-4 汇率查询先于 A102-2 财务应付单保存/A302-2 财务应收单保存(如需携带汇率) | 页面维护动作,非代码强制(未维护不传) | ### 8.3 断链恢复语义(各链路通用模式) - **本地失败、远端未落单**:状态保持 unsynced(应付/应收/开票)或 failed(付款申请/收票),可重推。 -- **远端成功、本地回滚**:重推时预检命中幂等回填——应付/应收走 A102-4/A302-4 防重键、付款申请走 A103-2 按 paymentNo、开票走 A303-4 按候选 billno(张次级)、收票走 A801-3 按 srcCode(登记 id)、认领走 A301-1 按 sourcebillnumber(⚠️ A301-3 重推会换新码,见 6.2)。 -- **推送成功后状态滞后**:定时任务回写(A103-2→paid/closed、A303-5→invoiced、A801-3→assigned 补偿;A301-1 暂为手动)。 +- **远端成功、本地回滚**:重推时预检命中幂等回填——应付/应收走 A102-4 财务应付单查询/A302-4 财务应收单查询 防重键、付款申请走 A103-2 付款申请单查询 按 paymentNo、开票走 A303-4 开票申请单查询 按候选 billno(张次级)、收票走 A801-3 收票单查询 按 srcCode(登记 id)、认领走 A301-1 收款单查询 按 sourcebillnumber(⚠️ A301-3 重推会换新码,见 6.2)。 +- **推送成功后状态滞后**:定时任务回写(A103-2 付款申请单查询→paid/closed、A303-5 开票发票查询→invoiced、A801-3 收票单查询→assigned 补偿;A301-1 收款单查询 暂为手动)。 - **批量语义**:整批预热 → 逐单独立事务 → 单条失败不中断整批(付款批量、收票推送、定时回写均如此)。 --- @@ -528,13 +542,13 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) | 表.字段 | 状态集 | 迁移 | 写入方 | |---|---|---|---| -| `blade_formal_settlement.kingdee_sync_status` | unsynced / synced | unsynced→(A102-2 或 A302-2 成功)→synced;失败回滚保持 unsynced;定时勾稽可纠偏 failed→synced(防御性,代码无 failed 写入) | FormalSettlementServiceImpl | -| `blade_payment_application.kingdee_status` | unsynced / synced / paying / paid(终) / closed(终) / failed | unsynced→(A103-1/A103-3)→synced→(Inpayment)→paying→(Alreadypay)→paid;billstatus=D→closed;推送异常→failed;failed 可被 A103-2 勾稽纠偏回 synced | PaymentApplicationServiceImpl + PaymentKingdeeResultTask | -| `blade_invoice_application.kingdee_status` | unsynced / synced / invoiced(终) | unsynced→(A303-1 全部张次成功)→synced→(A303-5 有新发票)→invoiced;失败回滚保持 unsynced(**无 failed 写入**) | InvoiceApplicationServiceImpl + 定时 | +| `blade_formal_settlement.kingdee_sync_status` | unsynced / synced | unsynced→(A102-2 财务应付单保存 或 A302-2 财务应收单保存 成功)→synced;失败回滚保持 unsynced;定时勾稽可纠偏 failed→synced(防御性,代码无 failed 写入) | FormalSettlementServiceImpl | +| `blade_payment_application.kingdee_status` | unsynced / synced / paying / paid(终) / closed(终) / failed | unsynced→(A103-1 应付下推生成付款申请/A103-3 预付款申请保存)→synced→(Inpayment)→paying→(Alreadypay)→paid;billstatus=D→closed;推送异常→failed;failed 可被 A103-2 付款申请单查询 勾稽纠偏回 synced | PaymentApplicationServiceImpl + PaymentKingdeeResultTask | +| `blade_invoice_application.kingdee_status` | unsynced / synced / invoiced(终) | unsynced→(A303-1 开票申请单新增 全部张次成功)→synced→(A303-5 开票发票查询 有新发票)→invoiced;失败回滚保持 unsynced(**无 failed 写入**) | InvoiceApplicationServiceImpl + 定时 | | `blade_invoice_application_sheet.kingdee_bill_no` | 空 / 金蝶单号 | 张次级单号,A303-1 推送或 A303-4 幂等命中时回填 | InvoiceApplicationServiceImpl | -| `blade_invoice_receipt.kingdee_push_status` | unsynced / pushed / assigned(终) / failed | unsynced→(A303-2)→pushed→(A302-6)→assigned;A302-6 失败/发票异常回收→failed;pushed 由定时补偿重发 A302-6(⚠️ 回写匹配缺陷,见 11.2) | InvoiceReceiptServiceImpl + 定时 | -| `blade_customer_archive.kingdee_status` | none / synced / failed / exist(终) | 审批通过→(A902-2)→synced(金蝶受理 B);金蝶审核 C 后定时复查→exist;推送失败/A/B→failed | CustomerArchiveServiceImpl + CustomerKingdeeSyncTask | -| `blade_receipt_claim.kingdee_bill_status` ★新增 | none / pushed / approved / received(终) / failed / voided | none→(A301-3)→pushed→(A301-1: C)→approved→(D)→received;A/B→pushed 维持;G/其他→failed;作废→voided+KDCX 冲单号(本地) | ReceiptFlowServiceImpl + ReceiptClaimRecordServiceImpl | +| `blade_invoice_receipt.kingdee_push_status` | unsynced / pushed / assigned(终) / failed | unsynced→(A303-2 收票单批量保存)→pushed→(A302-6 收票指定应付)→assigned;A302-6 失败/发票异常回收→failed;pushed 由定时补偿重发 A302-6(⚠️ 回写匹配缺陷,见 11.2) | InvoiceReceiptServiceImpl + 定时 | +| `blade_customer_archive.kingdee_status` | none / synced / failed / exist(终) | 审批通过→(A902-2 客户保存)→synced(金蝶受理 B);金蝶审核 C 后定时复查→exist;推送失败/A/B→failed | CustomerArchiveServiceImpl + CustomerKingdeeSyncTask | +| `blade_receipt_claim.kingdee_bill_status` ★新增 | none / pushed / approved / received(终) / failed / voided | none→(A301-3 收款认领结果推送)→pushed→(A301-1 收款单查询: C)→approved→(D)→received;A/B→pushed 维持;G/其他→failed;作废→voided+KDCX 冲单号(本地) | ReceiptFlowServiceImpl + ReceiptClaimRecordServiceImpl | | `blade_kingdee_receipt_flow.claim_status` ★新增 | unclaimed / partial / claimed | 公告同步不覆盖;认领分摊→partial/claimed;全部作废→unclaimed | ReceiptFlowServiceImpl | --- @@ -543,10 +557,10 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) | 任务类 | 方法 | 覆盖接口 | cron(默认) | 开关(默认) | 手动端点 | |---|---|---|---|---|---| -| PaymentKingdeeResultTask | syncKingdeePaymentResult | ① A103-2 + A103-4 ② A302-4 勾稽 ③ A303-5 | `thirdParty.bfi.payResult.cron` = `0 */10 * * * ?` | `thirdParty.bfi.payResult.syncEnabled` = **false** | `POST /payment-application/sync-kingdee-result`、`POST /formal-settlement/sync-kingdee-ar-result`、`POST /invoice-application/sync-kingdee-invoice-result` | -| PaymentKingdeeResultTask | syncKingdeeReceivedInvoiceResult | A801-3 回写 | `thirdParty.bfi.receivedInvoiceResult.cron` = `0 */10 * * * ?` | `thirdParty.bfi.receivedInvoiceResult.syncEnabled` = **false** | `POST /invoice-receipt/sync-kingdee-result` | -| CustomerKingdeeSyncTask | syncKingdeeCustomerData | A902-1 复查 + A902-2 重推 | `thirdParty.bfi.customer.cron` = `0 0 2 * * ?` | `thirdParty.bfi.customer.syncEnabled` = **false** | `POST /customer-archive/sync-kingdee` | -| (预留未接) | — | A301-1 认领回写 | — | `thirdParty.bfi.receiptClaimResult.*`(代码未消费) | `POST /receipt-claim-record/query-kingdee-result`(手动) | +| PaymentKingdeeResultTask | syncKingdeePaymentResult | ① A103-2 付款申请单查询 + A103-4 付款处理查询 ② A302-4 财务应收单查询 勾稽 ③ A303-5 开票发票查询 | `thirdParty.bfi.payResult.cron` = `0 */10 * * * ?` | `thirdParty.bfi.payResult.syncEnabled` = **false** | `POST /payment-application/sync-kingdee-result`、`POST /formal-settlement/sync-kingdee-ar-result`、`POST /invoice-application/sync-kingdee-invoice-result` | +| PaymentKingdeeResultTask | syncKingdeeReceivedInvoiceResult | A801-3 收票单查询 回写 | `thirdParty.bfi.receivedInvoiceResult.cron` = `0 */10 * * * ?` | `thirdParty.bfi.receivedInvoiceResult.syncEnabled` = **false** | `POST /invoice-receipt/sync-kingdee-result` | +| CustomerKingdeeSyncTask | syncKingdeeCustomerData | A902-1 客户查询 复查 + A902-2 客户保存 重推 | `thirdParty.bfi.customer.cron` = `0 0 2 * * ?` | `thirdParty.bfi.customer.syncEnabled` = **false** | `POST /customer-archive/sync-kingdee` | +| (预留未接) | — | A301-1 收款单查询 认领回写 | — | `thirdParty.bfi.receiptClaimResult.*`(代码未消费) | `POST /receipt-claim-record/query-kingdee-result`(手动) | > 所有已接线的定时开关**代码默认关闭**,需 Nacos 显式开启。⚠️ 客商定时任务仍有联调残留过滤(只处理 KS000067,见 11.1-#3),开启前必须移除。收款认领三接口**全部手动触发**(A301-2 sync、A301-3 认领、A301-1 回写),与方案文档「每天定时获取金蝶收款流水」不一致(见 11.3)。 @@ -570,7 +584,7 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) | # | 问题 | 证据 | 状态 vs 09-23 | |---|---|---|---| | 1 | **A801-3 定时回写按发票号码匹配防重键,而 A303-2 落库 awrp_src_code=收票登记 id** → 回写(异常回收/指定补偿/单号补登)永远匹配不到 TMS 自推单据,静默跳过。09-23 的 243d5be 只修复了推送预检与 A302-6 路径 | InvoiceReceiptServiceImpl(syncKingdeeReceivedInvoiceResult:`map(InvoiceReceipt::getInvoiceNo)` + `matched.get(receipt.getInvoiceNo())`,而预检路径已改 `String.valueOf(receipt.getId())`) | ⚠️ **部分修复(回写路径残留)** | -| 2 | A103-1 `awrp_finapbill_entryid` 实际传**主结算单应付单防重键(本地组装)**,而 PaymentApplicationServiceImpl 与 KingdeeApBillSyncSupport 注释仍写「A102-4 反查 awrp_entry_code_p,禁止各接口自行拼接」——注释与代码矛盾;金蝶侧按单据头 srcCode 勾稽付款计划是否成立需联调确认 | PaymentApplicationServiceImpl:962-963 vs :1129 注释;KingdeeApBillSyncSupport:81/:359-361 注释 | ⚠️ **新增待确认** | +| 2 | A103-1(应付下推生成付款申请)`awrp_finapbill_entryid` 实际传**主结算单应付单防重键(本地组装)**,而 PaymentApplicationServiceImpl 与 KingdeeApBillSyncSupport 注释仍写「A102-4 反查 awrp_entry_code_p,禁止各接口自行拼接」——注释与代码矛盾;金蝶侧按单据头 srcCode 勾稽付款计划是否成立需联调确认 | PaymentApplicationServiceImpl:962-963 vs :1129 注释;KingdeeApBillSyncSupport:81/:359-361 注释 | ⚠️ **新增待确认** | | 3 | 应收/开票链路 `failed` 状态无写入路径(回滚后停留 unsynced;轮询集合中的 failed 只能人工改库产生) | FormalSettlementServiceImpl、InvoiceApplicationServiceImpl | ❌ **仍在** | | 4 | 其他链路推送前不校验客商 `kingdee_status`,客商未推送/未审核也会带编码推单 | KingdeeApBillSyncSupport.queryCustomerCode | ❌ **仍在** | | 5 | A103-3 预付款分录 `e_bebank_number`(往来银行.编码)取 `entity.getBankName()`(名称字段)——与 A103-1 取联行号的口径不一致,疑似取错字段 | PaymentApplicationServiceImpl:885 vs :969 | ⚠️ **新增待确认** | @@ -588,14 +602,14 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) | SC-05「结算审批时同步录入进项发票…同步生成收票单并自动绑定」 | 流程拆分:收票登记独立审批 → 人工批量 `push-kingdee` 推送(非结算审批时自动) | | SC-07「每天定时获取金蝶收款流水」 | A301-2 当前**手动按钮触发**,无定时(`receiptClaimResult.*` 开关预留未接) | | SC-08「按发票张数生成多个金蝶开票申请单,按票面行拆分明细」 | ✅ **已实现**(张次拆分) | -| 汇率「TMS 结算时通过接口获取金蝶汇率记录在结算单据上」 | ✅ **已实现**(A801-4 → 结算单页面维护 → 随 A102-2/A302-2 推送) | +| 汇率「TMS 结算时通过接口获取金蝶汇率记录在结算单据上」 | ✅ **已实现**(A801-4 汇率查询 → 结算单页面维护 → 随 A102-2 财务应付单保存/A302-2 财务应收单保存 推送) | | 客商「TMS 按机构类别转换伙伴类型再推送」 | `customer_nature` 字典映射;分组 1101/1102 已按落库 customerKind 区分 | **vs《TMS业财融合-集成流程图文字版与业务流程梳理》(09-14)** | 文字版描述 | 代码现状 | |---|---| -| 图05/第6节 票池「定时/手动拉取全票池 → 本地镜像 → 查本地镜像选票」 | 已演进:**实时回源 A801-1**(无定时拉取),镜像降级为登记存根(09-23 已按此定稿) | +| 图05/第6节 票池「定时/手动拉取全票池 → 本地镜像 → 查本地镜像选票」 | 已演进:**实时回源 A801-1 全票池发票查询**(无定时拉取),镜像降级为登记存根(09-23 已按此定稿) | | 图05 状态一览「A303-2/A302-6/A801-3 已编码未联调」 | 已联调推进:A801-3 预检与 A302-6 防重键 bug 已修(243d5be),回写仍带缺陷(11.2-#1) | | 图06/第7节 收款认领 A301-1/A301-2/A301-3 | ✅ **已接线**(手动触发;公告 claimusers 须与 TMS 用户姓名匹配;多个 A301-3 字段待金蝶确认) | | 第9节 汇率 A801-4「TMS 结算时获取」 | ✅ 已实现 | @@ -621,7 +635,7 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) - 客户端常量两处并存(`bfi/constant/BfiConstant` 与 `transport/constant/KingdeeBfiConstant`),语义重复,改动需双侧核对。 - `BfiBillSaveResultVO.successCount/failCount` 为 String 类型。 - token 缓存 `setIfAbsent`:已有缓存时强制取的新令牌不会覆盖旧缓存。 -- 操作码与 URL 资源域不完全对应(A303-2/A801-3 挂 `/ap/ap_invoice/`,A303-1/4 挂 `/sim/`,A301 系列挂 `/cas/`),检索时勿按 URL 前缀猜操作码。 +- 操作码与 URL 资源域不完全对应(A303-2 收票单批量保存/A801-3 收票单查询 挂 `/ap/ap_invoice/`,A303-1 开票申请单新增/A303-4 开票申请单查询 挂 `/sim/`,A301 系列收款认领接口挂 `/cas/`),检索时勿按 URL 前缀猜操作码。 - 结算单认领(claim-invoices)**仍无保存重查闸门**(前端传票面直接落库,09-23 提案未实现)。 - 主数据初始化 SQL `kingdee_master_data_init_20260924.sql`(计量单位金蝶编码 + `blade_kingdee_material`)须先执行,物料/单位映射才有正确档案值;认领链路须先执行 `blade_receipt_flow_claim_20260924.sql`。 @@ -631,4 +645,4 @@ iPaaS 网关(/gwzh/BFI,不直连金蝶) - 本文是**总纲更新版**:跨链路顺序、依赖、公共机制、状态机、差异风险汇总,基线为 2026-09-24 代码(`50573e7`)。 - 单链路细节仍以 `doc/docs/业务流程/` 对应文档为深入参考;其中与代码冲突处一律以本文第 11 节结论 + 当前代码为准。 -- 09-23 版总纲中被本文覆盖的过时结论:A301 系列/A801-4 未调用、A102-4 反查、付款申请分录口径、开票单张口径、客商 1102 问题——请以本文为准。 +- 09-23 版总纲中被本文覆盖的过时结论:A301 系列收款认领接口/A801-4 汇率查询 未调用、A102-4 财务应付单查询 反查、付款申请分录口径、开票单张口径、客商 1102 问题——请以本文为准。 diff --git a/doc/docs/业务流程/2026-09-30-金蝶(星瀚)BFI定时任务逻辑全梳理(付款回写-应收勾稽-开票回写-收票回写-客商同步-收款认领推送).md b/doc/docs/业务流程/2026-09-30-金蝶(星瀚)BFI定时任务逻辑全梳理(付款回写-应收勾稽-开票回写-收票回写-客商同步-收款认领推送).md new file mode 100644 index 00000000..f3045260 --- /dev/null +++ b/doc/docs/业务流程/2026-09-30-金蝶(星瀚)BFI定时任务逻辑全梳理(付款回写-应收勾稽-开票回写-收票回写-客商同步-收款认领推送).md @@ -0,0 +1,209 @@ +# 金蝶(星瀚)BFI 定时任务逻辑全梳理(付款回写 + 应收勾稽 + 开票回写 + 收票回写 + 客商同步 + 收款认领推送) + +> 整理日期:2026-09-30 +> 依据:当前代码(git 基线 `2e91e11` 收款认领本地化提交),代码位置 `blade-service/blade-transport/src/main/java/org/springblade/transport/task/` 与各服务实现。 +> 关联文档:`2026-09-27-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总·更新版).md`(总纲)、`2026-09-27-blade-transport定时任务全梳理(金蝶客商同步-金蝶结果回写-收票回写).md`(上一版,**未收录收款认领推送任务,行号已过时,以本文为准**)。 +> 接口名称口径与总纲一致:编号后标注金蝶侧接口名称。 + +--- + +## 1. 任务总览 + +task 目录共 **3 个任务类、4 个调度方法**,覆盖 6 条金蝶(星瀚)链路的回写/同步/推送。调度由 `TransportApplication` 的 `@EnableScheduling` 开启;每个方法 = Nacos 开关 + cron 表达式,业务逻辑全部收敛在服务层方法(任务类只做开关判断、异常捕获与中文摘要日志),定时与手动端点共用同一服务方法。 + +| 调度方法 | 开关(thirdParty.bfi.*,代码默认) | 默认 cron | 服务方法(代码位置) | 覆盖接口 | 手动端点 | +|---|---|---|---|---|---| +| `PaymentKingdeeResultTask.syncKingdeePaymentResult` | `payResult.syncEnabled`(false) | `0 */10 * * * ?` | `PaymentApplicationServiceImpl.syncKingdeePaymentResult`(:644,编排 3 个子流程) | ① A103-2 付款申请单查询 + A103-4 付款处理查询 ② A302-4 财务应收单查询 勾稽 ③ A303-5 开票发票查询 | `POST /payment-application/sync-kingdee-result`、`POST /formal-settlement/sync-kingdee-ar-result`、`POST /invoice-application/sync-kingdee-invoice-result` | +| `PaymentKingdeeResultTask.syncKingdeeReceivedInvoiceResult` | `receivedInvoiceResult.syncEnabled`(false) | `0 */10 * * * ?` | `InvoiceReceiptServiceImpl.syncKingdeeReceivedInvoiceResult`(:968) | A801-3 收票单查询(回写) | `POST /invoice-receipt/sync-kingdee-result` | +| `CustomerKingdeeSyncTask.syncKingdeeCustomerData` | `customer.syncEnabled`(false) | `0 0 2 * * ?` 每天 02:00 | `CustomerArchiveServiceImpl.syncKingdeeCustomerData`(:637) | A902-1 客户查询 复查 + A902-2 客户保存 推送 | `POST /customer-archive/sync-kingdee` | +| `ReceiptClaimPushTask.pushClaimToKingdee` ★新增 | `receiptClaimPush.syncEnabled`(false) | `0 */10 * * * ?` | `ReceiptFlowServiceImpl.pushClaimByTask`(:714) | A301-3 收款认领结果推送 | `POST /receipt-flow/push-kingdee`(单条手动) | + +> 认领链路另两个接口仍为**纯手动、无定时**:A301-2 收款认领公告查询(`POST /receipt-flow/sync`)、A301-1 收款单查询(`POST /receipt-claim-record/query-kingdee-result`)。旧总纲「收款认领三接口全部手动触发」的说法已被本任务打破:**A301-3 自 `2e91e11` 起为定时 + 手动双入口**。 +> ⚠️ 代码开关默认值均为 `false`;`doc/nacos/third-party-api.yaml` 样例中 `payResult.syncEnabled` 的环境变量兜底值为 `true`(`BFI_PAY_RESULT_SYNC_ENABLED:true`),以 Nacos 实际下发为准。 + +--- + +## 2. 公共机制 + +### 2.1 single-flight 分布式锁(KingdeeSyncLockSupport,★09-27 后新建) + +6 个服务方法入口统一经 `KingdeeSyncLockSupport.runSingleFlight(锁前缀, 租约秒数, 任务名, action)`(`support/KingdeeSyncLockSupport.java:61`): + +- **零等待抢锁**:`tryLock(0, leaseSeconds)`,已有轮次在跑立刻放弃本轮并返回「XX进行中,本轮跳过」摘要(定时下轮幂等重试,手动稍后再试);多副本部署的定时任务与手动触发共用一道闸,同一轮全局最多一份在跑。 +- **锁键 = 前缀 + 租户**:定时任务无登录上下文时按 `default` 租户加锁,锁粒度与多租户数据隔离边界一致。 +- **租约兜底**:租约(各任务默认 1800 秒)须大于最坏一轮耗时(含翻页远程查询),否则租约到期另一副本会并发起跑;进程异常退出时锁到期自动释放。 +- **降级**:`blade.lock.enabled=false` 时不加锁直接执行(多副本各自起跑),打 warn 日志——生产必须开启分布式锁。 + +各任务锁前缀与租约配置键: + +| 任务 | 锁键前缀(transport:…) | 租约配置键(默认 1800) | +|---|---|---| +| 付款结果回写 | `payment-application:kingdee-pay-result-sync:` | `thirdParty.bfi.payResult.lockLeaseSeconds` | +| 应收勾稽 | `formal-settlement:…`(AR_RESULT) | `thirdParty.bfi.arResult.lockLeaseSeconds` | +| 开票结果回写 | `invoice-application:…`(INVOICE_RESULT) | `thirdParty.bfi.invoiceResult.lockLeaseSeconds` | +| 收票结果回写 | `invoice-receipt:…`(RECEIVED_RESULT) | ⚠️ `thirdParty.bfi.receivedInvoice.lockLeaseSeconds`(前缀少「Result」,与其余开关键不同名) | +| 客商同步 | `customer-archive:…`(CUSTOMER_SYNC) | `thirdParty.bfi.customer.lockLeaseSeconds` | +| 收款认领推送 | `receipt-flow:…`(RECEIPT_CLAIM_PUSH) | `thirdParty.bfi.receiptClaimPush.lockLeaseSeconds` | + +### 2.2 批量与时间窗口径(IBfiService 契约) + +- 批量查询统一 **50 键/批**(付款 `PAY_RESULT_QUERY_BATCH=50`、开票 `INVOICE_RESULT_QUERY_BATCH=50`、客商信用代码 `KINGDEE_CUSTOMER_QUERY_BATCH=50`),页大小 200,`BfiUtil.queryAllPages` 自动翻页(页空/`lastPage`/不满页/超 50 页终止)。 +- 查询类接口时间窗:A103-2 付款申请单查询 / A103-4 付款处理查询 / A302-4 财务应收单查询 / A303-4 开票申请单查询 / A301-1 收款单查询 **≤ 1 个月**;A902-1 客户查询 **≤ 6 个月**;A301-2 收款认领公告查询 无接口级窗口(代码按 `thirdParty.bfi.claimAnnounce.months`,默认近 6 个月,逐认领人查询)。 +- 事务边界:任务方法与 single-flight 主体**无事务**;单据级写操作经自代理/`TransactionTemplate` 走**独立事务**(如 `applyPayApplyResult`、`applyPayBillRecord`、`pushClaimLocked`),单条失败只回滚自身并计数,不中断整批。 +- 可观测:每个子流程返回中文执行摘要(扫描数、成功/失败/纠偏/跳过计数),任务类 `log.info` 落盘。 + +--- + +## 3. 任务一:付款结果回写(PaymentKingdeeResultTask.syncKingdeePaymentResult) + +编排 **3 个互相独立的子流程**(各自 try/catch,一个异常不影响其余两个): + +### 3.1 子流程①:A103-2 付款申请单查询 + A103-4 付款处理查询 + +实现:`PaymentApplicationServiceImpl.executePaymentResultSync`(:655)。 + +``` +待回写付款申请(approved + kingdeeStatus IN (synced, paying, failed),paid/closed 终态不再轮询) + → A103-2 按 paymentNo(awrp_src_code)50/批查询,同一来源单号取 modifytime 最新一条 + 命中 ──> 清除消失计数 ──> applyPayApplyResult(独立事务): + 回填 kingdee_bill_id / kingdee_bill_no + 状态映射:paystatus=Alreadypay→paid(终)> billstatus=D→closed(终)> Inpayment→paying > 其余→synced + 已付金额 = 分录 e_paidamt 汇总(分录缺失且 paid 时兜底=申请金额) + paid 联动履约:syncPreSettlementAdvance(预结算预付)+ syncFormalSettlementPayments(实付分摊/结算付款记录) + 未命中 ──> 消失解锁判定(ADR-0005,见下) + → A103-4 付款记录回填: + 候选 = 本轮 A103-2 查得 paystatus≠Unpaid 的单 + 已 paid 但无真实付款记录的单(补偿) + 按付款组织编码分组(org_number 优先取 A103-2 返回 payorg_number,兜底按合同所属组织解析,取不到 warn 跳过) + 每组按最近 1 个月业务日期窗口翻页查询(接口 ≤1 个月约束) + 勾稽:sourcebillid==kingdee_bill_id 优先,sourcebillnumber==kingdee_bill_no 兜底 + 命中 ──> applyPayBillRecord(独立事务):清理 MOCK-PAY- 模拟记录 → upsert blade_payment_application_record + (billno→payment_no、actpayamt→paid_amount、paydate→paid_date、整单 JSON→voucher_json 留档) +``` + +**★ 消失解锁(ADR-0005,`handlePayApplyMissed` :575)**:A103-2 连续未查得「已生成/付款中」单据时,按 Redis 计数累计消失轮次(键 `transport:payment-application:kingdee-pay-miss:{id}`,TTL 2100 秒≈覆盖 3 个回写周期;生成失败单据查不到属正常态不计数)。**连续 2 轮**(`PAY_RESULT_MISS_UNLOCK_ROUNDS=2`)未查得即判定金蝶侧已反审核删除,调用 `unlockBill`(:544):`kingdeeStatus synced/paying→failed`,**清空 kingdee_bill_id / kingdee_bill_no**(原值日志留档),不自动重推,等待人工核实后重推;重推时 A103-2 幂等预检兜底,误判场景(金蝶单据实际存在)自动回填不产生重复单据。Redis 异常时放弃本轮判定(解锁是破坏性动作,宁可延迟不可误判)。本轮重新查得或单据解锁时清除计数。 + +### 3.2 子流程②:A302-4 财务应收单查询 勾稽纠偏 + +实现:`FormalSettlementServiceImpl.executeArResultSync`(:690)。 + +- 轮询:approved + settlementType=receivable + kingdeeSyncStatus IN (synced, **failed**)。 +- 逐单查结算明细组装防重键(与 A302-2 财务应收单保存传参一致:`正式结算单号_结算明细单据号…`),同一防重键取首单。 +- `queryArBillExistNos` 批量查询:查到 → 回填缺失的 kingdee_bill_no、状态统一置 synced、记 syncedTime(failed→synced 即生成失败纠偏;synced 且已有单号的跳过);查不到 → 状态不变。 +- 同步成功回写已含 **kingdee_bill_id(星瀚单据内码,A902 附件上传定位用)**。 + +### 3.3 子流程③:A303-5 开票发票查询 结果回写 + +实现:`InvoiceApplicationServiceImpl.executeInvoiceResultSync`(:878)。 + +- 轮询:approved + kingdeeStatus=synced + kingdee_bill_no 非空(invoiced 终态不再轮询;failed 无真实单号,纠偏走重推时的 A303-4 批量预检)。 +- kingdee_bill_no 逗号拼接拆分为逐个 serialNo → 按开票单位反查客商档案取销方税号(取不到跳过并计数)→ 按税号分组 → 每组 ≤50/批调 A303-5 开票发票查询(票易通 openApi)。 +- 发票落库:发票号批量去重(批次内预查已有记录),新增插入 `blade_invoice_application_invoice`(serial_no、invoice_no、invoice_date、invoice_amount、**发票文件地址四字段**:板式文件/预览图/数电 PDF/数电 XML、raw_json);**已有记录缺失文件地址的按本次返回字段级补空**(不覆盖已有值)。 +- 有新增发票 → 申请单推进 invoiced(终态);发票插入、地址补齐、申请单更新三段**分开批量落库**。 + +--- + +## 4. 任务二:收票结果回写(A801-3 收票单查询,PaymentKingdeeResultTask.syncKingdeeReceivedInvoiceResult) + +实现:`InvoiceReceiptServiceImpl.executeReceivedInvoiceResultSync`(:979)。处理推送链中断/异常的补偿,一轮一体处理三类场景: + +``` +待回写收票登记(kingdeePushStatus IN (pushed, assigned) + 非作废 + 已绑定票池镜像) + → 批量加载 blade_kingdee_invoice_pool 镜像,按镜像购方名称分组(接口必填购方名称) + → pushed 单据批量预组装财务应付单防重键(分摊→结算单→结算明细,一票一应付才可组装) + → 回看窗口 = thirdParty.bfi.receivedInvoice.resultWindowDays(默认 30 天),按购方分组翻页查询 + 匹配键 = 收票登记 id(★已修复:与 A303-2 收票单批量保存 落库的 awrp_src_code 同源, + 旧版按发票号匹配永远查不到自推单据的缺陷已关闭) + 窗口内查不到 ──> 不判删除(金蝶修改时间延迟/滑窗),保持现状由人工重推兜底 + 命中后三分支: + ① 发票状态异常(作废/红冲等) ──> 异常回收:置 failed +「金蝶删除/解除指定后重推」,记同步流水 + ② pushed 且状态正常 ──> 指定补偿:凭预组装防重键重发 A302-6 收票指定应付 → assigned + 防重键缺失(不满足一票一应付)→ 置 failed 报人工 + ③ assigned 但缺金蝶收票单号 ──> 单号补登:仅补 kingdee_received_invoice_no(历史响应丢失数据) + → 统一走 applyReceivedInvoicePushResult 落库(单号/状态/失败原因/推送时间),终态/失败记 blade_invoice_receipt_record 流水 +``` + +--- + +## 5. 任务三:客商数据定时同步(CustomerKingdeeSyncTask.syncKingdeeCustomerData) + +实现:`CustomerArchiveServiceImpl.executeCustomerDataSync`(:648)。一次查出全部审批通过客商(**★原 11.1-#3 硬编码 `.eq(customerCode,"KS000067")` 联调过滤已移除**),内存按金蝶协同状态分三组,单条失败不中断批次: + +``` +审批通过客商(approvalStatus=approved,一次全量查询) + ① 待推送(none/failed)── 批量 A902-1 客户查询 复查(信用代码去重大写、50/批、自动翻页,避免逐条 N+1) + 金蝶已有且已审核(status=C) ──> 直接置 exist(终态),跳过推送 + 金蝶侧暂存/已提交(A/B,审核中) ──> 置 failed「金蝶侧该客商审核中,请稍后重推」 + 金蝶不存在 ──> 逐条 A902-2 客户保存 推送(收款账户按客商 id 批量一次查,组装报文避免 N+1) + 成功 → synced / 失败 → failed + 原因(截断留痕) + ② 已推送复查(synced)── 与③共用同一次批量 A902-1(信用代码合并去重,仅调一次接口) + status=C ──> 升级 exist(终态,A902-2 受理 B 只是中间态) + 仍在审核 ──> 保持 synced 下轮再看(不重推,防金蝶重复建档);查无档案 warn 计数 + ③ 停用联动候选(synced/exist 且本地启用)── 复查结果 enable=0 + ──> TMS 档案联动停用(status=2);金蝶恢复可用不自动启用(单向,人工控制) + → 集中批量落库:updateBatchById 更新档案 + Db.saveBatch 插入 blade_customer_change_record 变更留痕(各自 try/catch) +``` + +--- + +## 6. 任务四:收款认领定时推送(ReceiptClaimPushTask.pushClaimToKingdee)★新增 + +> 背景:`2e91e11` 收款认领本地化改造——页面「认领」动作只在本地登记(`blade_receipt_claim` + 分摊表),**A301-3 收款认领结果推送改为「定时 + 手动」双入口**,按收款流水归集推送。旧方案的「认领事务内同步推送」「SKCL+时间戳防重码」口径均已废弃。 + +实现:`ReceiptFlowServiceImpl.pushClaimByTask`(:714,定时)与 `pushClaimToKingdee`(:685,手动单条)共用推送主体 `doPushClaimToKingdee`(:820)。 + +### 6.1 定时扫描与编排(executeReceiptClaimPush :728) + +``` +候选流水(blade_kingdee_receipt_flow):bill_id 非空(已同步到公告) + + push_status IN (none, failed)(未推送/推送失败,pushed 终态闸门不重推) + + claim_status = claimed(认领完成是唯一可推送状态)+ 有效 + → 按「认领人」分组(blade_receipt_claim.claimer_name,有效认领 claimed),认领人姓名排序串行处理: + 每个认领人一次 A301-2 收款认领公告查询(claimusers=认领人姓名,近 claimAnnounce.months 个月全量翻页) + ——定时任务无登录人上下文,A301-2 的 claimusers 又是单姓名必填,故必须逐人查询 + 公告按公告id(bill_id)归集 → 其名下每条候选流水按 bill_id 匹配: + 公告查询失败 ──> 该认领人名下流水全部跳过(计数),下轮重试 + 未命中公告 ──> 单条跳过(计数) + 命中 ──> pushClaimLocked 推送(成功后加入本轮已推集合,同一流水被多人认领只推一次) + → 摘要:扫描 N 条,成功 X,失败 Y,公告未命中跳过 Z +``` + +### 6.2 单条推送主体(doPushClaimToKingdee,事务 + 行锁) + +- **并发防护**:`pushClaimLocked` 经 `TransactionTemplate` 开事务,流水行 `FOR UPDATE` 行锁覆盖闸门、对账、推送、落库全过程——防定时任务与手动按钮对同一流水双推。 +- **闸门**:`push_status=pushed` 拒绝重复推送(作废重认领使条件再满足也不推,金蝶侧人工处置);公告 id 缺失拦截「请先手动同步金蝶收款流水」。 +- **推送前对账**:以最新公告刷新公告侧未认领金额/公告认领状态/我已认领,并按全部分摊行重算累计认领金额与 `claim_status` 快照落库;**非 claimed(金额与公告未认领金额不一致)拦截不推**。 +- **防重键 ★口径变化**:`awrp_src_code` = 该流水**全部有效分摊行的结算单号去重下划线拼接**(不再是 `SKCL+yyyyMMddHHmmss+尾号`)。分摊不变则重试编码不变,金蝶按 `awrp_src_sys + awrp_src_code` 幂等,防重试双单;失败落库后下轮自动重试复用同一编码。 +- **A301-3 报文(单分录)**:`billid`=公告 id(必填);`recpaytype_number`=常量 `999`(待金蝶确认)、`paymenttype`=`bd_customer`、`recviewpayer`/`recpayer`=流水付款方名称/账号;分录仅一条,`e_receivableamt`=累计认领金额(全部未推送金额),`seq`/`e_corebillno`=第一张结算单 id(`e_corebilltype` 已注释不传)。 +- **结果处理**:全项 `billStatus=true` 才算成功;成功响应 `number` 为空,金蝶收款单号取**回执键值 `keys.awrp_src_code`**。成功 → 流水 `push_status=pushed`、`kingdee_bill_no`、`kingdee_bill_status=pushed`、清失败原因,并级联刷新名下未作废认领主单冗余字段 + 记 `blade_receipt_flow_record` 流水;失败 → `push_status=failed` + 原因(截 1000 字),本地认领数据保留,等待下轮/手动重推。 +- **A301-1 收款单查询 回写**(仍纯手动 `POST /receipt-claim-record/query-kingdee-result`)按 `sourcebillnumber == 流水 awrp_src_code` 匹配回写收款单号与状态(A/B→pushed、C→approved、D→received 终态、G→failed),与上表状态机衔接。 + +--- + +## 7. 配置开关汇总(Nacos thirdParty.bfi.*) + +| 配置键 | 代码默认 | yaml 样例(doc/nacos/third-party-api.yaml) | 说明 | +|---|---|---|---| +| `payResult.syncEnabled` / `payResult.cron` | false / `0 */10 * * * ?` | **true**(`BFI_PAY_RESULT_SYNC_ENABLED`)/ 同左 | 付款结果回写总开关(含应收勾稽、开票回写) | +| `payResult.lockLeaseSeconds` | 1800 | — | single-flight 锁租约 | +| `receivedInvoiceResult.syncEnabled` / `.cron` | false / `0 */10 * * * ?` | — | 收票结果回写 | +| `receivedInvoice.lockLeaseSeconds` / `receivedInvoice.resultWindowDays` | 1800 / 30 | — | ⚠️ 键名无「Result」后缀;回看窗口天数 | +| `customer.syncEnabled` / `customer.cron` | false / `0 0 2 * * ?` | — | 客商同步 | +| `customer.lockLeaseSeconds` | 1800 | — | single-flight 锁租约 | +| `receiptClaimPush.syncEnabled` / `.cron` / `.lockLeaseSeconds` | false / `0 */10 * * * ?` / 1800 | false / 同左 / 1800 | 收款认领定时推送 ★新增 | +| `claimAnnounce.months` | 近 6 个月 | — | A301-2 公告查询窗口(同步/手动推送对账/定时推送对账共用) | + +> 分布式锁依赖 `blade.lock.enabled=true`;未开启时 single-flight 降级为直接执行(生产必须开启)。 + +--- + +## 8. 共性设计与既有关键机制的落地状态 + +1. **single-flight 全覆盖**:6 条定时/手动共用链路全部经分布式锁串行化,杜绝多副本重复回写/重复推送。 +2. **failed 纳入轮询的自愈纠偏**:付款申请(A103-2 查到即纠正 synced)、应收结算单(A302-4 勾稽纠偏)——金蝶侧已生成说明上次推送实际成功,查到即回填,避免人工重推产生重复单据。 +3. **消失解锁兜底**(ADR-0005):金蝶侧反审核删除的单据经「连续 2 轮查询消失」解锁置失败,解决 synced/paying 单据在金蝶被删后 TMS 永久锁死的问题。 +4. **终态不轮询**:paid/closed(付款)、exist(客商)、invoiced(开票)、assigned/failed+窗口外(收票)、pushed(认领流水)均退出扫描集。 +5. **批量防 N+1**:50/批远程查询、页大小 200 自动翻页、收款账户/镜像/分摊/结算明细批量预取、集中批量落库。 +6. **对照 09-27 总纲第 11 节的两个状态变化**: + - 11.2-#1「A801-3 定时回写按发票号匹配」缺陷 **已修复**(现按收票登记 id 匹配,`queryReceivedInvoiceBySrcCodes`); + - 11.1-#3「客商定时同步硬编码 KS000067」**已移除**。 +7. **旧文档过时点**(以本文为准):09-27 定时任务全梳理未收录 `ReceiptClaimPushTask`;「收款认领三接口全部手动」「A301-3 在认领事务内推送、awrp_src_code=SKCL 随机码」的描述已被认领本地化改造取代。