# 合同管理完整对接方案

> 日期: 2026-03-19 | 服务: hl-contract-service

---

## 一、功能说明

订单确认后，定制师需要为订单签订旅游合同。支持两种模式：
- **标准模式（STANDARD）**：电子签，平台自动发送签署短信给出行人，出行人在线签署
- **同步模式（SYNC）**：线下签，管理员上传已签署的PDF文件

### 合同状态流转

```
标准模式（电子签）:
  创建 → GENERATED(已生成) → SIGNING(签署中) → SIGNED(已签署)
                                                    ↓
                                             VOIDED(已作废) ← VOIDING(作废中)

同步模式（线下签）:
  创建 → REPORTED(已上报) → UPLOADED(已上传PDF)
                              ↓
                       VOIDED(已作废) ← VOIDING(作废中)
```

### 关联字典

**`contract_status`（合同状态）**：

| dict_value | dict_label |
|-----------|-----------|
| PENDING | 待生成 |
| GENERATED | 已生成 |
| SIGNING | 签署中 |
| SIGNED | 已签署 |
| REPORTED | 已上报 |
| UPLOADED | 已上传 |
| VOIDING | 作废中 |
| VOIDED | 已作废 |

**`contract_platform`（签约平台）**：

| dict_value | dict_label |
|-----------|-----------|
| 12301 | 全国旅游监管平台 |
| FADADA | 法大大 |

**`contract_type`（合同类型）**：

| dict_value | dict_label |
|-----------|-----------|
| TOUR | 旅游合同 |
| INSURANCE | 保险单 |

---

## 二、接口清单

| # | 方法 | 路径 | 说明 | 使用场景 |
|---|------|------|------|---------|
| 1 | POST | `/admin/contract/create` | 创建电子签合同 | 订单详情 → 去签合同 |
| 2 | POST | `/admin/contract/report` | 报备合同（线下签） | 订单详情 → 线下签合同 |
| 3 | GET | `/admin/contract/list` | 合同列表（分页） | 合同管理页 |
| 4 | GET | `/admin/contract/{id}` | 合同详情 | 合同详情页 |
| 5 | GET | `/admin/contract/by-order/{orderId}` | 按订单查合同 | 订单详情 → 合同信息 |
| 6 | GET | `/admin/contract/active-by-order/{orderId}` | 订单有效合同 | 订单详情 → 当前合同 |
| 7 | GET | `/admin/contract/{id}/status` | 刷新合同状态 | 合同详情 → 刷新 |
| 8 | POST | `/admin/contract/{id}/invalidate` | 作废合同 | 合同详情 → 作废 |
| 9 | POST | `/admin/contract/{id}/resend-sms` | 重发签署短信 | 合同详情 → 重发 |
| 10 | POST | `/admin/contract/{id}/upload-pdf` | 上传签署PDF | 同步模式 → 上传 |
| 11 | GET | `/admin/contract/templates` | 合同模板列表 | 创建合同弹窗 → 模板选择 |
| 12 | GET | `/admin/contract/platforms` | 可用平台列表 | 创建合同弹窗 → 平台选择 |
| 13 | GET | `/admin/contract/agencies` | 旅行社列表 | 创建合同弹窗 → 旅行社选择 |
| 14 | GET | `/admin/contract/clause-template/list` | 补充约定模板 | 创建合同弹窗 → 补充约定 |

### 补充约定模板管理

| # | 方法 | 路径 | 说明 |
|---|------|------|------|
| 15 | GET | `/admin/contract/clause-template/list-all` | 全部模板（含停用） |
| 16 | POST | `/admin/contract/clause-template` | 创建模板 |
| 17 | PUT | `/admin/contract/clause-template/{id}` | 更新模板 |
| 18 | PUT | `/admin/contract/clause-template/{id}/toggle-status` | 启用/停用 |
| 19 | DELETE | `/admin/contract/clause-template/{id}` | 删除模板 |

---

## 三、核心接口详细定义

### 1. 创建电子签合同（标准模式）

**接口**：`POST /admin/contract/create`

**使用场景**：订单详情页点击「去签合同」，弹窗填写信息后提交

**请求体**：
```json
{
  "orderId": "2031625428892893186",
  "contractType": "TOUR",
  "templateCode": "TOURAGE_STANDARD",
  "destination": "哈尔滨-亚布力-雪乡",
  "routeName": "嗨·冰雪3.0南线（6天5晚）",
  "departureDate": "2026-03-11",
  "returnDate": "2026-03-16",
  "days": 6,
  "nights": 5,
  "departureCity": "哈尔滨",
  "signatoryName": "张三",
  "signatoryPhone": "13800138000",
  "signatoryIdNumber": "110101199001011234",
  "signatoryMode": 1,
  "contactName": "OOPS",
  "contactPhone": "17600545225",
  "adultCost": 5700.00,
  "childCost": 0,
  "totalAmount": 5700.00,
  "paymentMethod": 3,
  "disputeResolution": 2,
  "supplementaryClause": "冬季出行补充约定...",
  "travelers": [
    {
      "name": "张三",
      "idCardNo": "110101199001011234",
      "phone": "13800138000",
      "idCardType": 1,
      "isSigner": true,
      "isChild": false
    }
  ]
}
```

**请求字段说明**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| orderId | String | 是 | 订单ID |
| contractType | String | 否 | 合同类型，默认 `TOUR`。可选：`TOUR`（旅游合同）/ `INSURANCE`（保险单） |
| templateCode | String | 是 | 模板编码，从模板列表接口获取 |
| platform | String | 否 | 平台，不传则由模板自动确定 |
| agencyCode | String | 否 | 旅行社编号，不传用系统默认值 |
| destination | String | 是 | 目的地 |
| routeName | String | 是 | 线路名称（产品名称） |
| departureDate | String | 是 | 出发日期，格式 `yyyy-MM-dd` |
| returnDate | String | 是 | 返回日期，格式 `yyyy-MM-dd` |
| days | Integer | 否 | 行程天数（不传自动计算） |
| nights | Integer | 否 | 住宿晚数（不传自动计算） |
| departureCity | String | 否 | 出发城市 |
| signatoryName | String | 是 | **签署人姓名**（出行人中 isSigner=true 的那位） |
| signatoryPhone | String | 是 | **签署人电话**（平台发短信用） |
| signatoryIdNumber | String | 是 | **签署人证件号码** |
| signatoryIdType | Integer | 否 | 签署人证件类型：1=身份证（默认）|
| signatoryMode | Integer | 否 | 签署模式：1=短信签署（默认）2=现场 3=线下 |
| contactName | String | 是 | 联系人姓名（可与签署人不同） |
| contactPhone | String | 是 | 联系人电话 |
| adultCost | Number | 是 | 成人费用（单价） |
| childCost | Number | 否 | 儿童费用（单价） |
| totalAmount | Number | 是 | 合同总金额 |
| paymentMethod | Integer | 否 | 付款方式：1=现金 2=转账 3=在线支付（默认2） |
| disputeResolution | Integer | 否 | 争议解决：1=仲裁 2=诉讼（默认2） |
| leastCustomerNumber | Integer | 否 | 最低成团人数（默认1） |
| supplementaryClause | String | 否 | 补充约定内容 |
| travelers | Array | 是 | 出行人列表，至少1人 |

**出行人（travelers）字段**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | String | 是 | 姓名 |
| idCardNo | String | 是 | 证件号码 |
| phone | String | 签署人必填 | 手机号（签署人需要接收短信） |
| idCardType | Integer | 否 | 证件类型：1=身份证（默认）2=护照 |
| isSigner | Boolean | 否 | 是否签署人（默认false），至少1人为true |
| isChild | Boolean | 否 | 是否儿童（默认false） |
| gender | String | 否 | 性别：male/female |
| age | Integer | 否 | 年龄 |

**响应示例**：
```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "contractId": "2035000000000000001",
    "orderId": "2031625428892893186",
    "templateCode": "TOURAGE_STANDARD",
    "templateName": "12301标准模板",
    "contractNumber": "CN20260319001",
    "platform": "12301",
    "contractType": "TOUR",
    "mode": "STANDARD",
    "status": "GENERATED",
    "statusLabel": "已生成",
    "signUrl": "https://dz.12301.cn/sign/xxxxx",
    "qrCodeUrl": "https://dz.12301.cn/qr/xxxxx",
    "fileUrl": null,
    "agencyCode": "L-FJ-100005",
    "travelAgencyName": "博华（厦门）国际旅行社有限公司",
    "destination": "哈尔滨-亚布力-雪乡",
    "departureDate": "2026-03-11",
    "returnDate": "2026-03-16",
    "totalAmount": 5700.00,
    "touristCount": 1,
    "contactName": "OOPS",
    "contactPhone": "17600545225",
    "createTime": "2026-03-19 17:30:00",
    "supplementaryClause": "冬季出行补充约定...",
    "travelers": [
      {
        "travelerId": "2035100000000000001",
        "name": "张三",
        "idCardType": "ID_CARD",
        "idCardNo": "1101****1234",
        "phone": "138****8000",
        "isSigner": true
      }
    ],
    "statusLogs": [
      {
        "logId": "2035200000000000001",
        "oldStatus": "PENDING",
        "newStatus": "GENERATED",
        "source": "MANUAL",
        "createTime": "2026-03-19 17:30:00"
      }
    ]
  }
}
```

**业务规则**：
- 同一订单同一类型（TOUR/INSURANCE）只能有一个有效合同，创建前会校验
- 如果订单已有有效的 TOUR 合同，需先作废再创建新的
- 创建成功后自动完成订单的 CONTRACT 待办

---

### 2. 报备合同（同步模式 / 线下签）

**接口**：`POST /admin/contract/report`

**使用场景**：线下已签署纸质合同，需在系统中报备

**请求体**：与创建合同相同，区别：
- `templateCode` 使用同步模式模板（如 `TOURAGE_SYNC`）
- `signatoryMode` 默认为 2（现场）
- 创建后状态直接为 `REPORTED`（已上报），无需等待签署

**后续步骤**：创建后需上传已签署的PDF → 调用上传接口

---

### 3. 合同列表（分页）

**接口**：`GET /admin/contract/list`

**使用场景**：合同管理页面

**请求参数**（Query）：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| orderId | String | 否 | 按订单ID筛选 |
| status | String | 否 | 按状态筛选（如 `SIGNED`） |
| platform | String | 否 | 按平台筛选（如 `12301`） |
| page | Integer | 否 | 页码（默认1） |
| pageSize | Integer | 否 | 每页条数（默认20，最大100） |

**响应示例**：
```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "list": [
      {
        "contractId": "2035000000000000001",
        "orderId": "2031625428892893186",
        "contractNumber": "CN20260319001",
        "platform": "12301",
        "contractType": "TOUR",
        "mode": "STANDARD",
        "status": "SIGNED",
        "statusLabel": "已签署",
        "signUrl": "https://...",
        "fileUrl": "https://pdf.12301.cn/CN20260319001.pdf",
        "destination": "哈尔滨-亚布力-雪乡",
        "departureDate": "2026-03-11",
        "returnDate": "2026-03-16",
        "totalAmount": 5700.00,
        "touristCount": 1,
        "contactName": "OOPS",
        "contactPhone": "17600545225",
        "createTime": "2026-03-19 17:30:00"
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  }
}
```

---

### 4. 合同详情

**接口**：`GET /admin/contract/{id}`

**路径参数**：`id` = 合同ID

**响应**：返回 `ContractDetailVO`（比列表多 `travelers`、`statusLogs`、`supplementaryClause`），结构同创建合同的响应。

---

### 5. 按订单查合同

**接口**：`GET /admin/contract/by-order/{orderId}`

**说明**：返回该订单所有合同（含已作废），按创建时间倒序

**响应**：`List<ContractVO>`

---

### 6. 获取订单有效合同

**接口**：`GET /admin/contract/active-by-order/{orderId}`

**说明**：返回最新的非作废合同，如果没有返回 null

**响应**：`ContractVO` 或 `null`

---

### 7. 刷新合同状态

**接口**：`GET /admin/contract/{id}/status`

**说明**：主动从签约平台同步最新状态（用于回调未及时到达的情况）

**响应**：`ContractVO`（包含最新状态）

---

### 8. 作废合同

**接口**：`POST /admin/contract/{id}/invalidate`

**请求体**：无

**响应**：`ContractVO`（状态变为 `VOIDING` 或 `VOIDED`）

**注意**：作废不可恢复，需要新签则重新创建

---

### 9. 重发签署短信

**接口**：`POST /admin/contract/{id}/resend-sms`

**说明**：仅 `SIGNING` 状态可用，重新发送签署链接短信给签署人

**响应**：`Boolean`（true=发送成功）

---

### 10. 上传签署PDF

**接口**：`POST /admin/contract/{id}/upload-pdf`

**说明**：同步模式专用，上传线下签署的合同PDF

**请求**：`multipart/form-data`，字段名 `file`

**响应**：`ContractVO`（状态变为 `UPLOADED`）

---

### 11. 合同模板列表

**接口**：`GET /admin/contract/templates`

**响应示例**：
```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "templateId": "1001",
      "templateCode": "TOURAGE_STANDARD",
      "templateName": "12301标准电子签模板",
      "platform": "12301",
      "mode": "STANDARD",
      "status": "ACTIVE",
      "description": "全国旅游监管平台标准电子签"
    },
    {
      "templateId": "1002",
      "templateCode": "TOURAGE_SYNC",
      "templateName": "12301同步报备模板",
      "platform": "12301",
      "mode": "SYNC",
      "status": "ACTIVE",
      "description": "线下签署后同步报备到12301"
    }
  ]
}
```

**模板字段说明**：

| 字段 | 类型 | 说明 |
|------|------|------|
| templateId | String | 模板ID |
| templateCode | String | **创建合同时传这个值** |
| templateName | String | 模板名称（下拉框显示） |
| platform | String | 所属平台 |
| mode | String | `STANDARD`=电子签 / `SYNC`=线下签 |
| status | String | `ACTIVE`=启用 / `INACTIVE`=停用 |

---

### 12. 补充约定模板列表

**接口**：`GET /admin/contract/clause-template/list`

**说明**：获取启用的补充约定模板，用于创建合同时选择/预填

**响应示例**：
```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "templateId": "2001",
      "name": "冬季出行补充约定",
      "content": "1. 冬季道路可能存在积雪结冰，行程可能适当调整...\n2. ...",
      "sortOrder": 1,
      "status": "ACTIVE"
    },
    {
      "templateId": "2002",
      "name": "高原出行补充约定",
      "content": "1. 高原地区海拔较高，请自备抗高反药物...",
      "sortOrder": 2,
      "status": "ACTIVE"
    }
  ]
}
```

---

## 四、前端页面布局

### 1. 订单详情 → 签合同入口

在订单待办面板的 CONTRACT 待办项，按钮为「去签合同」：

```
┌──────────────────────────────────────────────┐
│ 待办事项                                       │
│                                              │
│ ✅ 确认订单          已完成                     │
│ ✅ 配置保险          已完成                     │
│ ⏳ 签订合同          [ 去签合同 ]               │
│ ...                                          │
└──────────────────────────────────────────────┘
```

点击「去签合同」→ 打开创建合同弹窗/抽屉

### 2. 创建合同弹窗

```
┌─────────────────────────────────────────────────────┐
│ 创建旅游合同                                          │
│                                                     │
│ 签约模板   [▼ 12301标准电子签模板        ]             │
│            选项来源: GET /admin/contract/templates    │
│                                                     │
│ ─── 行程信息（从订单自动填充） ───                      │
│ 目的地     [ 哈尔滨-亚布力-雪乡        ]               │
│ 线路名称   [ 嗨·冰雪3.0南线（6天5晚）  ]               │
│ 出发日期   [ 2026-03-11 ]                            │
│ 返回日期   [ 2026-03-16 ]                            │
│ 出发城市   [ 哈尔滨      ]                            │
│                                                     │
│ ─── 费用信息（从订单自动填充） ───                      │
│ 成人费用   [ 5700.00 ]                               │
│ 儿童费用   [ 0        ]                              │
│ 总金额     [ 5700.00  ]                              │
│ 付款方式   [▼ 在线支付  ]                             │
│                                                     │
│ ─── 签署人信息（从出行人选择） ───                      │
│ 签署人     [▼ 张三 - 110101****1234 ]                 │
│            选择后自动填充姓名/电话/证件号                │
│                                                     │
│ ─── 联系人信息（从订单自动填充） ───                    │
│ 联系人     [ OOPS           ]                        │
│ 联系电话   [ 17600545225    ]                        │
│                                                     │
│ ─── 补充约定（可选） ───                              │
│ 快捷选择   [▼ 冬季出行补充约定       ]                 │
│            选项来源: GET /clause-template/list        │
│ 约定内容   [ 多行文本框，选模板后自动填充 ]              │
│                                                     │
│ ─── 出行人列表（从订单出行人自动加载） ───               │
│ ☑ 张三    身份证 110101****1234  13800138000  [签署人] │
│                                                     │
│              [ 取消 ]  [ 创建合同 ]                    │
└─────────────────────────────────────────────────────┘
```

### 3. 合同管理列表页

```
┌────────────────────────────────────────────────────────────┐
│ 合同管理                                                     │
│                                                            │
│ 状态 [▼ 全部] 平台 [▼ 全部]              [ 搜索 ] [ 重置 ]   │
│                                                            │
│ 合同编号         订单号              产品       状态    操作   │
│ CN20260319001   HL2026031114...    冰雪南线   已签署   详情   │
│ CN20260318005   HL2026031800...    海岛亲子   签署中   详情 重发短信 │
│ CN20260317002   HL2026031700...    冰雪北线   已作废   详情   │
└────────────────────────────────────────────────────────────┘
```

### 4. 合同详情页

```
┌──────────────────────────────────────────────────────┐
│ 合同详情                                               │
│                                                      │
│ 合同编号: CN20260319001    平台: 12301                  │
│ 状态: 已签署 ✅           模式: 电子签                   │
│ 目的地: 哈尔滨-亚布力-雪乡                               │
│ 出发: 2026-03-11 → 返回: 2026-03-16                   │
│ 总金额: ¥5,700.00         出行人数: 1                   │
│                                                      │
│ ─── 出行人 ───                                        │
│ 张三  身份证 110101****1234  ⭐签署人                   │
│                                                      │
│ ─── 操作按钮（按状态显示） ───                           │
│ [查看签署链接] [重发短信] [刷新状态] [查看PDF] [作废]      │
│                                                      │
│ ─── 状态日志 ───                                       │
│ 2026-03-19 17:30  PENDING → GENERATED   (创建)        │
│ 2026-03-19 17:35  GENERATED → SIGNING   (回调)        │
│ 2026-03-19 17:40  SIGNING → SIGNED      (回调)        │
└──────────────────────────────────────────────────────┘
```

### 5. 操作按钮显示规则

| 状态 | 可用操作 |
|------|---------|
| GENERATED | 查看签署链接、刷新状态、作废 |
| SIGNING | 重发短信、刷新状态、作废 |
| SIGNED | 查看PDF、作废 |
| REPORTED | 上传PDF、作废 |
| UPLOADED | 查看PDF、作废 |
| VOIDING | 刷新状态 |
| VOIDED | 无操作 |

---

## 五、数据预填逻辑

创建合同弹窗打开时，大部分字段可从订单详情自动填充：

| 合同字段 | 来源 | 说明 |
|---------|------|------|
| orderId | 当前订单 | 自动 |
| routeName | order.productName | 产品名称 |
| destination | order.destination 或从行程中提取 | 可能需要手动填 |
| departureDate | order.departureDate | 出发日期 |
| returnDate | 计算：departureDate + tripDays - 1 | 自动计算 |
| days | order.tripDays | 行程天数 |
| nights | tripDays - 1 | 自动计算 |
| contactName | order.contactName | 联系人 |
| contactPhone | order.contactPhone | 联系电话 |
| totalAmount | order.totalPrice | 订单总价 |
| adultCost | order.totalPrice / adultCount | 参考值，可修改 |
| travelers | 从 `GET /admin/order/{orderId}/travelers` 获取 | 自动加载 |
| signatoryName/Phone/IdNumber | 从出行人中选择签署人 | 用户选择 |

---

## 六、完整调用示例

```javascript
import { http } from '@/utils/request'

// 1. 获取模板列表（创建弹窗打开时）
const templates = await http.get('/contract/templates')
// templates = [{ templateCode: 'TOURAGE_STANDARD', templateName: '12301标准电子签模板', ... }]

// 2. 获取补充约定模板（可选）
const clauses = await http.get('/contract/clause-template/list')
// clauses = [{ name: '冬季出行补充约定', content: '...', ... }]

// 3. 创建电子签合同
const contract = await http.post('/contract/create', {
  orderId: currentOrder.orderId,
  templateCode: 'TOURAGE_STANDARD',
  destination: '哈尔滨-亚布力-雪乡',
  routeName: currentOrder.productName,
  departureDate: currentOrder.departureDate,
  returnDate: '2026-03-16',
  contactName: currentOrder.contactName,
  contactPhone: currentOrder.contactPhone,
  signatoryName: selectedTraveler.name,
  signatoryPhone: selectedTraveler.phone,
  signatoryIdNumber: selectedTraveler.idCardNo,
  adultCost: 5700.00,
  totalAmount: currentOrder.totalPrice,
  supplementaryClause: selectedClause?.content || null,
  travelers: orderTravelers.map(t => ({
    name: t.name,
    idCardNo: t.idCardNo,
    phone: t.phone,
    idCardType: 1,
    isSigner: t.travelerId === selectedSignerId
  }))
})
// contract.signUrl → 签署链接（可复制/展示二维码给出行人）
// contract.status → 'GENERATED'

// 4. 查看订单的有效合同
const activeContract = await http.get(`/contract/active-by-order/${orderId}`)

// 5. 刷新合同状态（轮询或手动刷新）
const updated = await http.get(`/contract/${contractId}/status`)

// 6. 重发签署短信
await http.post(`/contract/${contractId}/resend-sms`)

// 7. 作废合同
await http.post(`/contract/${contractId}/invalidate`)

// 8. 上传PDF（同步模式）
const formData = new FormData()
formData.append('file', pdfFile)
await http.post(`/contract/${contractId}/upload-pdf`, formData)

// 9. 合同列表（合同管理页）
const list = await http.get('/contract/list', {
  params: { page: 1, pageSize: 20, status: 'SIGNED' }
})
```

---

## 七、与订单待办的关联

1. 合同创建成功后，系统**自动完成**订单的 `CONTRACT` 待办
2. 前端点击订单待办的「去签合同」按钮时：
   - 先检查 `GET /admin/contract/active-by-order/{orderId}` 是否已有合同
   - 有 → 跳转合同详情
   - 无 → 打开创建合同弹窗
3. 合同作废后，如果订单流程需要重签，会重新生成 `CONTRACT` 待办

---

## 八、注意事项

- 同一订单的 TOUR 和 INSURANCE 合同互相独立，可各有一份有效合同
- 出行人变更（增删改）会自动级联作废已有 TOUR 合同
- 签署链接（`signUrl`）有有效期，过期需重发短信（`resend-sms`）
- 合同PDF（`fileUrl`）签署完成后才有值
- 12301平台通过回调自动推送状态变更，前端也可手动刷新（`/{id}/status`）
