# 订单详情「合同·保险」Tab —— 创建合同 / 投保 接口对接（管理后台）

- 端类型：管理后台
- 变更类型：新增接口（前端首次对接；**后端接口早已存在**，本文为对接说明）
- 关联：订单详情「合同·保险」Tab（懒加载 GET 仅回显，写操作走本文 4 接口）
- 日期：2026-06-26

---

## ① 接口背景

订单详情「合同·保险」Tab 的懒加载接口 `GET /v3/admin/order/{id}/contract-insurance` 是**只读回显**（返回合同/保险当前状态 + 时间线），**不含**写操作。Tab 页面上的两组操作按钮——「选择合同方案 + 创建合同」「选择保险方案 + 投保」——各由独立写接口支撑。本文给出这 4 个接口的完整契约供前端对接。

> 两个「按方案」接口都是**简化版**：前端只传 `orderId + schemeId`，后端自动从订单取出行人、行程日期、联系人，结合方案配置去创建合同 / 逐段投保。

---

## ② 变更清单

| 序 | 方法 | 路径 | 用途 |
|---|---|---|---|
| 1 | GET | `/v3/admin/contract/scheme/list` | 合同方案下拉（启用方案） |
| 2 | POST | `/v3/admin/contract/create-by-scheme` | 创建合同（按方案） |
| 3 | GET | `/v3/admin/insurance/scheme/list` | 保险方案下拉（启用方案，可按行程天数筛） |
| 4 | POST | `/v3/admin/insurance/purchase-by-scheme` | 投保（按方案，逐段投保） |

统一响应 `Result<T>`：`{ code, message, data, success }`，`code=200` 成功。

---

## ③ 接口详情

### 接口 1：合同方案下拉
```
GET /v3/admin/contract/scheme/list
```
无入参。返回当前**启用（ACTIVE）**的合同方案列表，供「选择合同方案」下拉。

### 接口 2：创建合同（按方案）
```
POST /v3/admin/contract/create-by-scheme
Content-Type: application/json
```
点「创建合同」时调。后端按方案 + 订单数据创建合同；若方案为电子签约模式（STANDARD），会触发推送签署链接（短信/微信至客户预留手机号）。

### 接口 3：保险方案下拉
```
GET /v3/admin/insurance/scheme/list?totalDays={行程天数}
```
返回**启用（ACTIVE）**保险方案，供「选择保险方案」下拉。`totalDays` 可选：传则按行程天数筛匹配方案（全程方案 totalDays=-1 任意天数都命中），不传返回全部启用方案。

### 接口 4：投保（按方案）
```
POST /v3/admin/insurance/purchase-by-scheme
Content-Type: application/json
```
点「投保」时调。后端按方案逐段投保（一个方案多段 → 每段建一张保单），自动按订单出行人投保。

---

## ④ 入参

### 接口 1（合同方案下拉）
无。

### 接口 2（创建合同）—— Body
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderId | Long(字符串) | 是 | 订单 ID |
| schemeId | Long(字符串) | 是 | 合同方案 ID（取自接口 1 的 schemeId） |

### 接口 3（保险方案下拉）—— Query
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| totalDays | Integer | 否 | 行程天数，按天数筛方案；不传返回全部启用 |

### 接口 4（投保）—— Body
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderId | Long(字符串) | 是 | 订单 ID |
| schemeId | Long(字符串) | 是 | 保险方案 ID（取自接口 3 的 schemeId） |

---

## ⑤ 出参

### 接口 1：`Result<List<ContractSchemeVO>>`
| 字段 | 类型 | 说明 |
|---|---|---|
| schemeId | String | 方案 ID（创建合同时回传） |
| name | String | 方案名称（下拉展示） |
| description | String | 方案描述 |
| contractPlatform | String | 合同平台：12301 / LOCAL / TENCENT_ESIGN |
| vendorCode | String | 平台厂商编码：TENCENT_HULAI=呼籁自有腾讯账号；12301 平台为 NULL |
| channel | String | 订单来源渠道：MP / ADMIN / CHANNEL；NULL=兜底方案 |
| templateCode | String | 合同模板编码（contractTemplateCode 别名） |
| contractTemplateName | String | 合同模板名称 |
| contractMode | String | 签约模式：STANDARD(电子签约) / SYNC(线下报备) |
| signatoryMode | Integer | 签署模式：1=短信签署 / 2=现场签署 / 3=线下签署 |
| agencyCode / agencyName | String | 旅行社编码 / 名称 |
| supplementaryClause | String | 补充条款 |
| transactorName / transactorPhone | String | 经办人姓名 / 电话 |
| sortOrder | Integer | 排序 |
| status | String | 状态：ACTIVE / INACTIVE |
| createTime | DateTime | 创建时间 |

### 接口 2：`Result<ContractDetailVO>`（创建成功返回合同详情）
| 字段 | 类型 | 说明 |
|---|---|---|
| contractId | Long(字符串) | 合同 ID |
| orderId / orderNo | Long(字符串) / String | 订单 ID / 订单编号 |
| schemeId | Long(字符串) | 合同方案 ID |
| templateCode / templateName | String | 模板编码 / 名称 |
| contractNumber | String | 合同编号 |
| platform | String | 签约平台 |
| contractType / contractTypeLabel | String | 合同类型 TOUR-旅游合同 / INSURANCE-保险单 + 标签 |
| mode / modeLabel | String | 签约模式 ONLINE-线上签署 / OFFLINE-线下签署 + 标签 |
| status / statusLabel | String | 合同状态 + 标签（见⑥） |
| signUrl / qrCodeUrl / fileUrl | String | 签署 URL / 二维码 URL / 合同文件 URL |
| agencyCode / travelAgencyName | String | 旅行社编号 / 名称 |
| destination | String | 目的地 |
| departureDate / returnDate | Date | 出发 / 返回日期 |
| totalAmount | BigDecimal(字符串) | 合同总金额 |
| touristCount | Integer | 出行人数 |
| contactName / contactPhone | String | 联系人姓名 / 电话（明文，脱敏前端处理） |
| supplementaryClause | String | 补充约定内容 |
| travelers | List | 出行人列表（travelerId/name/idCardType/idCardTypeLabel/idCardNo/phone/isSigner） |
| statusLogs | List | 状态变更日志 |
| createTime | DateTime | 创建时间 |

### 接口 3：`Result<List<InsuranceSchemeVO>>`
| 字段 | 类型 | 说明 |
|---|---|---|
| schemeId | Long(字符串) | 方案 ID（投保时回传） |
| schemeName | String | 方案名称（下拉展示，name 为兼容别名同值） |
| description | String | 方案描述 |
| isOverseas | Boolean | 是否境外 |
| totalDays | Integer | 适用行程天数（-1=全程方案，任意天数命中） |
| sortOrder | Integer | 排序 |
| status | String | 状态：ACTIVE-启用 / INACTIVE-停用 |
| insuranceProductId / productName | Long(字符串) / String | 保险产品 ID / 名称 |
| planId / planName | Long(字符串) / String | 保险计划 ID / 名称 |
| enabled | Boolean | 是否启用 |
| segmentCount | Integer | 段数量 |
| segments | List | 适用行程段列表（见下） |
| createTime / updateTime | DateTime | 创建 / 更新时间 |

`segments[]`（SegmentItem）：segmentId / segmentName / dayOffsetStart / dayOffsetEnd(-1=最后一天) / minDays / maxDays / productId / productName / planId / planName / sortOrder。

### 接口 4：`Result<Void>`
投保成功返回 `data=null`、`code=200`。投保结果（保单）通过懒加载 GET 接口的 `insurance.policies[]` 回显。

---

## ⑥ 枚举 / 数据字典

- 合同方案 contractMode：STANDARD 电子签约 / SYNC 线下报备
- 合同方案 contractPlatform：12301 / LOCAL / TENCENT_ESIGN（对应字典 contract_platform）
- 合同方案 signatoryMode：1 短信签署 / 2 现场签署 / 3 线下签署
- 合同 contractType：TOUR 旅游合同 / INSURANCE 保险单
- 合同 mode：ONLINE 线上签署 / OFFLINE 线下签署
- 合同 status：GENERATING 生成中 / GENERATED 已生成 / SIGNED 已签 / UPLOADED 已上传回执 / VOIDED 已作废 / RESIGNING 重签中
- 保险方案 status：ACTIVE 启用 / INACTIVE 停用
- 方案/产品/计划 status 通用：ACTIVE / INACTIVE

---

## ⑦ 错误码

- 创建合同 / 投保失败抛 `BusinessException`，统一响应 `code≠200 + message`，前端按 message 提示。
- 下拉查询接口正常返 200。

---

## ⑧ 示例

### 接口 1 响应（合同方案下拉）
```json
{ "code": 200, "message": "操作成功", "success": true,
  "data": [
    { "schemeId": "2054961027789152258", "name": "标准国内电子签约方案",
      "contractPlatform": "TENCENT_ESIGN", "contractMode": "STANDARD",
      "signatoryMode": 1, "status": "ACTIVE" }
  ] }
```

### 接口 2 请求（创建合同）
```json
{ "orderId": "2070039025715982337", "schemeId": "2054961027789152258" }
```
响应 `data` = ContractDetailVO（contractId/status=GENERATED/signUrl 等）。

### 接口 3 响应（保险方案下拉，传 totalDays=5）
```json
{ "code": 200, "success": true,
  "data": [
    { "schemeId": "2067500963379245057", "schemeName": "境外旅行险·标准版",
      "totalDays": -1, "status": "ACTIVE", "segmentCount": 1,
      "segments": [ { "segmentId": "...", "segmentName": "全程", "dayOffsetStart": 0, "dayOffsetEnd": -1 } ] }
  ] }
```

### 接口 4 请求（投保）
```json
{ "orderId": "2070039025715982337", "schemeId": "2067500963379245057" }
```
响应 `{ "code": 200, "data": null, "success": true }`。

---

## ⑨ 业务边界

- 两个下拉只返回 **ACTIVE** 方案；INACTIVE 不出现。
- 创建合同 / 投保只需 `orderId + schemeId`，其余数据后端从订单自动取。
- 投保为**逐段投保**：一个方案多段 → 生成多张保单，懒加载 GET 的 `insurance.policies[]` 为数组。
- 投保是异步出单：接口 4 返回成功表示**受理成功**，最终出单状态以懒加载 GET 的 policies status（INSURING→INSURED）为准。
- 创建合同若方案为 STANDARD（电子签约），后端会推送签署链接到客户预留手机号。

---

## ⑩ 修改前后对比

不适用（前端首次对接，无旧版本）。

---

## ⑪ 影响评估 / 回滚

- 后端接口早已存在且稳定，前端对接不涉及后端改动，无回滚项。
- 前端对接顺序建议：进 Tab → 调懒加载 GET 回显 → 无合同/保险时展示下拉 → 选方案 → 创建合同 / 投保 → 重新调懒加载 GET 刷新状态与时间线。

---

## ⑫ 注意事项

- Long ID（schemeId / orderId / contractId 等）与金额均**字符串化**返回（防 JS 精度丢失），请求时同样以字符串传 ID。
- 创建合同 / 投保为写操作，前端需做按钮防重复点击 + loading。
- 操作成功后刷新走懒加载 GET `/v3/admin/order/{id}/contract-insurance`（合同 5 步时间线 + 保险 policies 数组，见 2026-06-25 那份终态 changelog）。

---

## ⑬ 关联 / 联系人

- 关联文档：`changelogs-v2/2026-06/25_4390_4403_订单详情合同保险Tab出参终态-修改接口-管理后台.md`（懒加载回显出参终态）
- 后端负责人：腰苏图
- 接口已部署测试服，Knife4j 可见（`/v3/admin/contract/**`、`/v3/admin/insurance/**`）。
