文件
hl-api-changelog/changelogs-v2/2026-10/02_8717_团期读端点定制师归属校验-修改接口-管理后台.md
T
API Changelog Bot和Claude Opus 5.5 b54834b3f5
changelog-filename-gate / validate (push) Failing after 1s
docs(changelog): #8717 团期 21 个读端点补定制师归属校验,非本人团期返回 589507
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 17:36:28 +08:00

1370 行
36 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: "hl-changelog/v2"
ticket: "8717"
title: "团期读端点定制师归属校验:21 个端点新增定制师非本人团期返回 589507"
consumer: "admin"
author: "wx(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "not_required"
frontend_status: "not_required"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: ""
updated_at: "2026-10-02"
base: "dev-v3"
---
# 团期读端点定制师归属校验:21 个端点新增 589507 拒绝码
> **存放目录**: 二期 → `changelogs-v2/2026-10/`
>
> **服务**: hl-order-service-v3
> **Issue**: #8717
> **日期**: 2026-10-02
> **影响范围**: 管理后台团期详情页及相关弹窗的 21 个数据读端点
---
## ⚠️ 关键变化
**CUSTOMIZER(定制师)角色对非本人团期的 21 个读端点,新增返回业务码 589507 的场景。** 该团期属于定制师本人(存在一笔在团且非 CANCELLED 的子订单其顾问 ID 等于本人)时,返回 200 与完整数据。不属于本人时返回 HTTP 200 + `code: 589507` + `data: null`。其他角色(管理员、超管、团期主管等)对这些端点**无变化**。
---
## 一、背景
#7949 授予 CUSTOMIZER 权限码 `group-batch:view` 用于打开自己团期的订单调整弹窗,但当时仅在 `GET /{id}` 与 `GET /{id}/orders` 两个端点补了数据级归属校验。同一团期的其余 21 个读视图端点(逐户名单、配房、行程、签单凭证等)仍缺少该校验,导致任一定制师可读全公司所有团期的客户名、联系人、已付金额等敏感信息。本次补齐这 21 个端点的归属校验,与既有两个端点采用同一口径:团期「属于」定制师当且仅当该团期存在在团(非 CANCELLED)子订单其顾问 ID 等于当前登录用户。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 需求汇总 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement-summary` | 修改 | 新增 589507(定制师非本人团期) |
| 2 | 逐户订房记录 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement/hotel-households` | 修改 | 新增 589507(定制师非本人团期) |
| 3 | 逐户用车记录 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement/vehicle-households` | 修改 | 新增 589507(定制师非本人团期) |
| 4 | 配房方案 | GET | `/v3/admin/order/group-batch/{groupBatchId}/room-plans` | 修改 | 新增 589507(定制师非本人团期) |
| 5 | 酒店芯片详情 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/hotel` | 修改 | 新增 589507(定制师非本人团期) |
| 6 | 用车芯片详情 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/vehicle` | 修改 | 新增 589507(定制师非本人团期) |
| 7 | 导游芯片详情 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/guide` | 修改 | 新增 589507(定制师非本人团期) |
| 8 | 摄影芯片详情 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/photo` | 修改 | 新增 589507(定制师非本人团期) |
| 9 | 合同芯片详情 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/contract` | 修改 | 新增 589507(定制师非本人团期) |
| 10 | 保险芯片详情 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/insurance` | 修改 | 新增 589507(定制师非本人团期) |
| 11 | 合同看板 | GET | `/v3/admin/order/group-batch/{groupBatchId}/contracts` | 修改 | 新增 589507(定制师非本人团期) |
| 12 | 团级行程单打印 | GET | `/v3/admin/order/group-batch/{groupBatchId}/print-itinerary` | 修改 | 新增 589507(定制师非本人团期) |
| 13 | 签单凭证 | GET | `/v3/admin/order/group-batch/{groupBatchId}/sign-voucher` | 修改 | 新增 589507(定制师非本人团期) |
| 14 | 行程汇总 | GET | `/v3/admin/order/group-batch/{groupBatchId}/itinerary` | 修改 | 新增 589507(定制师非本人团期) |
| 15 | 行程节点明细 | GET | `/v3/admin/order/group-batch/{groupBatchId}/itinerary/nodes/{nodeKey}` | 修改 | 新增 589507(定制师非本人团期) |
| 16 | 状态流水 | GET | `/v3/admin/order/group-batch/{groupBatchId}/status-logs` | 修改 | 新增 589507(定制师非本人团期) |
| 17 | 物资清单 | GET | `/v3/admin/order/group-batch/{groupBatchId}/supplies` | 修改 | 新增 589507(定制师非本人团期) |
| 18 | 物资候选 | GET | `/v3/admin/order/group-batch/{groupBatchId}/supplies/candidates` | 修改 | 新增 589507(定制师非本人团期) |
| 19 | 撤团候选 | GET | `/v3/admin/order/group-batch/{groupBatchId}/withdraw-candidates` | 修改 | 新增 589507(定制师非本人团期) |
| 20 | 撤团预览 | GET | `/v3/admin/order/group-batch/{groupBatchId}/sub-order/{orderId}/withdraw-preview` | 修改 | 新增 589507(定制师非本人团期) |
| 21 | 转团候选 | GET | `/v3/admin/order/group-batch/{groupBatchId}/transfer-candidates` | 修改 | 新增 589507(定制师非本人团期) |
---
## 三、接口详情
**公共说明**:所有 21 个接口的行为变化相同。改前 CUSTOMIZER 对任意团期 ID 均返回 200。改后仅本人团期返回 200,其余返回 HTTP 200 + `code: 589507` + `data: null`。非 CUSTOMIZER 角色(ADMIN / SUPER_ADMIN / GROUP_BATCH_MANAGER / FINANCE 等)无变化。
### 1. 需求汇总 `GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary`
**VO**: `GroupRequirementSummaryRespVO`
#### 使用场景
团期详情页"需求"Tab 的汇总数据(酒店间数、用车数量等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 详见改前契约;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/requirement-summary
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "hotelCount": 8, "vehicleCount": 3, "totalGuests": 24 },
"success": true
}
```
#### 空数据 / 降级响应
- 团期不存在:返回 589500「团期不存在」(顺序:角色码 → 归属 → 存在性)。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:CUSTOMIZER 对非本人团期(无在团子订单或顾问 ID 不匹配)请求时返回。
- **定制师本人团期**:该团期下存在一笔在团(非 CANCELLED 状态)的子订单其 `consultantId` 等于当前登录用户。
- **系统上下文**:以 system 用户(无登录态)请求时放行,不返回 589507。
### 2. 逐户订房记录 `GET /v3/admin/order/group-batch/{groupBatchId}/requirement/hotel-households`
**VO**: `GroupHotelHouseholdsRespVO`
#### 使用场景
团期详情页"需求"Tab 的逐户订房明细(客户名、订房房型、金额等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 含 customerName / consultantName / remark 等;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/requirement/hotel-households
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "households": [ { "customerName": "张三", "roomType": "大床房", "nights": 3 } ] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无订房需求:返回空数组。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
- **列表可能为空**:即使 CUSTOMIZER 本人团期,若无订房需求也返回空数组而非 589507。
### 3. 逐户用车记录 `GET /v3/admin/order/group-batch/{groupBatchId}/requirement/vehicle-households`
**VO**: `GroupVehicleHouseholdsRespVO`
#### 使用场景
团期详情页"需求"Tab 的逐户用车明细(客户名、车型、备注等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 含 customerName / specialTags / remark 等;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/requirement/vehicle-households
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "households": [ { "customerName": "李四", "vehicleType": "中巴", "remark": "轮椅可达" } ] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无用车需求:返回空数组。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 4. 配房方案 `GET /v3/admin/order/group-batch/{groupBatchId}/room-plans`
**VO**: `GroupBatchRoomPlanDetailRespVO`
#### 使用场景
团期详情页"配房"Tab 的配房方案(酒店、房型、间数、价格等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 含 hotelName / roomTypeName / travelerCount 等;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/room-plans
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "hotelName": "如家酒店", "roomType": "标准间", "travelerCount": 8 },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无配房数据:返回空数据或空数组。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 5. 酒店芯片详情 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/hotel`
**VO**: `GroupBatchChipDetailVO`
#### 使用场景
团期详情页"芯片"弹窗中酒店芯片的详细信息(人数、供应商列表、备注等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 芯片聚合数据;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/chips/hotel
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "count": 3, "staffList": [], "items": [] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无酒店芯片:返回空数据。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 6. 用车芯片详情 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/vehicle`
**VO**: `GroupBatchChipDetailVO`
#### 使用场景
团期详情页"芯片"弹窗中用车芯片的详细信息。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 芯片聚合数据;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/chips/vehicle
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "count": 1, "staffList": [], "items": [] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无用车芯片:返回空数据。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 7. 导游芯片详情 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/guide`
**VO**: `GroupBatchChipDetailVO`
#### 使用场景
团期详情页"芯片"弹窗中导游芯片的详细信息。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 芯片聚合数据;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/chips/guide
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "count": 2, "staffList": [], "items": [] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无导游芯片:返回空数据。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 8. 摄影芯片详情 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/photo`
**VO**: `GroupBatchChipDetailVO`
#### 使用场景
团期详情页"芯片"弹窗中摄影芯片的详细信息。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 芯片聚合数据;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/chips/photo
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "count": 1, "staffList": [], "items": [] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无摄影芯片:返回空数据。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 9. 合同芯片详情 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/contract`
**VO**: `GroupBatchChipDetailVO`
#### 使用场景
团期详情页"芯片"弹窗中合同芯片的详细信息。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 芯片聚合数据;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/chips/contract
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "count": 1, "staffList": [], "items": [] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无合同芯片:返回空数据。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 10. 保险芯片详情 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/insurance`
**VO**: `GroupBatchChipDetailVO`
#### 使用场景
团期详情页"芯片"弹窗中保险芯片的详细信息。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 芯片聚合数据;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/chips/insurance
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "count": 1, "staffList": [], "items": [] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无保险芯片:返回空数据。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 11. 合同看板 `GET /v3/admin/order/group-batch/{groupBatchId}/contracts`
**VO**: `GroupBatchContractBoardVO`
#### 使用场景
团期详情页"合同"Tab 的合同签署看板(签署状态、进度等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 合同看板数据;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/contracts
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "items": [] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无合同数据:返回空数据。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 12. 团级行程单打印 `GET /v3/admin/order/group-batch/{groupBatchId}/print-itinerary`
**VO**: `GroupPrintItineraryRespVO`
#### 使用场景
团期详情页"文档"弹窗中团级行程单的打印数据(日期、酒店、金额等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 含 contactName / dayTotalAmount / totalAmount 等;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/print-itinerary
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "contactName": "王五", "totalAmount": "15000.00" },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无行程数据:返回空数据。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 13. 签单凭证 `GET /v3/admin/order/group-batch/{groupBatchId}/sign-voucher`
**VO**: `GroupSignVoucherRespVO`
#### 使用场景
团期详情页"文档"弹窗中签单凭证的打印数据(联系人、电话、金额等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
| showAmount | Query | Boolean | ❌ | - | 是否显示金额 |
| scope | Query | String | ❌ | - | 凭证范围 |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 含 contactPerson / contactPhone / totalAmount 等;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/sign-voucher?showAmount=true
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "contactPerson": "张三", "contactPhone": "13800138000", "totalAmount": "15000.00" },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无凭证数据:返回空数据。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
- **查询参数不变**:`showAmount` 与 `scope` 的默认值、约束保持不变。
### 14. 行程汇总 `GET /v3/admin/order/group-batch/{groupBatchId}/itinerary`
**VO**: `GroupBatchItineraryRespVO`
#### 使用场景
团期详情页"行程"Tab 的逐日汇总(日期、酒店、景点、金额等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 含 contactName / dayTotalAmount / totalAmount 等;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/itinerary
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "days": [ { "dayNumber": 1, "hotel": "如家酒店", "dayTotalAmount": "2000.00" } ] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无行程数据:返回空数据或空数组。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 15. 行程节点明细 `GET /v3/admin/order/group-batch/{groupBatchId}/itinerary/nodes/{nodeKey}`
**VO**: `GroupBatchItineraryNodeDetailVO`
#### 使用场景
团期详情页"行程"Tab 中行程节点(某天的具体景点、酒店、出发地点等)的明细信息。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
| nodeKey | Path | String | ✅ | - | 行程节点键 |
| dayNumber | Query | Integer | ❌ | - | 第几天(可选) |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 节点级行程数据;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/itinerary/nodes/node_001?dayNumber=1
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "nodeKey": "node_001", "location": "颐和园", "dayNumber": 1 },
"success": true
}
```
#### 空数据 / 降级响应
- 节点不存在:返回空数据。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
- **路径参数**:`nodeKey` 必填,`dayNumber` 为可选查询参数。
### 16. 状态流水 `GET /v3/admin/order/group-batch/{groupBatchId}/status-logs`
**VO**: `无请求体 → List<GroupBatchStatusLogVO>`
#### 使用场景
团期详情页"概览"等区域的操作历史(谁在什么时间做了什么操作)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 状态流水数组;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/status-logs
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": [ { "operator": "李四", "operationTime": "2026-10-02 14:30", "action": "确认配房" } ],
"success": true
}
```
#### 空数据 / 降级响应
- 团期无操作历史:返回空数组。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 17. 物资清单 `GET /v3/admin/order/group-batch/{groupBatchId}/supplies`
**VO**: `GroupBatchSuppliesRespVO`
#### 使用场景
团期详情页"物资"区域的清单数据(物资名称、单价、数量等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 含单价等信息;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/supplies
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "supplies": [ { "name": "导游讲解", "unitPrice": "100.00" } ] },
"success": true
}
```
#### 空数据 / 降级响应
- 团期无物资数据:返回空数组。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 18. 物资候选 `GET /v3/admin/order/group-batch/{groupBatchId}/supplies/candidates`
**VO**: `无请求体 → List<SupplyCandidateVO>`
#### 使用场景
团期详情页"物资"区域添加物资时的候选物资下拉/搜索数据。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 物资候选数据;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/supplies/candidates
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": [ { "id": "2105...", "name": "导游讲解", "unitPrice": "100.00" } ],
"success": true
}
```
#### 空数据 / 降级响应
- 无可选物资:返回空数组。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 19. 撤团候选 `GET /v3/admin/order/group-batch/{groupBatchId}/withdraw-candidates`
**VO**: `WithdrawCandidateVO` 列表
#### 使用场景
团期详情页"撤团"弹窗中可撤团的子订单候选列表(客户名、已付金额等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 含 customerName / paidAmount 等;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/withdraw-candidates
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": [ { "orderId": "2105...", "customerName": "张三", "paidAmount": "5000.00" } ],
"success": true
}
```
#### 空数据 / 降级响应
- 团期无可撤订单:返回空数组。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 20. 撤团预览 `GET /v3/admin/order/group-batch/{groupBatchId}/sub-order/{orderId}/withdraw-preview`
**VO**: `无请求体 → WithdrawPreviewVO`
#### 使用场景
团期详情页"撤团"弹窗中选中某个子订单后,展示该订单的撤团预览数据(退款金额、涉及的应收应付等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
| orderId | Path | Long | ✅ | - | 子订单 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 撤团预览数据(金额等);定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/sub-order/2105709698592440322/withdraw-preview
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": { "refundAmount": "5000.00", "hasPaymentRecord": true },
"success": true
}
```
#### 空数据 / 降级响应
- 订单不存在或不属于该团期:返回空数据或错误。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。
### 21. 转团候选 `GET /v3/admin/order/group-batch/{groupBatchId}/transfer-candidates`
**VO**: `TransferCandidateVO` 列表
#### 使用场景
团期详情页"转团"弹窗中可转入的候选团期/订单列表,支持按手机号跨团期搜索(客户名、已付金额等)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
| keyword | Query | String | ❌ | - | 搜索关键字(手机号/姓名) |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| 出参结构不变 | - | 含 customerName / paidAmount 等;定制师本人团期返回完整数据 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2105709698592440321/transfer-candidates?keyword=13800138000
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": [ { "orderId": "2105...", "customerName": "李四", "paidAmount": "3000.00" } ],
"success": true
}
```
#### 空数据 / 降级响应
- 搜索无结果或无可转订单:返回空数组。
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null,
"success": false
}
```
#### 业务边界
- **触发 589507**:同上(#1)。本端点搜索支持跨团期,但归属校验作用在 `{groupBatchId}` 参数上,仅限定制师本人团期可查该团期的候选。
---
## 四、契约约束与正确调用方式
- **共同变化**:所有 21 个接口的请求参数、响应结构无变化,仅新增 589507 错误场景。
- **CUSTOMIZER 对非本人团期**:返回 HTTP 200 + `code: 589507` + `data: null`,与其他业务错误码(如 589500)走同一套错误处理流程。
- **hl-ui 现有处理**:v2.1 通用请求拦截器(`src/utils/request.js` 的业务错误分支)对非成功业务码一律弹出后端 `message`,没有 589507 专属分支。团期详情页头部 `GET /{id}` 对非本人团期早已返回 589507(#7949),本次补的是同页兄弟端点。
- **前端无需新增代码**:现有拦截器即可覆盖。如果希望定制师打开非本人团期时只提示一次、不按区块各弹一次,可以在页头 `GET /{id}` 返回 589507 后不再拉取各区块,这属于体验优化,不影响正确性。
---
## 五、数据库行为
- 无表结构变更、无 Flyway 迁移。
- 归属校验基于已有子订单投影(`OrderService.listInGroupOrdersByGroupBatchId`),仅在读路径内存查询,不涉及写入。
---
## 六、边界行为
- **系统上下文**:无登录态(system user)时放行,不返回 589507。
- **其他角色**:ADMIN / SUPER_ADMIN / GROUP_BATCH_MANAGER / FINANCE 等对这些端点无变化,仍返回 200。
- **团期不存在**:顺序 — 角色码 → 归属 → 存在性;CUSTOMIZER 查不存在的团期返回 589507(因无在团子订单),不区分「不存在」与「不是你的」。
- **子订单全部取消 / 团期已无在团子订单**:定制师名下在该团期的子订单全部变为 CANCELLED(或已移出团期)后,同一定制师再调这 21 个端点同样返回 589507;取消前是 200。测试服已实测这一翻转。
- **判据只看子订单顾问**:「本人团期」= 该团期存在一笔在团、非 CANCELLED 的子订单,其 `consultantId` 等于当前登录用户;团期本身的创建人不参与判定。
- **转团候选 `transfer-candidates`**:校验接在该端点的判权分支内,测试服当前对定制师非本人团期返回 589507。本人团期没有可转团候选时返回 `200 + []`,这是业务空结果,不是权限拒绝。
- **灰度与开关**:除上一条外,没有灰度开关,所有环境均生效。
---
## 六.5 枚举
不新增枚举值。
---
## 六.6、修改前后对比
| 项 | 改前 | 改后 |
|----|------|------|
| CUSTOMIZER 对任意团期 ID | 返回 200 + 完整数据 | 仅本人团期 200,其余 589507 + null |
| 非 CUSTOMIZER 角色 | 返回 200 + 完整数据 | 返回 200 + 完整数据(无变化) |
| 系统上下文 | 返回 200 + 完整数据 | 返回 200 + 完整数据(无变化) |
| 定制师本人团期判据 | 仅 2 个端点检查 | 21 个端点统一检查 |
---
## 六.7、影响评估
- **是否破坏向后兼容**:否。仅对 CUSTOMIZER 非本人团期新增拒绝,正常调用链路无影响。
- **前端是否必须同步上线**:否。hl-ui v2.1 现有的通用业务错误拦截器会把后端返回的 `message` 原样呈现,无需新增前端代码。
- **前端 workaround 清理点**:无。定制师在团期详情页的所有操作链路已依赖 `GET /{id}` 的 589507 拒绝(该接口已在 #7949 加过校验),本次只是补全兄弟端点的一致性。
---
## 七、不影响范围
- **仅影响**:CUSTOMIZER 角色对非本人团期的这 21 个读端点。
- **零影响**:其他角色、写操作端点、一期 HL(无 order-v3 标签)、其他权限码(如 LIST / FINANCE_VIEW / AUDIT_VIEW)。
- **按审批单 ID(`{approvalId}`)或产品批次 ID(`{productBatchId}`)取数的团期相关端点**(如流团审批详情):本次**没有**加这道校验,对定制师不会返回 589507;行为与改前相同。
- **staff/meal-info**:跨前缀且数据类别不同(员工档案 vs 客户信息),本次未动。
---
## 八、测试环境已验证
测试服 order-v3 `1f8b1dacd`(2026-10-02)经网关实测:21 个端点,加上 `GET /{id}` 与 `GET /{id}/orders` 两个既有对照端点,共 23 个端点 × 3 种身份 = 69 组调用,全部符合预期。
- 定制师 → 非本人团期:均为 `HTTP 200 + code 589507 + data null`,message 为「无操作权限(当前角色未授予团期权限,或该团期不在您名下)」。
- 定制师 → 本人团期:均为 `code 200`。
- 管理员 → 同一非本人团期:均为 `code 200`。
- 定制师本人团期的子订单全部取消后复测:由 200 转为 589507。
---
## 十、相关文档
- 工单 #7949(授权 CUSTOMIZER 查看本人团期)。
- 源码 `GroupBatchOwnershipGuard`(新公共守卫)。
---
## 关联 / 联系人
### 联系人
- **后端负责人**: @wx