:feat: 项目立项、合同审批通过后即时推送HCS合规系统(复用单条重推链路,扫描任务降级为兜底补偿,附前端接入设计§6增补)

This commit is contained in:
刘泉佳
2026-10-10 18:24:05 +08:00
parent 7855254a4f
commit 2ea5901238
6 changed files with 293 additions and 11 deletions
@@ -11,7 +11,7 @@
| # | 原则 |
|---|---|
| P1 | **前端不直连 HCS**。所有 C 系列接口由 tms-api 代理:`Authorization`/`Key-Auth` 静态双头只存在于服务端,浏览器不可见,也无跨域问题 |
| P2 | **自动推送不动**。C102-1/2、C103-1/11 仍走"审批通过 → 置 unsynced → 扫描任务推送"链路(零埋点,ADR-0001),前端只补**状态展示 + 手动重推 + 对账回查** |
| P2 | **自动推送保留扫描兜底**。C102-1/2、C103-1/11 走"审批通过 → 置 unsynced → 扫描任务推送"链路(ADR-0001);2026-10-10 增补**审批通过即时推送**(§6),扫描任务降级为兜底补偿。前端只补**状态展示 + 手动重推 + 对账回查** |
| P3 | **附件主存储仍在 TMS OSS**。表单上传逻辑不改;HCS 转传由后端推送链路自动完成(HcsAttachmentSupport 已实现)。前端新增的是**手动转传**(特批材料、确定性上传)与**HCS 附件回下载** |
| P4 | **前端结算币种/合同分类不改**。两个下拉继续用本地字典(`currency_type`/`contractCategory`),存量值与交互零变化;C103-12 币种、C103-7 分类只在后端推送侧映射(`currency-name-mapping`/`template-mapping`,已实现),前端无触点 |
| P5 | UI 照**"同步金蝶"按钮模式**(参考 `customer-archive.vue`、`formal-settlement-table-panel.vue` 的 kingdeeSyncStatus 列 + 推送按钮),HCS 用 `hcsSyncStatus` 等同构字段 |
@@ -110,11 +110,11 @@ POST /hcs/contract/spec-apply (body:{ contractId, attachments: [{fdId,
组装报文调 C103-8(报文模式预估照 C106-3:fd_contract_id=hcs_fd_id、fd_contract_no、
fd_doc_subject="合同特批-"+contractNo、特批附件 mechanisms.attachment 带 fdBindType=ADD)
→ 返回 data.fdId → hcs_spec_fd_id,状态置 processing;失败置 need 并记 hcs_spec_msg
→ C103-8 v4 未含报文,联调前按上述预估实现并加 TODO 标注(§6-U1)
→ C103-8 v4 未含报文,联调前按上述预估实现并加 TODO 标注(§7-U1)
POST /hcs/callback/spec-result (B103 合规→TMS 回调接收端点)
公开端点(@PreAuth(AuthConstant.PERMIT_ALL) + 报文验签字段待合规方口径,先记录原文留痕)
body:{ 合同标识/特批流程fdId, 审批结果, 审批意见 } ← 字段名待 B103 报文(§6-U1)
body:{ 合同标识/特批流程fdId, 审批结果, 审批意见 } ← 字段名待 B103 报文(§7-U1)
处理:定位合同 → 回写 hcs_spec_status=approved/rejected + hcs_spec_msg
→ approved 时把合同 hcs_sync_status 重置 unsynced,由既有扫描任务自动走 C103-1(零新增链路)
→ B103 报文未到位前端点先落库留痕不置状态,联调时接线
@@ -241,11 +241,67 @@ export const applyContractSpec = data =>
| F1 基础 | `GET /hcs/master-data/template` + `@PreAuth`/菜单 SQL | `api/business/hcs.js`;管理端主数据页 | 无 |
| F2 状态与推送 | 无(端点已有) | 两列表页:状态列/筛选 + 推送合规按钮 | F1(hcs.js) |
| F3 对账与附件 | `/hcs/risk`、`/hcs/attachment/upload`、`/hcs/attachment/download` | 对账弹窗 + 合规附件下载 + 手动转传 + 附件行 `hcsFdId` | F2 |
| F4 特批 | 合同特批字段组 SQL;`POST /hcs/contract/spec-apply` + `POST /hcs/callback/spec-result`(B103);合同推送链路加 need 挂起判断 | 发起特批弹窗 + 特批状态标签 + 按钮联动 | C103-8/B103 报文(§6-U1,可先按预估模式实现联调替换) |
| F4 特批 | 合同特批字段组 SQL;`POST /hcs/contract/spec-apply` + `POST /hcs/callback/spec-result`(B103);合同推送链路加 need 挂起判断 | 发起特批弹窗 + 特批状态标签 + 按钮联动 | C103-8/B103 报文(§7-U1,可先按预估模式实现联调替换) |
| F5 即时推送(2026-10-10 增补,纯后端) | `HcsImmediatePushSupport` + 4 处审批入口触发(§6.3)+ 开关配置 | 无 | 无(复用既有 Support,扫描兜底保留) |
---
## 6. 未决事项
## 6. 审批通过即时推送(2026-10-10 增补,后端链路增强)
### 6.1 目标与原则
| # | 原则 |
|---|---|
| I1 | **秒级触达**:项目立项(C102-1/2)、合同(C103-1/11)审批通过(含变更通过)事务提交后立即异步推送,不再等扫描周期(原最长 ≤5 分钟) |
| I2 | **ADR-0001 修订**:审批代码埋点由"零"放宽为"一行触发调用"(仅传 id,不含任何推送逻辑);推送逻辑仍全部收敛在 hcs support 包,扫描任务保留为**兜底**(失败重试、批量补偿、停机窗口漏单) |
| I3 | **复用既有链路**:执行体直接调 `repushProject(id)` / `repushContract(id)`(已含 approved 校验、状态重置、失败 markFailed、高风险挂起判断),不新写报文与状态机 |
### 6.2 新增组件:HcsImmediatePushSupport(仅 1 个类)
位置 `org.springblade.transport.support.hcs`,职责只有一个:**事务提交后异步触发一次单条推送**。
```java
public void pushProjectAfterCommit(Long id) // 项目 C102
public void pushContractAfterCommit(Long id) // 合同 C103
```
执行模型:
1. **afterCommit 触发**:审批方法在事务内调用本组件 → `TransactionSynchronizationManager.registerSynchronization` 注册 afterCommit 回调,审批事务回滚则不推送。MK 回调路径若未包事务(单条 updateById 自动提交)则直接提交异步,语义等价。
2. **异步执行**:自建 2 线程 daemon 池(线程名 `hcs-immediate-push`,`@PreDestroy` 释放)。工程未启用 `@EnableAsync`,不为此新增全局配置;HCS 推送含风险校验+附件转传+起草可达 20s+,绝不能阻塞审批响应。
3. **执行体**(与 HcsPushTask 同线程模型,无租户上下文,与定时扫描行为一致):
- 开关:`thirdParty.hcs.push.immediate-enabled`(默认 false,灰度打开)
- 短路:查库 `approvalStatus ∉ {approved, change_approved}` 或 `hcs_sync_status = synced` → 跳过(已被扫描/人工重推处理过)
- 单条锁:Redis `setIfAbsent("blade:third:hcs:push:lock:{bizType}:{id}", TTL 120s)`,抢不到直接放弃(扫描兜底),持有值比对后释放(照 HcsPushTask 惯例)
- 推送:`repushProject(id)` / `repushContract(id)`;异常仅 `log.error` 吞掉(Support 内部已 markFailed,扫描按 `hcs_retry_count < max-retry` 自动补偿,超限人工重推,D7 不变)
### 6.3 触发点(4 处,各加 1 行)
| 入口 | 位置 | 触发调用 |
|---|---|---|
| 本地审批 | `ProjectApplyServiceImpl.approve()`(事务内,含变更通过,现有置 unsynced 行之后) | `pushProjectAfterCommit(id)` |
| 本地审批 | `ContractManageServiceImpl.approve()`(事务内,含变更通过 + 履约行置 add,`updateById` 之后) | `pushContractAfterCommit(id)` |
| MK 回调 | `MkProcessBizHandlers.ProjectHandler.approveFromProcess()`(`updateById` 返回 true 后) | `pushProjectAfterCommit(id)` |
| MK 回调 | `MkProcessBizHandlers.ContractHandler.approveFromProcess()`(同上) | `pushContractAfterCommit(id)` |
### 6.4 并发与失败语义
| 场景 | 语义 |
|---|---|
| 即时推送 vs 扫描任务并发 | 单条锁 + synced 短路 + HCS 侧判重(C102-3 / C103 判重、更新幂等)三重吸收;极端双推为更新语义,无脏数据 |
| 即时推送失败 | `markFailed` → 扫描下轮自动补偿 → 超限人工重推(与现状完全一致,零新增重试机制) |
| 高风险合同 | `pushContract` 既有 `hcs_spec_status ∈ {need, processing}` 挂起判断不变,即时推送同样被挂起,特批通过后恢复推送 |
| 开关关闭 / 组件异常 | 完全回落扫描链路,行为与现状一致 |
### 6.5 配置
| 配置项 | 默认 | 说明 |
|---|---|---|
| `thirdParty.hcs.push.immediate-enabled` | false | 即时推送总开关,灰度打开;普通 `@Value` 重启生效即可(扫描兜底使风险可控,无需 @RefreshScope) |
---
## 7. 未决事项
| # | 事项 | 影响 | 当前处理 |
|---|---|---|---|