文件
hl-api-changelog/changelogs-v2/2026-09/10_7455_团期十五个端点补判权-修改接口-管理后台.md
Mimingguang 6fee8114b8
changelog-filename-gate / validate (push) Failing after 2s
chore(changelog): #7455 前端 no-op 回写 not_required
2026-09-10 18:01:41 +08:00

1040 行
39 KiB
Markdown

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: "hl-changelog/v2"
ticket: "7455"
title: "团期十五个 admin 端点补判权:写口 group-batch:manage / 详情名单 view / 列表看板 list"
consumer: "admin"
author: "jw(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "not_required"
frontend_owner: "mmg"
frontend_ref: ""
target_release: ""
verified_at: "2026-09-10"
status_note: "⚠️ 对外行为变更。团期域十五个 admin 端点此前零判权——任意能登录后台的账号都能调,其中六个是写口(物资增改删、确认物资会推进团期阶段、保存人员配置会扇出到团内全部活跃订单、设主次报账人),读口里子订单还带逐户出行人明细。现按三层收口:物资写与人员写要 group-batch:manage,详情/子订单/物资读/人员读要 group-batch:view,列表/看板/产品选择器要 group-batch:list。三个码取自权限种子 V20260831_002 的注册描述,不是新拍的,也不新增授权种子。管理员/超管完全不受影响;财务能看不能改;定制师全部被挡(他的入口在订单侧提需求,不碰团期域端点)。前端两条必看:① 入参出参既有错误码一律未变,不需要改请求代码,只需处理新出现的 589507;② 建议按码做菜单级/按钮级预判——list 决定团期菜单可见、view 决定详情可进、manage 决定编辑按钮可点。判权在进入业务逻辑之前,被拒时零写入、不写时间线、不触发扇出(已用 TEST SQL 佐证)。TEST 上十五个端点×三种角色 45 次调用逐条取证。前端实证 no-op:15 端点请求/响应/错误码未变,唯一新增 589507 由拦截器透 message;本仓从不按权限码前端显隐(grep 零 hasPermission/v-if 按 group-batch 码),可见即可点+589507 兜底,不采纳后端「按码显隐」建议。"
updated_at: "2026-09-10"
base: "dev-v3"
---
# 团期十五个 admin 端点补判权
## 一、给前端的一句话
团期的**列表 / 看板 / 详情 / 子订单名单 / 物资 / 人员配置**这十五个端点,以前**任何能登录后台的账号都能调**
(一个权限都不判),现在按三层收口:看列表要 `group-batch:list`,看详情和名单要 `group-batch:view`,
改物资和人员配置要 `group-batch:manage`。**管理员 / 超级管理员不受影响**,财务**能看不能改**,其余角色返 `589507`。
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 管理员新增团期备品行 | POST | `/v3/admin/order/group-batch/:groupBatchId/supplies` | 修改 | 补判权 `group-batch:manage`,其余不变 |
| 2 | 调整团期备品数量 | PUT | `/v3/admin/order/group-batch/supplies/:batchSuppliesId/quantity` | 修改 | 补判权 `group-batch:manage`,其余不变 |
| 3 | 软删团期备品行 | DELETE | `/v3/admin/order/group-batch/supplies/:batchSuppliesId` | 修改 | 补判权 `group-batch:manage`,其余不变 |
| 4 | 确认团期物资 | POST | `/v3/admin/order/group-batch/:groupBatchId/confirm-material` | 修改 | 补判权 `group-batch:manage`,其余不变 |
| 5 | 全量保存团期人员配置 | PUT | `/v3/admin/group-batch/:productBatchId/staff` | 修改 | 补判权 `group-batch:manage`,其余不变 |
| 6 | 设置团期报账人等级 | PUT | `/v3/admin/group-batch/:productBatchId/staff/:staffId/reporter-rank` | 修改 | 补判权 `group-batch:manage`,其余不变 |
| 7 | 团期详情 | GET | `/v3/admin/order/group-batch/:groupBatchId` | 修改 | 补判权 `group-batch:view`,其余不变 |
| 8 | 团期下子订单列表 | GET | `/v3/admin/order/group-batch/:groupBatchId/orders` | 修改 | 补判权 `group-batch:view`,其余不变 |
| 9 | 查询团期备品列表 | GET | `/v3/admin/order/group-batch/:groupBatchId/supplies` | 修改 | 补判权 `group-batch:view`,其余不变 |
| 10 | 查询备品候选 | GET | `/v3/admin/order/group-batch/:groupBatchId/supplies/candidates` | 修改 | 补判权 `group-batch:view`,其余不变 |
| 11 | 查询团期人员配置列表 | GET | `/v3/admin/group-batch/:productBatchId/staff` | 修改 | 补判权 `group-batch:view`,其余不变 |
| 12 | 查询团期人员候选 | GET | `/v3/admin/group-batch/:productBatchId/staff/candidates` | 修改 | 补判权 `group-batch:view`,其余不变 |
| 13 | 团期分页列表 | GET | `/v3/admin/order/group-batch` | 修改 | 补判权 `group-batch:list`,其余不变 |
| 14 | 团期看板列表 | GET | `/v3/admin/order/group-batch/board` | 修改 | 补判权 `group-batch:list`,其余不变 |
| 15 | 团期控制台产品选择器 | GET | `/v3/admin/order/group-batch/products` | 修改 | 补判权 `group-batch:list`,其余不变 |
## 三、接口详情
> 十五个端点的**入参、出参、业务语义、既有错误码一律未变**,唯一变化是请求进入业务逻辑前多一道权限校验。
### 1. 管理员新增团期备品行 `POST /v3/admin/order/group-batch/:groupBatchId/supplies`
**VO**: `AddSuppliesReqVO`
#### 使用场景
团期详情「物资清单」页手工加一行备品。可从备品库选(带 `suppliesResourceId`),也可纯手填。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `groupBatchId` | path | Long | 是 | 雪花 ID,按字符串传 | 团期 ID |
| `suppliesName` | body | String | 否 | — | 备品名称;从库选时可由服务端回填 |
| `suppliesResourceId` | body | Long | 否 | — | 备品库资源 ID;传了就按库里口径取计费方式与单价 |
| `unitPrice` | body | BigDecimal | 否 | — | 单价;不传取库价 |
| `quantity` | body | Integer | **是** | 非空 | 数量 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | String | 新建的备品行 ID(雪花,按字符串下发) |
#### 请求示例
```http
POST /v3/admin/order/group-batch/2097498512387104770/supplies
Authorization: Bearer <admin token>
Content-Type: application/json
{"suppliesName":"矿泉水","quantity":20,"unitPrice":2.00}
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": "2097963098638876675", "success": true }
```
#### 空数据 / 降级响应
写口无空数据形态。备品库不可用仍报 `589522`,所选备品不存在或已下架仍报 `589519`——**与本次变更无关,行为未改**。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:manage`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时**一行都不落库**(已用 TEST SQL 佐证:被拒后表里查不到该行)。
- 既有的阶段门、备品重复校验(`589523`)全部保留,**顺序在判权之后**。
- 权限码 `group-batch:manage`。
### 2. 调整团期备品数量 `PUT /v3/admin/order/group-batch/supplies/:batchSuppliesId/quantity`
**VO**: `AdjustSuppliesQuantityReqVO`
#### 使用场景
物资清单里改某一行的数量。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `batchSuppliesId` | path | Long | 是 | 雪花 ID | 备品行 ID |
| `quantity` | body | Integer | 是 | — | 新数量 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | null | 无返回体 |
#### 请求示例
```http
PUT /v3/admin/order/group-batch/supplies/8801/quantity
Authorization: Bearer <admin token>
Content-Type: application/json
{"quantity":30}
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": null, "success": true }
```
#### 空数据 / 降级响应
写口无空数据形态。备品行不存在仍报 `589521`,行为未改。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:manage`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时数量**不被改写**。
- 行存在性校验与阶段门保留,顺序在判权之后。
- 权限码 `group-batch:manage`。
### 3. 软删团期备品行 `DELETE /v3/admin/order/group-batch/supplies/:batchSuppliesId`
**VO**: `无请求体`
#### 使用场景
物资清单里删掉一行。软删,不物理删除。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `batchSuppliesId` | path | Long | 是 | 雪花 ID | 备品行 ID |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | null | 无返回体 |
#### 请求示例
```http
DELETE /v3/admin/order/group-batch/supplies/8801
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": null, "success": true }
```
#### 空数据 / 降级响应
写口无空数据形态。行不存在仍报 `589521`,行为未改。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:manage`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时**不软删任何行**。
- 软删语义与阶段门保留,顺序在判权之后。
- 权限码 `group-batch:manage`。
### 4. 确认团期物资 `POST /v3/admin/order/group-batch/:groupBatchId/confirm-material`
**VO**: `无请求体`
#### 使用场景
物资备齐后点「确认物资清单」。这个动作会把团期从「物料准备中」推向「待出发」(需同时满足子订单全确认的双门)。**六个写口里它的破坏力最大——它推的是团期阶段。**
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `groupBatchId` | path | Long | 是 | 雪花 ID,按字符串传 | 团期 ID |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | null | 无返回体 |
#### 请求示例
```http
POST /v3/admin/order/group-batch/2097498512387104770/confirm-material
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": null, "success": true }
```
#### 空数据 / 降级响应
写口无空数据形态。双门未同时满足时状态停在「物料准备中」并照常返回成功,这是既有语义,行为未改。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:manage`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时**团期阶段不被推进**,`material_confirmed` 不置位,时间线不写。
- 团期不存在仍 `589500`、状态不符仍 `589501`;**顺序:先判权,再判状态**。
- 权限码 `group-batch:manage`。
### 5. 全量保存团期人员配置 `PUT /v3/admin/group-batch/:productBatchId/staff`
**VO**: `BatchStaffConfigReqVO`
#### 使用场景
团期「导游 / 摄影」页整批保存人员配置。**传入列表即最终状态(全量覆盖)**,保存成功后**异步扇出到团内全部活跃订单**。⚠️ 路径前缀是 `/v3/admin/group-batch`(**不在 `/order/` 下**),路径变量是产品侧排期 ID。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `productBatchId` | path | Long | 是 | 雪花 ID;**产品侧排期 ID,不是团期主键** | 产品侧排期 ID |
| `staffList` | body | Array | **是** | 非空字段(`#7377` 起字段缺失一律 400;**要清空请显式传 `[]`**) | 人员配置列表,全量覆盖 |
| `staffList[].staffId` | body | Long | 是 | — | 员工 ID |
| `staffList[].staffRole` | body | String | 是 | 取值域 + 资源域实际 `staffType` 双校验(`582114`) | 角色位 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | Object | 保存后的配置快照(结构未变) |
#### 请求示例
```http
PUT /v3/admin/group-batch/2097498511120478210/staff
Authorization: Bearer <admin token>
Content-Type: application/json
{"staffList":[{"staffId":3301,"staffRole":"GUIDE"}]}
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": { "staffList": [] }, "success": true }
```
#### 空数据 / 降级响应
显式传 `staffList: []` 表示清空配置,仍是合法请求(`#7377` 定的语义,未改);字段缺失一律 400,也未改。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:manage`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时**既不覆盖配置,也不触发向团内订单的扇出**。
- `#7377` 的 `@NotNull`、`582114` 类型校验保留,顺序在判权之后。
- 权限码 `group-batch:manage`。
### 6. 设置团期报账人等级 `PUT /v3/admin/group-batch/:productBatchId/staff/:staffId/reporter-rank`
**VO**: `SetReporterRankReqVO`
#### 使用场景
指定团期的主 / 次报账人。报账人决定核团阶段**谁来记账、成本记在谁头上**,不是无关紧要的标记。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `productBatchId` | path | Long | 是 | 雪花 ID;**产品侧排期 ID,不是团期主键** | 产品侧排期 ID |
| `staffId` | path | Long | 是 | 必须是该团期已配置的 staff | 员工 ID |
| `reporterRank` | body | String(枚举) | 是 | `PRIMARY` / `SECONDARY` / `NONE` | 报账人等级 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | null | 无返回体 |
#### 请求示例
```http
PUT /v3/admin/group-batch/2097498511120478210/staff/3301/reporter-rank
Authorization: Bearer <admin token>
Content-Type: application/json
{"reporterRank":"PRIMARY"}
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": null, "success": true }
```
#### 空数据 / 降级响应
写口无空数据形态。团期尚未成团仍报 `589552`,行为未改。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:manage`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时**报账人一个字段都不被改动**(含「设新主报账人时原主自动降级」这条连带效果)。
- 团期内 `PRIMARY` / `SECONDARY` 各唯一的既有语义保留。
- 权限码 `group-batch:manage`。
### 7. 团期详情 `GET /v3/admin/order/group-batch/:groupBatchId`
**VO**: `GroupBatchDetailRespVO`
#### 使用场景
团期详情页主接口。出参含**整团应收 / 已收 / 已预支金额**、四项资源就绪位、成团门槛与报账人——比 `chips/*` 更该判权,而 `chips/*` 早就判了。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `groupBatchId` | path | Long | 是 | 雪花 ID,按字符串传 | 团期 ID |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | Object | 团期详情(字段结构未变) |
| `data.totalReceivable` / `totalReceived` | Number | 整团应收 / 已收(实时聚合) |
| `data.minToForm` | Integer | 成团门槛(户) |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2097498512387104770
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": { "groupBatchId": "2097498512387104770", "batchStatus": "RESOURCE_PREPARING" }, "success": true }
```
#### 空数据 / 降级响应
读口。团期不存在仍报 `589500`(不是空对象),行为未改。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:view`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时**不查团期、不返回任何金额字段**。
- 团期不存在的 `589500` 排在判权之后。
- ⚠️ `#7376` 的旁路探针 `GB_DETAIL_ACL_PROBE` 随本次判权落地**已删除**——判权之后它只会记录必然 `hasView=true` 的调用。
- 权限码 `group-batch:view`。
### 8. 团期下子订单列表 `GET /v3/admin/order/group-batch/:groupBatchId/orders`
**VO**: `GroupBatchOrderItemRespVO`
#### 使用场景
团期详情「子订单」页。`includeTravelers=true` 时**带出逐户出行人明细**(证件号不返回),是本批里最敏感的读口。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `groupBatchId` | path | Long | 是 | 雪花 ID,按字符串传 | 团期 ID |
| `includeTravelers` | query | Boolean | 否 | 缺省 false | 是否附出行人明细 |
| `includeNeeds` | query | Boolean | 否 | 缺省 false | 是否附房数 / 房型 / 特殊需求 |
| `includeCancelled` | query | Boolean | 否 | 缺省 false | 是否含已取消子订单 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | Array | 子订单列表(字段结构未变) |
| `data[].travelers` | Array | 出行人明细;仅 `includeTravelers=true` 时出现 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2097498512387104770/orders?includeTravelers=true
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": [], "success": true }
```
#### 空数据 / 降级响应
团期无活跃子订单时返回空数组,不报错——既有语义,未改。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:view`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时**出行人明细一条都不返回**。
- 活跃集口径(缺省排除已取消)未变。
- 权限码 `group-batch:view`。
### 9. 查询团期备品列表 `GET /v3/admin/order/group-batch/:groupBatchId/supplies`
**VO**: `GroupBatchSuppliesRespVO`
#### 使用场景
团期详情「物资清单」页的列表。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `groupBatchId` | path | Long | 是 | 雪花 ID,按字符串传 | 团期 ID |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | Array | 备品行列表(字段结构未变) |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2097498512387104770/supplies
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": [], "success": true }
```
#### 空数据 / 降级响应
无备品行时返回空数组,不报错——既有语义,未改。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:view`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时不查任何备品行。
- 与写口同在一个页面,但**读走 `view`、写走 `manage`**:能看不等于能改。
- 权限码 `group-batch:view`。
### 10. 查询备品候选 `GET /v3/admin/order/group-batch/:groupBatchId/supplies/candidates`
**VO**: `SuppliesCandidateRespVO`
#### 使用场景
加备品行时的备品库下拉候选,支持按分类与关键字过滤。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `groupBatchId` | path | Long | 是 | 雪花 ID,按字符串传 | 团期 ID |
| `categoryCode` | query | String | 否 | — | 分类过滤 |
| `keyword` | query | String | 否 | `#7377` 起 LIKE 通配符已转义 | 名称关键字 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | Array | 候选备品列表(字段结构未变) |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2097498512387104770/supplies/candidates?keyword=水
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": [], "success": true }
```
#### 空数据 / 降级响应
无匹配候选时返回空数组。搜 `%` 或 `_` 从 `#7377` 起是零结果而非全库,行为本次未改。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:view`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时不查备品库。
- `#7377` 的关键字转义保留。
- 权限码 `group-batch:view`。
### 11. 查询团期人员配置列表 `GET /v3/admin/group-batch/:productBatchId/staff`
**VO**: `BatchStaffConfigRespVO`
#### 使用场景
团期「导游 / 摄影」页的配置列表,手机号脱敏(前 3 后 4)后返回。⚠️ 路径变量是产品侧排期 ID。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `productBatchId` | path | Long | 是 | 雪花 ID;**产品侧排期 ID,不是团期主键** | 产品侧排期 ID |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | Object | 人员配置(字段结构未变,手机号脱敏) |
#### 请求示例
```http
GET /v3/admin/group-batch/2097498511120478210/staff
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": { "staffList": [] }, "success": true }
```
#### 空数据 / 降级响应
未配置人员时返回空列表,不报错——既有语义,未改。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:view`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时不查人员配置。
- 手机号脱敏规则未变。
- 权限码 `group-batch:view`。
### 12. 查询团期人员候选 `GET /v3/admin/group-batch/:productBatchId/staff/candidates`
**VO**: `StaffCandidateRespVO`
#### 使用场景
配人员时按角色位拉候选人。`#7028` 起勾选态按配置位收敛,`assignedRole` 仍带真实角色。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `productBatchId` | path | Long | 是 | 雪花 ID;**产品侧排期 ID,不是团期主键** | 产品侧排期 ID |
| `role` | query | String | 是 | 角色位 | 按角色位过滤候选人 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | Array | 候选人列表(字段结构未变) |
#### 请求示例
```http
GET /v3/admin/group-batch/2097498511120478210/staff/candidates?role=GUIDE
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": [], "success": true }
```
#### 空数据 / 降级响应
无候选人时返回空数组。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:view`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时不查资源域候选人。
- `#7028` 的勾选态收敛口径未变。
- 权限码 `group-batch:view`。
### 13. 团期分页列表 `GET /v3/admin/order/group-batch`
**VO**: `GroupBatchPageItemRespVO`
#### 使用场景
团期列表页。出参含**整团已收合计**(实时聚合)。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `pageNum` / `pageSize` | query | Integer | 否 | — | 分页参数 |
| `batchStatus` 等筛选项 | query | String | 否 | — | 既有筛选项,未变 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data.records` | Array | 团期行(字段结构未变) |
| `data.total` | Number | 总条数 |
#### 请求示例
```http
GET /v3/admin/order/group-batch?pageNum=1&pageSize=20
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": { "records": [], "total": 0 }, "success": true }
```
#### 空数据 / 降级响应
无数据时 `records` 为空数组、`total` 为 0,不报错。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:list`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时不查列表。
- ⚠️ **走 `group-batch:list` 不是 `view`**——权限种子把 GB-ADM-001 划给了 `list`。
- 权限码 `group-batch:list`。
### 14. 团期看板列表 `GET /v3/admin/order/group-batch/board`
**VO**: `GroupBatchBoardItemRespVO`
#### 使用场景
团期看板。以产品全班期为基底左连订单侧团期记录,返回命中 / 未命中 / 孤儿三类行。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `productId` | query | Long | 是 | 雪花 ID | 产品 ID |
| `scope` | query | String | 否 | `ONGOING` / `FINISHED` / `ALL`,缺省 `ALL` | 班期范围 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | Array | 看板行(字段结构未变) |
#### 请求示例
```http
GET /v3/admin/order/group-batch/board?productId=2097498511120478210
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": [], "success": true }
```
#### 空数据 / 降级响应
产品无班期时返回空数组,不报错。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:list`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时不查看板。
- `scope` 缺省行集与改前逐字一致(`#7189`),本次未动。
- 权限码 `group-batch:list`。
### 15. 团期控制台产品选择器 `GET /v3/admin/order/group-batch/products`
**VO**: `GroupBatchProductItemRespVO`
#### 使用场景
看板上游的产品下拉。返回 PUBLISHED 产品 + 有活跃订单的非 PUBLISHED 产品。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `productType` | query | String | 否 | 缺省不限 | 产品类型 |
| `keyword` | query | String | 否 | — | 产品名称关键词 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | Array | 产品列表(字段结构未变) |
#### 请求示例
```http
GET /v3/admin/order/group-batch/products
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": [], "success": true }
```
#### 空数据 / 降级响应
无匹配产品时返回空数组。
#### 错误响应
**本次新增的拒绝形态**——当前角色没有 `group-batch:list`:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
缺少或无效的 token 由网关拦截返 `401`。本端点不新增其它业务错误码。
#### 业务边界
- 判权发生在**进入业务逻辑之前**;权限源不可用时**失败关闭**按拒绝处理。
- 拒绝时不查产品。
- 与看板同性质,同走 `list`。
- 权限码 `group-batch:list`。
## 四、契约约束与正确调用方式
1. **前端不需要改任何请求代码。** 入参、出参、既有错误码一律未变,只是多了一种失败可能。
2. **要处理 `589507`。** 这十五个口现在可能返 `589507`,按「无权限」提示即可,别当业务失败重试。
3. **更好的做法是按钮级 / 菜单级预判**:能登录后台不等于能进团期。建议按当前角色是否持有
`group-batch:list` 决定团期菜单可见性,`group-batch:view` 决定详情页可进,
`group-batch:manage` 决定物资与人员配置的编辑按钮可见 / 置灰。
4. **三个码是分层的,不要混用**:`list`(列表 / 看板 / 产品选择器)→ `view`(详情 / 名单 / 物资读 / 人员读)
→ `manage`(物资写 / 人员写)。这个划分取自权限种子 `V20260831_002` 的注册描述,不是本单新拍。
5. **雪花 ID 一律按字符串处理**。
6. ⚠️ **别把 `productBatchId` 当成 `groupBatchId`**:人员配置那四个口的路径变量是**产品侧排期 ID**,
前缀也不在 `/order/` 下。这是既有形态,本次未改。
## 五、数据库行为
- **无表变更、无 Flyway、无数据迁移。**
- 三个权限码 `group-batch:list` / `view` / `manage` **早已存在**(`V20260831_002` #6902、`V20260906_002` #7158),
本次**不新增授权种子**。
- 拒绝路径**零写入**:判权在进入 Service 事务之前,被拒的请求不产生任何行变更、不写时间线、不触发扇出。
已用 TEST SQL 佐证(CUSTOMIZER 调「新增备品行」被拒后,表里查不到该行)。
## 六、边界行为
| 场景 | 行为 |
|---|---|
| 角色持有对应权限码 | 与改前完全一致 |
| 角色没有对应码 | `589507`,且**零副作用** |
| 缺少 / 无效 token | 网关拦截返 `401`(未到业务层) |
| 权限源(user-service)不可用 | **失败关闭 → 按无权限拒绝**,不放行 |
| 团期不存在 / 状态不对 / 备品行不存在 | 既有错误码不变,**但判权在前**——无权限时先返 `589507` |
| `confirm-material` 被拒 | 团期阶段不推进、`material_confirmed` 不置位、时间线不写 |
| `saveConfig` 被拒 | 配置不覆盖、**扇出不触发** |
| `reporter-rank` 被拒 | 报账人不改动,原主 / 次不降级 |
| 子订单列表被拒 | **出行人明细一条都不返回** |
## 六.6、修改前后对比
| 端点组 | 改前 | 改后 |
|---|---|---|
| 物资 3 写 + `confirm-material` + 人员 2 写 | **零判权**:任意能登录后台的账号都可调 | `group-batch:manage` |
| 详情 / 子订单 / 物资读 2 / 人员读 2 | **零判权** | `group-batch:view` |
| 分页列表 / 看板 / 产品选择器 | **零判权** | `group-batch:list` |
| 判权位置 | 无 | Controller 方法体第一行(进 Service 事务之前) |
| 权限源不可用时 | 无判权,直接放行 | 失败关闭,按无权限拒绝 |
改前的实测证据(`origin/dev-v3`,用 `role=CUSTOMIZER` 打真实网关):这十五个口一律 **200**;
而同团期的 `chips/*`、`finance`、`settlement/*`、`contracts`、`requirement/*`、`status-logs`、`itinerary`
一律 `589507`。**同一个 controller 包里有的加了有的没加,是漏加不是设计如此。**
## 六.7、影响评估
TEST 库实测的授予关系:
| 权限码 | 授予角色 |
|---|---|
| `group-batch:list` | `SUPER_ADMIN`、`ADMIN`、`FINANCE` |
| `group-batch:view` | `SUPER_ADMIN`、`ADMIN`、`FINANCE` |
| `group-batch:manage` | `SUPER_ADMIN`、`ADMIN` |
系统里**没有独立的「团期管理员」角色**(`sys_role` 共 11 个),原型所说的团期管理员就是 `ADMIN`。
**完全不受影响**:`SUPER_ADMIN`、`ADMIN`。
**读得到、改不了**:`FINANCE`——能看团期列表、详情、物资、人员配置,但不能改物资与人员配置。这是修正而非损失:改物资清单、改人员配置本来就不是财务的职责。
**全部被挡**:`CUSTOMIZER`、`ROOM_MANAGER`、`VEHICLE_MANAGER`、`OPERATOR`、`CUSTOMER_SERVICE`、`MATERIAL_ADMIN`、`house_keeper_lead`、`order_console`。
三条判断依据:
1. **`group-batch:manage` 早已在「成团」端点上跑通**(`#7158`),`list` / `view` 也早已在统计条、导出、
六芯片、合同面板上跑通。这几个码覆盖的就是真实操作人群,本次只是把漏网的十五个口接上同一套码。
2. **定制师本来就不该有**:jw 2026-09-10 口径——定制师负责**提需求**,走订单侧
`POST /v3/admin/order/:id/adjustment/submit`;团期管理员确认后再流转到房务 / 车务。
定制师不碰团期域端点。**TEST 探针日志实测印证**:`GB_DETAIL_ACL_PROBE` 采到
`roleKey=CUSTOMIZER hasView=false`、`SUPER_ADMIN hasView=true`、`FINANCE hasView=true`,与种子完全一致。
3. **房务 / 车务走自己的域**:房务在 `com.hulalv.house`(`#7322`~`#7327`),车务在 `fleet`(`#7439`~`#7444`),
都有各自的权限体系,不依赖团期域这十五个口。
**如果上线后发现某个角色确实需要**:在 user-service 给该角色补对应码即可,**不需要改代码**。
⚠️ `order_console`(测试用角色)会被挡,TEST 上若有自动化脚本用它调这些口需改角色或补授权。
## 七、不影响范围
- 不改任何端点的路径、入参、出参、既有错误码。
- **不改任何业务逻辑**:阶段门、双门推进、重复校验、扇出、时间线、活跃集口径全部原样保留,只是排在判权之后。
- 无表变更、无 Flyway、无新增错误码(复用 `589507`)、无网关路由变更、无新增授权种子。
- 只滚 `hl-order-service-v3` 一个服务。
## 八、测试环境已验证
TEST(`api.test.1814.love`)真实网关,**十五个端点 × 三种角色 = 45 次调用逐条取证**(2026-09-10)。
角色 token 用 dev 默认 JWT 密钥自签,打的是真实网关与真实权限源。
| # | 端点 | 权限码 | `CUSTOMIZER` | `FINANCE` | `SUPER_ADMIN` |
|---|---|---|---|---|---|
| 1 | 物资 新增行 | manage | `589507` | `589507` | 越过判权(`589520` 业务前置) |
| 2 | 物资 改数量 | manage | `589507` | `589507` | 越过判权(`589521` 行不存在) |
| 3 | 物资 删行 | manage | `589507` | `589507` | 越过判权(`589521` 行不存在) |
| 4 | 确认物资 | manage | `589507` | `589507` | 越过判权(`589501` 状态不符) |
| 5 | 人员配置 保存 | manage | `589507` | `589507` | 越过判权(`589552` 尚未成团) |
| 6 | 设报账人 | manage | `589507` | `589507` | 越过判权(`589552` 尚未成团) |
| 7 | 团期详情 | view | `589507` | `200` | `200` |
| 8 | 子订单(带出行人明细) | view | `589507` | `200` | `200` |
| 9 | 物资列表 | view | `589507` | `200` | `200` |
| 10 | 备品候选 | view | `589507` | `200` | `200` |
| 11 | 人员配置列表 | view | `589507` | `200` | `200` |
| 12 | 人员候选 | view | `589507` | `200` | `200` |
| 13 | 分页列表 | list | `589507` | `200` | `200` |
| 14 | 看板 | list | `589507` | `200`(17 行) | `200`(17 行) |
| 15 | 产品选择器 | list | `589507` | `200` | `200` |
**放行侧怎么证明的**:`manage` 组是写口,不能真跑(会造脏数据),改用「必然失败的业务前置」——
`SUPER_ADMIN` 拿到的是各自的**业务错误码**而不是 `589507`,说明请求**已经越过判权层进入业务逻辑**。
**零写入的 SQL 佐证**:`CUSTOMIZER` 调「新增备品行」被拒后,
`SELECT count(*) FROM order_batch_supplies WHERE supplies_name LIKE '#7455探针%'` → **0**。两轮验收各查一次,均为 0。
**分层判权生效**:`FINANCE` 在 `view` / `list` 组全部 `200`、在 `manage` 组全部 `589507`——
这正是「能看不能改」的设计意图,不是巧合。
**探针日志聚合(AC-1,删除探针前采集)**:`GB_DETAIL_ACL_PROBE` 采到
`roleKey=CUSTOMIZER hasView=false`、`SUPER_ADMIN hasView=true`、`FINANCE hasView=true`,
与权限种子 `V20260831_002` 的角色集完全一致。
⚠️ **如实标注**:样本是自造流量(TEST 无真实运营流量),只能证明探针工作正常与判定口径正确,
**不能代表生产调用方分布**;收口决策的主依据是职责链路论证(见「六.7 影响评估」第 2 条)。
**回归取证(AC-11)**:以 `SUPER_ADMIN` 走完看板 → 列表 → 详情 → 子订单 → 物资 → 人员配置
一条完整路径,全部 `200`,团期页面主链路未被打断。
**本机全量**:`mvn -pl hl-order-service-v3 test` → **Tests run: 9657, Failures: 0, Errors: 0, Skipped: 7, BUILD SUCCESS**
(含 `MapperBoundaryArchTest` 26 例、`LayerEnforcementTest` 5 例——跨域引用 guard 未破边界)。
## 十、相关文档
- 工单:`#7455`(本单),前序 `#7376`(旁路探针,本单是它的收口)
- 同类先例:`#7316`(全团需求汇总补 `view`)、`#7411`(核单共享成本补判权,本单的单测范式来自它)
- 权限种子:`V20260831_002`(`list` / `view` / `export`,#6902)、`V20260906_002`(`manage`,#7158)
- 契约:《团期模块接口文档 v2.0》GB-ADM-000/001/002/003/014/024/029/035/036/037
## 关联 / 联系人
- 工单:https://git.1814.love:8443/wx/HL/issues/7455
- PR:https://git.1814.love:8443/wx/HL/pulls/7483
- 后端:jw;前端:mmg