# 订单配房配车 + 合同管理 + 保险管理 - 前端对接指南

> **日期**: 2026-03-19
> **后端状态**: ✅ 已完成，可直接对接
> **涉及模块**: hl-order-service（配房配车+交通信息）、hl-contract-service（合同管理）、hl-insurance-service（保险管理）

---

## 功能说明

本文档涵盖订单内部流程中 **配房 → 配车 → 合同签署 → 保险投保** 四大模块的完整前端对接方案。

### 订单内部流程状态机（8步）

```
PENDING_INFO(待补全信息)
  → PROCESSING(待内部流程)
    → PENDING_INSURANCE(待配保险)
      → PENDING_CONTRACT(待签合同)
        → PENDING_ROOM(待配房)
          → PENDING_VEHICLE(待配车)
            → PENDING_FINANCE(待核算)
              → READY(就绪)
```

每一步通过调用 `PUT /admin/order/{orderId}/process-status` 推进到下一步，或通过具体的配房/配车/合同/保险操作自动推进。

---

# 第一部分：配房配车 + 交通信息

---

## 接口清单（配房配车）

| # | 接口 | 方法 | 路径 | 说明 |
|---|------|------|------|------|
| 1 | 更新房间信息（文本） | PUT | `/admin/order/{orderId}/room-info` | 简单文本描述房间分配 |
| 2 | 分配酒店（结构化） | PUT | `/admin/order/{orderId}/hotel-assignment` | 按家庭分配酒店、房型、入住退房日期 |
| 3 | 检测房型一致性 | POST | `/admin/order/{orderId}/room-info/check-diff` | 检测当前配置与产品快照是否一致 |
| 4 | 更新车辆信息（文本） | PUT | `/admin/order/{orderId}/vehicle-info` | 简单文本描述车辆分配 |
| 5 | 分配车辆（结构化） | PUT | `/admin/order/{orderId}/vehicle-assignment` | 详细分配车型、品牌、司机 |
| 6 | 检测车型一致性 | POST | `/admin/order/{orderId}/vehicle-info/check-diff` | 检测当前配置与产品快照是否一致 |
| 7 | 保存交通信息 | PUT | `/admin/order/{orderId}/transport` | 批量保存航班/车次信息（全量替换） |
| 8 | 查询交通信息 | GET | `/admin/order/{orderId}/transport` | 查询订单的所有交通信息 |
| 9 | 推进内部流程 | PUT | `/admin/order/{orderId}/process-status` | 手动推进到下一步 |

---

## 接口 1：更新房间信息（文本模式）

**使用场景**：配房管理员填写房间分配的文字描述，适用于简单场景（如"标准双人间2间，大床房1间"）。

```
PUT /admin/order/{orderId}/room-info
```

### 请求参数

| 参数 | 类型 | 位置 | 必填 | 说明 |
|------|------|------|------|------|
| orderId | Long | 路径 | ✅ | 订单ID |

### 请求体

```json
{
  "roomInfo": "标准双人间2间，大床房1间"
}
```

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| roomInfo | String | ✅ | 房间信息描述，最长2000字 |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": null
}
```

### 业务规则
- 仅限 **房务管理员** 或 **超级管理员** 操作
- 保存成功后自动完成订单中的 `ARRANGE_ROOM` 待办
- 系统会发送"房间信息已更新"通知给用户
- 记录操作时间线

---

## 接口 2：分配酒店（结构化模式）

**使用场景**：按家庭维度详细分配酒店和房型，适用于复杂场景（多家庭、不同酒店、不同房型）。

```
PUT /admin/order/{orderId}/hotel-assignment
```

### 请求参数

| 参数 | 类型 | 位置 | 必填 | 说明 |
|------|------|------|------|------|
| orderId | Long | 路径 | ✅ | 订单ID |

### 请求体

```json
{
  "assignments": [
    {
      "familyIndex": 1,
      "hotelName": "呼和浩特香格里拉大酒店",
      "roomType": "豪华大床房",
      "checkInDate": "2026-07-01",
      "checkOutDate": "2026-07-03"
    },
    {
      "familyIndex": 2,
      "hotelName": "呼和浩特香格里拉大酒店",
      "roomType": "标准双人间",
      "checkInDate": "2026-07-01",
      "checkOutDate": "2026-07-03"
    }
  ]
}
```

### 请求字段

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| assignments | Array | ✅ | 酒店分配列表，不能为空 |
| assignments[].familyIndex | Integer | ✅ | 家庭序号（从1开始） |
| assignments[].hotelName | String | ✅ | 酒店名称 |
| assignments[].roomType | String | ✅ | 房型名称 |
| assignments[].checkInDate | String | ✅ | 入住日期（yyyy-MM-dd） |
| assignments[].checkOutDate | String | ✅ | 退房日期（yyyy-MM-dd） |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": null
}
```

### 前端实现建议
- 每个家庭一行，用 `el-form` 动态表单 + `el-button` 添加/删除行
- 酒店名称可用 `el-select` 远程搜索（调用资源服务酒店列表接口）
- 房型可联动酒店选择后加载（调用酒店房型列表接口）
- 日期用 `el-date-picker` 的 `daterange` 模式

---

## 接口 3：检测房型一致性

**使用场景**：配房前或配房后，检测当前房间配置是否与产品快照（下单时锁定的房型）一致，如不一致前端弹窗提示。

```
POST /admin/order/{orderId}/room-info/check-diff
```

### 请求参数

| 参数 | 类型 | 位置 | 必填 | 说明 |
|------|------|------|------|------|
| orderId | Long | 路径 | ✅ | 订单ID |

### 请求体

无（自动读取当前 roomInfo 与产品快照对比）

### 响应示例

一致时：
```json
{
  "code": 200,
  "message": "成功",
  "data": null
}
```

不一致时：
```json
{
  "code": 200,
  "message": "成功",
  "data": "产品快照为标准双人间×2，当前配置为大床房×2"
}
```

### 响应字段

| 字段 | 类型 | 说明 |
|------|------|------|
| data | String / null | null=一致；非null=不一致的描述文本 |

### 前端实现建议
- 配房保存后自动调用此接口
- 如返回非null，用 `el-message-box` 弹窗显示差异，按钮："已知晓，继续" / "返回修改"

---

## 接口 4：更新车辆信息（文本模式）

**使用场景**：配车管理员填写车辆分配的文字描述。

```
PUT /admin/order/{orderId}/vehicle-info
```

### 请求体

```json
{
  "vehicleInfo": "7座商务车1辆，司机张师傅，电话13800138000"
}
```

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| vehicleInfo | String | ✅ | 车辆信息描述，最长2000字 |

### 业务规则
- 仅限 **车务管理员** 或 **超级管理员** 操作
- 保存成功后自动完成 `ARRANGE_VEHICLE` 待办
- 系统会发送"车辆信息已更新"通知给用户

---

## 接口 5：分配车辆（结构化模式）

**使用场景**：详细分配车型、品牌、车牌号和司机信息。

```
PUT /admin/order/{orderId}/vehicle-assignment
```

### 请求体

```json
{
  "vehicleModel": "考斯特19座",
  "vehicleBrand": "丰田",
  "plateNumber": "蒙A88888",
  "driverName": "张师傅",
  "driverPhone": "13800138000"
}
```

### 请求字段

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| vehicleModel | String | ✅ | 车型名称（如：考斯特19座、别克GL8） |
| vehicleBrand | String | ✅ | 品牌（如：丰田、别克、奔驰） |
| plateNumber | String | 否 | 车牌号 |
| driverName | String | 否 | 司机姓名 |
| driverPhone | String | 否 | 司机电话 |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": null
}
```

### 前端实现建议
- 车型可用 `el-select` 加载资源服务车型列表（`GET /admin/vehicle/models/all`）
- 品牌联动车型选择后自动填充
- 司机信息手工填写

---

## 接口 6：检测车型一致性

**使用场景**：检测当前车辆配置与产品快照是否一致。

```
POST /admin/order/{orderId}/vehicle-info/check-diff
```

### 响应

同接口3，data 为 null 表示一致，非 null 为差异描述。

---

## 接口 7：保存交通信息

**使用场景**：为订单添加航班/火车等交通信息，支持多段行程。**全量替换**——每次保存会删除旧数据，以新提交的为准。

```
PUT /admin/order/{orderId}/transport
```

### 请求体

```json
{
  "segments": [
    {
      "transportType": "FLIGHT",
      "transportNo": "CA1234",
      "departStation": "北京首都T3",
      "arriveStation": "呼和浩特白塔",
      "departTime": "2026-07-01T08:30:00",
      "arriveTime": "2026-07-01T10:15:00",
      "remark": "请提前2小时到达机场"
    },
    {
      "transportType": "FLIGHT",
      "transportNo": "CA4321",
      "departStation": "呼和浩特白塔",
      "arriveStation": "北京首都T3",
      "departTime": "2026-07-07T18:00:00",
      "arriveTime": "2026-07-07T19:45:00",
      "remark": ""
    }
  ]
}
```

### 请求字段

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| segments | Array | ✅ | 交通分段列表，不能为空 |
| segments[].transportType | String | ✅ | 交通类型（字典 `transport_type`）：`FLIGHT`-航班、`TRAIN`-火车 |
| segments[].transportNo | String | 否 | 航班号/车次号，如 CA1234、G1234 |
| segments[].departStation | String | 否 | 出发站/出发机场 |
| segments[].arriveStation | String | 否 | 到达站/到达机场 |
| segments[].departTime | String | 否 | 出发时间（ISO格式：`yyyy-MM-ddTHH:mm:ss`） |
| segments[].arriveTime | String | 否 | 到达时间（ISO格式） |
| segments[].remark | String | 否 | 备注 |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "segmentId": "2026279738275856400",
      "orderId": "2026270000000000001",
      "transportType": "FLIGHT",
      "transportNo": "CA1234",
      "departStation": "北京首都T3",
      "arriveStation": "呼和浩特白塔",
      "departTime": "2026-07-01T08:30:00",
      "arriveTime": "2026-07-01T10:15:00",
      "remark": "请提前2小时到达机场",
      "sortOrder": 0
    }
  ]
}
```

### 响应字段

| 字段 | 类型 | 说明 |
|------|------|------|
| segmentId | String | 交通分段ID（雪花ID） |
| orderId | String | 订单ID |
| transportType | String | 交通类型 |
| transportNo | String | 航班号/车次号 |
| departStation | String | 出发站 |
| arriveStation | String | 到达站 |
| departTime | String | 出发时间 |
| arriveTime | String | 到达时间 |
| remark | String | 备注 |
| sortOrder | Integer | 排序序号（按提交顺序自动编号） |

### 前端实现建议
- 动态表单，每段一行，支持添加/删除
- `transportType` 用 `el-select` 绑定字典 `transport_type`
- 时间用 `el-date-picker` 的 `datetime` 模式
- 保存时将所有段一次性提交

---

## 接口 8：查询交通信息

**使用场景**：打开订单详情时加载已有的交通信息。

```
GET /admin/order/{orderId}/transport
```

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "segmentId": "2026279738275856400",
      "orderId": "2026270000000000001",
      "transportType": "FLIGHT",
      "transportNo": "CA1234",
      "departStation": "北京首都T3",
      "arriveStation": "呼和浩特白塔",
      "departTime": "2026-07-01T08:30:00",
      "arriveTime": "2026-07-01T10:15:00",
      "remark": "请提前2小时到达机场",
      "sortOrder": 0
    }
  ]
}
```

---

## 接口 9：推进内部流程

**使用场景**：每个阶段完成后，手动点击"推进到下一步"按钮。

```
PUT /admin/order/{orderId}/process-status
```

### 请求参数

| 参数 | 类型 | 位置 | 必填 | 说明 |
|------|------|------|------|------|
| orderId | Long | 路径 | ✅ | 订单ID |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": null
}
```

### 业务规则
- 按固定顺序推进：`PENDING_INFO → PROCESSING → PENDING_INSURANCE → PENDING_CONTRACT → PENDING_ROOM → PENDING_VEHICLE → PENDING_FINANCE → READY`
- 每次推进一步，不能跳步
- 自动完成对应阶段的待办

---

## 订单详情中的配房配车字段

订单详情接口 `GET /admin/order/{orderId}` 返回的 `OrderDetailVO` 包含以下相关字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| processStatus | String | 当前内部流程状态（见下方字典） |
| processStatusLabel | String | 流程状态中文标签 |
| roomInfo | String | 房间信息（JSON字符串或文本） |
| vehicleInfo | String | 车辆信息（JSON字符串或文本） |
| hotelAssignments | String | 酒店分配信息（JSON数组） |
| transportSegments | Array | 交通分段列表（结构化数据） |
| checklistConfirmed | Boolean | 清单是否已确认 |
| balancePayMethod | String | 尾款支付方式：`ONLINE`-线上微信支付，`OFFLINE`-线下转账 |

---

# 第二部分：合同管理

---

## 接口清单（合同管理）

| # | 接口 | 方法 | 路径 | 说明 |
|---|------|------|------|------|
| 10 | 获取合同平台列表 | GET | `/admin/contract/platforms` | 可用签约平台 |
| 11 | 获取旅行社列表 | GET | `/admin/contract/agencies` | 旅行社列表 |
| 12 | 获取合同模板列表 | GET | `/admin/contract/templates` | 合同模板列表 |
| 13 | 创建合同（电子签约） | POST | `/admin/contract/create` | 标准电子签约模式 |
| 14 | 报备合同（同步模式） | POST | `/admin/contract/report` | 线下签约后报备 |
| 15 | 合同列表 | GET | `/admin/contract/list` | 分页查询合同 |
| 16 | 合同详情 | GET | `/admin/contract/{id}` | 含出行人+状态日志 |
| 17 | 按订单查合同 | GET | `/admin/contract/by-order/{orderId}` | 查询订单所有合同 |
| 18 | 获取订单有效合同 | GET | `/admin/contract/active-by-order/{orderId}` | 获取最新非作废合同 |
| 19 | 刷新合同状态 | GET | `/admin/contract/{id}/status` | 从平台同步最新状态 |
| 20 | 作废合同 | POST | `/admin/contract/{id}/invalidate` | 作废合同 |
| 21 | 重发签署短信 | POST | `/admin/contract/{id}/resend-sms` | 重发签署链接短信 |
| 22 | 上传已签署PDF | POST | `/admin/contract/{id}/upload-pdf` | 同步模式上传PDF |
| 23 | 补充约定模板-列表(启用) | GET | `/admin/contract/clause-template/list` | 下拉选择用 |
| 24 | 补充约定模板-全部列表 | GET | `/admin/contract/clause-template/list-all` | 管理用 |
| 25 | 补充约定模板-创建 | POST | `/admin/contract/clause-template` | 创建模板 |
| 26 | 补充约定模板-更新 | PUT | `/admin/contract/clause-template/{id}` | 更新模板 |
| 27 | 补充约定模板-切换状态 | PUT | `/admin/contract/clause-template/{id}/toggle-status` | 启用/停用 |
| 28 | 补充约定模板-删除 | DELETE | `/admin/contract/clause-template/{id}` | 删除模板 |

---

## 接口 10：获取合同平台列表

**使用场景**：创建合同时，选择签约平台下拉框的数据源。

```
GET /admin/contract/platforms
```

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "code": "12301",
      "name": "全国旅游监管平台",
      "enabled": true
    },
    {
      "code": "FADADA",
      "name": "法大大",
      "enabled": true
    }
  ]
}
```

---

## 接口 11：获取旅行社列表

**使用场景**：创建合同时选择旅行社。

```
GET /admin/contract/agencies
```

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "code": "L-FJ-100005",
      "name": "内蒙古呼籁国际旅行社有限公司"
    }
  ]
}
```

---

## 接口 12：获取合同模板列表

**使用场景**：创建合同时选择模板。

```
GET /admin/contract/templates
```

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "templateId": "2026279738275856380",
      "templateCode": "TOURAGE_STANDARD",
      "templateName": "国内旅游标准合同",
      "platform": "12301",
      "mode": "STANDARD",
      "status": "ACTIVE",
      "description": "全国旅游监管平台标准旅游合同模板"
    }
  ]
}
```

### 响应字段

| 字段 | 类型 | 说明 |
|------|------|------|
| templateId | String | 模板ID |
| templateCode | String | 模板编码（创建合同时传此值） |
| templateName | String | 模板名称 |
| platform | String | 签约平台（字典 `contract_platform`） |
| mode | String | 签约模式：`STANDARD`-电子签约，`SYNC`-同步报备 |
| status | String | 模板状态：`ACTIVE`-启用，`INACTIVE`-停用 |
| description | String | 模板描述 |

---

## 接口 13：创建合同（电子签约模式）

**使用场景**：为订单创建电子合同，系统会调用第三方平台（如12301）生成签署链接，通过短信发送给签署人。

```
POST /admin/contract/create
```

### 请求体

```json
{
  "orderId": "2026270000000000001",
  "contractType": "TOUR",
  "platform": "12301",
  "templateCode": "TOURAGE_STANDARD",
  "agencyCode": "L-FJ-100005",
  "transactorName": "王经理",
  "transactorPhone": "13900139001",
  "destination": "呼伦贝尔",
  "routeName": "呼伦贝尔草原7日深度游",
  "days": 7,
  "nights": 6,
  "departureDate": "2026-07-01",
  "returnDate": "2026-07-07",
  "departureCity": "北京",
  "groupId": "GRP20260701001",
  "signatoryMode": 1,
  "signatoryName": "张三",
  "signatoryPhone": "13800138000",
  "signatoryIdType": 1,
  "signatoryIdNumber": "110101199001011234",
  "signingPlace": "北京市朝阳区",
  "adultCost": 5980.00,
  "childCost": 2990.00,
  "totalAmount": 14950.00,
  "paymentMethod": 2,
  "disputeResolution": 2,
  "leastCustomerNumber": 1,
  "contactName": "张三",
  "contactPhone": "13800138000",
  "supplementaryClause": "夏季草原行程补充约定...",
  "vehicleModel": "考斯特19座",
  "travelers": [
    {
      "name": "张三",
      "gender": "male",
      "age": 35,
      "idCardType": 1,
      "idCardNo": "110101199001011234",
      "phone": "13800138000",
      "isSigner": true,
      "isChild": false,
      "health": "健康"
    },
    {
      "name": "张小明",
      "gender": "male",
      "age": 8,
      "idCardType": 1,
      "idCardNo": "110101201801011234",
      "phone": "",
      "isSigner": false,
      "isChild": true,
      "health": "健康"
    }
  ]
}
```

### 请求字段

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| orderId | Long | 否 | 关联订单ID |
| contractType | String | 否 | 合同类型：`TOUR`-旅游合同（默认）、`INSURANCE`-保险单 |
| platform | String | 否 | 签约平台，不传则根据模板自动确定。字典 `contract_platform` |
| templateCode | String | ✅ | 模板编码（从接口12获取） |
| agencyCode | String | 否 | 旅行社编号，不填使用默认配置 |
| transactorName | String | 否 | 经办人姓名 |
| transactorPhone | String | 否 | 经办人电话 |
| destination | String | ✅ | 目的地 |
| routeName | String | ✅ | 线路名称 |
| days | Integer | 否 | 行程天数 |
| nights | Integer | 否 | 住宿晚数 |
| departureDate | String | ✅ | 出发日期（yyyy-MM-dd） |
| returnDate | String | ✅ | 返回日期（yyyy-MM-dd） |
| departureCity | String | 否 | 出发城市 |
| groupId | String | 否 | 团号 |
| signatoryMode | Integer | 否 | 签署模式：`1`-短信（默认）、`2`-现场、`3`-线下 |
| signatoryName | String | ✅ | 签署人姓名 |
| signatoryPhone | String | ✅ | 签署人电话 |
| signatoryIdType | Integer | 否 | 签署人证件类型：`1`-身份证（默认） |
| signatoryIdNumber | String | ✅ | 签署人证件号码 |
| signingPlace | String | 否 | 签约地点 |
| adultCost | BigDecimal | ✅ | 成人费用 |
| childCost | BigDecimal | 否 | 儿童费用 |
| totalAmount | BigDecimal | ✅ | 合同总金额 |
| paymentMethod | Integer | 否 | 付款方式：`1`-现金、`2`-转账（默认）、`3`-在线 |
| disputeResolution | Integer | 否 | 争议解决：`1`-仲裁、`2`-诉讼（默认） |
| leastCustomerNumber | Integer | 否 | 最低成团人数（默认1） |
| contactName | String | ✅ | 联系人姓名 |
| contactPhone | String | ✅ | 联系人电话 |
| supplementaryClause | String | 否 | 补充约定内容 |
| vehicleModel | String | 否 | 车型名称（产品快照） |
| travelers | Array | ✅ | 出行人列表（至少1人） |

### 出行人字段 (travelers[])

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | String | ✅ | 姓名 |
| gender | String | 否 | 性别：`male`-男、`female`-女 |
| age | Integer | 否 | 年龄 |
| idCardType | Integer | 否 | 证件类型：`1`-身份证（默认）、`2`-护照 |
| idCardNo | String | ✅ | 证件号码 |
| phone | String | 否 | 手机号 |
| isSigner | Boolean | 否 | 是否为签署人（默认false） |
| isChild | Boolean | 否 | 是否儿童（默认false） |
| health | String | 否 | 健康信息 |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "contractId": "2026279738275856500",
    "orderId": "2026270000000000001",
    "templateCode": "TOURAGE_STANDARD",
    "templateName": "国内旅游标准合同",
    "contractNumber": "CN20260701001",
    "platform": "12301",
    "contractType": "TOUR",
    "mode": "STANDARD",
    "status": "GENERATED",
    "statusLabel": "已生成",
    "signUrl": "https://12301.cn/sign/xxxx",
    "qrCodeUrl": "https://12301.cn/qr/xxxx",
    "fileUrl": null,
    "agencyCode": "L-FJ-100005",
    "travelAgencyName": "内蒙古呼籁国际旅行社有限公司",
    "destination": "呼伦贝尔",
    "departureDate": "2026-07-01",
    "returnDate": "2026-07-07",
    "totalAmount": 14950.00,
    "touristCount": 2,
    "contactName": "张三",
    "contactPhone": "13800138000",
    "createTime": "2026-03-19T10:30:00",
    "supplementaryClause": "夏季草原行程补充约定...",
    "travelers": [
      {
        "travelerId": "2026279738275856510",
        "name": "张三",
        "idCardType": "1",
        "idCardNo": "110101****1234",
        "phone": "138****8000",
        "isSigner": true
      }
    ],
    "statusLogs": [
      {
        "logId": "2026279738275856520",
        "oldStatus": "PENDING",
        "newStatus": "GENERATED",
        "source": "MANUAL",
        "createTime": "2026-03-19T10:30:00"
      }
    ]
  }
}
```

---

## 接口 14：报备合同（同步模式）

**使用场景**：线下已签署的合同，上传后同步报备到12301平台。请求结构与接口13相同，`signatoryMode` 默认为 `2`（现场）。

```
POST /admin/contract/report
```

请求字段同接口13。

---

## 接口 15：合同列表

**使用场景**：合同管理列表页分页查询。

```
GET /admin/contract/list?page=1&pageSize=20&orderId=2026270000000000001&status=SIGNED
```

### 请求参数

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| orderId | Long | 否 | 按订单ID过滤 |
| status | String | 否 | 按合同状态过滤 |
| platform | String | 否 | 按签约平台过滤 |
| page | Integer | 否 | 页码（默认1） |
| pageSize | Integer | 否 | 每页条数（默认20） |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "total": 15,
    "page": 1,
    "pageSize": 20,
    "records": [
      {
        "contractId": "2026279738275856500",
        "orderId": "2026270000000000001",
        "templateCode": "TOURAGE_STANDARD",
        "templateName": "国内旅游标准合同",
        "contractNumber": "CN20260701001",
        "platform": "12301",
        "contractType": "TOUR",
        "mode": "STANDARD",
        "status": "SIGNED",
        "statusLabel": "已签署",
        "signUrl": "https://12301.cn/sign/xxxx",
        "qrCodeUrl": null,
        "fileUrl": "https://12301.cn/file/xxxx.pdf",
        "destination": "呼伦贝尔",
        "departureDate": "2026-07-01",
        "returnDate": "2026-07-07",
        "totalAmount": 14950.00,
        "touristCount": 2,
        "contactName": "张三",
        "contactPhone": "13800138000",
        "createTime": "2026-03-19T10:30:00"
      }
    ]
  }
}
```

---

## 接口 16：合同详情

```
GET /admin/contract/{id}
```

返回 `ContractDetailVO`，在 `ContractVO` 基础上增加：

| 字段 | 类型 | 说明 |
|------|------|------|
| supplementaryClause | String | 补充约定内容 |
| travelers | Array | 出行人列表 |
| travelers[].travelerId | String | 出行人ID |
| travelers[].name | String | 姓名 |
| travelers[].idCardType | String | 证件类型 |
| travelers[].idCardNo | String | 证件号码（脱敏） |
| travelers[].phone | String | 手机号（脱敏） |
| travelers[].isSigner | Boolean | 是否签署人 |
| statusLogs | Array | 状态变更日志 |
| statusLogs[].logId | String | 日志ID |
| statusLogs[].oldStatus | String | 旧状态 |
| statusLogs[].newStatus | String | 新状态 |
| statusLogs[].source | String | 来源：`CALLBACK`-平台回调、`POLLING`-轮询、`MANUAL`-手动 |
| statusLogs[].createTime | String | 变更时间 |

---

## 接口 17-18：按订单查合同

```
GET /admin/contract/by-order/{orderId}        # 所有合同
GET /admin/contract/active-by-order/{orderId}  # 最新有效合同
```

---

## 接口 19：刷新合同状态

**使用场景**：等待签署中的合同，点击"刷新状态"从平台同步最新签署状态。

```
GET /admin/contract/{id}/status
```

返回更新后的 `ContractVO`。

---

## 接口 20：作废合同

```
POST /admin/contract/{id}/invalidate
```

### 业务规则
- 仅 `GENERATED` / `SIGNING` 状态的合同可以作废
- 作废后状态变为 `VOIDED`，不可恢复

---

## 接口 21：重发签署短信

```
POST /admin/contract/{id}/resend-sms
```

返回 `Boolean`，true=发送成功。

---

## 接口 22：上传已签署PDF（同步模式）

**使用场景**：同步模式的合同，上传线下签署的PDF文件。

```
POST /admin/contract/{id}/upload-pdf
Content-Type: multipart/form-data
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| file | File | ✅ | PDF文件 |

---

## 接口 23-28：补充约定模板管理

### 创建模板（接口25）

```
POST /admin/contract/clause-template
```

```json
{
  "name": "夏季小团补充约定",
  "content": "1. 如遇恶劣天气，行程可能调整...\n2. 草原蚊虫较多，建议自备驱蚊用品...",
  "sortOrder": 1
}
```

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | String | ✅ | 模板名称 |
| content | String | ✅ | 模板内容（支持换行） |
| sortOrder | Integer | 否 | 排序（升序，越小越前） |

### 模板VO响应

| 字段 | 类型 | 说明 |
|------|------|------|
| templateId | String | 模板ID |
| name | String | 模板名称 |
| content | String | 模板内容 |
| sortOrder | Integer | 排序 |
| status | String | 状态：`ACTIVE`-启用、`INACTIVE`-停用 |
| createTime | String | 创建时间 |

---

# 第三部分：保险管理

---

## 接口清单（保险管理）

| # | 接口 | 方法 | 路径 | 说明 |
|---|------|------|------|------|
| 29 | 保险产品列表 | GET | `/admin/insurance/products` | 已同步的保险产品分页列表 |
| 30 | 保险产品详情 | GET | `/admin/insurance/products/{id}` | 产品详情 |
| 31 | 保险计划及费率 | GET | `/admin/insurance/products/{id}/plans` | 获取产品下所有计划+费率表 |
| 32 | 同步保险产品 | POST | `/admin/insurance/sync-products` | 从保游网拉取最新产品 |
| 33 | 投保公司主体列表 | GET | `/admin/insurance/entities` | 投保公司列表 |
| 34 | 投保下单 | POST | `/admin/insurance/purchase` | 为出行人投保（每人独立保单） |
| 35 | 退保 | POST | `/admin/insurance/cancel/{id}` | 取消保险单 |
| 36 | 保险订单列表 | GET | `/admin/insurance/orders` | 分页查询保险订单 |
| 37 | 保险订单详情 | GET | `/admin/insurance/orders/{id}` | 含被保人列表 |
| 38 | 按订单查保险单 | GET | `/admin/insurance/orders/by-order/{orderId}` | 查询旅行订单的所有保险单 |
| 39 | 保险保障汇总 | GET | `/admin/insurance/coverage/{orderId}` | 订单保险覆盖情况 |
| 40 | 保费试算 | POST | `/admin/insurance/trial-price` | 本地费率试算保费 |
| 41 | 查询账户余额 | GET | `/admin/insurance/balance` | 保游网账户余额 |
| 42 | 下载保单PDF | GET | `/admin/insurance/policy/{id}/download` | Base64格式保单 |
| 43 | 在线充值 | POST | `/admin/insurance/recharge` | 保游网账户充值 |
| 44 | 分享保单 | POST | `/admin/insurance/share-policy` | 合并PDF上传OSS |
| 45 | 保险方案-活跃列表 | GET | `/admin/insurance/scheme/list` | 下拉选择用 |
| 46 | 保险方案-全部列表 | GET | `/admin/insurance/scheme/list-all` | 管理用 |
| 47 | 保险方案-详情 | GET | `/admin/insurance/scheme/{id}` | 方案详情含段 |
| 48 | 保险方案-创建 | POST | `/admin/insurance/scheme` | 创建方案模板 |
| 49 | 保险方案-更新 | PUT | `/admin/insurance/scheme/{id}` | 更新方案 |
| 50 | 保险方案-切换状态 | PUT | `/admin/insurance/scheme/{id}/toggle-status` | 启用/停用 |
| 51 | 保险方案-删除 | DELETE | `/admin/insurance/scheme/{id}` | 软删除 |
| 52 | 保险方案-预览应用 | POST | `/admin/insurance/scheme/preview-apply` | 预览方案应用到订单的效果 |

---

## 接口 29：保险产品列表

```
GET /admin/insurance/products?page=1&pageSize=20&isOverseas=false
```

### 请求参数

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | Integer | 否 | 页码（默认1） |
| pageSize | Integer | 否 | 每页条数（默认20） |
| isOverseas | Boolean | 否 | 是否境外产品：`false`-境内、`true`-境外 |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "total": 8,
    "page": 1,
    "pageSize": 20,
    "records": [
      {
        "productId": "2026279738275856385",
        "extProductGuid": "ABC-123-DEF",
        "productName": "畅游神州境内游（旅行社专享）",
        "companyName": "中国太平洋保险",
        "productType": "旅行险",
        "isOverseas": false,
        "minDays": 1,
        "maxDays": 90,
        "minAge": 0,
        "maxAge": 85,
        "status": "ACTIVE",
        "createTime": "2026-03-01T10:00:00"
      }
    ]
  }
}
```

### 响应字段

| 字段 | 类型 | 说明 |
|------|------|------|
| productId | String | 保险产品ID |
| extProductGuid | String | 保游网产品GUID |
| productName | String | 产品名称 |
| companyName | String | 保险公司名称 |
| productType | String | 产品类型 |
| isOverseas | Boolean | 是否境外产品 |
| minDays | Integer | 最短保障天数 |
| maxDays | Integer | 最长保障天数 |
| minAge | Integer | 最小投保年龄 |
| maxAge | Integer | 最大投保年龄 |
| status | String | 产品状态：`ACTIVE`-可用、`INACTIVE`-不可用 |
| createTime | String | 同步时间 |

---

## 接口 31：保险计划及费率

**使用场景**：选定产品后，展示该产品下所有计划及费率表，供管理员选择具体计划。

```
GET /admin/insurance/products/{id}/plans
```

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "planId": "2026279738275856390",
      "productId": "2026279738275856385",
      "extPlanGuid": "PLAN-001",
      "planName": "经济版",
      "securityList": "<p>意外伤害：10万元</p><p>意外医疗：1万元</p>",
      "rates": [
        {
          "rateId": "2026279738275856395",
          "extRateCode": "RATE-001",
          "ageRange": "0-85",
          "dayRange": "1-3",
          "dayType": 10,
          "premium": 5.00
        },
        {
          "rateId": "2026279738275856396",
          "extRateCode": "RATE-002",
          "ageRange": "0-85",
          "dayRange": "4-7",
          "dayType": 10,
          "premium": 10.00
        }
      ]
    }
  ]
}
```

### 响应字段

| 字段 | 类型 | 说明 |
|------|------|------|
| planId | String | 计划ID（投保时需传） |
| productId | String | 所属产品ID |
| planName | String | 计划名称（如：经济版、豪华版） |
| securityList | String | 保障内容（HTML格式） |
| rates | Array | 费率列表 |
| rates[].rateId | String | 费率ID |
| rates[].ageRange | String | 适用年龄范围（如 `0-85`） |
| rates[].dayRange | String | 适用天数范围（如 `4-7`） |
| rates[].dayType | Integer | 天数类型：`10`-按天、`20`-按月、`30`-按年 |
| rates[].premium | BigDecimal | 单人保费（元） |

### 前端实现建议
- 用 `el-table` 展示费率表格
- `securityList` 用 `v-html` 渲染
- 用户选择计划后，根据行程天数和年龄自动匹配费率

---

## 接口 34：投保下单

**使用场景**：为订单的出行人投保。系统会为每个被保人创建独立保单，调用保游网API完成投保。

```
POST /admin/insurance/purchase
```

### 请求体

```json
{
  "orderId": "2026270000000000001",
  "planId": "2026279738275856390",
  "startDate": "2026-07-01",
  "endDate": "2026-07-07",
  "entityCode": "hulai",
  "remark": "草原7日游保险",
  "insuredPersons": [
    {
      "name": "张三",
      "idCardType": "ID_CARD",
      "idCardNo": "110101199001011234",
      "birthday": "1990-01-01",
      "gender": "MALE",
      "phone": "13800138000",
      "isBeneficiary": false
    },
    {
      "name": "李四",
      "idCardType": "ID_CARD",
      "idCardNo": "110101199201011234",
      "birthday": "1992-01-01",
      "gender": "FEMALE",
      "phone": "13900139000",
      "isBeneficiary": false
    }
  ]
}
```

### 请求字段

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| orderId | Long | 否 | 关联旅行订单ID |
| planId | Long | ✅ | 保险计划ID（从接口31获取） |
| startDate | String | ✅ | 保障开始日期（yyyy-MM-dd） |
| endDate | String | ✅ | 保障结束日期（yyyy-MM-dd） |
| entityCode | String | 否 | 投保公司主体编码（从接口33获取，不填使用默认） |
| remark | String | 否 | 备注 |
| insuredPersons | Array | ✅ | 被保人列表（至少1人） |

### 被保人字段 (insuredPersons[])

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | String | ✅ | 姓名 |
| idCardType | String | 否 | 证件类型（默认 `ID_CARD`）：`ID_CARD`-身份证、`PASSPORT`-护照、`OTHER`-其他 |
| idCardNo | String | ✅ | 证件号码 |
| birthday | String | ✅ | 出生日期（yyyy-MM-dd） |
| gender | String | ✅ | 性别：`MALE`-男、`FEMALE`-女 |
| phone | String | 否 | 手机号 |
| isBeneficiary | Boolean | 否 | 是否为受益人（默认false） |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "insuranceOrderId": "2026279738275856400",
      "orderId": "2026270000000000001",
      "productId": "2026279738275856385",
      "productName": "畅游神州境内游（旅行社专享）",
      "planId": "2026279738275856390",
      "planName": "经济版",
      "extOrderNo": "BYW202607010001",
      "extPolicyNo": "P202607010001",
      "totalPremium": 10.00,
      "insuredCount": 1,
      "policyHolderName": "内蒙古呼籁国际旅行社有限公司",
      "entityCode": "hulai",
      "startDate": "2026-07-01",
      "endDate": "2026-07-07",
      "status": "INSURED",
      "statusLabel": "已承保",
      "remark": "草原7日游保险",
      "createTime": "2026-03-19T10:30:00",
      "insuredPersons": [
        {
          "insuredId": "2026279738275856410",
          "name": "张三",
          "idCardType": "ID_CARD",
          "idCardNo": "110101****1234",
          "birthday": "1990-01-01",
          "gender": "MALE",
          "phone": "138****8000",
          "isBeneficiary": false
        }
      ]
    }
  ]
}
```

**注意**：返回的是**数组**，因为每个被保人一张独立保单。

---

## 接口 35：退保

```
POST /admin/insurance/cancel/{id}
```

| 参数 | 类型 | 位置 | 必填 | 说明 |
|------|------|------|------|------|
| id | Long | 路径 | ✅ | 保险订单ID |

返回退保后的 `InsuranceOrderVO`，状态变为 `CANCELLED`。

---

## 接口 36：保险订单列表

```
GET /admin/insurance/orders?page=1&pageSize=20&orderId=2026270000000000001&status=INSURED
```

### 请求参数

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| orderId | Long | 否 | 按旅行订单ID过滤 |
| status | String | 否 | 按状态过滤 |
| page | Integer | 否 | 页码（默认1） |
| pageSize | Integer | 否 | 每页条数（默认20，最大100） |

---

## 接口 37：保险订单详情

```
GET /admin/insurance/orders/{id}
```

返回 `InsuranceOrderDetailVO`，在 `InsuranceOrderVO` 基础上增加 `insuredPersons` 被保人列表。

---

## 接口 39：保险保障汇总

**使用场景**：在订单详情中展示保险覆盖情况。

```
GET /admin/insurance/coverage/{orderId}
```

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "orderId": "2026270000000000001",
    "orderCount": 3,
    "insuranceOrders": [
      {
        "insuranceOrderId": "2026279738275856400",
        "productName": "畅游神州境内游（旅行社专享）",
        "planName": "经济版",
        "status": "INSURED",
        "statusLabel": "已承保",
        "totalPremium": 10.00,
        "startDate": "2026-07-01",
        "endDate": "2026-07-07",
        "insuredPersons": [
          {
            "name": "张三",
            "idCardType": "ID_CARD",
            "idCardNo": "110101****1234"
          }
        ]
      }
    ]
  }
}
```

---

## 接口 40：保费试算

**使用场景**：投保前预估保费。

```
POST /admin/insurance/trial-price
```

### 请求体

```json
{
  "planId": "2026279738275856390",
  "startDate": "2026-07-01",
  "endDate": "2026-07-07",
  "insuredCount": 4
}
```

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| planId | Long | ✅ | 保险计划ID |
| startDate | String | ✅ | 保障开始日期 |
| endDate | String | ✅ | 保障结束日期 |
| insuredCount | Integer | ✅ | 被保人数量 |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "unitPremium": 10.00,
    "totalPremium": 40.00,
    "coverageDays": 7,
    "insuredCount": 4
  }
}
```

---

## 接口 41：查询账户余额

```
GET /admin/insurance/balance
```

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "Data": "1256.50"
  }
}
```

---

## 接口 43：在线充值

```
POST /admin/insurance/recharge
```

### 请求体

```json
{
  "money": 500.00,
  "payType": 33
}
```

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| money | BigDecimal | ✅ | 充值金额（元），最小0.01 |
| payType | Integer | ✅ | 支付方式：`11`-支付宝扫码、`33`-微信扫码 |
| backUrl | String | 否 | 同步回调地址（支付宝用） |

---

## 接口 44：分享保单

**使用场景**：合并指定被保人的所有保单PDF，上传OSS生成分享链接。

```
POST /admin/insurance/share-policy
```

### 请求体

```json
{
  "orderId": "2026270000000000001",
  "insuredName": "张三",
  "insuredIdCardNo": "110101199001011234"
}
```

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| orderId | Long | ✅ | 旅游订单ID |
| insuredName | String | ✅ | 被保人姓名 |
| insuredIdCardNo | String | ✅ | 被保人证件号码（明文） |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "pdfBase64": "JVBERi0xLjQK...",
    "personName": "张三",
    "policyCount": 2,
    "failedCount": 0
  }
}
```

---

## 接口 48：创建保险方案模板

**使用场景**：创建可复用的保险方案，适用于不同行程的保险配置模板。方案由多个"段"组成，每段对应一个保险产品+计划，覆盖行程中的某几天。

```
POST /admin/insurance/scheme
```

### 请求体

```json
{
  "name": "草原7日标准方案",
  "description": "适用于7日国内草原行程，前段境内险+后段高原险",
  "isOverseas": false,
  "sortOrder": 0,
  "segments": [
    {
      "segmentName": "前段-境内险",
      "dayOffsetStart": 1,
      "dayOffsetEnd": 4,
      "productId": "2026279738275856385",
      "planId": "2026279738275856390",
      "sortOrder": 0
    },
    {
      "segmentName": "后段-高原险",
      "dayOffsetStart": 5,
      "dayOffsetEnd": -1,
      "productId": "2026279738275856386",
      "planId": "2026279738275856391",
      "sortOrder": 1
    }
  ]
}
```

### 请求字段

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | String | ✅ | 方案名称（最长100字符） |
| description | String | 否 | 方案描述（最长500字符） |
| isOverseas | Boolean | ✅ | 是否境外：`false`-境内、`true`-境外 |
| sortOrder | Integer | 否 | 排序序号（越小越靠前） |
| segments | Array | ✅ | 保险段列表（至少1个） |

### 方案段字段 (segments[])

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| segmentName | String | 否 | 段名称（最长100字符） |
| dayOffsetStart | Integer | ✅ | 相对起始天（1-based，1=出发日） |
| dayOffsetEnd | Integer | ✅ | 相对结束天（1-based，**-1 表示行程最后一天**） |
| productId | Long | ✅ | 保险产品ID |
| planId | Long | ✅ | 保险计划ID |
| sortOrder | Integer | 否 | 段排序序号 |

### 响应 InsuranceSchemeVO

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "schemeId": "2026279738275856500",
    "name": "草原7日标准方案",
    "description": "适用于7日国内草原行程",
    "isOverseas": false,
    "sortOrder": 0,
    "status": "ACTIVE",
    "adminId": "100",
    "createTime": "2026-03-19T10:30:00",
    "segmentCount": 2,
    "segments": [
      {
        "segmentId": "2026279738275856510",
        "segmentName": "前段-境内险",
        "dayOffsetStart": 1,
        "dayOffsetEnd": 4,
        "productId": "2026279738275856385",
        "productName": "畅游神州境内游（旅行社专享）",
        "planId": "2026279738275856390",
        "planName": "经济版",
        "sortOrder": 0
      }
    ]
  }
}
```

---

## 接口 52：预览应用方案

**使用场景**：选择方案后，预览应用到某个订单的效果。系统会将方案中的相对天数解析为实际日期，并计算预估保费。

```
POST /admin/insurance/scheme/preview-apply
```

### 请求体

```json
{
  "schemeId": "2026279738275856500",
  "orderId": "2026270000000000001",
  "departureDate": "2026-07-01",
  "returnDate": "2026-07-07",
  "insuredCount": 4
}
```

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| schemeId | Long | ✅ | 方案ID |
| orderId | Long | ✅ | 订单ID（获取出行信息） |
| departureDate | String | 否 | 出发日期（不传从订单获取） |
| returnDate | String | 否 | 返回日期（不传从订单获取） |
| insuredCount | Integer | 否 | 被保人数量（不传从订单获取，最小1） |

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "schemeId": "2026279738275856500",
    "schemeName": "草原7日标准方案",
    "departureDate": "2026-07-01",
    "returnDate": "2026-07-07",
    "tripDays": 7,
    "insuredCount": 4,
    "totalEstimatedPremium": 320.00,
    "segments": [
      {
        "segmentName": "前段-境内险",
        "dayOffsetStart": 1,
        "dayOffsetEnd": 4,
        "resolvedStartDate": "2026-07-01",
        "resolvedEndDate": "2026-07-04",
        "coverageDays": 4,
        "productId": "2026279738275856385",
        "productName": "畅游神州境内游（旅行社专享）",
        "planId": "2026279738275856390",
        "planName": "经济版",
        "unitPremium": 10.00,
        "estimatedPremium": 40.00,
        "skipped": false,
        "skipReason": null
      },
      {
        "segmentName": "后段-高原险",
        "dayOffsetStart": 5,
        "dayOffsetEnd": -1,
        "resolvedStartDate": "2026-07-05",
        "resolvedEndDate": "2026-07-07",
        "coverageDays": 3,
        "productId": "2026279738275856386",
        "productName": "高原专享旅行险",
        "planId": "2026279738275856391",
        "planName": "标准版",
        "unitPremium": 40.00,
        "estimatedPremium": 160.00,
        "skipped": false,
        "skipReason": null
      }
    ],
    "warnings": []
  }
}
```

### 响应字段

| 字段 | 类型 | 说明 |
|------|------|------|
| schemeId | String | 方案ID |
| schemeName | String | 方案名称 |
| departureDate | String | 行程出发日期 |
| returnDate | String | 行程返回日期 |
| tripDays | Integer | 行程天数 |
| insuredCount | Integer | 被保人数量 |
| totalEstimatedPremium | BigDecimal | 预估总保费 |
| segments | Array | 解析后的保险段 |
| segments[].segmentName | String | 段名称 |
| segments[].resolvedStartDate | String | 实际开始日期 |
| segments[].resolvedEndDate | String | 实际结束日期 |
| segments[].coverageDays | Integer | 覆盖天数 |
| segments[].productName | String | 保险产品名称 |
| segments[].planName | String | 保险计划名称 |
| segments[].unitPremium | BigDecimal | 单人保费 |
| segments[].estimatedPremium | BigDecimal | 段预估总保费（单人×人数） |
| segments[].skipped | Boolean | 是否跳过（超出行程范围） |
| segments[].skipReason | String | 跳过原因 |
| warnings | Array | 警告列表（日期间隙、重叠等） |

### 前端实现建议
- 选择方案后自动调用此接口展示预览
- 用 `el-table` 展示保险段，高亮被跳过的段
- `warnings` 不为空时用 `el-alert` 展示警告
- 预览确认后，再逐段调用投保接口（接口34）下单

---

# 关联字典

| 字典类型 | 字典值 | 中文标签 | 说明 |
|----------|--------|----------|------|
| **process_status** | PENDING_INFO | 待补全信息 | 订单内部流程 |
| | PROCESSING | 待内部流程 | |
| | PENDING_INSURANCE | 待配保险 | |
| | PENDING_CONTRACT | 待签合同 | |
| | PENDING_ROOM | 待配房 | |
| | PENDING_VEHICLE | 待配车 | |
| | PENDING_FINANCE | 待核算 | |
| | READY | 就绪 | |
| | INTERNAL_CONFIRMED | 内部已确认 | |
| **transport_type** | FLIGHT | 航班 | 交通类型 |
| | TRAIN | 火车 | |
| **contract_platform** | 12301 | 全国旅游监管平台 | 签约平台 |
| | FADADA | 法大大 | |
| | ESIGN | e签宝 | |
| | TENCENT_ESS | 腾讯电子签 | |
| **contract_type** | TOUR | 旅游合同 | 合同类型 |
| | INSURANCE | 保险单 | |
| **contract_mode** | STANDARD | 电子签约 | 签约模式 |
| | SYNC | 同步报备 | |
| **contract_status** | PENDING | 待生成 | 合同状态 |
| | GENERATED | 已生成 | 签署URL已生成 |
| | SIGNING | 签署中 | 出行人正在签署 |
| | SIGNED | 已签署 | 签署完成 |
| | VOIDING | 作废中 | 作废请求已发送 |
| | VOIDED | 已作废 | 不可恢复 |
| | REPORTED | 已上报 | 同步模式报备完成 |
| | UPLOADED | 已上传 | 同步模式PDF已上传 |
| **insurance_status** | PENDING | 待出单 | 保险订单状态 |
| | INSURED | 已承保 | |
| | CANCELLED | 已退保 | |
| | FAILED | 投保失败 | |
| **id_card_type** | ID_CARD | 身份证 | 证件类型（保险） |
| | PASSPORT | 护照 | |
| | OTHER | 其他 | |
| **gender** | MALE | 男 | 性别（保险） |
| | FEMALE | 女 | |
| **common_status** | ACTIVE | 启用 | 通用状态 |
| | INACTIVE | 停用 | |
| **balance_pay_method** | ONLINE | 线上微信支付 | 尾款支付方式 |
| | OFFLINE | 线下转账 | |

---

# 页面布局建议

## 订单详情页 - 配房配车Tab

```
┌─────────────────────────────────────────────────────┐
│  订单详情 > 配房配车                                    │
├─────────────────────────────────────────────────────┤
│  当前流程状态：[待配房] ──── [待配车] ──── [待核算]        │
│                  ✅           ○            ○          │
├─────────────────────────────────────────────────────┤
│  🏨 房间分配                            [检测一致性]   │
│  ┌───────────────────────────────────────────────┐  │
│  │ 模式切换: ○ 文本模式  ○ 结构化模式              │  │
│  │                                               │  │
│  │ [文本模式] 房间信息: [__________________]      │  │
│  │                                               │  │
│  │ [结构化模式]                                   │  │
│  │  家庭1: 酒店[____] 房型[____] 入住[__] 退房[__]│  │
│  │  家庭2: 酒店[____] 房型[____] 入住[__] 退房[__]│  │
│  │  [+ 添加家庭]                                 │  │
│  └───────────────────────────────────────────────┘  │
│                                        [保存房间信息]  │
├─────────────────────────────────────────────────────┤
│  🚗 车辆分配                            [检测一致性]   │
│  ┌───────────────────────────────────────────────┐  │
│  │ 车型: [____] 品牌: [____] 车牌: [____]        │  │
│  │ 司机: [____] 电话: [____]                     │  │
│  └───────────────────────────────────────────────┘  │
│                                        [保存车辆信息]  │
├─────────────────────────────────────────────────────┤
│  ✈️ 交通信息                                        │
│  ┌───────────────────────────────────────────────┐  │
│  │ #1 类型[航班▼] 班次[CA1234] 出发[北京T3]       │  │
│  │    到达[呼和浩特] 出发时间[____] 到达时间[____]   │  │
│  │ #2 类型[火车▼] 班次[G1234] ...               │  │
│  │ [+ 添加交通段]                                │  │
│  └───────────────────────────────────────────────┘  │
│                                        [保存交通信息]  │
├─────────────────────────────────────────────────────┤
│                              [推进到下一步 →]         │
└─────────────────────────────────────────────────────┘
```

## 合同管理页

```
┌─────────────────────────────────────────────────────┐
│  合同管理                                [+ 创建合同]  │
├─────────────────────────────────────────────────────┤
│  过滤: 订单号[____] 状态[全部▼] 平台[全部▼]           │
├─────────────────────────────────────────────────────┤
│  合同编号    | 平台   | 类型 | 状态   | 金额   | 操作  │
│  CN2026...  | 12301  | 旅游 | 已签署 | ¥14950 | 详情  │
│  CN2026...  | 法大大  | 旅游 | 签署中 | ¥5000  | 刷新  │
│                                                     │
│  [< 1 2 3 >]                                        │
└─────────────────────────────────────────────────────┘
```

## 创建合同弹窗（管理员自主选择平台）

管理员可以自主选择在哪个合同平台签署，前端页面需提供平台选择下拉框。

```
┌──────────────────── 创建合同 ────────────────────────┐
│                                                      │
│  签约平台:  [全国旅游监管平台 ▼]                       │
│             ┌──────────────────────┐                  │
│             │ ● 全国旅游监管平台     │ ← available=true │
│             │ ● 法大大              │ ← available=true │
│             │ ○ e签宝 (未开通)      │ ← available=false│
│             │ ○ 腾讯电子签 (未开通)  │ ← available=false│
│             └──────────────────────┘                  │
│  合同模板:  [国内旅游标准合同 ▼]   ← 根据选中平台过滤   │
│  旅行社:    [呼籁国际旅行社 ▼]                         │
│  签署模式:  ○ 短信签署  ○ 现场签署  ○ 线下签署         │
│                                                      │
│  ─── 合同信息 ───                                     │
│  目的地:    [________]    线路名称: [________]         │
│  出发日期:  [____-__-__]  返回日期: [____-__-__]       │
│  出发城市:  [________]    行程天数: [__]               │
│                                                      │
│  ─── 签署人信息 ───                                   │
│  签署人:    [________]    电话: [________]             │
│  证件号:    [________________]                        │
│                                                      │
│  ─── 费用信息 ───                                     │
│  成人费用:  [________]    儿童费用: [________]         │
│  合同总额:  [________]                                │
│                                                      │
│  ─── 出行人列表 ───                                   │
│  姓名       | 证件号        | 性别 | 年龄 | 签署人     │
│  张三       | 110101...1234 | 男   | 36   | ✓        │
│  李四       | 110101...5678 | 女   | 34   |          │
│  [+ 添加出行人]                                       │
│                                                      │
│                         [取消]  [确认创建]              │
└──────────────────────────────────────────────────────┘
```

### 平台选择交互逻辑

1. **数据源**: 调用 `GET /admin/contract/platforms`，返回每个平台的 `code`/`name`/`enabled`
2. **下拉显示**: `enabled=true` 的平台可选，`enabled=false` 的灰显并标注"未开通"
3. **模板联动**: 选择平台后，从 `GET /admin/contract/templates` 返回的模板列表中过滤出 `platform` 匹配的模板
4. **提交**: 创建合同请求中的 `platform` 字段传所选平台的 `code` 值。不传则后端根据所选模板自动确定
5. **默认值**: 可默认选中第一个 `enabled=true` 的平台

## 保险管理页

```
┌─────────────────────────────────────────────────────┐
│  保险管理                                            │
│  Tab: [保险投保] [保险方案] [账户管理]                  │
├─────────────────────────────────────────────────────┤
│  [保险投保Tab]                                       │
│  ① 选择产品: [畅游神州境内游▼]                         │
│  ② 选择计划: [经济版▼]  费率: 1-3天 ¥5/人             │
│  ③ 保障日期: [2026-07-01] 至 [2026-07-07]            │
│  ④ 试算保费: [试算] → 单人¥10 × 4人 = 总计¥40        │
│  ⑤ 被保人:                                          │
│     [√] 张三  110101...1234  1990-01-01  男           │
│     [√] 李四  110101...5678  1992-01-01  女           │
│  ⑥ [确认投保]                                        │
├─────────────────────────────────────────────────────┤
│  [保险方案Tab]                                       │
│  方案名称  | 境内/外 | 段数 | 状态 | 操作              │
│  草原7日   | 境内    |  2  | 启用 | 编辑 预览 停用      │
│  海岛5日   | 境外    |  1  | 启用 | 编辑 预览 停用      │
│  [+ 创建方案]                                        │
├─────────────────────────────────────────────────────┤
│  [账户管理Tab]                                       │
│  当前余额: ¥1,256.50          [刷新]                  │
│  充值: 金额[____] 方式[微信▼]  [充值]                  │
└─────────────────────────────────────────────────────┘
```

---

# 完整调用示例

## 示例1：配房流程

```javascript
// 1. 结构化分配酒店
await request.put(`/admin/order/${orderId}/hotel-assignment`, {
  assignments: [
    { familyIndex: 1, hotelName: '香格里拉', roomType: '豪华大床房', checkInDate: '2026-07-01', checkOutDate: '2026-07-03' },
    { familyIndex: 2, hotelName: '香格里拉', roomType: '标准双人间', checkInDate: '2026-07-01', checkOutDate: '2026-07-03' }
  ]
})

// 2. 检测一致性
const diff = await request.post(`/admin/order/${orderId}/room-info/check-diff`)
if (diff.data) {
  ElMessageBox.confirm(diff.data, '房型不一致提醒', { confirmButtonText: '已知晓', cancelButtonText: '返回修改' })
}

// 3. 推进流程到"待配车"
await request.put(`/admin/order/${orderId}/process-status`)
```

## 示例2：创建合同

```javascript
// 1. 获取模板列表
const templates = await request.get('/admin/contract/templates')

// 2. 获取补充约定模板
const clauses = await request.get('/admin/contract/clause-template/list')

// 3. 创建合同
const contract = await request.post('/admin/contract/create', {
  orderId,
  templateCode: 'TOURAGE_STANDARD',
  destination: '呼伦贝尔',
  routeName: '呼伦贝尔草原7日深度游',
  departureDate: '2026-07-01',
  returnDate: '2026-07-07',
  signatoryName: '张三',
  signatoryPhone: '13800138000',
  signatoryIdNumber: '110101199001011234',
  adultCost: 5980.00,
  totalAmount: 14950.00,
  contactName: '张三',
  contactPhone: '13800138000',
  supplementaryClause: clauses.data[0]?.content || '',
  travelers: orderTravelers.map(t => ({
    name: t.name,
    idCardNo: t.idCardNo,
    phone: t.phone,
    isSigner: t.name === '张三'
  }))
})

// 4. 如状态为GENERATED，可展示签署链接或二维码
if (contract.data.signUrl) {
  // 展示签署链接或二维码
}
```

## 示例3：方案化投保

```javascript
// 1. 选择方案
const schemes = await request.get('/admin/insurance/scheme/list')

// 2. 预览方案应用效果
const preview = await request.post('/admin/insurance/scheme/preview-apply', {
  schemeId: schemes.data[0].schemeId,
  orderId
})

// 3. 展示预览：预估总保费、各段明细
console.log(`预估总保费: ¥${preview.data.totalEstimatedPremium}`)

// 4. 确认后，逐段投保
for (const segment of preview.data.segments) {
  if (segment.skipped) continue
  await request.post('/admin/insurance/purchase', {
    orderId,
    planId: segment.planId,
    startDate: segment.resolvedStartDate,
    endDate: segment.resolvedEndDate,
    insuredPersons: travelers.map(t => ({
      name: t.name,
      idCardNo: t.idCardNo,
      birthday: t.birthday,
      gender: t.gender,
      phone: t.phone
    }))
  })
}
```

---

🤖 Generated with [Claude Code](https://claude.com/claude-code)
