# 订单待办完成接口补齐业务前置校验（杜绝空点成功）

**日期**: 2026-04-23
**PR**: #1327 (dev)
**Issue**: #1324
**类型**: fix
**服务**: hl-order-service-v2

---

## 摘要

订单详情页「待办」按钮（分配房间 / 分配车辆 / 确认清单 / 购买保险 / 签署合同）**过去即使对应业务未完成也能直接点成功**，仅改 `order_todo.status=COMPLETED`，但 `order.checklist_confirmed` / `process_status` / 下游保险合同事件全未触发，相当于 UI 假成功。

本次修复在 `PUT /admin/order/todo/{todoId}/complete` 内部补齐业务前置校验，未就绪直接返回业务异常。

## 接口

**路径没变**：`PUT /admin/order/todo/{todoId}/complete`

**新的错误响应**（HTTP 仍为 200，业务码在 `Result.code`）：

| 触发场景 | `Result.code` | `Result.msg` |
|---|---|---|
| 点「分配房间」但房间未安排 | BUSINESS_ERROR | `请先在行程安排中完成房间分配` |
| 点「分配车辆」但车辆未安排 | BUSINESS_ERROR | `请先在行程安排中完成车辆分配` |
| 点「购买保险」但未投保 | BUSINESS_ERROR | `请先完成保险购买` |
| 点「签署合同」但合同未签 | BUSINESS_ERROR | `请先完成合同签署` |
| 点「确认清单」但 `process_status ≠ PENDING_CHECKLIST_CONFIRM` | BUSINESS_ERROR | `流程状态不正确,当前: {状态label}` |
| 点「确认清单」但尾款为负 | BUSINESS_ERROR | `确认后尾款为负(¥X),请调整优惠/增项后再确认` |

> 正常成功仍返回 `{"code":"SUCCESS","data":null}`。

## 「确认清单」待办的完整行为（本次接通）

以前：点「确认清单」只改 `order_todo.status`，没任何副作用。
现在：点「确认清单」会真正执行锁单全流程：

- 状态机 `PENDING_CHECKLIST_CONFIRM → READY`
- `order.checklist_confirmed = true`，`order.ready_at` 填充
- 所有未确认的 `order_discount` / `order_surcharge` 批量 `confirmed = 1`（优惠/增项金额锁定）
- 尾款锁定
- afterCommit 发布 `ChecklistConfirmedEvent` → 下游保险自动投保 + 合同自动签约流程启动
- Timeline 新增 `CHECKLIST_CONFIRMED` 记录

## 前端联调建议

1. 待办按钮点击后的错误提示继续按 `Result.msg` 显示即可，无需改接口调用方式
2. 「确认清单」按钮点击后建议**刷新订单详情页**，因为会联动触发 process_status + checklist_confirmed + 保险/合同状态变化
3. 行程安排 tab 的「暂未安排房间/车辆」与顶部待办按钮是一个系统：只要这边没数据，对应待办就点不动（符合直觉）
4. 「购买保险」/「签署合同」待办：只有当前端子模块或管理员在保险/合同页面成功操作后，对应待办才能被点

## 幂等与并发

- 「确认清单」受 `@Idempotent(keyArg=orderId+operatorId, timeout=60s)` + `@Lock4j('order:checklist:' + orderId)` 双重保护
- 其余待办按钮依然被单例 activeTodo 机制限制（只有当前序号最小的 PENDING 待办能被完成）

## 关联字典

无新增字典。`ProcessStatus` 枚举取值见 PR #1318 已发布的字典项（本次不动）。

## 风险与回滚

- 纯后端校验增强，**不改接口签名/返回结构**
- 回滚方式：revert PR #1327 即可恢复到空点成功的旧行为
