# 定制师订单待办 — 新增接口 — 管理后台

> 涉及 PR：#4805 / #4814 / #4819　Issue：#4803 / #4811 / #4817
> 日期：2026-07-07　责任人：腰苏图

---

## ① 接口背景

为管理后台定制师角色新增「订单待办」功能模块。
系统在订单关键节点（补全出行人、提交房型需求、提交用车需求、确认行程、退款/取消/终止审核等）自动生成待办，定制师亦可手动创建自定义提醒事项。
本次共新增 7 个管理端接口，涵盖：分页查询、手动新增/修改/完成/重开/取消，以及手动待办时订单下拉选项。

---

## ② 变更清单

| # | 方法 | 路径 | 名称 | 变更类型 |
|---|------|------|------|----------|
| 1 | GET | `/v3/admin/order-todos/my/page` | 我的待办分页 | ✨ 新增 |
| 2 | POST | `/v3/admin/order-todos/manual` | 手动新增待办 | ✨ 新增 |
| 3 | PUT | `/v3/admin/order-todos/{todoId}` | 修改手动待办 | ✨ 新增 |
| 4 | PUT | `/v3/admin/order-todos/{todoId}/complete` | 完成手动待办 | ✨ 新增 |
| 5 | PUT | `/v3/admin/order-todos/{todoId}/reopen` | 重开手动待办 | ✨ 新增 |
| 6 | DELETE | `/v3/admin/order-todos/{todoId}` | 取消手动待办 | ✨ 新增 |
| 7 | GET | `/v3/admin/order-todos/order-options` | 手动待办订单下拉 | ✨ 新增 |

---

## ③ 接口详情

**认证**：所有接口均需管理后台 JWT，从 token 中自动解析当前管理员 ID（`assigneeAdminId`），无需前端传入。

**幂等性**：
- 接口 2（手动新增）：同一 (adminId, orderId, MANUAL type) 并发写有乐观锁防重（报 581708 冲突让前端重试）。
- 接口 4/5/6（状态操作）：对已是目标状态的待办调用无副作用，返回当前状态。

**限流**：无特殊限流，走全局网关限流。

**操作范围**：接口 2-6 仅能操作来源为 `MANUAL` 的待办；`SYSTEM` 待办由订单业务流自动管理，前端调接口 2-6 均会返回错误码 581703。

---

## ④ 入参

### 4.1 接口 1：GET /v3/admin/order-todos/my/page（Query 参数）

继承分页基类字段 `pageNo`（默认 1）和 `pageSize`（默认 10）：

| 字段 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| pageNo | Integer | 否 | 页码，默认 1 | 1 |
| pageSize | Integer | 否 | 每页条数，默认 10 | 20 |
| status | String | 否 | 待办状态，见枚举 TodoStatus | PENDING |
| todoSource | String | 否 | 待办来源，见枚举 TodoSource | MANUAL |
| orderId | Long（字符串） | 否 | 精确匹配订单 ID | "2073707641535696898" |
| fromDate | String | 否 | 待办日期起（含，格式 yyyy-MM-dd） | 2026-07-01 |
| toDate | String | 否 | 待办日期止（含，格式 yyyy-MM-dd） | 2026-07-31 |
| keyword | String | 否 | 模糊搜索：标题 或 订单号 | 护照 |

**排序固定**：`todoDate asc → sequence asc → createTime desc`，不接受前端自定义排序。

### 4.2 接口 2：POST /v3/admin/order-todos/manual（请求体 JSON）

| 字段 | 类型 | 必填 | 校验 | 说明 | 示例 |
|------|------|------|------|------|------|
| orderId | Long（字符串） | 是 | 非空；须为当前定制师名下订单 | 关联订单 ID | "2073707641535696898" |
| todoLabel | String | 是 | 非空，最多 50 字符 | 待办标题 | "跟进客户护照信息" |
| todoDate | String | 是 | 非空，格式 yyyy-MM-dd | 待办日期 | "2026-07-20" |

### 4.3 接口 3：PUT /v3/admin/order-todos/{todoId}（路径参数 + 请求体 JSON）

路径参数：`todoId`（Long，字符串形式）

| 字段 | 类型 | 必填 | 校验 | 说明 |
|------|------|------|------|------|
| todoLabel | String | 否 | 最多 50 字符；不传则保持原值 | 修改标题 |
| todoDate | String | 否 | 格式 yyyy-MM-dd；不传则保持原值 | 修改日期 |

> 两个字段均可选，至少传一个有意义（全不传返回原数据不报错）。

### 4.4 接口 4：PUT /v3/admin/order-todos/{todoId}/complete

无请求体。路径参数：`todoId`（Long，字符串形式）。

### 4.5 接口 5：PUT /v3/admin/order-todos/{todoId}/reopen

无请求体。路径参数：`todoId`（Long，字符串形式）。

### 4.6 接口 6：DELETE /v3/admin/order-todos/{todoId}

无请求体。路径参数：`todoId`（Long，字符串形式）。

### 4.7 接口 7：GET /v3/admin/order-todos/order-options（Query 参数）

| 字段 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| keyword | String | 否 | 订单号/团号/产品名 LIKE 模糊搜索 | 草原 |

不分页，返回当前定制师名下（排除已取消订单）全量，按 orderId 倒序。

---

## ⑤ 出参字段

### 5.1 OrderTodoRespVO（接口 1-5 出参单项）

| 字段 | 类型 | 说明 | 来源备注 |
|------|------|------|----------|
| todoId | String | 待办 ID（Long 序列化为字符串） | |
| orderId | String | 关联订单 ID（Long 序列化为字符串） | |
| orderNo | String | 订单号，如 HL202607070001 | |
| teamNo | String | 团号（可为 null） | **仅接口 1 分页连表查询有值；接口 2-5 单表转换，该字段恒为 null** |
| productName | String | 产品名称（可为 null） | **仅接口 1 有值** |
| departDate | String | 出发日期 yyyy-MM-dd（可为 null） | **仅接口 1 有值** |
| todoType | String | 待办类型 code，见枚举 TodoType | |
| todoTypeName | String | 待办类型中文名 | |
| todoLabel | String | 标题/描述 | |
| todoSource | String | 来源 code（SYSTEM / MANUAL） | |
| todoSourceName | String | 来源中文名（系统生成 / 手动添加） | |
| todoDate | String | 待办日期 yyyy-MM-dd | |
| actionType | String | 前端跳转动作类型，见枚举 ActionType | |
| status | String | 状态 code，见枚举 TodoStatus | |
| statusName | String | 状态中文名 | |
| assigneeAdminId | String | 指派定制师管理员 ID（Long 序列化为字符串） | |
| completedBy | String | 完成人管理员 ID（未完成时为 null） | |
| completedAt | String | 完成时间（ISO 8601，未完成时为 null） | |
| relatedBizType | String | 关联业务类型（仅系统待办有值，如 REFUND_APPLICATION；手动待办为 null） | |
| relatedBizId | String | 关联业务 ID（Long 序列化为字符串；无时为 null） | |
| createTime | String | 创建时间 ISO 8601 | |

**重要**：`teamNo` / `productName` / `departDate` 三字段**只有分页接口 1 通过连表查询填充**。接口 2-5 返回的 OrderTodoRespVO 中这三个字段恒为 null。建议前端操作成功后直接重拉分页列表（接口 1），不依赖操作返回值渲染这三个字段。

### 5.2 接口 1 响应结构（分页）

```json
{
  "code": 200,
  "data": {
    "list": [],
    "total": 42
  }
}
```

### 5.3 接口 6（DELETE 取消）响应

返回 `Result<Boolean>`，data 为 `true` 表示取消成功。

### 5.4 OrderTodoOrderOptionVO（接口 7 出参单项）

| 字段 | 类型 | 说明 |
|------|------|------|
| orderId | String | 订单 ID（Long 序列化为字符串） |
| orderNo | String | 订单号 |
| teamNo | String | 团号（无团期时为 null） |
| productName | String | 产品名称（可为 null） |
| departDate | String | 出发日期 yyyy-MM-dd（可为 null） |

接口 7 响应结构：

```json
{
  "code": 200,
  "data": []
}
```

---

## ⑥ 枚举 / 数据字典

### TodoStatus — 待办状态

| code | 中文名 | 说明 |
|------|--------|------|
| PENDING | 待处理 | 初始状态 |
| COMPLETED | 已处理 | 手动完成或系统自动关闭 |
| CANCELLED | 已取消 | 仅手动待办可取消（软取消，不物理删除） |

### TodoSource — 待办来源

| code | 中文名 | 说明 |
|------|--------|------|
| SYSTEM | 系统生成 | 由订单业务流自动创建，前端只读 |
| MANUAL | 手动添加 | 定制师手动创建，可增删改 |

### TodoType — 待办类型

| code | 中文名 | 系统自动触发 | 可手动创建 |
|------|--------|----------|-------|
| FILL_TRAVELER | 补全出行人 | 是 | 否 |
| ASSIGN_ROOM | 房型需求 | 是 | 否 |
| ASSIGN_VEHICLE | 用车需求 | 是 | 否 |
| CONFIRM_ORDER | 确认行程 | 是 | 否 |
| PRE_TRIP_REFUND | 出行前退款 | 是 | 否 |
| CANCEL_ORDER | 取消订单 | 是 | 否 |
| TERMINATE_TRIP | 终止行程 | 是 | 否 |
| CONFIRM_CHECKLIST | 确认失败兜底 | 是 | 否 |
| FINANCE_REVIEW | 财务审核 | 预留未启用 | 否 |
| BUY_INSURANCE | 购买保险 | 预留未启用 | 否 |
| SIGN_CONTRACT | 签署合同 | 预留未启用 | 否 |
| MANUAL | 手动待办 | 否 | 是 |

> `FINANCE_REVIEW` / `BUY_INSURANCE` / `SIGN_CONTRACT` 当前预留未启用，**不会出现在实际待办数据中**。

### ActionType — 前端跳转动作（字符串字面量）

| 值 | 对应待办类型 | 建议跳转 |
|----|-------------|--------|
| info | FILL_TRAVELER | 订单出行人详情页 |
| room | ASSIGN_ROOM | 房型需求模块 |
| vehicle | ASSIGN_VEHICLE | 用车需求模块 |
| confirm | CONFIRM_ORDER | 确认行程操作 |
| refund | PRE_TRIP_REFUND | 退款申请详情 |
| cancel | CANCEL_ORDER | 取消订单操作 |
| terminate | TERMINATE_TRIP | 终止行程操作 |
| manual | MANUAL | 无固定跳转，前端可展示标题或无操作 |

---

## ⑦ 错误码

| 错误码 | 中文描述 | 触发场景 |
|--------|---------|--------|
| 581700 | 待办不存在 | todoId 无对应记录 |
| 581701 | 无权操作该待办 | 操作他人的待办 |
| 581702 | 待办必须关联订单 | 内部校验，前端正常调用不触发 |
| 581703 | 系统待办不允许手动完成或重开 | 对 SYSTEM 来源待办调接口 3/4/5/6 |
| 581704 | 待办标题不能为空 | 接口 2 的 todoLabel 为空 |
| 581705 | 待办日期不能为空 | 接口 2 的 todoDate 为空 |
| 581706 | 待办业务事件不支持 | 内部 SYSTEM 同步逻辑，前端不直接触发 |
| 581707 | 仅订单定制师可维护该订单待办 | 接口 2 传入的 orderId 不属于当前管理员名下订单 |
| 581708 | 手动待办创建冲突，请重试 | 并发新增触发乐观锁，前端重试一次即可 |

---

## ⑧ 示例

### 8.1 典型成功 — 分页查询我的待办（接口 1）

请求：

```
GET /v3/admin/order-todos/my/page?status=PENDING&pageNo=1&pageSize=10
Authorization: Bearer <admin-jwt>
```

响应：

```json
{
  "code": 200,
  "data": {
    "list": [
      {
        "todoId": "2073707641535696900",
        "orderId": "2073707641535696898",
        "orderNo": "HL202607070001",
        "teamNo": "T202607070001",
        "productName": "夏季草原行",
        "departDate": "2026-07-20",
        "todoType": "ASSIGN_ROOM",
        "todoTypeName": "房型需求",
        "todoLabel": "房型需求 · 待提交",
        "todoSource": "SYSTEM",
        "todoSourceName": "系统生成",
        "todoDate": "2026-07-15",
        "actionType": "room",
        "status": "PENDING",
        "statusName": "待处理",
        "assigneeAdminId": "10001",
        "completedBy": null,
        "completedAt": null,
        "relatedBizType": null,
        "relatedBizId": null,
        "createTime": "2026-07-07T11:00:00"
      }
    ],
    "total": 1
  }
}
```

### 8.2 边界情况 — 手动新增待办后 teamNo 等字段为 null（接口 2）

请求：

```
POST /v3/admin/order-todos/manual
Authorization: Bearer <admin-jwt>
Content-Type: application/json

{
  "orderId": "2073707641535696898",
  "todoLabel": "跟进客户护照信息",
  "todoDate": "2026-07-20"
}
```

响应（teamNo / productName / departDate 为 null，操作成功后建议重拉分页接口 1 获取完整上下文）：

```json
{
  "code": 200,
  "data": {
    "todoId": "2073707641535696901",
    "orderId": "2073707641535696898",
    "orderNo": "HL202607070001",
    "teamNo": null,
    "productName": null,
    "departDate": null,
    "todoType": "MANUAL",
    "todoTypeName": "手动待办",
    "todoLabel": "跟进客户护照信息",
    "todoSource": "MANUAL",
    "todoSourceName": "手动添加",
    "todoDate": "2026-07-20",
    "actionType": "manual",
    "status": "PENDING",
    "statusName": "待处理",
    "assigneeAdminId": "10001",
    "completedBy": null,
    "completedAt": null,
    "relatedBizType": null,
    "relatedBizId": null,
    "createTime": "2026-07-07T14:00:00"
  }
}
```

### 8.3 业务失败 — 对系统待办调完成接口（接口 4，最小复现）

请求（todoId 对应 SYSTEM 来源待办）：

```
PUT /v3/admin/order-todos/2073707641535696900/complete
Authorization: Bearer <admin-jwt>
```

响应：

```json
{
  "code": 581703,
  "msg": "系统待办不允许手动完成或重开"
}
```

另一业务失败 — 订单不属于当前定制师（接口 2 传入他人订单 orderId）：

```json
{
  "code": 581707,
  "msg": "仅订单定制师可维护该订单待办"
}
```

---

## ⑨ 业务边界

**适用**：
- 当前登录管理员为某订单的指派定制师（`assigneeAdminId` 即当前 admin ID）
- 待办分页（接口 1）只返回属于当前定制师的待办
- 手动操作（接口 2-6）只能操作当前定制师名下的 MANUAL 来源待办

**不适用**：
- 查看全部定制师的待办（暂无此接口，当前版本只支持我的）
- 对系统待办（SYSTEM）做完成/重开/取消/修改操作
- 删除已取消（CANCELLED）的待办（取消后为软删除，不提供物理删除接口）

**特殊边界**：
- 接口 7 订单下拉的过滤条件：订单状态非 CANCELLED（注意是订单状态，不是待办状态）
- 系统待办由订单流程自动生灭，FINANCE_REVIEW / BUY_INSURANCE / SIGN_CONTRACT 三类待办预留不会出现
- 房型/用车需求被打回时系统更新已有待办标题为已打回，待办 ID 不变

---

## ⑩ 修改前后对比

本次全部为新增接口，无修改前状态。

---

## ⑪ 影响评估 / 回滚

全新增接口，对现有接口零影响。如需回滚，停止调用以上 7 个接口即可，无破坏性变更。

---

## ⑫ 注意事项

1. **Long 型 ID 均序列化为字符串**：`todoId` / `orderId` / `assigneeAdminId` / `completedBy` / `relatedBizId` 在 JSON 中均为字符串类型（如 "2073707641535696898"），防止 JS 精度丢失。

2. **操作返回值中 teamNo / productName / departDate 恒为 null**：仅分页接口 1 通过连表查询填充这三个字段；接口 2-5 的返回值是单表转换结果。建议前端操作成功后重拉分页列表，不从操作返回值取这三字段。

3. **SYSTEM 待办只读**：对 `todoSource === "MANUAL"` 的待办才显示修改/完成/重开/取消按钮，系统待办只展示不操作。

4. **分页排序固定**：`todoDate asc → sequence asc → createTime desc`，无需也不支持前端传排序参数。

5. **接口 7 订单下拉不分页**：返回当前定制师名下排除已取消订单的全量列表，数量通常不超过数十条。

6. **并发新增防抖**：请求发出后禁用提交按钮，收到 581708 时提示用户重试。

---

## ⑬ 关联 / 联系人

| 类型 | 链接 |
|------|------|
| Issue（初始需求） | https://git.1814.love:8443/wx/HL/issues/4803 |
| Issue（列表字段+打回追溯） | https://git.1814.love:8443/wx/HL/issues/4811 |
| Issue（订单下拉选项） | https://git.1814.love:8443/wx/HL/issues/4817 |
| PR #4805（6 个管理接口 + 系统待办落库） | https://git.1814.love:8443/wx/HL/pulls/4805 |
| PR #4814（列表出参补 teamNo/productName/departDate + 打回追溯修复） | https://git.1814.love:8443/wx/HL/pulls/4814 |
| PR #4819（新增 order-options 下拉接口） | https://git.1814.love:8443/wx/HL/pulls/4819 |
| Commit（PR #4805） | https://git.1814.love:8443/wx/HL/commit/405ea69f0a96b1624a0a10f86663f6664c2d283d |
| Commit（PR #4814） | https://git.1814.love:8443/wx/HL/commit/de0c92b12049628c2c3d1c7bc43c01d9afe2fe0f |
| Commit（PR #4819） | https://git.1814.love:8443/wx/HL/commit/8c6ad153d9699e001f058321744a570b2bd60fc6 |
| 后端负责人 | 腰苏图 |
