# 团期级 staff 两级配置（团期共享 + 订单专属）

> 端类型：**管理后台**（v3）｜变更类型：**新增接口 + 修改接口**｜服务：hl-order-service-v3
> Issue #3560 ｜ PR #3567 ｜日期：2026-06-07

## ① 接口背景

团期产品（小蒙马等）同一团期（groupBatchId）下多个订单**共享同一批出团团队**（领队/摄影/司导）。原先 staff 只能逐订单配置（`order_staff_assignment`），同团期 N 个订单要重复配 N 次。

本次新增**团期级**配置：按团期配一次，后端自动扇出到团内所有订单；同时保留**订单级专属**配置，两级叠加。前端形成两个配置入口：
- 独立的「团期 staff 配置页」（按 groupBatchId 配，本次新增接口）
- 订单详情「staff 配置」（订单专属，沿用原接口，出参新增 source 字段区分来源）

## ② 变更清单

| 接口 | 类型 | 说明 |
|---|---|---|
| `GET /v3/admin/group-batch/{groupBatchId}/staff` | 新增 | 查团期 staff 配置列表 |
| `PUT /v3/admin/group-batch/{groupBatchId}/staff` | 新增 | 全量保存团期配置 + 扇出团内订单 |
| `GET /v3/admin/order/{id}/staff` | 修改 | 出参新增 `source` 字段（GROUP_BATCH/ORDER） |
| `DELETE /v3/admin/order/{id}/staff/{staffAssignmentId}` | 行为变更 | 团期来源行（source=GROUP_BATCH）禁止在订单侧删除（582109） |
| `PUT /v3/admin/order/{id}/staff/{staffAssignmentId}` | 行为变更 | 团期来源行禁止在订单侧编辑（582109） |

## ③ 接口详情

### 接口 A：查询团期 staff 配置
`GET /v3/admin/group-batch/{groupBatchId}/staff`
打开「团期 staff 配置页」时调用，回填当前团期已配的共享 staff。

### 接口 B：保存团期 staff 配置（全量 + 扇出）
`PUT /v3/admin/group-batch/{groupBatchId}/staff`
**全量覆盖语义**：传入列表即为最终状态，后端软删旧配置后插入新配置。保存成功后**异步**扇出到团内所有活跃订单的 `order_staff_assignment`（source=GROUP_BATCH），订单专属行（source=ORDER）不受影响。

### 接口 C：订单 staff 列表（出参加字段）
`GET /v3/admin/order/{id}/staff` 出参每项新增 `source`，前端据此区分「团期共享」（置灰、不可在订单侧增删改）与「订单专属」（可增删改）。

## ④ 入参

### 接口 A（GET）
| 参数 | 位置 | 类型 | 必填 | 含义 |
|---|---|---|---|---|
| groupBatchId | path | Long | 是 | 团期批次 ID |

### 接口 B（PUT）
路径参数：`groupBatchId`（Long，必填）

请求体 `BatchStaffConfigReqVO`：
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
| staffList | List\<Item\> | 是 | staff 配置**全集**（全量覆盖，传空数组=清空团期配置） |

`Item`：
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
| staffId | Long | 是 | 用户域员工 ID |
| staffRole | String | 是 | 员工角色（见枚举） |
| sortOrder | Integer | 否 | 展示排序（默认 0） |
| remark | String | 否 | 备注（≤500） |

> 注：`staffName` / `staffPhone` / `avatarUrl` 由后端 Feign 反查冻结快照，**前端不传**。

**入参示例**
```json
{
  "staffList": [
    {"staffId": 40001, "staffRole": "LEADER", "sortOrder": 0, "remark": "首席领队"},
    {"staffId": 40002, "staffRole": "PHOTOGRAPHER", "sortOrder": 1, "remark": "跟拍摄影"}
  ]
}
```

## ⑤ 出参

### 接口 A：`Result<List<BatchStaffItemVO>>`
### 接口 B：`Result<BatchStaffConfigRespVO>`

`BatchStaffConfigRespVO`：
| 字段 | 类型 | 含义 |
|---|---|---|
| staffList | List\<BatchStaffItemVO\> | 保存后当前团期配置快照 |
| affectedOrderCount | int | 扇出影响的活跃订单数（已触发异步写入） |

`BatchStaffItemVO`：
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 记录 ID（batch_staff_id） |
| staffId | Long | 用户域员工 ID |
| staffRole | String | 员工角色 |
| staffName | String | 员工姓名（快照） |
| staffPhone | String | 员工手机（脱敏前 3 后 4） |
| avatarUrl | String | 头像 URL（快照） |
| sortOrder | Integer | 展示排序 |
| remark | String | 备注 |

### 接口 C：`GET /order/{id}/staff` 出参 `StaffAssignmentVO` 新增字段
| 字段 | 类型 | 含义 |
|---|---|---|
| source | String | **新增**。GROUP_BATCH=团期共享（不可在订单侧改） / ORDER=订单专属 |

**接口 B 出参示例**
```json
{
  "code": 200, "message": "成功", "success": true,
  "data": {
    "staffList": [
      {"id": 770001, "staffId": 40001, "staffRole": "LEADER", "staffName": "刘领队",
       "staffPhone": "138****6677", "avatarUrl": "https://oss/40001.jpg", "sortOrder": 0, "remark": "首席领队"}
    ],
    "affectedOrderCount": 3
  }
}
```

## ⑥ 枚举 / 数据字典

| 枚举 | 取值 | 含义 |
|---|---|---|
| staffRole | LEADER / DRIVER / PHOTOGRAPHER / OTHER | 领队 / 司机 / 摄影师 / 其他 |
| source | GROUP_BATCH / ORDER | 团期共享 / 订单专属 |

## ⑦ 错误码

| code | message | 触发 |
|---|---|---|
| 582109 | 团期共享 staff 不可在订单侧增删改，请到团期配置页操作 | 对 source=GROUP_BATCH 的行调订单侧 DELETE/PUT staff |
| 400 | 参数校验失败 | staffId/staffRole 缺失等 |
| 401 | 缺少有效的 Authorization 头 | 未携带 token |

## ⑧ 示例（典型 / 边界 / 异常）

- **典型**：团期配「领队+摄影」→ 保存返回 affectedOrderCount=3（团内 3 个活跃订单已扇出）。
- **边界**：staffList 传 `[]` → 清空该团期配置，并扇出删除团内订单的所有 GROUP_BATCH 行（订单专属行保留）。
- **异常**：在订单详情对一个「团期共享」的领队点删除 → 返回 582109，前端应禁用团期行的删除/编辑按钮。

## ⑨ 业务边界

- **全量覆盖**：保存即最终状态，未在列表中的旧团期 staff 会被软删并从团内订单移除。
- **扇出范围**：仅团内**活跃**订单（已完成 / 已取消订单不被扇出刷新，保留历史团队记录）。
- **两级叠加**：订单详情 staff = 团期共享（GROUP_BATCH） + 订单专属（ORDER），同一员工去重。
- **下单自动写入**：团期已配 staff 后新下单的订单，创建时自动带上团期 staff。
- **异步最终一致**：扇出为异步，保存接口返回后团内订单的更新可能有秒级延迟（affectedOrderCount 表示已触发的订单数）。

## ⑩ 修改前后对比

| | 修改前 | 修改后 |
|---|---|---|
| 团期共享配置 | 无，同团期逐单重复配 | 团期配一次自动扇出团内 |
| order/{id}/staff 出参 | 无 source | 每项带 source（GROUP_BATCH/ORDER） |
| 订单侧删/改团期行 | 可删（会造成与团期不一致） | 禁止（582109），引导去团期页 |

## ⑪ 影响评估 / 回滚

- **存量兼容**：`order_staff_assignment` 加 source 列默认 ORDER，存量行全部视为订单专属，原订单 staff 展示不变。
- **前端必改**：订单 staff 列表需按 source 渲染（团期行置灰禁操作）；新增团期配置页对接 A/B 接口。
- **回滚**：接口为新增 + 加字段，回滚后前端忽略 source 字段即可，无破坏性。

## ⑫ 注意事项

- 团期配置页的「团期信息」（团期号/出发日期/产品）由**产品服务 admin 接口**提供，本接口只管 staff。
- 保存后 affectedOrderCount 是「已触发异步写入」数，非「已写完」数；前端如需精确状态可重查订单。
- staffPhone 出参已脱敏（前 3 后 4），列表展示直接用即可。

## ⑬ 关联 / 联系人

- Issue：https://git.1814.love:8443/wx/HL/issues/3560
- PR：https://git.1814.love:8443/wx/HL/pulls/3567
- 后端负责人：腰苏图（订单 v3）
