feat: A301-2 认领公告查询接口适配新参数(billstatus/modifytimeS/modifytimeE),时间窗口配置化(Nacos thirdParty.bfi.claimAnnounce.months,默认6个月)

This commit is contained in:
刘泉佳
2026-09-28 15:49:53 +08:00
parent 85b703b7d5
commit 4d62bb6df7
9 changed files with 721 additions and 21 deletions
@@ -65,6 +65,11 @@ public class BfiProperties {
*/
private ReceivedInvoice receivedInvoice = new ReceivedInvoice();
/**
* 收款认领公告查询配置(A301-2)
*/
private ClaimAnnounce claimAnnounce = new ClaimAnnounce();
/**
* 付款申请推送配置项
*/
@@ -114,4 +119,16 @@ public class BfiProperties {
private String materialNumber = "SP0324";
}
/**
* 收款认领公告查询配置项(A301-2)
*/
@Data
public static class ClaimAnnounce {
/**
* 查询时间窗口(月数),默认 6(近半年)
*/
private int months = 6;
}
}
@@ -55,6 +55,7 @@ import org.springblade.transport.service.IReceiptFlowService;
import org.springblade.transport.service.IFormalSettlementService;
import org.springblade.transport.support.TransportBusinessSupport;
import org.springblade.transport.wrapper.ReceiptFlowWrapper;
import org.springblade.thirdparty.bfi.pojo.dto.BfiClaimAnnounceQueryDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiClaimPushDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiClaimPushEntryDTO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultItemVO;
@@ -314,10 +315,25 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
if (Func.isEmpty(claimusers)) {
throw new ServiceException("当前用户无姓名,无法同步金蝶认领公告");
}
// 从配置读取查询时间窗口(月数),默认近半年
int months = bfiProperties.getClaimAnnounce().getMonths();
LocalDate endDate = LocalDate.now();
LocalDate startDate = endDate.minusMonths(months);
String modifytimeS = startDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
String modifytimeE = endDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
long start = System.currentTimeMillis();
log.info("调用BFI接口 收款认领公告查询开始 claimusers:{}", claimusers);
List<BfiClaimAnnounceVO> announces = BfiUtil.queryAllPages(pageNo ->
bfiService.queryClaimAnnounce(claimusers, pageNo, SYNC_PAGE_SIZE));
log.info("调用BFI接口 收款认领公告查询开始 claimusers:{},时间窗口:{} ~ {}(近{}个月)",
claimusers, modifytimeS, modifytimeE, months);
List<BfiClaimAnnounceVO> announces = BfiUtil.queryAllPages(pageNo -> {
BfiClaimAnnounceQueryDTO queryDTO = new BfiClaimAnnounceQueryDTO();
queryDTO.setClaimusers(claimusers);
queryDTO.setModifytimeS(modifytimeS);
queryDTO.setModifytimeE(modifytimeE);
queryDTO.setPageNo(pageNo);
queryDTO.setPageSize(SYNC_PAGE_SIZE);
return bfiService.queryClaimAnnounce(queryDTO);
});
log.info("调用BFI接口 收款认领公告查询结束 耗时:{}ms,拉取 {} 条",
System.currentTimeMillis() - start, announces.size());
@@ -415,15 +415,21 @@ public interface IBfiClient {
/**
* A301-2 收款认领公告查询
*
* @param accessToken 访问令牌,通过 getToken 获取
* @param claimusers 参与认领用户名
* @param pageNo 页码,从1开始
* @param pageSize 页数
* @param accessToken 访问令牌,通过 getToken 获取
* @param claimusers 参与认领用户名
* @param billstatus 单据状态(0-未认领,1-已认领)
* @param modifytimeS 修改时间-开始时间,格式:yyyy-MM-dd
* @param modifytimeE 修改时间-结束时间,格式:yyyy-MM-dd
* @param pageNo 页码,从1开始
* @param pageSize 页数
* @return 收款认领公告分页数据
*/
@GetMapping(CLAIM_ANNOUNCE_QUERY_URL)
BfiResultVO<BfiPageVO<BfiClaimAnnounceVO>> claimAnnounceQuery(@RequestHeader(BfiConstant.HEADER_ACCESS_TOKEN) String accessToken,
@RequestParam("claimusers") String claimusers,
@RequestParam(value = "billstatus", required = false) String billstatus,
@RequestParam("modifytimeS") String modifytimeS,
@RequestParam("modifytimeE") String modifytimeE,
@RequestParam("pageNo") Integer pageNo,
@RequestParam("pageSize") Integer pageSize);
@@ -36,7 +36,7 @@ import java.io.Serializable;
* <p>
* 请求示例:
* <pre>{@code
* GET /ierp/kapi/v2/awrp/cas/cas_claimannounce/query?claimusers=苏浩晖&pageSize=10&pageNo=1
* GET /ierp/kapi/v2/awrp/cas/cas_claimannounce/query?claimusers=苏浩晖&billstatus=0&modifytimeS=2024-01-01&modifytimeE=2024-06-30&pageNo=1&pageSize=10
* }</pre>
*
* @author Chill
@@ -50,6 +50,18 @@ public class BfiClaimAnnounceQueryDTO implements Serializable {
* 【必填】参与认领用户名
*/
private String claimusers;
/**
* 单据状态(0-未认领,1-已认领)
*/
private String billstatus;
/**
* 【必填】修改时间-开始时间,格式:yyyy-MM-dd
*/
private String modifytimeS;
/**
* 【必填】修改时间-结束时间,格式:yyyy-MM-dd
*/
private String modifytimeE;
/**
* 【必填】页码,从1开始
*/
@@ -264,12 +264,10 @@ public interface IBfiService {
/**
* A301-2 收款认领公告查询
*
* @param claimusers 参与认领用户名
* @param pageNo 页码,从1开始
* @param pageSize 页数
* @param param 查询参数,claimusers/modifytimeS/modifytimeE 必填,页码从1开始
* @return 收款认领公告分页数据
*/
BfiPageVO<BfiClaimAnnounceVO> queryClaimAnnounce(String claimusers, Integer pageNo, Integer pageSize);
BfiPageVO<BfiClaimAnnounceVO> queryClaimAnnounce(BfiClaimAnnounceQueryDTO param);
/**
* A301-3 收款认领结果推送
@@ -42,6 +42,7 @@ import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillSaveDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiArBillQueryDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiArBillSaveBodyDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiArBillSaveDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiClaimAnnounceQueryDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiClaimPushBodyDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiClaimPushDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerQueryDTO;
@@ -473,15 +474,19 @@ public class BfiServiceImpl implements IBfiService {
}
@Override
public BfiPageVO<BfiClaimAnnounceVO> queryClaimAnnounce(String claimusers, Integer pageNo, Integer pageSize) {
if (StringUtil.isBlank(claimusers)) {
public BfiPageVO<BfiClaimAnnounceVO> queryClaimAnnounce(BfiClaimAnnounceQueryDTO param) {
if (param == null || StringUtil.isBlank(param.getClaimusers())) {
throw new BfiException("查询BFI收款认领公告失败,参与认领用户名不能为空");
}
if (pageNo == null || pageSize == null) {
if (StringUtil.isBlank(param.getModifytimeS()) || StringUtil.isBlank(param.getModifytimeE())) {
throw new BfiException("查询BFI收款认领公告失败,修改时间范围不能为空");
}
if (param.getPageNo() == null || param.getPageSize() == null) {
throw new BfiException("查询BFI收款认领公告失败,分页参数不能为空");
}
String accessToken = getAccessToken();
BfiResultVO<BfiPageVO<BfiClaimAnnounceVO>> result = commonCall("收款认领公告查询", () -> client.claimAnnounceQuery(accessToken, claimusers, pageNo, pageSize));
BfiResultVO<BfiPageVO<BfiClaimAnnounceVO>> result = commonCall("收款认领公告查询", () -> client.claimAnnounceQuery(accessToken,
param.getClaimusers(), param.getBillstatus(), param.getModifytimeS(), param.getModifytimeE(), param.getPageNo(), param.getPageSize()));
return result.getData();
}
@@ -480,16 +480,28 @@ class BfiServiceImplTest {
@Test
void queryClaimAnnounceShouldValidateParams() {
assertThrows(BfiException.class, () -> service.queryClaimAnnounce(null, 1, 10));
assertThrows(BfiException.class, () -> service.queryClaimAnnounce("张三", null, 10));
BfiClaimAnnounceQueryDTO nullParam = null;
assertThrows(BfiException.class, () -> service.queryClaimAnnounce(nullParam));
BfiClaimAnnounceQueryDTO noTimeRange = new BfiClaimAnnounceQueryDTO();
noTimeRange.setClaimusers("张三");
noTimeRange.setPageNo(1);
noTimeRange.setPageSize(10);
assertThrows(BfiException.class, () -> service.queryClaimAnnounce(noTimeRange));
}
@Test
void queryClaimAnnounceShouldReturnPage() {
mockAccessToken();
BfiPageVO<BfiClaimAnnounceVO> page = new BfiPageVO<>();
when(client.claimAnnounceQuery("token-1", "张三", 1, 10)).thenReturn(success(page));
assertEquals(page, service.queryClaimAnnounce("张三", 1, 10));
when(client.claimAnnounceQuery("token-1", "张三", null, "2026-01-01", "2026-06-30", 1, 10)).thenReturn(success(page));
BfiClaimAnnounceQueryDTO param = new BfiClaimAnnounceQueryDTO();
param.setClaimusers("张三");
param.setModifytimeS("2026-01-01");
param.setModifytimeE("2026-06-30");
param.setPageNo(1);
param.setPageSize(10);
assertEquals(page, service.queryClaimAnnounce(param));
}
@Test
@@ -59,7 +59,7 @@
### 3.1 编排
1. **确定参与认领用户**:`UserCache.getUserRealName(AuthUtil.getUserId())`,为空拦截「当前用户无姓名,无法同步认领公告」。
2. **全量翻页**:`BfiUtil.queryAllPages(pageNo -> bfiService.queryClaimAnnounce(claimusers, pageNo, 200))`,受 `BfiUtil.DEFAULT_MAX_PAGES`(50 页)保护。
2. **全量翻页**:`BfiUtil.queryAllPages(pageNo -> bfiService.queryClaimAnnounce(queryDTO))`,受 `BfiUtil.DEFAULT_MAX_PAGES`(50 页)保护。时间窗口从 Nacos 配置 `thirdParty.bfi.claimAnnounce.months` 读取(默认 6 个月)。
3. **幂等落库**:按 `detail_serial_no`(取 `detailid`,缺省回退 `tradedetailno`/`bankdetailno`)`FOR UPDATE` 查重后 insert/update 收款流水表;重复明细流水号抛「明细流水号重复」拦截。
4. **操作留痕**:`blade_receipt_flow_record` 记 `sync` 操作(新增/更新逐行记录,operator=当前用户)。
@@ -0,0 +1,634 @@
# 金蝶(星瀚)BFI 接口调用全链路梳理(按业务域汇总·更新版)
> 整理日期:2026-09-27
> 依据:当前代码(`tms-api/blade-third-party-api/blade-bfi-api` 客户端 + `tms-api/blade-service/blade-transport` 业务实现,git 基线 `50573e7` 2026-09-24)与三份材料逐条核对:
> ① `doc/docs/TMS业财融合-集成流程图文字版与业务流程梳理.md`(2026-09-14,6 张集成流程图 OCR 文字版);
> ② `doc/docs/TMS业财融合方案.docx`(V1.2,2026-08-10,8 大集成场景方案);
> ③ `doc/docs/业务流程/2026-09-23-金蝶(星瀚)BFI接口调用全链路梳理(按业务域汇总).md`(上一版总纲)。
> 本文是 09-23 总纲的**全面更新版**:自 09-23 以来代码有 12 个提交(09-23 19:00 收票防重键修复 + 09-24 两批重构/新增),接口清单、分录口径、勾稽方式、联调残留与缺陷清单均有实质变化,全文按当前代码重新核实。
> 每个接口仍按 **触发场景 → 调用顺序 → 参数来源 → 响应存储 → 依赖与幂等** 五个维度说明;文末附全景依赖、状态机、定时任务与配置开关、差异/风险清单。
---
## 目录
- [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新增)
- [7. 业务域六:公共取数(A801-4 汇率)★新启用](#7-业务域六公共取数a801-4-汇率新启用)
- [8. 全景:接口先后顺序与依赖关系](#8-全景接口先后顺序与依赖关系)
- [9. 状态机汇总](#9-状态机汇总)
- [10. 定时任务与配置开关汇总](#10-定时任务与配置开关汇总)
- [11. 代码与三份材料差异 / 风险清单(更新版)](#11-代码与三份材料差异--风险清单更新版)
---
## 0. 阅读须知与本次更新要点
1. **接口编号口径**:金蝶星瀚侧开放接口以 `Axxx-x` 操作码标识,代码常量、文档标题、日志均沿用该口径。
2. **权威顺序**:以**当前代码**为准;与 09-23 总纲及单链路文档冲突处一律按本文第 11 节结论。
3. **本次(09-23 → 09-27)核心变化一览**:
| # | 变化 | 说明 |
|---|---|---|
| 1 | **收款认领链路接线(新增业务域五)** | A301-2 公告同步 / A301-3 认领推送 / A301-1 收款单回写由「客户端已实现无调用」变为「业务侧已接线(全部手动触发,无定时)」 |
| 2 | **A801-4 汇率查询启用(新增业务域六)** | 新增 `GET /bfi/exchange-rate`,结算页面按币种+日期取金蝶汇率;A102-2/A302-2 推送时传 `exchangerate/exratedate`(结算单页面维护值) |
| 3 | **应付/应收/收票分录口径重构** | 分录 = **结算明细行(每行一条)+ 手工结算合计费用项(每行一条)**(新表 `blade_formal_settlement_summary_fee`,仅 manualFlag=1);分录新增「运输货物 / 车号」字段(仅明细行传) |
| 4 | **A103-1 勾稽方式重大变化** | **A102-4 反查付款计划分录 `awrp_entry_code_p` 的步骤已移除**;`awrp_finapbill_entryid` 改为**本地组装的主结算单应付单防重键**(`settlementApBillSrcCode`),分录由多条费用行改为**单条**(付款申请号-1) |
| 5 | **A103-1 结算方式与往来账户完善** | `e_settlementtype_number` 按付款方式显式映射(汇票→012-02 应付票据电子银行承兑汇票,其余→02 银行转账);`e_bebank_number` 改取客商收款账户(`blade_customer_receipt_account`)**联行号**,未维护即拦截 |
| 6 | **开票申请按发票张次拆分** | 新表 `blade_invoice_application_sheet`:**每张发票张次推一张金蝶开票申请单**;billno 单张=申请单号、多张=申请单号-张次序号;A303-4 预检改为**按全部候选 billno 批量幂等预检**(已生成张次回填跳过、未生成张次重推) |
| 7 | **收票推送防重键修复** | A302-6 指定应付与 A801-3 **推送预检**统一改用收票登记 id(原误用发票号,导致指定应付报「收票单不存在」、预检永不命中);⚠️ **A801-3 定时回写仍按发票号匹配,缺陷残留**(见 11.2) |
| 8 | **物料/计量单位真实映射链路建成(调试覆盖仍在)** | 物料按「装卸大类 / 国际业务 / 国内+运输方式」映射名称 → `BFI_MATERIAL_NUMBER_MAP` 映射金蝶编码;计量单位按合同计费方案计费单位 → 计量单位档案金蝶编码(`blade_measurement_unit` 已初始化、新表 `blade_kingdee_material`);但三处 `if(true)` 调试组合块仍做**最终覆盖**(兜底 物流配送 Item-00001080+TON;收票侧未命中还**随机取组合**) |
| 9 | **客商分组 1102 问题修复** | `customerKind` 已落库(`CustomerArchive.customerKind`),internal→1101 / 其余→1102 可正确分支 |
| 10 | **主数据初始化 SQL** | `doc/sql/transport/kingdee_master_data_init_20260924.sql`:计量单位金蝶编码(TON/CUM/PCS/KG/KM/25/04/sou/014/013)+ 金蝶服务类物料档案 `blade_kingdee_material` |
4. **两条推送主线 + 两条数据主线 + 一条认领主线**:
- 应付主线:财务应付单(A102-2)→ 付款申请下推(A103-1,预付款走 A103-3)→ 状态回写(A103-2/A103-4);
- 应收主线:财务应收单(A302-2)→ 开票申请(A303-1,按张次拆分)→ 开票结果(A303-5);
- 数据主线:客商档案(A902-1/A902-2)、金蝶发票池(A801-1)、汇率(A801-4);
- 认领主线:收款认领公告(A301-2)→ 认领结果推送(A301-3)→ 收款单回写(A301-1)。
---
## 1. 集成总览与公共机制
### 1.1 调用架构
```
blade-transport 业务服务
│ 调用 IBfiService(参数校验 + 自动取令牌)
▼
blade-bfi-api(Feign 客户端 IBfiClient,@FeignClient url=${thirdParty.bfi.baseUrl})
│ 请求头:access_token(业务令牌) + Authorization(iPaaS 网关认证,BfiRequestInterceptor 统一注入)
▼
iPaaS 网关(/gwzh/BFI,不直连金蝶)
▼
金蝶星瀚 /ierp/kapi/...
```
- 客户端模块:`blade-third-party-api/blade-bfi-api`(`org.springblade.thirdparty.bfi`)。
- 业务编排模块:`blade-service/blade-transport`(`support/` 三个推送组件、`service/impl/` 各业务实现、`task/` 定时任务)。
- **无重试**:Feign 未配 Retryer(NEVER_RETRY)、无 fallback;失败抛 `BfiException`,由调用方决定(批量循环逐条 catch,单条失败不中断整批)。
- 配置前缀两套并存(读同一批 nacos 键的不同子集):
- bfi-api `BfiProperties`(prefix `third-party.bfi`):baseUrl / authorization / clientId / clientSecret / username / accountId / language / mockEnabled;
- transport `BfiProperties`(prefix `thirdparty.bfi`):srcSys(默认 WLTMS)/ payApply.*(billtype=`ap_payapply_oth_BT_S`、exratetable=`01`、srcType=`0KzXs`)/ invoiceApply.*(srcType=`0KzXs`)/ receivedInvoice.*;
- `payResult.*`、`receivedInvoiceResult.*`、`customer.*` 由定时任务以 `@Value` 消费。
- 配置样例见 `doc/nacos/third-party-api.yaml`(默认 baseUrl `http://172.16.203.134:8000/gwzh/BFI`)。
### 1.2 接口清单总表(含在用状态,2026-09-27)
来源:`blade-bfi-api/.../feign/IBfiClient.java`(URL 可被 `thirdParty.bfi.*Url` 配置覆盖)。
| 操作码 | 星瀚端点 | Feign 方法 | 用途 | 业务侧是否在用 |
|---|---|---|---|---|
| A100-1 | POST /ierp/kapi/oauth2/getToken | getToken | 获取令牌 | ✅ 自动(每次调用前) |
| A100-2 | POST /ierp/kapi/oauth2/verifyToken | verifyToken | 校验令牌 | ❌ 已实现无调用 |
| A100-3 | POST /ierp/kapi/oauth2/withdrawToken | withdrawToken | 撤回令牌 | ❌ 已实现无调用 |
| A102-2 | POST /ierp/kapi/v2/awrp/ap/ap/ap_finapbill/save | apBillSave | 财务应付单保存 | ✅ 正式结算单(应付)推送 |
| A102-4 | GET /ierp/kapi/v2/awrp/ap/ap_finapbill/query | apBillQuery | 财务应付单查询 | ✅ 预检/硬校验/批量预热(**反查付款计划分录已移除**) |
| A103-1 | POST /ierp/kapi/v2/awrp/ap/ap/ap_finapbill/push | payApplyPush | 应付下推生成付款申请 | ✅ 付款申请推送(单分录) |
| A103-2 | GET /ierp/kapi/v2/awrp/ap/ap_payapply/query | payApplyQuery | 付款申请单查询 | ✅ 推送预检 + 定时回写 |
| A103-3 | POST /ierp/kapi/v2/awrp/ap/ap/ap_payapply/save | savePrePayApply | 预付款申请保存 | ✅ 预付款(项目垫资/进度垫资)直推 |
| A103-4 | GET /ierp/kapi/v2/awrp/cas/cas_paybill/query | payBillQuery | 付款处理查询 | ✅ 定时回写付款记录 |
| A301-1 | GET /ierp/kapi/v2/awrp/cas/cas_recbill/queryRecBill | recBillQuery | 收款单查询(收款处理查询) | ✅ **认领结果回写(手动)** ★新启用 |
| A301-2 | GET /ierp/kapi/v2/awrp/cas/cas_claimannounce/query | claimAnnounceQuery | 收款认领公告查询 | ✅ **收款流水同步(手动)** ★新启用 |
| A301-3 | POST /ierp/kapi/v2/awrp/cas/cas/cas_claimbill/claimbillPush | claimBillPush | 收款认领结果推送 | ✅ **认领推送(认领事务内)** ★新启用 |
| A302-2 | POST /ierp/kapi/v2/awrp/ar/ar/ar_finarbill/save | arBillSave | 财务应收单保存 | ✅ 正式结算单(应收)推送 |
| A302-4 | GET /ierp/kapi/v2/awrp/ar/ar_finarbill/query | arBillQuery | 财务应收单查询 | ✅ 预检/硬校验/定时勾稽 |
| A302-6 | POST /ierp/kapi/v2/awrp/ap/ap/ap_invoice/assign | invoiceAssign | 收票指定应付 | ✅ 收票推送第二步 |
| A303-1 | POST /ierp/kapi/v2/awrp/sim/sim_original_bill/save | invoiceApplySave | 开票申请单新增 | ✅ **按发票张次逐张推送** |
| A303-2 | POST /ierp/kapi/v2/awrp/ap/ap_invoice/cusBatchSave | receivedInvoiceSave | 收票单批量保存 | ✅ 收票登记推送 |
| A303-4 | GET /ierp/kapi/v2/awrp/sim/sim_original_bill/query | invoiceApplyQuery | 开票申请单查询 | ✅ **批量 billno 幂等预检** |
| A303-5 | POST /ierp/kapi/app/sim/openApi | invoiceApplyOpenApi | 开票发票查询(票易通 openApi,Base64 包裹) | ✅ 定时回写开票结果 |
| A801-1 | POST /ierp/kapi/v2/awrp/rim/rim_invoice/query | invoicePoolQuery | 全票池发票查询(rim 契约) | ✅ 选票/保存重查/补票种(实时,无定时) |
| A801-2 | POST /ierp/kapi/v2/awrp/bosext/attachment/download | invoiceDownload | 进项发票文件下载 | ❌ 已实现无调用(备用) |
| A801-3 | GET /ierp/kapi/v2/awrp/ap/ap_invoice/apinvoicequery | receivedInvoiceQuery | 收票单查询 | ✅ 推送预检 + 定时回写 |
| A801-4 | GET /ierp/kapi/v2/awrp/base/bd_exrate_tree/Forexquery | forexQuery | 汇率查询 | ✅ **结算取汇率(手动,页面触发)** ★新启用 |
| A902-1 | GET /ierp/kapi/v2/awrp/basedata/bd_customer/query | customerQuery | 客户查询 | ✅ 档案回填/推送前复查/定时复查 |
| A902-2 | POST /ierp/kapi/v2/awrp/basedata/bd_customer/save | customerSave | 客户保存 | ✅ 档案推送 |
> 与基础业务文档《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 无时间窗参数(按认领用户+分页)。
### 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`,已有未过期缓存时新令牌不会覆盖(排障需知)。
- **注入**:双头——业务方法 `@RequestHeader("access_token")` 显式携带;`BfiRequestInterceptor` 统一加 `Authorization` 网关头。`BfiFeignClientConfig` 用空拦截器覆盖全局 BladeFeignRequestInterceptor;超时 10s/60s。
### 1.4 响应封装、分页与异常处理
- **统一响应** `BfiResultVO<T>`:`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)。
- **异常转换**:`BfiExceptionTranslator`(@RestControllerAdvice)→ `R.fail(500)`。
### 1.5 批量同步上下文 KingdeeSyncContext(09-24 重构)
`transport/service/KingdeeSyncContext.java`:Java record,8 个可写缓存:
| 缓存 | 内容 | 变化 |
|---|---|---|
| `measureUnitCodeByName` | 计量单位名称→金蝶编码 | ★新增(配合主数据初始化 SQL) |
| `contractCache` | 合同 id→ContractManage | 保留 |
| `customerCodeCache` | 名称→客商编码 | 保留 |
| `projectCache` | 项目 id→ProjectApply | 保留 |
| `apBillNoCache` | 应付单防重键→金蝶单号 | 保留(含「未生成」空串标记) |
| `settlementDetailCache` | 结算单 id→结算明细列表 | 保留 |
| `settlementCache` | 结算单 id→FormalSettlement | ★新增(结算单维度传参) |
| `receiptAccountCache` | 客商 id→收款账户 | ★新增(付款联行号取数) |
| ~~`cargoCodeByName`~~ / ~~`apBillPlanCodeCache`~~ | — | **已删除**(货物编码映射被物料映射替代;付款计划分录反查已移除) |
- 生命周期 = 一次批量同步方法调用(`empty()` 创建,方法结束即弃),非 Spring Bean;缓存未命中自动回退逐单查询。
- 使用方:`PaymentApplicationServiceImpl`(批量前 `preloadApBillExistNos` 整批预热)、`FormalSettlementServiceImpl`、`InvoiceReceiptServiceImpl`。
### 1.6 防重键(候选键)总表(2026-09-27 口径)
金蝶侧按「来源系统 + 来源单据号」幂等(`awrp_src_sys` + `awrp_src_code`,分录级 `awrp_entry_code`)。组装工具 `ExternalSrcCodeSupport.buildSrcCode`(下划线拼接、去空白、去重保序)。
| 金蝶单据 | 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 |
### 1.7 公共档案取数(应付/应收/收票三侧复用 `KingdeeApBillSyncSupport`)
| 取数项 | 规则 | 变化 |
|---|---|---|
| 组织 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 回填);未维护不送 | ★新增 |
| 往来户 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;**收票侧未命中随机取**) | ★重构 |
| **计量单位** | 费用类型/费用项匹配合同计费方案计费单位 → `blade_measurement_unit` 金蝶编码(`context.measureUnitCodeByName`);⚠️ 同被调试组合块覆盖 | ★重构 |
| 金额(含税) | 结算明细行=结算金额(含税);手工合计费用项行=合计行结算金额;原三级取数兜底保留 | 调整 |
| 金额(不含税) | 结算明细行取结算金额(不含税);**手工合计费用项行无不含税金额不传** | 调整 |
| **运输货物 / 车号** | 结算明细行传货物名称 / 车号(金蝶接口新字段);合计费用项行不传 | ★新增 |
| 日期文本 | 统一 `yyyy-MM-dd HH:mm:ss` | 不变 |
---
## 2. 业务域一:客商档案(A902-1 / A902-2)
**定位**:全局数据前提。其他链路的往来户编码(`customer_code`)来自 `blade_customer_archive`,与金蝶客户的对应关系由本链路建立(**金蝶客户编码 = TMS 客商编号**,无独立存储)。
### 2.1 A902-1 客户查询(回填 / 复查)
**触发场景(3 个入口)**
| 入口 | 触发 | 代码 |
|---|---|---|
| ① 前端新增回填 | 录入统一社会信用代码满 18 位防抖查询 → `GET /bfi/customer/query-by-credit-code`(pageNo=1/pageSize=1) | BfiCustomerController |
| ② 单条推送前复查 | `/customer-archive/sync-kingdee` 或审批通过内部推送前,`syncKingdee(id)` 先查一次 | CustomerArchiveServiceImpl |
| ③ 定时批量复查 | `CustomerKingdeeSyncTask`(cron `thirdParty.bfi.customer.cron` 默认 `0 0 2 * * ?`,开关默认 false) | CustomerKingdeeSyncTask |
**调用顺序**:无固定前置;定时路径按 50 个信用代码/批、`BfiUtil.queryAllPages` 自动翻页取全,键=大写信用代码,单批异常不中断。
**参数来源**:`societycreditcode` = `blade_customer_archive.unified_credit_code`;分页参数固定。
**响应存储**:
- 前端回填:`status=C` 回填名称/法人/地址/经营范围/注册资金/经营期限等表单字段(锁定客商名称/法人/注册资金);`A/B` 前端阻断保存。原始报文不存储。
- 后端复查:`C`→`kingdee_status=exist`(终态);`A/B`→`failed`(「金蝶侧该客商审核中,请稍后重推」)。
- 定时附加职责:synced/exist 且启用客商复查 `C`→exist;`enable=0` → 联动停用 TMS 档案 `status=2`(单向,金蝶恢复不自动启用)。
- 落库字段:`kingdee_status / kingdee_fail_reason / kingdee_sync_time`。
### 2.2 A902-2 客户保存(推送)
**触发场景(3 入口,收敛于 `pushCustomerToKingdee`)**:① 审批通过自动推送(`POST /customer-archive/approve`,失败不影响审批);② 列表人工重推(`POST /customer-archive/sync-kingdee`,仅 approved,synced/exist 幂等返回);③ 定时重推(pending = approved 且 none/failed)。
**调用顺序**:先 A902-1 复查(金蝶已有 `C` 直接置 exist 跳过)→ 组装报文(`CustomerBfiConverter.buildSaveDTO`)→ A902-2 保存。**只推新增建档**:审批后的档案变更不重推(无更新链路);`synced`(金蝶受理 B)不重推,由定时复查升级 exist。
**参数要点**:
- `number`=`customer_code`(submit 时生成 `KS`+6 位自增);`name/societycreditcode/artificialperson` = full_name/unified_credit_code/legal_person;
- `type`=customer_nature→字典 dictKey(解析失败抛异常,不兜底);`bizfunction` 固定 `1,2,3,4`;
- `entry_groupstandard`:**★已修复** —— `customerKind` 已落库(`CustomerArchive.customerKind`),internal→1101(内部组织)、其余→1102(外部客商);
- `entry_bank`:`blade_customer_receipt_account`(账号/开户行/CNY/isdefault);⚠️ `bankNumber` 仍硬编码 "001110012009"(CustomerBfiConverter:141,todo);
- `entry_linkman`:principal + contact_phone;`status` 不传。
**响应存储**:`result[0].billStatus=true` → `synced`;`false` → `failed` + errors.rowMsg(截 500 字);异常 → `failed`。返回的 number/id 不落库。
**依赖与幂等**:无前置;被应付/应收/收票/开票链路取 `customer_code`;⚠️ 其他链路**不校验客商 `kingdee_status`**(软依赖,见 11.2-#3)。幂等:本地 synced/exist 直接返回;金蝶按 `societycreditcode` 防重。
---
## 3. 业务域二:财务应付与付款
**链路总览(三环节接力,★ 标注本次变化)**
```
环节一 正式结算单页面「同步金蝶」(POST /formal-settlement/sync-kingdee)
A102-4 预检 ──▶ A102-2 保存应付单(明细行+手工合计费用项分录,汇率随单)──▶ 结算单回写 kingdee_bill_no + synced
环节二 付款管理「同步金蝶 / 批量同步」(POST /payment-application/sync-kingdee[-batch])
A102-4 硬校验(应付单已生成,逐关联结算单) ★不再反查付款计划分录
──▶ A103-2 幂等预检 ──▶ A103-1 下推(单分录,勾稽键=主结算单应付防重键;预付款走 A103-3)──▶ 回写 kingdee_bill_id/no + synced
环节三 PaymentKingdeeResultTask 定时(每10分钟,默认关)
A103-2 查状态(synced→paying→paid/closed) ──▶ A103-4 查付款处理单 ──▶ 付款记录落库(含 voucher_json)
```
### 3.1 A102-2 财务应付单保存
- **触发**:`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`;
- org/payorg/币种/往来户/往来类型:见 1.7;**exchangerate/exratedate=结算单页面维护值(未维护不送)** ★新增;
- purmode=CASH、款项性质=2001、isincludetax/ispricetotal=true;**settlementtype_number(结算方式)不在应付单送**(仅 A103-1 分录需要);
- **分录(★新口径)**:`resolveEntryGroups` = 结算明细行(每行一条)+ **手工结算合计费用项**(`blade_formal_settlement_summary_fee.manualFlag=1`,每行一条;系统自动汇总行不送,防与明细重复);
- `awrp_entry_code`:明细行=结算明细单据号;合计费用项行=`结算单号-费用类型-费用项`拼接;
- 明细行带运输货物(货物名称)/车号;数量=运输量(兜底 1,合计行默认 1);单价=结算金额/数量反算;
- ⚠️ 调试组合块强制物料/计量单位(见 1.7);⚠️ 项目编码仍传测试值 `999`;
- **planentity 仍不传** → 金蝶按分录自动汇总生成付款计划。
- **响应存储**:`result[0].number` → `blade_formal_settlement.kingdee_bill_no`、`kingdee_sync_status='synced'`;id 不落库。失败抛异常整体回滚保持 unsynced(**不落 failed**)。
- **依赖**:本地数据齐备即可;**是 A103-1 与 A303-2(收票)的硬前置**。
- **幂等**:A102-4 按防重键预检,已存在直接返回已有 billno(alreadyExist=true)。
### 3.2 A102-4 财务应付单查询(★用途缩减)
- **触发**:纯内部步骤,无独立入口。**三个用途**(原四个,反查已移除):① A102-2 推送前幂等预检;② A103-1 推送前「应付单已生成」硬校验(逐关联结算单,未生成报「请先在正式付款单页面同步金蝶」);③ 批量同步整批预热(`preloadApBillExistNos`,未命中编码补空串标记避免重复远程查询)。
- ~~原用途④「反查付款计划分录 awrp_entry_code_p」已随 09-24 重构移除~~:A103-1 的勾稽键改为本地组装(见 3.3)。
- **参数/存储**:`awrp_src_code`(多值,50 键/批、页大小 200)+ `awrp_src_sys`;结果不落库,仅入 `apBillNoCache`。
- **幂等**:本身即防重手段。
### 3.3 A103-1 付款申请保存(应付下推)/ A103-3 预付款直推(★大改)
- **触发**:`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 参数(★新口径)**:
- 防重键 `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)** ★改善。
- **响应存储**:`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 查重。
### 3.4 A103-2 付款申请单查询(状态回写 + 推送预检)
- **触发**:① 定时 `PaymentKingdeeResultTask.syncKingdeePaymentResult`(默认 `0 */10 * * * ?`,开关默认 false);② 手动 `POST /payment-application/sync-kingdee-result`;③ A103-1/A103-3 推送链内部幂等预检。
- **顺序**:查待回写(approved 且 status ∈ {synced, paying, **failed**})→ paymentNo 去重按 50/批查询 → 同一 srcCode 取 `modifytime` 最新 → 逐单独立事务回写。
- **响应存储**:`billno`→kingdee_bill_no、`id`→kingdee_bill_id;状态映射:`Alreadypay`→**paid**(终)> `billstatus=D`→**closed**(终)> `Inpayment`→**paying** > 其余→synced;`entry[].e_paidamt` 汇总→`paid_amount`(缺失且 paid 时兜底=申请金额);paid 联动履约(写 `blade_pre_settlement_advance` 与 `blade_formal_settlement_payment`);**failed 纳入勾稽纠偏**:查到即视为上次推送实际成功,纠正为 synced。
- **依赖/幂等**:按防重键查询,无硬前置;主键更新天然幂等;终态不再轮询;查无此单状态不变。
### 3.5 A103-4 付款处理查询(付款记录回写)
- **触发**:定时子流程 + 手动端点。**仅查询,不推送**。
- **顺序**:候选 = 本轮 A103-2 查到 `paystatus≠Unpaid` 的单 + 已 paid 但无真实付款记录的单(补偿);按付款组织编码分组逐组查询;按源单勾稽匹配(`sourcebillid`==kingdee_bill_id 优先,`sourcebillnumber`==kingdee_bill_no 兜底)。
- **参数**:`org_number` 优先取 A103-2 返回 `payorg_number`,兜底按合同所属组织解析;业务日期窗口=最近 1 个月;页大小 200 翻页。
- **响应存储**:upsert `blade_payment_application_record`——`billno`→payment_no+kingdee_bill_no;`actpayamt`→paid_amount;`paydate`→paid_date;**整单 JSON→`voucher_json`**(凭证号/银行流水/电子回单留档);清理 MOCK-PAY- 历史模拟记录。
- **依赖/幂等**:勾稽需已知 kingdee_bill_id → 必然晚于推送链;付款组织解析失败仅 warn 跳过下轮重试;按付款处理单号判重更新。
---
## 4. 业务域三:财务应收与开票
**链路总览**
```
环节一 正式结算单页面「同步金蝶」(应收类型)
A302-4 预检 ──▶ A302-2 保存应收单(明细行+手工合计费用项分录,汇率随单)──▶ 结算单回写 kingdee_bill_no + synced
环节二 开票申请「同步金蝶」
A302-4 硬校验(应收单已生成) ──▶ A303-4 批量 billno 幂等预检 ──▶ A303-1 按发票张次逐张新增 ──▶ 张次/申请单回写单号 + synced
环节三 定时回写(PaymentKingdeeResultTask 串行第 2/3 步)
A302-4 勾稽纠偏(failed→synced / 补单号)
A303-5 开票发票查询(serialNo 按 application.kingdee_bill_no 逗号拆分)──▶ 发票落库 ──▶ invoiced(终态)
```
> 推送链路**无批量端点**;查询侧为批量(防重键 50/批 + 自动翻页 200/页)。
### 4.1 A302-2 财务应收单保存
- **触发**:`POST /formal-settlement/sync-kingdee`,`settlementType=receivable` 路由 → `KingdeeArBillSyncSupport.pushArBill`。用户动作,无定时。
- **顺序**:防重键组装 → **A302-4 幂等预检**(命中即返回已有单号跳过保存)→ 结算单维度取数(合同/组织/币种/客商)→ 组装 `BfiArBillSaveDTO` → 保存 → 回写。
- **参数**:防重键同应付口径;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 预检防重推。
### 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 硬顺序。
### 4.3 A303-4 开票申请单查询(★批量幂等预检)
- **触发**:仅一处——A303-1 推送前预检。
- **★变化**:按**全部候选 billno 列表**(单张=application_no;多张=application_no-1/2/3…)一次性查询(pageSize=max(张数,10)),命中的张次幂等回填跳过,未命中的张次重新推送——支持「部分张次已生成」的断链重推。
- **参数/存储**:查询键 `billno`(多值);命中结果仅用于跳过与回填 sheet.kingdee_bill_no。
### 4.4 A303-1 开票申请单新增(★按张次拆分重写)
- **触发**:用户动作 `POST /invoice-application/sync-kingdee` → `syncKingdee`。单申请一次。
- **调用顺序**:校验 approved → synced/invoiced 幂等返回 → 关联结算单非空 → **A302-4 硬校验** → **A303-4 批量预检** → 销/购方取数校验 → **逐张发票张次**组装保存(`requireBfiSaveSuccess` 校验每张)→ 批量回写张次与申请单。
- **参数**:
- **张次(★新表 `blade_invoice_application_sheet`)**:每张金蝶开票申请单对应一个张次;`billno`=单张取 `application_no`(兼容存量)、多张取 `application_no-张次序号`;价税合计=本张 `invoice_amount`;分录=本张商品行(`blade_invoice_application_line` 按 `invoice_sheet_id` 分组、lineNo 升序),`awrp_entry_code`=商品行 id;
- 头字段(沿用):`orgid_number`=承办部门 dept_code;`billdate`=application_date;`invoicetype`:数电专票 `08xdp` / 数电普票 `10xdp`;`billproperties=1`、`taxationstyle=0`、`buyeremail`=department_emails;购方=申请单受票方快照列;销方=salername/开票单位税号(客商档案)/地址电话/银行账号;`customnameid_number`=receiver_customer_id→客商编码;`hsbz`:普票=1 否则 0;`awrp_project_number`=999;systemsource=WLTMS。
- **响应存储(★)**:每张 `sheet.kingdee_bill_no`=金蝶返回 number(幂等命中的张次回填候选 billno);**`entity.kingdee_bill_no`=全部张次单号逗号拼接**;`kingdee_status='synced'`;记操作历史。
- **依赖/幂等**:硬前置 = A302-2 应收单已生成;幂等 = billno 候选键 + A303-4 批量预检回填(**未生成张次可断点续推**)。
### 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 包裹)。
- **响应存储**:`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 产生的单号;唯一键防重;批次失败仅计数不中断。
---
## 5. 业务域四:收票与金蝶票池
**架构现状**:票池浏览**实时回源 A801-1**(全量定时同步任务已退役),镜像表 `blade_kingdee_invoice_pool` 降级为**登记存根**;推送链 A303-2 → A302-6 人工批量按钮触发;A801-3 承担推送预检 + 定时回写。
### 5.1 A801-1 全票池发票查询(选票 / 保存重查 / 补票种)
- **触发**:① 选票弹窗:收票登记 `GET /invoice-receipt/invoice-pool`、结算单认领 `GET /formal-settlement/invoice-pool`;② 收票登记**保存重查**(`persistMirrorFromRemote`,按票号精确单查,作为「保存重查闸门」);③ 推送时镜像缺票种编码的按票号窄查补填。**无定时任务**。
- **顺序**:游标分页(`PoolScanCursor`)→ 首拉 size+1 探测 → 不足按契约上限 500 行/次补拉,单请求最多 3 次远程调用 → 短页即耗尽。
- **参数**:`invoiceStatus`="0"(正常票)、`expenseStatus`="1"(未用,后端固定下推);票号/销购方税号/票种/日期来自前端 DTO(日期强校验)。
- **行过滤**:票号非空 + `amountUseStatus="0"` + `availableAmount>0`;按票号查询时 trim 全等防御(防远程模糊匹配假命中)。本地打标 registered/claimed 纯展示。
- **响应存储**:浏览**不落库**;落库仅保存收票登记(按票号精确单查→可用性复核→`(tenant_id, invoice_no)` FOR UPDATE + upsert 存根)与推送补查。镜像列含 `invoice_type_code`(票易通原始编码,A303-2 取数)、税率(小数→百分数)、票面四要素、`check_status`、`attachments_json`、`items_json`(仅存档,**不作分录取数**)。
- **依赖/幂等**:仅 BFI 网关;**金蝶不可用则选票与收票保存整体不可用**(无镜像读降级);保存重查闸门拦截红冲/作废/被占用竞态;一票一登记(`assertInvoiceUnused`)。
### 5.2 A303-2 收票单批量保存 + A302-6 指定应付(推送链)
- **触发**:`POST /invoice-receipt/push-kingdee`。单笔失败不中断整批,逐单报因。
- **调用顺序**(`pushOneReceivedInvoice`):
1. 批量取数防 N+1(分摊/镜像/结算单/明细一次取回);
2. 组装推送计划:approved、状态≠assigned、已绑定镜像、`checkStatus=2`(查验通过)、**恰好分摊 1 张审批通过应付结算单(一票一应付)**、有结算明细;不满足写 `blockedReason` 不改状态;
3. **A102-4 预检**:关联结算单应付单必须已生成,缺则拦截「请先同步金蝶应付单」;
4. **A801-3 幂等预检**:按镜像购方名称分组、窗口 30 天翻页,本地按 `awrpSrcSys+awrpSrcCode`(=**收票登记 id**,★243d5be 修复)匹配;命中且状态正常 → 跳过 A303-2 直接续推指定(断点恢复);命中但发票状态异常(作废 2/红冲 3/部分红冲 7/全部红冲 8)→ 置 failed 报人工;
5. 镜像缺票种 → A801-1 按票号窄查回填;
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 同源)。
- **响应存储**:`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)。
### 5.3 A801-3 收票单查询(预检 + 定时回写)
- **触发**:① 推送链内部预检(上);② 定时 `PaymentKingdeeResultTask.syncKingdeeReceivedInvoiceResult`(独立开关默认 false,cron 默认每 10 分钟);③ 手动 `POST /invoice-receipt/sync-kingdee-result`。
- **回写顺序**:轮询 `kingdee_push_status ∈ (pushed, assigned)` 且非作废、已绑定镜像 → 按 `receiver_name`(接口必填购方名称)分组 → 窗口 30 天翻页 → 逐单处置:发票状态异常→`failed`+「已回收推送状态」;`pushed`→**补偿重发 A302-6**(★防重键已修复为登记 id)→ assigned;`assigned` 缺单号→仅补登单号;**窗口内查不到不改状态**。
- **⚠️ 疑似缺陷残留(★未修复)**:定时回写**仍按发票号码**组装查询集与匹配(`entry.getValue().stream().map(InvoiceReceipt::getInvoiceNo)` + `matched.get(receipt.getInvoiceNo())`),而 A303-2 落库的 `awrp_src_code` 是**收票登记 id** → 回写将**永远匹配不到 TMS 自推单据**(静默跳过,异常回收/指定补偿/单号补登全部失效)。09-23 的 243d5be 提交只修复了**推送预检与 A302-6** 路径,回写路径漏改。见 11.2-#1。
### 5.4 选票 / 认领流(是否调金蝶对照)
| 流 | 入口 | 金蝶调用 | 本地存储 |
|---|---|---|---|
| 收票登记 | `POST /invoice-receipt/save` | 保存重查 A801-1(闸门) | 镜像存根 upsert + 绑定 + 分摊 + 流水 |
| 结算单认领(按票) | `POST /formal-settlement/claim-invoices`;查票 `GET /formal-settlement/invoice-pool` | 查票实时 A801-1;**确认认领时不调金蝶** | `blade_formal_settlement_invoice` + 联动付款申请 matched;⚠️ **仍无保存重查闸门**(前端传票面直接落库,09-23 提案至今未实现) |
| 结算单收款认领(收票流水) | ReceiptFlowController / ReceiptClaimRecordController | **不调金蝶**(镜像流水由外部 `sync` 推入) | `blade_kingdee_receipt_flow`、`blade_receipt_claim(_settlement)`、`blade_receipt_flow_record` |
---
## 6. 业务域五:收款认领(A301-2 / A301-3 / A301-1)★新增
**定位**:图06「回款认领」采用**业务先行认领(场景二)**:金蝶司库收到银行流水完成第一轮自动认领(到客户维度),TMS 二次认领到结算单(应收单)维度后回推金蝶生成收款单。**三个接口本期全部接线,均为手动触发,无定时任务**(定时开关 `thirdParty.bfi.receiptClaimResult.*` 预留未接)。专项文档:`2026-09-24-收款认领金蝶全链路业务流程….md`。
```
阶段一 POST /receipt-flow/sync(手动同步流水按钮)
A301-2 认领公告查询(claimusers=当前登录用户真实姓名)
→ BfiUtil.queryAllPages 全量翻页 → 按 detail_serial_no 幂等 upsert blade_kingdee_receipt_flow
阶段二 POST /receipt-flow/claim(页面认领)
本地认领落库(blade_receipt_claim + 分摊表)+ A301-3 认领结果推送 同一事务
→ 成功回写金蝶收款单号 + kingdee_bill_status=pushed;失败抛异常整体回滚
阶段三 POST /receipt-claim-record/query-kingdee-result(手动回写)
A301-1 收款单查询(近 30 天窗口全量翻页)→ sourcebillnumber==awrp_src_code 匹配 → 回写单号与状态
```
### 6.1 A301-2 收款认领公告查询(公告同步)
- **触发**:仅手动 `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 互不覆盖**。
- **依赖/幂等**:独立;唯一索引 `(tenant_id, detail_serial_no)`。
### 6.2 A301-3 收款认领结果推送
- **触发**:页面认领 `POST /receipt-flow/claim`(流水详情/批量认领页选择审批通过的应收正式结算单按比例分摊)。
- **顺序**:本地校验(锁行、分摊金额上限、同一项目/组织/收付款方)→ 本地落库(claim + 分摊 + 流水 claimed_amount/claim_status 刷新 + 结算单收款汇总)→ 生成 `awrp_src_code = SKCL+yyyyMMddHHmmss+认领id尾号6位` → **A301-3 推送**(同事务)→ 成功回写 `kingdee_bill_no`(result[0].number)+ `kingdee_bill_status=pushed`;失败抛 `ServiceException` 整体回滚(无残留)。
- **参数**:`billid`=流水 `bill_id`(**必填**,缺失拦截「认领公告id缺失,请先同步金蝶收款流水」);`awrp_src_sys`=WLTMS;`awrp_src_code`=SKCL 码;`recpaytype_number`=常量(示例 101,**待金蝶确认**);`paymenttype`=bd_customer(**待确认**);`recpayer`=流水 payer_id(**待确认**:金蝶付款人 master id,联调若强校验需按名称反查 A902-1 补齐);分录 `entryentity[]`=各结算单分摊行:`e_receivableamt`=分摊金额、`e_corebilltype`=`ar_finarbill`、`e_corebillno`=分摊结算单 `kingdee_bill_no`(缺失不传)、`seq`=行号、`e_receivingtype_number` 同 recpaytype_number(待确认)。
- **依赖**:分摊结算单须审批通过、未作废、且已完成 A302-2 同步(有 kingdee_bill_no)以便分录携带应收单号。
- **幂等**:`awrp_src_sys+awrp_src_code` 为金蝶候选键;⚠️ 推送成功但本地回滚后重推会**重新生成 awrp_src_code** 产生新单(联调期以推送失败回滚为主路径规避)。
### 6.3 A301-1 收款单查询(认领结果回写)
- **触发**:仅手动 `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。窗口内查不到不改状态(金蝶回单延迟兜底)。**不改本地认领金额/分摊**。
- **依赖/幂等**:依赖 A301-3 产生的 awrp_src_code;主键更新幂等。
---
## 7. 业务域六:公共取数(A801-4 汇率)★新启用
- **触发**:前端结算页面按「结算币种 + 汇率日期」调用 `GET /bfi/exchange-rate?currencyCode=&effectdate=`(BfiExchangeRateController),用于正式结算单页面维护汇率/汇率日期。手动、单次。
- **顺序**:按日期查 A801-4 全量汇率(当日默认,页大小固定 10)→ 本地按 `orgcur_number` 匹配币种 → 返回汇率 VO。
- **响应**:不落库(汇率/汇率日期随结算单提交落 `blade_formal_settlement.exchange_rate / exchange_rate_date`);查不到币种/日期返回 R.fail。
- **下游消费**:A102-2 / A302-2 保存时传 `exchangerate`(结算汇率)与 `exratedate`(汇率日期),未维护不传;A103-1 下推汇率相关字段不传(按应付单自动带出)。
- **与方案文档对照**:方案 V1.2「汇率在金蝶维护,TMS 结算时通过接口获取并记录在结算单据上,期末调汇全部在金蝶处理」——**代码已落地**。
---
## 8. 全景:接口先后顺序与依赖关系
### 8.1 链路依赖图
```
┌────────────────── 客商档案(全局前提,软依赖) ──────────────────┐
│ A902-2 客户保存(审批通过自动/人工/定时重推) │
│ A902-1 客户查询(前端回填/推送前复查/定时复查) │
│ ⇒ 本地 customer_code(其他链路只查本地表,不校验金蝶侧状态) │
└───────────────────────────┬───────────────────────────────────┘
▼
【应付主线】
正式结算单(应付) ─A102-4 预检─▶ A102-2 保存应付单 ─▶ 结算单.kingdee_bill_no + synced
│ (汇率 exchangerate/exratedate ← A801-4 页面取数)
│ A102-4 硬校验(每张关联结算单) │ A102-4 预检(收票推送硬前置)
▼ ▼
付款申请 ─A103-2 预检─▶ A103-1 下推(单分录;勾稽键=主结算单应付防重键,本地组装)
(预付款→A103-3 直推) ─▶ 付款申请.kingdee_bill_id/no + synced
│
定时:A103-2 状态回写(synced→paying→paid/closed, failed勾稽纠偏)
▼
A103-4 付款处理查询 ─▶ blade_payment_application_record(+voucher_json)
【应收主线】
正式结算单(应收) ─A302-4 预检─▶ A302-2 保存应收单 ─▶ 结算单.kingdee_bill_no + synced
│ A302-4 硬校验
▼
开票申请 ─A303-4 批量billno预检─▶ A303-1 逐张次新增 ─▶ 张次.kingdee_bill_no + 申请单.kingdee_bill_no(逗号拼接) + synced
│
定时:A302-4 勾稽纠偏 + A303-5(serialNo 按逗号拆分)─▶ 发票落库 ─▶ invoiced(终态)
【收票主线】(与应收主线无调用耦合,与应付主线强耦合)
金蝶票池 A801-1(实时选票 / 保存重查闸门 / 补票种) ─▶ 镜像存根 blade_kingdee_invoice_pool
│ 保存收票登记(一票一登记)
▼
收票登记 ─A102-4 预检(应付已生成) + A801-3 预检(防重键=登记id)─▶ A303-2 收票单保存 ─▶ pushed
▼
A302-6 指定应付(引用=登记id)─▶ assigned(终态)
│
定时:A801-3 回写(⚠️ 仍按发票号匹配,疑缺陷残留)
【收款认领主线】★新增(全部手动触发,无定时)
金蝶第一轮自动认领(到客户)─▶ A301-2 公告同步(claimusers=用户姓名)─▶ blade_kingdee_receipt_flow
│ 页面二次认领(分摊到应收结算单)
▼
认领落库 + A301-3 推送(billid=公告id;src_code=SKCL码;分录=e_corebillno=结算单应收单号)─▶ pushed
▼
手动:A301-1 收款单查询(sourcebillnumber==SKCL码)─▶ approved/received(终)
【公共取数】
结算页面 ─▶ A801-4 汇率查询 ─▶ 结算单.exchange_rate/exchange_rate_date ─▶ 随 A102-2/A302-2 推送
```
### 8.2 硬顺序规则(代码强制)
| # | 顺序规则 | 强制方式 |
|---|---|---|
| 1 | 客商档案先于一切推送:本地 `customer_code` 必须存在 | 查不到直接报错拦截;**金蝶侧是否已审核不校验**(软依赖) |
| 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 |
| 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(如需携带汇率) | 页面维护动作,非代码强制(未维护不传) |
### 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 暂为手动)。
- **批量语义**:整批预热 → 逐单独立事务 → 单条失败不中断整批(付款批量、收票推送、定时回写均如此)。
---
## 9. 状态机汇总
| 表.字段 | 状态集 | 迁移 | 写入方 |
|---|---|---|---|
| `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_kingdee_receipt_flow.claim_status` ★新增 | unclaimed / partial / claimed | 公告同步不覆盖;认领分摊→partial/claimed;全部作废→unclaimed | ReceiptFlowServiceImpl |
---
## 10. 定时任务与配置开关汇总
| 任务类 | 方法 | 覆盖接口 | 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`(手动) |
> 所有已接线的定时开关**代码默认关闭**,需 Nacos 显式开启。⚠️ 客商定时任务仍有联调残留过滤(只处理 KS000067,见 11.1-#3),开启前必须移除。收款认领三接口**全部手动触发**(A301-2 sync、A301-3 认领、A301-1 回写),与方案文档「每天定时获取金蝶收款流水」不一致(见 11.3)。
---
## 11. 代码与三份材料差异 / 风险清单(更新版)
### 11.1 联调残留(上线前必须处理;对照 09-23 清单标注状态)
| # | 位置 | 问题 | 状态 vs 09-23 |
|---|---|---|---|
| 1 | KingdeeApBillSyncSupport:299、KingdeeArBillSyncSupport:234、KingdeeReceivedInvoiceSyncSupport:244 | `if(true)` 调试组合块强制物料/计量单位(固定 4 组合,兜底 物流配送 Item-00001080+TON) | ⚠️ **部分解决**:真实映射链路已建成(装卸/国际/国内+运输方式 → 金蝶编码;计量单位档案编码),但调试块仍做**最终覆盖** |
| 2 | KingdeeReceivedInvoiceSyncSupport 调试块 | 收票侧映射表与应付侧不一致,且**未命中组合时随机取一个物料**(`new Random().nextInt`)——同一票每次推送物料可能不同 | ⚠️ **新增风险** |
| 3 | CustomerArchiveServiceImpl:629 | 定时客商同步硬编码 `.eq(customerCode, "KS000067")`,只处理一条 | ❌ **仍在** |
| 4 | CustomerBfiConverter:141 | 客商银行分录 `bankNumber` 硬编码 "001110012009"(todo) | ❌ **仍在**(付款申请侧已改为取客商收款账户真实联行号,仅客商推送残留) |
| 5 | KingdeeApBillSyncSupport / KingdeeArBillSyncSupport / A103-1 分录 / 开票申请 | 项目编码传测试值 `999` | ⚠️ **部分解决**:A103-3 预付款已按项目档案真实编码(无项目才 999);其余仍 999 |
| 6 | KingdeeBfiConstant 遗留常量 | `BFI_AP_BILL_MATERIAL_NUMBER_TEST(SP0324)`、`BFI_AP_BILL_BANKACCOUNT_NUMBER_TEST` 等联调常量仍在(BfiProperties.receivedInvoice.materialNumber 默认值亦为 SP0324) | ⚠️ 无调用方或被覆盖,建议清理 |
### 11.2 疑似缺陷(建议人工确认)
| # | 问题 | 证据 | 状态 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 注释 | ⚠️ **新增待确认** |
| 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 | ⚠️ **新增待确认** |
| 6 | 收款认领 A301-3:推送成功但本地回滚后重推会**重新生成 awrp_src_code** 产生金蝶重复收款单(联调期以推送失败回滚规避);且 `recpaytype_number/paymenttype/recpayer/e_receivingtype_number` 等字段取值待金蝶侧确认 | ReceiptFlowServiceImpl(09-24 专项文档 §4.1/§7) | ⚠️ **新增待确认** |
### 11.3 代码与三份材料的口径差异
**vs《TMS业财融合方案.docx》(V1.2 方案)**
| 方案描述 | 代码现状 |
|---|---|
| SC-04「付款申请可分多次下推」 | A103-1 当前**单分录单次下推**(一申请一单,`paymentNo-1`);多次下推未实现 |
| SC-04 预付/尾款两模式、实付=全额−预付抵扣−已付 | 本地金额口径已实现;金蝶侧预付冲应付由金蝶自动处理(代码不传冲抵明细) |
| 附件推送(审批单据/磅单/合同签章随单推金蝶 A902 附件上传) | **未实现**:A902 未在客户端实现,各推送链路均不传附件 |
| SC-05「结算审批时同步录入进项发票…同步生成收票单并自动绑定」 | 流程拆分:收票登记独立审批 → 人工批量 `push-kingdee` 推送(非结算审批时自动) |
| SC-07「每天定时获取金蝶收款流水」 | A301-2 当前**手动按钮触发**,无定时(`receiptClaimResult.*` 开关预留未接) |
| SC-08「按发票张数生成多个金蝶开票申请单,按票面行拆分明细」 | ✅ **已实现**(张次拆分) |
| 汇率「TMS 结算时通过接口获取金蝶汇率记录在结算单据上」 | ✅ **已实现**(A801-4 → 结算单页面维护 → 随 A102-2/A302-2 推送) |
| 客商「TMS 按机构类别转换伙伴类型再推送」 | `customer_nature` 字典映射;分组 1101/1102 已按落库 customerKind 区分 |
**vs《TMS业财融合-集成流程图文字版与业务流程梳理》(09-14)**
| 文字版描述 | 代码现状 |
|---|---|
| 图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 结算时获取」 | ✅ 已实现 |
| 第9节 黑名单 A801-5、A901-1 供应商查询、A913 项目、A101-1/2 合同 | ❌ 均未实现(合同/项目/风险走合同合规系统 C 类接口,本期不在 BFI 范围) |
| 第9节 附件 A902 上传 | ❌ 未实现(方案中随单附件推送金蝶暂缺) |
**vs《2026-09-23 全链路梳理》(上一版总纲)**
| 09-23 结论 | 当前状态 |
|---|---|
| A301-1/2/3「业务侧无调用」 | ❌ 已过时:收款认领链路接线(业务域五) |
| A801-4「业务侧无调用」 | ❌ 已过时:结算取汇率(业务域七) |
| A102-4 用途④「反查 awrp_entry_code_p,是 A103-1 硬前置」 | ❌ 已过时:反查移除,勾稽键本地组装(11.2-#2) |
| A103-1 分录「逐费用行一条,e_bebank_number=测试常量」 | ❌ 已过时:单分录、联行号取客商收款账户、结算方式显式映射 |
| 开票申请「billno=application_no,一张申请一张金蝶单」 | ❌ 已过时:按张次拆分多单,billno 加张次后缀,A303-4 批量预检 |
| 9.1-#5 customerKind 恒推 1102 | ✅ 已修复(字段落库) |
| 9.2-#1 A801-3 回写匹配缺陷 | ⚠️ 部分修复(预检/A302-6 已修,回写残留) |
| 9.1-#1 物料 SP0324/TON 强制、#2 项目 999、#3 银行硬编码 | ⚠️ 演进(真实映射+调试覆盖并存;见 11.1) |
| 9.1-#3 付款 e_bebank_number 测试值 | ✅ 已修复(联行号);客商 converter 残留 |
### 11.4 其他已知注意点
- 客户端常量两处并存(`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 前缀猜操作码。
- 结算单认领(claim-invoices)**仍无保存重查闸门**(前端传票面直接落库,09-23 提案未实现)。
- 主数据初始化 SQL `kingdee_master_data_init_20260924.sql`(计量单位金蝶编码 + `blade_kingdee_material`)须先执行,物料/单位映射才有正确档案值;认领链路须先执行 `blade_receipt_flow_claim_20260924.sql`。
---
## 附:本文档与单链路文档的关系
- 本文是**总纲更新版**:跨链路顺序、依赖、公共机制、状态机、差异风险汇总,基线为 2026-09-24 代码(`50573e7`)。
- 单链路细节仍以 `doc/docs/业务流程/` 对应文档为深入参考;其中与代码冲突处一律以本文第 11 节结论 + 当前代码为准。
- 09-23 版总纲中被本文覆盖的过时结论:A301 系列/A801-4 未调用、A102-4 反查、付款申请分录口径、开票单张口径、客商 1102 问题——请以本文为准。