---
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`
- 负责人：腰苏图
