7个接口详细定义 + 字典 + 页面布局 + 权限控制 + 调用示例 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
18 KiB
订单待办事项(Todo)完整对接方案
日期: 2026-03-19 | 服务: hl-order-service
一、功能说明
订单支付成功后,系统自动生成一组「待办事项」,按顺序推进内部流程。每个待办有对应的负责角色,完成后自动推进到下一步。
待办流程(6步)
确认订单 → 配置保险 → 签订合同 → 安排住宿 → 安排车辆 → 确认清单
(定制师) (定制师) (定制师) (房管) (车管) (定制师)
关联字典
order_todo_type(订单待办类型):
| dict_value | dict_label | 序号 | 负责角色 |
|---|---|---|---|
| CONFIRM_ORDER | 确认订单 | 1 | CUSTOMIZER(定制师) |
| INSURANCE | 配置保险 | 2 | CUSTOMIZER(定制师) |
| CONTRACT | 签订合同 | 3 | CUSTOMIZER(定制师) |
| ARRANGE_ROOM | 安排住宿 | 4 | ROOM_MANAGER(房管) |
| ARRANGE_VEHICLE | 安排车辆 | 5 | VEHICLE_MANAGER(车管) |
| CONFIRM_CHECKLIST | 确认清单 | 6 | CUSTOMIZER(定制师) |
| PROCESS_REFUND | 处理退款 | 7 | CUSTOMIZER(定制师) |
待办状态:
| 值 | 含义 |
|---|---|
| PENDING | 待处理 |
| COMPLETED | 已完成 |
| CANCELLED | 已取消(订单取消/退款时自动取消) |
二、接口清单
| # | 方法 | 路径 | 说明 | 使用场景 |
|---|---|---|---|---|
| 1 | GET | /admin/order/{orderId}/todos |
获取订单待办列表 | 订单详情页 → 待办面板 |
| 2 | PUT | /admin/order/todo/{todoId}/complete |
完成待办 | 点击「完成」/「去投保」按钮 |
| 3 | GET | /admin/order/todo/my-list |
我的待办列表 | 首页/工作台 → 待办列表 |
| 4 | GET | /admin/order/todo/my-count |
我的待办数量 | 首页/菜单 → 红点角标 |
| 5 | GET | /admin/order/todo/customizer-list |
定制师列表 | 更换定制师弹窗 → 下拉选择 |
| 6 | PUT | /admin/order/{orderId}/customizer |
更换定制师 | 更换定制师弹窗 → 确认 |
| 7 | GET | /admin/order/todo/contract-eligible |
可签合同订单列表 | 合同管理页 → 待签合同 |
三、接口详细定义
1. 获取订单待办列表
接口:GET /admin/order/{orderId}/todos
使用场景:订单详情页右侧「待办事项」面板,展示该订单所有待办项的时间线
路径参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderId | Long(String) | 是 | 订单ID |
响应示例:
{
"code": 200,
"message": "成功",
"data": [
{
"todoId": "1902000000000000001",
"orderId": "1901000000000000001",
"orderNo": "HL202603191600255172",
"todoType": "CONFIRM_ORDER",
"todoLabel": "确认订单",
"sequence": 1,
"status": "COMPLETED",
"assigneeRoleKey": "CUSTOMIZER",
"assigneeAdminId": "1800000000000000010",
"completedBy": "1800000000000000010",
"completedAt": "2026-03-19 10:30:00",
"createTime": "2026-03-19 10:00:00"
},
{
"todoId": "1902000000000000002",
"orderId": "1901000000000000001",
"orderNo": "HL202603191600255172",
"todoType": "INSURANCE",
"todoLabel": "配置保险",
"sequence": 2,
"status": "PENDING",
"assigneeRoleKey": "CUSTOMIZER",
"assigneeAdminId": "1800000000000000010",
"completedBy": null,
"completedAt": null,
"createTime": "2026-03-19 10:00:00"
},
{
"todoId": "1902000000000000003",
"orderId": "1901000000000000001",
"orderNo": "HL202603191600255172",
"todoType": "CONTRACT",
"todoLabel": "签订合同",
"sequence": 3,
"status": "PENDING",
"assigneeRoleKey": "CUSTOMIZER",
"assigneeAdminId": "1800000000000000010",
"completedBy": null,
"completedAt": null,
"createTime": "2026-03-19 10:00:00"
},
{
"todoId": "1902000000000000004",
"orderId": "1901000000000000001",
"orderNo": "HL202603191600255172",
"todoType": "ARRANGE_ROOM",
"todoLabel": "安排住宿",
"sequence": 4,
"status": "PENDING",
"assigneeRoleKey": "ROOM_MANAGER",
"assigneeAdminId": null,
"completedBy": null,
"completedAt": null,
"createTime": "2026-03-19 10:00:00"
},
{
"todoId": "1902000000000000005",
"orderId": "1901000000000000001",
"orderNo": "HL202603191600255172",
"todoType": "ARRANGE_VEHICLE",
"todoLabel": "安排车辆",
"sequence": 5,
"status": "PENDING",
"assigneeRoleKey": "VEHICLE_MANAGER",
"assigneeAdminId": null,
"completedBy": null,
"completedAt": null,
"createTime": "2026-03-19 10:00:00"
},
{
"todoId": "1902000000000000006",
"orderId": "1901000000000000001",
"orderNo": "HL202603191600255172",
"todoType": "CONFIRM_CHECKLIST",
"todoLabel": "确认清单",
"sequence": 6,
"status": "PENDING",
"assigneeRoleKey": "CUSTOMIZER",
"assigneeAdminId": "1800000000000000010",
"completedBy": null,
"completedAt": null,
"createTime": "2026-03-19 10:00:00"
}
]
}
返回字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| todoId | String | 待办ID |
| orderId | String | 订单ID |
| orderNo | String | 订单号 |
| todoType | String | 待办类型(字典:order_todo_type) |
| todoLabel | String | 待办名称(中文) |
| sequence | Integer | 排序序号(1-6,决定显示顺序) |
| status | String | 状态:PENDING=待处理 / COMPLETED=已完成 / CANCELLED=已取消 |
| assigneeRoleKey | String | 负责角色标识:CUSTOMIZER / ROOM_MANAGER / VEHICLE_MANAGER |
| assigneeAdminId | String | 负责管理员ID(可能为null,表示尚未分配具体人) |
| completedBy | String | 完成人ID(未完成时为null) |
| completedAt | String | 完成时间(未完成时为null),格式 yyyy-MM-dd HH:mm:ss |
| createTime | String | 创建时间,格式 yyyy-MM-dd HH:mm:ss |
2. 完成待办
接口:PUT /admin/order/todo/{todoId}/complete
使用场景:点击待办项的「完成」或「去投保」按钮
路径参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| todoId | Long(String) | 是 | 待办ID |
请求体:无
响应示例:
{
"code": 200,
"message": "成功",
"data": null
}
权限说明:
- 需要对应角色权限,例如
ARRANGE_ROOM待办需要ROOM_MANAGER角色 SUPER_ADMIN可以完成所有类型的待办- 权限不足时返回 403
业务逻辑:
- 完成待办后,系统自动检查是否满足推进条件
- 所有6个待办全部完成后,内部流程状态自动推进到
READY(就绪)
3. 我的待办列表
接口:GET /admin/order/todo/my-list
使用场景:首页/工作台的「我的待办」列表,展示当前登录管理员需要处理的所有未完成待办
请求参数:无(根据登录用户的 adminId 和 roleKey 自动筛选)
响应示例:
{
"code": 200,
"message": "成功",
"data": [
{
"todoId": "1902000000000000002",
"orderId": "1901000000000000001",
"orderNo": "HL202603191600255172",
"todoType": "INSURANCE",
"todoLabel": "配置保险",
"sequence": 2,
"status": "PENDING",
"assigneeRoleKey": "CUSTOMIZER",
"assigneeAdminId": "1800000000000000010",
"completedBy": null,
"completedAt": null,
"createTime": "2026-03-19 10:00:00",
"productName": "嗨·冰雪3.0南线7日游",
"contactName": "张三",
"departureDate": "2026-04-01",
"orderStatus": "PAID",
"blocked": false,
"blockedReason": null
}
]
}
返回字段说明(在基础字段上增加关联信息):
| 字段 | 类型 | 说明 |
|---|---|---|
| productName | String | 产品名称(关联查询) |
| contactName | String | 联系人姓名(关联查询) |
| departureDate | String | 出发日期(关联查询),格式 yyyy-MM-dd |
| orderStatus | String | 订单状态(字典:order_status) |
| blocked | Boolean | 是否被阻塞(前序待办未完成时为true) |
| blockedReason | String | 阻塞原因(如 "等待「配置保险」完成") |
角色筛选逻辑:
SUPER_ADMIN:看到所有未完成待办CUSTOMIZER:只看到CONFIRM_ORDER/INSURANCE/CONTRACT/CONFIRM_CHECKLIST类型的待办ROOM_MANAGER:只看到ARRANGE_ROOM类型的待办VEHICLE_MANAGER:只看到ARRANGE_VEHICLE类型的待办
4. 我的待办数量
接口:GET /admin/order/todo/my-count
使用场景:菜单栏/首页角标红点提醒
请求参数:无
响应示例:
{
"code": 200,
"message": "成功",
"data": 5
}
5. 定制师列表
接口:GET /admin/order/todo/customizer-list
使用场景:更换定制师弹窗中的下拉选择框
请求参数:无
响应示例:
{
"code": 200,
"message": "成功",
"data": [
{
"adminId": "1800000000000000010",
"realName": "王骁",
"avatar": "https://oss.example.com/avatar/wx.jpg"
},
{
"adminId": "1800000000000000011",
"realName": "李明",
"avatar": "https://oss.example.com/avatar/lm.jpg"
}
]
}
返回字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| adminId | String | 管理员ID |
| realName | String | 姓名 |
| avatar | String | 头像URL |
6. 更换定制师
接口:PUT /admin/order/{orderId}/customizer
使用场景:更换定制师弹窗 → 确认按钮
路径参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderId | Long(String) | 是 | 订单ID |
请求体:
{
"customizerId": "1800000000000000011"
}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| customizerId | Long(String) | 是 | 目标定制师的管理员ID,从定制师列表接口获取 |
响应示例:
{
"code": 200,
"message": "成功",
"data": null
}
业务逻辑:
- 更换后,该订单中所有
CUSTOMIZER角色的待办自动转移给新定制师 - 订单时间线会记录「更换定制师」操作
7. 可签合同订单列表
接口:GET /admin/order/todo/contract-eligible
使用场景:合同管理页面,展示保险已完成、可以进入签合同环节的订单列表
请求参数:无
响应示例:
{
"code": 200,
"message": "成功",
"data": [
{
"todoId": "1902000000000000003",
"orderId": "1901000000000000001",
"orderNo": "HL202603191600255172",
"todoType": "CONTRACT",
"todoLabel": "签订合同",
"sequence": 3,
"status": "PENDING",
"assigneeRoleKey": "CUSTOMIZER",
"assigneeAdminId": "1800000000000000010",
"completedBy": null,
"completedAt": null,
"createTime": "2026-03-19 10:00:00",
"productName": "嗨·冰雪3.0南线7日游",
"contactName": "张三",
"departureDate": "2026-04-01",
"orderStatus": "PAID",
"blocked": false,
"blockedReason": null
}
]
}
返回字段与「我的待办列表」相同(
OrderTodoVO)
四、前端页面布局
1. 订单详情页 → 待办事项面板
在订单详情页右侧(或底部),使用时间线组件展示待办进度:
┌──────────────────────────────────────────────┐
│ 待办事项 │
│ │
│ ✅ 确认订单 已完成 2026-03-19 10:30 │
│ │
│ ⏳ 配置保险 [ 去投保 ] │
│ │
│ ○ 签订合同 (等待上一步完成) │
│ │
│ ○ 安排住宿 (等待上一步完成) │
│ │
│ ○ 安排车辆 (等待上一步完成) │
│ │
│ ○ 确认清单 (等待上一步完成) │
│ │
│ [更换定制师] │
└──────────────────────────────────────────────┘
2. 待办项状态展示规则
| 状态 | 图标 | 按钮 | 说明文字 |
|---|---|---|---|
COMPLETED |
✅ 绿色勾 | 无 | 显示 completedAt 完成时间 |
PENDING + 非阻塞 |
⏳ 黄色 | 显示操作按钮 | 当前可操作项 |
PENDING + 阻塞 |
○ 灰色 | 无 | 显示 blockedReason(如"等待上一步完成") |
CANCELLED |
❌ 红色 | 无 | 显示"已取消" |
3. 操作按钮规则
| 待办类型 | 按钮文案 | 按钮类型 | 点击行为 |
|---|---|---|---|
| CONFIRM_ORDER | 完成 | primary | 调用完成待办接口 |
| INSURANCE | 去投保 | warning | 跳转到保险配置页面(或弹窗配置后调用完成接口) |
| CONTRACT | 去签合同 | primary | 跳转到合同签署页面(或弹窗操作后调用完成接口) |
| ARRANGE_ROOM | 完成 | primary | 调用完成待办接口(仅ROOM_MANAGER角色可见) |
| ARRANGE_VEHICLE | 完成 | primary | 调用完成待办接口(仅VEHICLE_MANAGER角色可见) |
| CONFIRM_CHECKLIST | 完成 | primary | 调用完成待办接口 |
4. 权限控制
- 当前登录用户的
roleKey不匹配待办的assigneeRoleKey时,不显示操作按钮 SUPER_ADMIN可以看到并操作所有待办的按钮- 具体映射:
CUSTOMIZER→ 可操作 CONFIRM_ORDER / INSURANCE / CONTRACT / CONFIRM_CHECKLISTROOM_MANAGER→ 可操作 ARRANGE_ROOMVEHICLE_MANAGER→ 可操作 ARRANGE_VEHICLE
五、首页待办角标
实现要点
- 页面加载时调用
GET /admin/order/todo/my-count获取待办数量 - 如果
data > 0,在菜单项(如「订单管理」)右侧显示红点或数字角标 - 点击菜单进入「我的待办」列表页
我的待办列表页
调用 GET /admin/order/todo/my-list,展示为卡片或表格:
┌──────────────────────────────────────────────────────────────┐
│ 我的待办 (5) │
├──────────────────────────────────────────────────────────────┤
│ 📋 配置保险 | HL202603191600255172 | 嗨·冰雪3.0南线7日游 │
│ 联系人: 张三 | 出发: 2026-04-01 | [ 去投保 ] [ 查看订单 ] │
├──────────────────────────────────────────────────────────────┤
│ 📋 签订合同 | HL202603191800123456 | 海岛亲子5日游 │
│ 联系人: 李四 | 出发: 2026-04-15 | ⚠️ 等待「配置保险」完成 │
├──────────────────────────────────────────────────────────────┤
│ ... │
└──────────────────────────────────────────────────────────────┘
六、更换定制师
实现要点
- 订单详情页待办面板底部放「更换定制师」按钮
- 点击弹出弹窗,调用
GET /admin/order/todo/customizer-list加载定制师列表 - 下拉选择目标定制师(显示
realName,值adminId) - 确认后调用
PUT /admin/order/{orderId}/customizer - 成功后刷新待办列表
七、调用示例
import { http } from '@/utils/request'
// 1. 获取订单待办列表(订单详情页加载时)
const todos = await http.get(`/order/${orderId}/todos`)
// todos = [{ todoId, todoType, todoLabel, status, completedAt, ... }]
// 2. 完成待办(点击「完成」按钮)
await http.put(`/order/todo/${todoId}/complete`)
// 3. 获取我的待办列表(首页/工作台)
const myTodos = await http.get('/order/todo/my-list')
// myTodos = [{ todoId, orderNo, todoLabel, productName, contactName, departureDate, blocked, ... }]
// 4. 获取待办数量(菜单角标)
const countRes = await http.get('/order/todo/my-count')
const todoCount = countRes // Number
// 5. 获取定制师列表(更换定制师弹窗)
const customizers = await http.get('/order/todo/customizer-list')
// customizers = [{ adminId, realName, avatar }]
// 6. 更换定制师
await http.put(`/order/${orderId}/customizer`, { customizerId: targetAdminId })
// 7. 获取可签合同订单(合同管理页)
const eligibleOrders = await http.get('/order/todo/contract-eligible')
八、待办生命周期
订单支付成功
↓
系统自动创建6个待办(status=PENDING)
↓
定制师逐步完成待办(PENDING → COMPLETED)
↓
所有待办完成 → 内部流程推进到 READY(就绪)
↓
如果订单取消/退款 → 所有未完成待办自动取消(status=CANCELLED)
注意事项:
- 待办按
sequence排序显示(1→6) - 虽然显示顺序固定,但 ARRANGE_ROOM(4) 和 ARRANGE_VEHICLE(5) 可以并行完成(不互相阻塞)
- 只有 INSURANCE 完成后,CONTRACT 才能操作(有依赖关系)
- 前端拿到
blocked: true的待办不显示操作按钮,显示blockedReason文字即可