19个接口详细定义 + 创建合同请求字段 + 状态流转 + 页面布局 + 数据预填逻辑 + 调用示例 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
24 KiB
合同管理完整对接方案
日期: 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
使用场景:订单详情页点击「去签合同」,弹窗填写信息后提交
请求体:
{
"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 | 否 | 年龄 |
响应示例:
{
"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) |
响应示例:
{
"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
响应示例:
{
"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
说明:获取启用的补充约定模板,用于创建合同时选择/预填
响应示例:
{
"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 | 从出行人中选择签署人 | 用户选择 |
六、完整调用示例
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' }
})
七、与订单待办的关联
- 合同创建成功后,系统自动完成订单的
CONTRACT待办 - 前端点击订单待办的「去签合同」按钮时:
- 先检查
GET /admin/contract/active-by-order/{orderId}是否已有合同 - 有 → 跳转合同详情
- 无 → 打开创建合同弹窗
- 先检查
- 合同作废后,如果订单流程需要重签,会重新生成
CONTRACT待办
八、注意事项
- 同一订单的 TOUR 和 INSURANCE 合同互相独立,可各有一份有效合同
- 出行人变更(增删改)会自动级联作废已有 TOUR 合同
- 签署链接(
signUrl)有有效期,过期需重发短信(resend-sms) - 合同PDF(
fileUrl)签署完成后才有值 - 12301平台通过回调自动推送状态变更,前端也可手动刷新(
/{id}/status)