:feat: 审批通过自动创建履约计划行,C103-9同步前置至特批挂起之前

This commit is contained in:
刘泉佳
2026-10-14 14:11:31 +08:00
parent 433015fde0
commit 076145d7f6
6 changed files with 209 additions and 33 deletions
@@ -1,7 +1,7 @@
# 新建合同推送 HCS(合同合规系统)流程梳理
> 依据代码现状整理,核心类:`HcsContractSyncSupport`(blade-service/blade-transport/.../support/hcs)。
> 对接接口:C101-1 风险校验、C103-2 附件上传、C103-3 合同判重、C103-1 合同发起、C103-11 合同更新、C103-8 特批、C103-9 履约规则;特批结果接收 `/hcs/contractLegal/result`(合规推送,替代未建设的 B103,见 §9.4)。
> 对接接口:C101-1 风险校验、C103-2 附件上传、C103-3 合同判重、C103-1 合同发起、C103-11 合同更新、C103-8 特批、C103-9 履约计划、C108-1/C108-2 履约记录;特批结果接收 `/hcs/contractLegal/result`(合规推送,替代未建设的 B103,见 §9.4)。
> "必填" 标注来源:代码注释中标注的 v4 接口文档要求(HCS 侧),以及 TMS 推送前强校验(缺失直接失败)。
---
@@ -34,13 +34,13 @@
④ 合同发起/更新
首推且未判重命中 → C103-1 发起(返回 fdContractId,必须持久化)
已有 hcsFdId(变更单)→ C103-11 更新(顶层 fdId 定位)
⑤ 自动特批(C103-8,高风险/黑名单 且 hcsSpecStatus ≠ approved 时)
置 hcsSpecStatus=need + currentNode="特批审核"(TMS 流程挂起展示)
→ 筛选「特批说明」附件(attachmentsJson 中 attachmentType=spec_explanation)转传 C103-2
→ 自动发起 C103-8 → hcsSpecStatus=processing
发起失败保持 need 仅留痕,可人工经 /hcs/contract/spec-apply 重发
⑥ 履约规则同步(C103-9,开关 exe-stage.sync-enabled,一期默认关闭)
随合同链路行级同步,失败抛出走下轮凭 hcs_fd_id 更新补偿
⑤ 履约计划同步(C103-9,开关 exe-stage.sync-enabled,一期默认关闭,详见 §十)
仅依赖 hcs_fd_id,先于特批挂起执行(2026-10-14 调整);
失败抛出走下轮凭 hcs_fd_id 更新补偿,不被特批门禁阻塞
⑥ 自动特批挂起(高风险/黑名单 且 hcsSpecStatus ≠ approved 时)
报文随 fdSepcailContract=true 在合规侧发起特批流程(2026-10-13 起,无独立 C103-8 发起接口)
→ 置 hcsSpecStatus=processing + currentNode="特批审核"(TMS 流程挂起展示,
等待合规推送 contractLegal/result 或 C103-4 定时查询联动;发起异常仅留痕)
```
结果回填:
@@ -139,7 +139,11 @@
| `contract.require-risk-check` | 推送前客商风险校验开关 | — |
| `risk.block-blacklist` | 黑名单阻断付款提交(付款链路);合同链路不再阻断推送,高风险/黑名单转自动特批(§九) | — |
| `attachment.transfer-enabled` | 附件转传开关 | 关闭时不挂附件 |
| `exe-stage.sync-enabled` | C103-9 履约规则同步开关(一期默认关) | — |
| `exe-stage.sync-enabled` | C103-9 履约计划同步开关(一期默认关) | — |
| `exe-stage.fund-type` | C103-9 行金额类型缺省值(默认 byAmount) | — |
| `operator-login-name` | C103-9 报文操作人登录名(默认 srvadmin,非承办人) | — |
| `perf-record.sync-enabled` | C108-1/2 履约记录同步开关(一期默认关) | — |
| `perf-record.xform-id` | 履约记录表单模板ID(待合规方提供) | 未配置不传 fd_xform_id |
---
@@ -148,6 +152,7 @@
| 字段 | 含义 |
| --- | --- |
| `hcs_fd_id` | 合规合同ID(C103-1 返回的 fdContractId,判重命中也回填) |
| `hcs_exeplan_id` | 履约计划ID(C103-9 返回的 fdExeplanId,整体 update 定位用) |
| `hcs_sync_status` | unsynced / synced / failed |
| `hcs_retry_count` | 推送重试次数 |
| `hcs_sync_time` / `hcs_sync_msg` | 最近推送时间 / 结果描述 |
@@ -193,8 +198,8 @@ TMS 审批通过(approved / change_approved)
→ 即时推送 / 扫描兜底 → pushContract
→ ① 风险校验留痕(risky 标志,不阻断)
→ ④ C103-1 起草成功(hcs_sync_status=synced,回填 hcs_fd_id)
→ ⑤ 置 hcs_spec_status=need + currentNode="特批审核"(TMS 流程挂起)
→ 筛「特批说明」附件转传 C103-2 → 自动发起 C103-8 → hcs_spec_status=processing
→ ⑤ C103-9 履约计划同步(先于特批挂起;失败单据落 failed,修复后重推不被特批门禁阻塞)
→ ⑥ 置 hcs_spec_status=processing + currentNode="特批审核"(TMS 流程挂起,合规侧随报文 fdSepcailContract=true 走特批)
```
- 原设计"黑名单挂起不推送、人工发起特批"存在死锁(挂起时无 `hcs_fd_id`,C103-8 报文必填合规合同ID 永远失败),本次变更为 **先 C103-1 起草、后 C103-8 特批**,特批通过即合规侧放行签约,无需恢复推送;
@@ -218,3 +223,77 @@ TMS 审批通过(approved / change_approved)
| rejected | `approvalStatus=rejected` + `currentNode="已驳回"`,走重新编辑 → 提交 → 审批 → 重新特批闭环(重新推送时 C103-11 更新后再次自动特批),reason 随原文见 `hcs_spec_msg` |
> 等效兜底:页面人工确认(`POST /hcs/contract/spec/confirm`,合同)与(`POST /hcs/spec-pay/confirm`,付款)与合规推送等效,二者均以先到为准。原推测性接收端点 `POST /hcs/callback/spec-result`(B103 候选键解析)已删除。
---
## 十、履约计划(C103-9)与履约记录(C108-1/C108-2)同步
> 业务定位:涉及收付款的合同须在合规侧建立履约计划,付款履约记录凭计划阶段ID(`hcs_stage_fd_id`)关联阶段。
> 一期 `exe-stage.sync-enabled=false`、`perf-record.sync-enabled=false` 暂不上报 HCS(2026-10-09 范围调整),但 `blade_contract_exe_stage` 表、维护端点与审批标记逻辑均在运行。
### 10.1 数据模型与维护入口
`blade_contract_exe_stage` 是 TMS 侧的**合同履约计划行项目表**:合同的收付款资金计划逐行存储(如"预付款 30 万 / 进度款 50 万 / 质保金 20 万"各一行),是 C103-9 推送 HCS 的数据源。单独建表而非挂合同主表 JSON 的原因:
- **行级同步**:C103-9 是行级 add/update/delete 操作,每行需独立 `row_op` 标记、独立回填合规阶段ID、独立同步状态
- **删除不可逆**:已同步行必须软删后凭 `hcs_stage_fd_id` 上报 delete,直接物理删会在合规侧留孤儿数据
- **重试粒度**:单行失败(如 add 未匹配到返回阶段ID)可单独保持 failed 下轮重试,不拖累整单
- **一期攒数据**:开关关闭不上报,但表与维护端点照常运行,打开后可凭已有数据补推
行字段(`HcsContractExeStage`):
| 字段 | 说明 |
| --- | --- |
| `perform_type` | 履约类型 payment=付款 / collection=收款 |
| `fund_nature` | 款项性质 prepayment=预付 / progress=进度 / acceptance=验收 / retention=质保 |
| `fund_amount` / `fund_date` | 计划金额(元)/ 计划资金日期 |
| `invoice_rule` / `invoice_cycle` | 开票规则 payFirst=先款后票 / invoiceFirst=先票后款;账期(天) |
| `fund_type` / `is_finish` | 金额类型(空取配置 `exe-stage.fund-type`)/ 是否完成 |
| `row_op` | 行级同步标记 none / add / update / delete |
| `hcs_stage_fd_id` | 合规阶段ID(C103-9 返回,行 update/delete 全靠它,必须持久化) |
| `hcs_sync_status` / `hcs_sync_msg` | 行级同步状态与结果 |
> `fund_type`(金额类型)是 HCS 报文要求的**金额口径声明**(byAmount=按固定金额),一期三级兜底恒为 byAmount(建表默认 → 行值为空取 `exe-stage.fund-type` → 推送组装时再兜底);其他口径(如按比例)值域 HCS 文档未给出,待联调后放开,代码不做枚举校验。
维护端点 `ContractExeStageController`(列表 / 批量提交 / 删除,`ContractExeStageServiceImpl`):
- 校验:`performType` 必填且仅 payment/collection,计划金额非负
- 新行置 `row_op=add` 待随合同链路首推;已同步行(有 `hcs_stage_fd_id`)编辑置 update、未同步行保持 add
- 删除:已同步行软删 + `row_op=delete`(须凭合规阶段ID 上报删除,不可物理删除);未同步行直接物理删除
### 10.2 同步时机
- 合同审批通过(`ContractManageServiceImpl#approve`):未同步行(`hcs_stage_fd_id is null`)统一置 `row_op=add`
- 执行位置:`pushContract` 第⑤步(先于特批挂起,2026-10-14 调整);前置强约束:合同须已有 `hcs_fd_id`(先 C103-1 起草、后 C103-9);开关关闭时直接跳过
### 10.3 C103-9 报文与回填(`HcsExeStageSyncSupport`)
顶层(`HcsExeplanSyncDTO`):`fdContractId`=`hcs_fd_id`(合规合同ID,非 TMS 主键)、`fdOuterSystem`=`src-system`、`fdLoginName`=配置 `operator-login-name`(默认 srvadmin)、`fdEopExeplanOperateType` 首次 add / 已同步 update(看 `hcs_exeplan_id`,已同步则同时传 `fdExeplanId`)。
行级 `fdStages[]`(对应待同步行):
| HCS 字段 | TMS 取值 | 备注 |
| --- | --- | --- |
| `fdId` | `hcs_stage_fd_id` | add 行不传;update/delete 必传 |
| `fdEopExeplanOperateType` | `row_op` | add / update / delete |
| `fdPerformType` | `perform_type` | 必填 |
| `fdFundNature` / `fdFundAmount` / `fdFundDate` | 同名字段 | 资金日期转毫秒时间戳 |
| `fdInvoiceRule` / `fdInvoiceCycle` | 同名字段 | 账期(天) |
| `fdFundType` | `fund_type`,空取 `exe-stage.fund-type` | 默认 byAmount |
| `fdIsFinish` | `is_finish` | boolean |
| `fdExtendContent` | 固定 `"{}"` | |
取数与回填:
- 取数 `selectSyncRows`:`row_op <> 'none'` 的行,**含软删的 delete 行**(原生 SQL 绕过逻辑删除过滤)
- 成功回填三件事:`fdExeplanId` → `blade_contract_manage.hcs_exeplan_id`;`fdChangeStages[]` 各行合规 fdId → `hcs_stage_fd_id`;成功行清 `row_op=none` 并置行级 synced
- `fdChangeStages` 与请求行对应关系 v4 未明确(待联调):行数一致按请求行序对应;不一致按 履约类型|款项性质|金额|资金日期 组合键匹配;add 行匹配不到保持 failed 待下轮重试
- 失败抛异常走合同 `markFailed`,下轮凭 `hcs_fd_id` 整体重推补偿(§二⑥)
### 10.4 履约记录(付款链路,C108-1/C108-2)
`HcsPerfRecordSyncSupport`,随付款推送链路执行(`HcsPaymentSyncSupport`,开关 `perf-record.sync-enabled`,一期默认关):
- 付款审批通过 → C108-1 新建,返回 fdId 回填付款申请 `hcs_perf_fd_id`(立即持久化)
- 实际付款推进(`paidAmount` 变化置待同步)→ C108-2 更新,`formData.fdId`=`hcs_perf_fd_id` 必填(响应无 data,success=true 即成功)
- 关联履约阶段:按合同 + 付款类型匹配**已同步**的计划行(`hcs_stage_fd_id` 非空),无关联不传 fd_id —— 履约计划同步是履约记录关联的前置