docs(changelog): v3保险投保接口入参契约说明(管理后台切v3联调补充)

补 v3 /v3/admin/insurance/purchase 入参契约:idCardType/idCardNo 标准名 +
@JsonAlias 兼容老名 idType/idNo(仍接收不下线);v3 被保人入参无 gender 字段;
insuredPersons/coverage 日期可空自动填充。澄清 404 属网关路由非契约范畴。
这个提交包含在:
yaosutu 2026-06-11 10:57:16 +08:00
父节点 04818b47d2
当前提交 71c06434ab

查看文件

@ -0,0 +1,184 @@
---
date: 2026-06-11
type: api-contract-clarify
module: hl-order-service-v3/insurance
priority: high
status: code-already-merged
restart_service: hl-order-service-v3
端类型: 管理后台
breaking_change: false
---
# v3 保险「投保」接口入参契约说明(管理后台保险全量切 v3 联调补充)
## ① 接口背景
管理后台保险模块前端已全量指向 v3,投保入参字段名按设计文档Knife4j / API 文档)硬切。
但 v3 投保接口(`/v3/admin/insurance/purchase`)此前**从未推送过入参契约 changelog**(仓库已有的 `#3642` 只说了出单状态/列表详情出参,未涉及投保入参)。
本文档把 v3 投保两个接口的**入参契约**完整内联,并明确**老字段名是否仍被接收**这一前端关心的问题。
> ⚠️ 本文档不涉及"保险页 404"——404 属网关路由 / 服务注册问题(需后端确认测试服 `/v3/admin/insurance/**` 是否已路由到 hl-order-service-v3:8086、order-service-v3 是否已启动),不在接口契约范畴。
## ② 变更清单
| # | 接口 | 方法 | 说明 |
|---|------|------|------|
| 1 | `/v3/admin/insurance/purchase` | POST | 手动投保(指定方案或产品+计划 + 可选被保人) |
| 2 | `/v3/admin/insurance/purchase-by-scheme` | POST | 按方案自动投保(仅 orderId + schemeId |
性质:**契约澄清,非破坏性**。字段名与 v2 一致;路径前缀由 `/admin/insurance` 变为 `/v3/admin/insurance`
## ③ 接口详情
### 3.1 手动投保
- **路径**`POST /v3/admin/insurance/purchase`
- **Content-Type**`application/json`
- **请求体**`PurchaseInsuranceRequest`
- **返回**`Result<List<InsuranceOrderDetailVO>>`(创建的保险订单详情列表)
### 3.2 按方案自动投保
- **路径**`POST /v3/admin/insurance/purchase-by-scheme`
- **请求体**`AutoPurchaseRequest`
- **返回**`Result<Void>`
## ④ 入参
### 4.1 PurchaseInsuranceRequest手动投保
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `orderId` | Long | ✅ | 业务订单 ID |
| `schemeId` | Long | 否 | 保险方案 ID优先用方案匹配;为空则用 `insuranceProductId`+`planId` |
| `insuranceProductId` | Long | 方案为空时必填 | 保险产品 ID |
| `planId` | Long | 方案为空时必填 | 保险计划 ID |
| `coverageStartDate` | LocalDate | 否 | 保障开始日期,为空时后端按订单 startDate 自动填充 |
| `coverageEndDate` | LocalDate | 否 | 保障结束日期,为空时后端按订单 endDate 自动填充 |
| `insuredPersons` | List<InsuredPersonItem> | 否 | 被保人列表;**为空时后端按订单出行人自动填充**,传了则以传入为准 |
| `remark` | String | 否 | 备注 |
#### InsuredPersonItem被保人
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | String | ✅ | 姓名 |
| `idCardType` | String | ✅ | 证件类型,枚举见 ⑥;**兼容老字段名 `idType`**`@JsonAlias` |
| `idCardNo` | String | ✅ | 证件号码(落库自动加密、查询自动解密,前端无感);**兼容老字段名 `idNo`** |
| `birthday` | LocalDate | 否 | 出生日期 |
| `phone` | String | 否 | 手机号 |
> ⚠️ **v3 被保人入参无 `gender`(性别)字段**v2 的 `InsuredPersonItem``gender`,v3 **没有**。前端从 v2 切 v3 若仍传 `gender`,将被 Jackson 静默忽略(`FAIL_ON_UNKNOWN_PROPERTIES=false`,不报错)。
### 4.2 AutoPurchaseRequest按方案投保
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `orderId` | Long | ✅ | 旅行订单 ID |
| `schemeId` | Long | ✅ | 保险方案 ID |
## ⑤ 出参
`/v3/admin/insurance/purchase` 返回 `List<InsuranceOrderDetailVO>`(新建保险订单详情)。
其中 `status` / `statusLabel` / `policyNo` 的**异步出单**语义(可能为 `INSURING`/出单中、`policyNo` 可能为空)见同目录 `#3642 保险出单改异步` changelog。
## ⑥ 枚举 / 数据字典
`idCardType` 证件类型(以代码注解为准,完整证件字典与 traveler 模块对齐):
| 值 | 含义 |
|----|------|
| `ID_CARD` | 身份证 |
| `PASSPORT` | 护照 |
| `HONG_KONG` | 港澳通行证 |
| `TAIWAN` | 台湾通行证 |
| `OTHER` | 其他 |
## ⑦ 错误码
| 场景 | 响应 |
|------|------|
| 缺 `orderId` | 400 「订单ID不能为空」 |
| 被保人缺 `idCardType`(或老名 `idType` | 400 「证件类型不能为空」 |
| 被保人缺 `idCardNo`(或老名 `idNo` | 400 「证件号码不能为空」 |
| 方案与产品+计划均为空 | 业务校验失败(无法定位保险产品) |
## ⑧ 示例
### 典型(推荐:标准字段名)
```json
POST /v3/admin/insurance/purchase
{
"orderId": "2026270000000000001",
"schemeId": "2026279738275856500",
"insuredPersons": [
{
"name": "张三",
"idCardType": "ID_CARD",
"idCardNo": "110101199001011234",
"phone": "13800138000",
"birthday": "1990-01-01"
}
],
"remark": "管理后台手动投保"
}
```
### 边界(不传被保人 → 后端按订单出行人自动填充)
```json
POST /v3/admin/insurance/purchase
{ "orderId": "2026270000000000001", "schemeId": "2026279738275856500" }
```
### 兼容(老字段名 idType/idNo 仍被接收,@JsonAlias 兜底)
```json
{
"orderId": "2026270000000000001",
"schemeId": "2026279738275856500",
"insuredPersons": [
{ "name": "张三", "idType": "ID_CARD", "idNo": "110101199001011234" }
]
}
```
### 异常(缺证件号 → 400
```json
请求: { "orderId": "...", "insuredPersons": [ { "name": "张三", "idCardType": "ID_CARD" } ] }
响应: { "code": 400, "message": "证件号码不能为空" }
```
## ⑨ 业务边界
- `insuredPersons` 为空 → 后端按订单出行人自动填充被保人。
- `coverageStartDate` / `coverageEndDate` 为空 → 后端按订单 startDate / endDate 填充。
- 同时传 `idCardType``idType`(不推荐)→ Jackson 按后出现的覆盖,建议**只传标准名**。
- 出单为异步:返回时保险订单可能处于 `INSURING``policyNo` 暂空,需轮询 / 回调获取最终保单号(详见 `#3642`)。
## ⑩ 修改前后对比
| 维度 | v2`/admin/insurance` | v3`/v3/admin/insurance` |
|------|--------------------------|-----------------------------|
| 路径前缀 | `/admin/insurance` | `/v3/admin/insurance` |
| `idCardType`/`idCardNo` | ✅(兼容 `idType`/`idNo` | ✅(兼容 `idType`/`idNo`,**一致** |
| 被保人 `gender` | ✅ 有 | ❌ **无** |
| 出单 | 同步 | 异步INSURING 状态,见 #3642 |
## ⑪ 影响评估 / 回滚
- 前端切标准字段名 `idCardType`/`idCardNo` **安全**;继续用老名 `idType`/`idNo` 也**不会 400**(兼容未下线)。
- 唯一需前端动作:**停止传 `gender`**v3 无此入参,传了被忽略;如业务需要被保人性别,由后端从出行人档案带,前端无需关心)。
- 无代码回滚项(本文档为存量契约说明,未改代码)。
## ⑫ 注意事项
- **「保险页 404」非本文档范畴**:属网关路由 / 服务注册问题,请后端在测试服核:① `/v3/admin/insurance/**` 是否路由到 hl-order-service-v3(8086);② order-service-v3 是否已启动;③ 小程序侧若经 BFF,`MpInsuranceFeignClient``name="hl-order-service"`(无 v3 后缀)在测试服注册到的是 v2 还是 v3。
- `idCardNo` 已挂加密 TypeHandler,落库加密、查询解密,日志脱敏,前端无感。
## ⑬ 关联 / 联系人
- 来源:管理后台保险全量切 v3 联调反馈(前端 sync-log
- 相关 changelog`#3642 保险出单改异步`(出参状态)、`changelogs/2026-05/07_fix_insurance_purchase_idcardtype_field_rename.md`v2 同源字段改名背景)
- 代码位置:`hl-order-service-v3/.../insurance/dto/PurchaseInsuranceRequest.java``AdminInsuranceController.java:124`
- 负责人:腰苏图