diff --git a/changelogs-v2/2026-10/02_8717_团期读端点定制师归属校验-修改接口-管理后台.md b/changelogs-v2/2026-10/02_8717_团期读端点定制师归属校验-修改接口-管理后台.md new file mode 100644 index 00000000..5fb939fc --- /dev/null +++ b/changelogs-v2/2026-10/02_8717_团期读端点定制师归属校验-修改接口-管理后台.md @@ -0,0 +1,1369 @@ +--- +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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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` + +#### 使用场景 + +团期详情页"概览"等区域的操作历史(谁在什么时间做了什么操作)。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期 ID | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| 出参结构不变 | - | 状态流水数组;定制师本人团期返回完整数据 | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/2105709698592440321/status-logs +Authorization: Bearer +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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` + +#### 使用场景 + +团期详情页"物资"区域添加物资时的候选物资下拉/搜索数据。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期 ID | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| 出参结构不变 | - | 物资候选数据;定制师本人团期返回完整数据 | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/2105709698592440321/supplies/candidates +Authorization: Bearer +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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 +``` + +#### 响应示例 + +```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