diff --git a/changelogs/2026-03/2026-03-19_order_todo.md b/changelogs/2026-03/2026-03-19_order_todo.md new file mode 100644 index 0000000..56382da --- /dev/null +++ b/changelogs/2026-03/2026-03-19_order_todo.md @@ -0,0 +1,559 @@ +# 订单待办事项(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 | + +**响应示例**: +```json +{ + "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 | + +**请求体**:无 + +**响应示例**: +```json +{ + "code": 200, + "message": "成功", + "data": null +} +``` + +**权限说明**: +- 需要对应角色权限,例如 `ARRANGE_ROOM` 待办需要 `ROOM_MANAGER` 角色 +- `SUPER_ADMIN` 可以完成所有类型的待办 +- 权限不足时返回 403 + +**业务逻辑**: +- 完成待办后,系统自动检查是否满足推进条件 +- 所有6个待办全部完成后,内部流程状态自动推进到 `READY`(就绪) + +--- + +### 3. 我的待办列表 + +**接口**:`GET /admin/order/todo/my-list` + +**使用场景**:首页/工作台的「我的待办」列表,展示当前登录管理员需要处理的所有未完成待办 + +**请求参数**:无(根据登录用户的 adminId 和 roleKey 自动筛选) + +**响应示例**: +```json +{ + "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` + +**使用场景**:菜单栏/首页角标红点提醒 + +**请求参数**:无 + +**响应示例**: +```json +{ + "code": 200, + "message": "成功", + "data": 5 +} +``` + +--- + +### 5. 定制师列表 + +**接口**:`GET /admin/order/todo/customizer-list` + +**使用场景**:更换定制师弹窗中的下拉选择框 + +**请求参数**:无 + +**响应示例**: +```json +{ + "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 | + +**请求体**: +```json +{ + "customizerId": "1800000000000000011" +} +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| customizerId | Long(String) | 是 | 目标定制师的管理员ID,从定制师列表接口获取 | + +**响应示例**: +```json +{ + "code": 200, + "message": "成功", + "data": null +} +``` + +**业务逻辑**: +- 更换后,该订单中所有 `CUSTOMIZER` 角色的待办自动转移给新定制师 +- 订单时间线会记录「更换定制师」操作 + +--- + +### 7. 可签合同订单列表 + +**接口**:`GET /admin/order/todo/contract-eligible` + +**使用场景**:合同管理页面,展示保险已完成、可以进入签合同环节的订单列表 + +**请求参数**:无 + +**响应示例**: +```json +{ + "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_CHECKLIST + - `ROOM_MANAGER` → 可操作 ARRANGE_ROOM + - `VEHICLE_MANAGER` → 可操作 ARRANGE_VEHICLE + +--- + +## 五、首页待办角标 + +### 实现要点 + +1. **页面加载时**调用 `GET /admin/order/todo/my-count` 获取待办数量 +2. 如果 `data > 0`,在菜单项(如「订单管理」)右侧显示红点或数字角标 +3. 点击菜单进入「我的待办」列表页 + +### 我的待办列表页 + +调用 `GET /admin/order/todo/my-list`,展示为卡片或表格: + +``` +┌──────────────────────────────────────────────────────────────┐ +│ 我的待办 (5) │ +├──────────────────────────────────────────────────────────────┤ +│ 📋 配置保险 | HL202603191600255172 | 嗨·冰雪3.0南线7日游 │ +│ 联系人: 张三 | 出发: 2026-04-01 | [ 去投保 ] [ 查看订单 ] │ +├──────────────────────────────────────────────────────────────┤ +│ 📋 签订合同 | HL202603191800123456 | 海岛亲子5日游 │ +│ 联系人: 李四 | 出发: 2026-04-15 | ⚠️ 等待「配置保险」完成 │ +├──────────────────────────────────────────────────────────────┤ +│ ... │ +└──────────────────────────────────────────────────────────────┘ +``` + +--- + +## 六、更换定制师 + +### 实现要点 + +1. 订单详情页待办面板底部放「更换定制师」按钮 +2. 点击弹出弹窗,调用 `GET /admin/order/todo/customizer-list` 加载定制师列表 +3. 下拉选择目标定制师(显示 `realName`,值 `adminId`) +4. 确认后调用 `PUT /admin/order/{orderId}/customizer` +5. 成功后刷新待办列表 + +--- + +## 七、调用示例 + +```javascript +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` 文字即可