211 行
7.7 KiB
Markdown
211 行
7.7 KiB
Markdown
# 新增:合同方案补充仲裁机构/管辖法院字段 + 12301 dispute 节点完整化
|
|
|
|
**类型**: 后端 FEAT(新增字段+老 BUG 修复)
|
|
**关联**: 工单 #1561 / PR #1565 (合并到 dev sha `d3190de5`)
|
|
**日期**: 2026-04-30
|
|
**前端处理者**: mmg
|
|
**影响范围**: 管理后台 → 合同方案管理 → 编辑合同方案弹窗 → 争议解决方式
|
|
|
|
---
|
|
|
|
## 业务背景
|
|
|
|
管理后台合同方案编辑页(`web.test.1814.love:9443/contract/scheme`),用户截图反馈:**选择"仲裁/诉讼"时必须填对应地点,合同 PDF 模板里要用**。
|
|
|
|
排查发现:
|
|
- 12301 平台 `dispute.tribunalName` 字段已支持,但 `ContractCreateService.assembleParam` **从未把 scheme.tribunalName 透传过去** → 合同 PDF 仲裁条款常年是空模板(老 BUG)
|
|
- 合同方案表 `contract_scheme` 没有诉讼地点字段
|
|
- `disputeResolution` 注释在不同代码处自相矛盾(`ContractSchemeRequest` 1=仲裁/2=诉讼 vs `ContractApplyParam` 1=诉讼/2=仲裁)
|
|
|
|
经过 4 轮 PM 整改 + 4 轮评审委员会评审(综合 9.925/10)+ 架构师勘误 + dev 编码,本次 PR 一并解决。
|
|
|
|
---
|
|
|
|
## ⚠️ 关键:disputeResolution 语义统一为 1=诉讼 / 2=仲裁
|
|
|
|
**原 `ContractSchemeRequest` 注释 "1=仲裁 2=诉讼" 写反了**,本次后端统一以 12301 平台真值为准:
|
|
|
|
| 值 | 含义 |
|
|
|---|---|
|
|
| 1 | 诉讼 (litigation) |
|
|
| 2 | 仲裁 (arbitration) |
|
|
|
|
**前端 hl-ui 字典 `dispute_resolution` 渲染如原按 "1=仲裁" 请翻转**,否则用户选错语义。
|
|
|
|
测试服 DB 现存方案数据已经按 1=诉讼/2=仲裁 自洽(scheme_id=3 dispute=1 国内冬季诉讼;scheme_id=2046840448906940417 dispute=2 仲裁,均合理)— 确认 ContractApplyParam 注释才是真值。
|
|
|
|
---
|
|
|
|
## 后端改动概要
|
|
|
|
### 1. 字段
|
|
`contract_scheme` 表增量加 2 列(已在测试服 DDL 落地):
|
|
|
|
```sql
|
|
ALTER TABLE contract_scheme
|
|
ADD COLUMN tribunal_name VARCHAR(100) NULL COMMENT '仲裁机构名称(disputeResolution=2 仲裁时必填)',
|
|
ADD COLUMN litigation_court VARCHAR(100) NULL COMMENT '管辖法院(disputeResolution=1 诉讼时必填)';
|
|
```
|
|
|
|
### 2. ReqVO / RespVO
|
|
|
|
`ContractSchemeRequest` / `ContractSchemeVO` 加 2 字段:
|
|
|
|
```java
|
|
@ApiModelProperty(value = "仲裁机构名称(disputeResolution=2 时必填)", example = "北京仲裁委员会")
|
|
@Size(max = 100, message = "仲裁机构名称长度不能超过 100 个字符")
|
|
private String tribunalName;
|
|
|
|
@ApiModelProperty(value = "管辖法院(disputeResolution=1 时必填)", example = "北京市朝阳区人民法院")
|
|
@Size(max = 100, message = "管辖法院长度不能超过 100 个字符")
|
|
private String litigationCourt;
|
|
```
|
|
|
|
### 3. 校验
|
|
联合校验在 Service 层(VO 层不加 `@NotBlank`,因为依赖 disputeResolution 值组合):
|
|
|
|
| 场景 | 校验 | 错误码 | 业务码 |
|
|
|---|---|---|---|
|
|
| disputeResolution=2(仲裁)缺 tribunalName | 拦截 | `SCHEME_TRIBUNAL_NAME_REQUIRED` | **510216** |
|
|
| disputeResolution=1(诉讼)缺 litigationCourt | 拦截 | `SCHEME_LITIGATION_COURT_REQUIRED` | **510217** |
|
|
| 反向字段(选仲裁但传了诉讼地点等)| 不强制清空 | - | - |
|
|
|
|
### 4. 合同条款渲染
|
|
`supplementaryClause` 拼接顺序改为:
|
|
|
|
```
|
|
[1] safetyNotice (安全告知书,平台标准条款)
|
|
[2] disputeClause (争议解决条款,本次新增)
|
|
[3] userClause (用户自定义补充约定)
|
|
```
|
|
|
|
`disputeClause` 文本格式(本期固定模板):
|
|
- 仲裁: "双方因履行本合同发生争议的,提交{tribunalName}仲裁解决。"
|
|
- 诉讼: "双方因履行本合同发生争议的,提交{litigationCourt}诉讼解决。"
|
|
|
|
12301 dispute 节点也会按 resolution 路由对应字段:
|
|
- 2=仲裁 → 输出 `dispute.tribunalName`
|
|
- 1=诉讼 → 输出 `dispute.litigationCourt`
|
|
|
|
腾讯电子签链路通过共享 `param.supplementaryClause` 自动同步获得 disputeClause 文本(后端零改动)。
|
|
|
|
### 5. 字符净化
|
|
`tribunalName` / `litigationCourt` 拼入条款文本时净化 `\n / \r / \t` 为空格(防 12301 errcode=301025,经验来自 PR #1551 reason 字段事故)。
|
|
|
|
---
|
|
|
|
## 错误响应示例
|
|
|
|
创建/修改合同方案时,选择仲裁但未填仲裁机构:
|
|
|
|
```jsonc
|
|
HTTP 200 (走业务错误码,非协议错误)
|
|
{
|
|
"code": 510216,
|
|
"message": "争议解决方式为仲裁时,仲裁机构名称不能为空",
|
|
"data": null
|
|
}
|
|
```
|
|
|
|
诉讼缺管辖法院:
|
|
|
|
```jsonc
|
|
{
|
|
"code": 510217,
|
|
"message": "争议解决方式为诉讼时,管辖法院不能为空",
|
|
"data": null
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 前端要做的改动 ⚠️
|
|
|
|
### 1. 字典 `dispute_resolution` 翻转(关键)
|
|
原可能渲染: `1=仲裁, 2=诉讼` → 改为 **`1=诉讼, 2=仲裁`**(与后端一致)。
|
|
|
|
如不翻转,用户选"仲裁" UI 传 1,后端按"1=诉讼"处理 → 数据完全反向。
|
|
|
|
### 2. 编辑合同方案弹窗 — 条件输入框
|
|
争议解决方式单选(仲裁/诉讼)下方,**动态显示**对应输入框:
|
|
|
|
| 选中 | 显示输入框 | 必填 | maxlength | placeholder |
|
|
|---|---|---|---|---|
|
|
| 仲裁(value=2)| 「仲裁机构名称」 | ✅ 必填 | 100 | 请填写仲裁委员会全称,例:北京仲裁委员会 |
|
|
| 诉讼(value=1)| 「管辖法院」 | ✅ 必填 | 100 | 请填写约定管辖法院,例:北京市朝阳区人民法院 |
|
|
|
|
### 3. 切换 resolution 不清空已填值
|
|
后端两列独立存,允许保留(用户切换"仲裁→诉讼→仲裁"时,仲裁地点会保留)。
|
|
|
|
### 4. 提交字段名
|
|
- `disputeResolution` (Integer)
|
|
- `tribunalName` (String)
|
|
- `litigationCourt` (String)
|
|
|
|
### 5. 编辑旧方案兼容
|
|
旧方案 `tribunalName/litigationCourt` 都为 null:
|
|
- 加载时不拦截,允许列表正常显示
|
|
- 用户**点击保存时**按新规校验拦截(后端会返 510216/510217)
|
|
|
|
### 6. 错误码字典
|
|
| 业务码 | 含义 |
|
|
|---|---|
|
|
| 510216 | 仲裁机构名称必填 |
|
|
| 510217 | 管辖法院必填 |
|
|
|
|
---
|
|
|
|
## 部署状态
|
|
|
|
- ✅ PR #1565 合并到 dev (sha `d3190de5`,2026-04-30 15:46)
|
|
- ✅ 测试服 DDL 已落 (V20260501 ALTER TABLE 添加 2 列)
|
|
- ⏳ Deploy Panel 部署 hl-order-service-v2 测试服(进行中,管理者跟踪)
|
|
- ⏳ 测试服 12301 + 腾讯电子签 round-trip 验证待执行
|
|
- ⏳ Release dev → main 待用户拍板
|
|
|
|
---
|
|
|
|
## 测试服 round-trip 计划
|
|
|
|
**经网关 https://api.test.1814.love:9443**:
|
|
|
|
1. POST `/admin/contract/scheme` `disputeResolution=2` 不带 `tribunalName` → 期望 510216
|
|
2. POST `/admin/contract/scheme` `disputeResolution=1` 不带 `litigationCourt` → 期望 510217
|
|
3. POST `/admin/contract/scheme` 仲裁带 tribunalName → 200 success,GET 详情验证字段回显
|
|
4. PUT 同一方案改诉讼带 litigationCourt → 200,字段独立保留
|
|
5. 真实下单走 12301 报送(仲裁场景)→ 验 dispute.tribunalName 输出 + supplementaryClause 含仲裁文本
|
|
6. 真实下单走腾讯电子签(仲裁场景)→ 验 supplementaryClause form 字段含仲裁文本(跨平台一致性)
|
|
|
|
---
|
|
|
|
## 单测
|
|
|
|
`mvn -pl hl-order-service-v2 -Dtest='ContractCreateServiceTest,TencentEsignRequestBuilderTest' test`:
|
|
- 84 tests,0 failures,0 errors
|
|
- 17 个新单测 (4 校验 + 8 corner case + 4 边界净化顺序隐藏改点 + 1 跨平台)
|
|
|
|
---
|
|
|
|
## 不动
|
|
|
|
- ❌ 腾讯电子签模板 widget(运营在腾讯控制台改模板时联动)
|
|
- ❌ disputeResolution `int → Integer` 重构(本期不动)
|
|
- ❌ MP / 字典服务 / Feign / MQ / 状态机 / 缓存(本期纯 admin 配置项)
|
|
- ❌ 历史合同回填(运营 TODO SQL 附 PR body)
|
|
|
|
---
|
|
|
|
## 运营 TODO(可选)
|
|
|
|
历史方案补齐(运营评估后由运维管理员执行,**本 PR 不自动跑**):
|
|
|
|
```sql
|
|
-- 仲裁方案缺仲裁委员会
|
|
SELECT scheme_id, name FROM contract_scheme
|
|
WHERE deleted_at IS NULL AND dispute_resolution = 2 AND (tribunal_name IS NULL OR tribunal_name = '');
|
|
|
|
-- 诉讼方案缺管辖法院
|
|
SELECT scheme_id, name FROM contract_scheme
|
|
WHERE deleted_at IS NULL AND dispute_resolution = 1 AND (litigation_court IS NULL OR litigation_court = '');
|
|
```
|