docs(changelog): 团期用车需求结构化 + 团期管理员只读查看子订单,两份前端交接件 (#8152 #8151 #8153 #8154)
changelog-filename-gate / validate (push) Failing after 2s
changelog-filename-gate / validate (push) Failing after 2s
- 22_8152_…:团级用车需求补 seats/count/specialTags/remark 四个结构化字段;新增 `GET .../requirement/vehicle-households` 子订单用车需求记录端点;requirement-summary 补 transferSummary 聚合;confirm-check 补 transferSubmitEnabled 与接送机缺口名单。 - 22_8154_…:团期管理员(GROUP_BATCH_MANAGER)可只读打开团期子订单详情(10 个端点放行), 13 个金额/成本/流水面端点对该角色收回(581008),写面全域拒绝。 两份均已回填测试服活体实测读数:order-v3 @ f1986f996,user-service / fleet @ c4321f961。 #8154 的前后对照含一条关键读数——改动前该角色读订单被拒、写订单却畅通,本批一并收口。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
文件差异内容过多而无法显示
加载差异
@@ -0,0 +1,559 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "8154"
|
||||||
|
title: "团期管理员可打开团期子订单详情(只读),金额/流水面 13 个端点对该角色收回,写面全域拒绝"
|
||||||
|
consumer: "admin"
|
||||||
|
author: "wx(GIT)"
|
||||||
|
change_type: "修复"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "not_required"
|
||||||
|
frontend_status: "pending"
|
||||||
|
frontend_owner: ""
|
||||||
|
frontend_ref: ""
|
||||||
|
target_release: ""
|
||||||
|
verified_at: ""
|
||||||
|
status_note: "本条只改权限判定,不改任何请求/响应字段结构。对 GROUP_BATCH_MANAGER 以外的任何角色(超管/定制师/运营/客服/财务/车务/房务)零行为变化。前端需要做的是:给团期管理员这一角色隐藏/兜底金额面与写操作入口,并处理 581008。gateway_status: not_required —— 零网关改动,涉及端点全部落在既有 /v3/admin/** 通配路由内。"
|
||||||
|
updated_at: "2026-09-22"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# order-v3 + user-service: 团期管理员只读查看团期子订单,金额面端点收回,写面全域拒绝
|
||||||
|
|
||||||
|
> **存放目录**: 二期(v3) → `changelogs-v2/2026-09/`
|
||||||
|
>
|
||||||
|
> **服务**: hl-order-service-v3(权限守卫)、hl-user-service(角色菜单绑定)
|
||||||
|
> **PR**: 见文末「关联 / 联系人」
|
||||||
|
> **Issue**: #8154
|
||||||
|
> **日期**: 2026-09-22
|
||||||
|
> **影响范围**: 管理后台,**仅** `GROUP_BATCH_MANAGER`(团期管理员)角色登录时的订单详情页与团期详情页子订单操作
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 关键变化(非必须,本版与上版行为不同 / 纠错 / 撤销时必写)
|
||||||
|
|
||||||
|
**本条只改「谁能调」,不改「调通了返回什么」。所有请求/响应字段结构逐字节不变。**
|
||||||
|
|
||||||
|
对 `GROUP_BATCH_MANAGER` 三件事同时生效:
|
||||||
|
|
||||||
|
1. **放开**:10 个需求核对类只读端点,从 581008 变为正常返回——**但只对团期子订单**(`groupBatchId` 非空)。散客单仍 581008。
|
||||||
|
2. **收回**:13 个金额 / 成本 / 流水读端点,对该角色返回 581008(本批之前它们与第 1 组是同一道守卫,一放就全放,所以这是**同批收回**,不是先放后收)。
|
||||||
|
3. **拒绝**:订单详情页与团期详情页能触发的**全部写端点**,对该角色返回 581008。
|
||||||
|
|
||||||
|
🔴 **「端点收回」不等于「金额不可见」**——见「六、边界行为」的已知缺口一节,主详情响应体里仍含 7 个金额字段。前端按该节处理。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、背景(选填)
|
||||||
|
|
||||||
|
团期管理员要核对团期下各子订单报了什么需求,此前点「进入子订单」一律 581008,页面打不开。放开读面时读面里混着两类端点:核对需求要看的,和暴露供应商成本与资金流水的。工单诉求只到前者,后者一旦被看到不可逆,故同一批里把后者单独拆出去收回;同时该角色的「只能看不能修改」必须在写面落地,否则放开读权后它能改同行人、改行程、发起退款、开合同、改保险、改大交通,甚至修改 / 取消 / 终止订单主单。
|
||||||
|
|
||||||
|
| 维度 | 本批之前 | 本批之后 |
|
||||||
|
|------|----------|----------|
|
||||||
|
| 打开团期子订单详情 | 581008 | 正常返回(仅团期子订单) |
|
||||||
|
| 打开散客单详情 | 581008 | 581008(不变) |
|
||||||
|
| 财务 / 发票 / 退款 / 流水等 13 个端点 | 581008 | 581008(不变,但改由独立守卫判定) |
|
||||||
|
| 订单域写端点 | 该角色调不到(读面进不去) | 明确 581008 |
|
||||||
|
| 前端路由 `/order-v2/detail/:id` | 该角色未绑菜单,点「进入」命中兜底路由 404 | 已绑菜单,路由可注册 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | 需求核对类只读端点(10 个) | GET | `/v3/admin/order/{id}` 等 | 权限放开 | 仅 GROUP_BATCH_MANAGER + 仅团期子订单 |
|
||||||
|
| 2 | 金额 / 成本 / 流水读端点(13 个) | GET | `/v3/admin/order/{id}/finance` 等 | 权限收回 | 对 GROUP_BATCH_MANAGER 返 581008 |
|
||||||
|
| 3 | 订单域写端点(14 个控制器 + 2 个团期动作) | POST / PUT / DELETE | `/v3/admin/order/**` 等 | 权限拒绝 | 对 GROUP_BATCH_MANAGER 返 581008 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. 需求核对类只读端点(10 个,权限放开) `GET /v3/admin/order/{id}`
|
||||||
|
|
||||||
|
**VO**: `OrderDetailReqVO → OrderDetailRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
团期管理员在「团期详情 → 子订单列表」点「进入」,打开子订单详情页核对该户报了什么需求。本组端点即该页面各 Tab 的取数入口。
|
||||||
|
|
||||||
|
**本组完整清单**(路径逐一列全,前端按此判断哪些请求现在能发):
|
||||||
|
|
||||||
|
| # | 方法 | 路径 | 页面位置 |
|
||||||
|
|---|------|------|----------|
|
||||||
|
| 1 | GET | `/v3/admin/order/{id}` | 订单详情主体(9 Tab 聚合入口) |
|
||||||
|
| 2 | GET | `/v3/admin/order/{id}/itinerary` | 行程安排 Tab |
|
||||||
|
| 3 | GET | `/v3/admin/order/{id}/confirm-checklist` | 确认清单 |
|
||||||
|
| 4 | GET | `/v3/admin/order/{id}/service-standard` | 服务标准 Tab |
|
||||||
|
| 5 | GET | `/v3/admin/order/{id}/status-log` | 状态记录时间线 Tab |
|
||||||
|
| 6 | GET | `/v3/admin/order/{id}/print-itinerary` | 打印行程单(司机 Driver Copy) |
|
||||||
|
| 7 | GET | `/v3/admin/order/{id}/push-records` | 推送记录 Tab |
|
||||||
|
| 8 | GET | `/v3/admin/order/{id}/contract-insurance` | 合同保险 Tab |
|
||||||
|
| 9 | GET | `/v3/admin/order/{orderId}/itinerary-document` | 电子行程单(对客视角) |
|
||||||
|
| 10 | GET | `/v3/admin/order/meal-info/list` | 用餐信息列表 |
|
||||||
|
|
||||||
|
#### 入参
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| id / orderId | Path | Long | ✅ | 前 9 个端点 | 子订单 ID;**必须是团期子订单**,散客单 581008 |
|
||||||
|
| orderId | Query | Long | ❌ | 第 10 个端点(`meal-info/list`) | 传了才逐单判权;不传按查询条件本身的口径取数 |
|
||||||
|
| documentType | Query | String | ✅ | 第 9 个端点 | `CUSTOMER` / `CUSTOMER_PRINT` / `CUSTOMER_QUOTE` |
|
||||||
|
| Authorization | Header | String | ✅ | Bearer token | 角色 key 由网关透传,前端不传角色 |
|
||||||
|
|
||||||
|
**请求参数与请求体结构本批零改动**,上表只列与判权相关的部分。
|
||||||
|
|
||||||
|
#### 出参 `Result<OrderDetailRespVO>`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| data | Object | **响应结构本批零改动**,与该角色之外的角色拿到的完全相同 |
|
||||||
|
| data.main | Object | 订单主信息;⚠️ 含 7 个金额字段,见「六、边界行为」已知缺口 |
|
||||||
|
| data.tags | Array | 订单标签 |
|
||||||
|
| data.overview | Object | 概览(含 hotelRemark / vehicleRemark 等) |
|
||||||
|
|
||||||
|
其余 9 个端点的响应结构同样零改动,此处不重复列出——本条 changelog 不引入任何新字段。
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/2101506167043985410 HTTP/1.1
|
||||||
|
Host: <网关域名>
|
||||||
|
Authorization: Bearer <团期管理员的 token>
|
||||||
|
```
|
||||||
|
|
||||||
|
无请求体。
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"main": {
|
||||||
|
"orderId": "2101506167043985410",
|
||||||
|
"orderNo": "GT-26-0081",
|
||||||
|
"groupBatchId": "2101506167098511362",
|
||||||
|
"orderStatus": "CONFIRMED"
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"overview": {}
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(示例省略了与本条无关的字段,实际响应结构与本批之前逐字段相同。)
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
部分 Tab 在无数据时 `data` 为 `null`(如服务标准快照缺失、无退款),这是**既有行为,本批不改**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "code": 200, "message": "成功", "data": null, "success": true }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
散客单(`groupBatchId` 为 NULL)对该角色仍然拒绝:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 581008,
|
||||||
|
"message": "无权查看此订单",
|
||||||
|
"success": false,
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- **放行条件是两个而不是一个**:角色为 `GROUP_BATCH_MANAGER` **且** `order.groupBatchId != null`。缺任一条 → 581008。
|
||||||
|
- 🔴 **放行范围是「全站团期子订单」,不是「他负责的那个团」**:按管理员归属隔离要走 `order_group_batch.batch_manager_id`,该列当前全站为 NULL、显式不启用,做不到隔离。前端不要据此假设「他只能看到自己的团」。
|
||||||
|
- 判权发生在**后端**,与前端菜单权限码无关:即使前端藏了入口,直接拼 URL 也按上面两条判。
|
||||||
|
- 房务管理员 / 房务组长在本组端点上仍是 581045(`房务角色无权查看订单详情,房务仅可配房`),与本批无关。
|
||||||
|
- 其余角色(超管 / 定制师 / 运营 / 客服 / 财务 / 车务)在本组端点上**零行为变化**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 金额 / 成本 / 流水读端点(13 个,权限收回) `GET /v3/admin/order/{id}/finance`
|
||||||
|
|
||||||
|
**VO**: `OrderFinanceReqVO → FinanceVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
订单详情页的财务 / 发票 / 退款等 Tab,以及预付、优惠加价、收款流水、手工收款等金额面取数。**团期管理员调用本组任一端点一律 581008**——前端应对该角色隐藏这些 Tab 与按钮,而不是让它点开后吃一个错误弹窗。
|
||||||
|
|
||||||
|
**本组完整清单(13 个 GET 端点,逐一列全)**:
|
||||||
|
|
||||||
|
| # | 路径 | 内容 |
|
||||||
|
|---|------|------|
|
||||||
|
| 1 | `/v3/admin/order/{id}/finance` | 财务 Tab |
|
||||||
|
| 2 | `/v3/admin/order/{id}/invoices` | 发票 Tab |
|
||||||
|
| 3 | `/v3/admin/order/{id}/refund` | 退款明细 Tab |
|
||||||
|
| 4 | `/v3/admin/order/{id}/cancel-preview` | 取消订单预览(金额 + 政策) |
|
||||||
|
| 5 | `/v3/admin/order/{id}/sign-voucher` | 签单凭证(对供应商核成本) |
|
||||||
|
| 6 | `/v3/admin/order/{orderId}/advances` | 预付列表 |
|
||||||
|
| 7 | `/v3/admin/order/{orderId}/advance/payee-candidates` | 预付收款方候选 |
|
||||||
|
| 8 | `/v3/admin/order/{orderId}/discount-surcharge/list` | 优惠 / 加价明细 |
|
||||||
|
| 9 | `/v3/admin/order/{orderId}/payment/list` | 收款流水 |
|
||||||
|
| 10 | `/v3/admin/order/{orderId}/payment/manual-receipt` | 手工收款记录 |
|
||||||
|
| 11 | `/v3/admin/order/{orderId}/payment/manual-receipt/options` | 手工收款选项 |
|
||||||
|
| 12 | `/v3/admin/order/invoice/{id}` | 发票详情 |
|
||||||
|
| 13 | `/v3/admin/order/invoice/{id}/push-logs` | 发票推送日志 |
|
||||||
|
|
||||||
|
#### 入参
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| id / orderId | Path | Long | ✅ | - | 订单 ID(第 12、13 项为发票 ID) |
|
||||||
|
| Authorization | Header | String | ✅ | Bearer token | 角色 key 由网关透传 |
|
||||||
|
|
||||||
|
入参结构本批零改动。
|
||||||
|
|
||||||
|
#### 出参 `Result<FinanceVO>`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| data | Object | **结构零改动**;对 GROUP_BATCH_MANAGER 永远拿不到(先抛 581008) |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/2101506167043985410/finance HTTP/1.1
|
||||||
|
Host: <网关域名>
|
||||||
|
Authorization: Bearer <团期管理员的 token>
|
||||||
|
```
|
||||||
|
|
||||||
|
无请求体。
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
对**非**团期管理员角色(超管 / 定制师 / 客服 / 财务 / 车务),响应与本批之前完全一致:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": { "payments": [] },
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
无数据时 `data` 为 `null` 或空数组,属既有行为,本批不改:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "code": 200, "message": "成功", "data": null, "success": true }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
团期管理员调用本组任一端点:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 581008,
|
||||||
|
"message": "无权查看此订单",
|
||||||
|
"success": false,
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 本组与第 1 组**使用同一个错误码 581008**,前端无法靠错误码区分「这个端点该角色永远调不了」与「这单不是团期子订单」。区分办法是看请求的是哪个端点:本组 13 个对该角色恒 581008。
|
||||||
|
- **收回是按端点清单做的,不是按响应体自动判的**:新增金额面端点不会自动纳入。若前端发现某个含金额的端点对该角色返回了 200,那是缺口,请报工单,不要当成允许。
|
||||||
|
- **对其他角色零行为变化**:本组端点改挂的守卫与本批之前的读面守卫在开关上逐字节相同(车务放行、房务 581045、其余须本单定制师),只把新放开的那一个角色收回去。
|
||||||
|
- 第 9~11 项在控制器与 Service 各判一次(刻意的双层防御),行为一致,不会出现「一层放一层拒」的中间态。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 订单域写端点(权限拒绝) `POST /v3/admin/order/group-batch/sub-order/{orderId}/withdraw`
|
||||||
|
|
||||||
|
**VO**: `SubOrderWithdrawReqVO → SubOrderWithdrawRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
订单详情页与团期详情页上一切「改」的动作。团期管理员是**只读**角色,本组一律 581008。前端应对该角色隐藏全部写入口(含详情页内的编辑按钮、Tab 内的新增/删除、团期侧的撤出/转入)。
|
||||||
|
|
||||||
|
**纳管范围(按控制器,逐一列全)**:
|
||||||
|
|
||||||
|
| 控制器 | 覆盖的写面 |
|
||||||
|
|--------|-----------|
|
||||||
|
| `OrderController` | 创建 / 修改 / 行前取消 / 终止预览 / 终止 / 状态流转 / 确认行程(7 个写端点) |
|
||||||
|
| `TravelerAdminController` | 出行人增删改 |
|
||||||
|
| `TransportPlanAdminController` | 大交通方案增删改 |
|
||||||
|
| `ItineraryAdminController` / `ItineraryEditAdminController` | 行程与行程编辑 |
|
||||||
|
| `AdminRefundController` | 退款发起与处理 |
|
||||||
|
| `AdminContractController` | 合同 |
|
||||||
|
| `AdminInsuranceController` | 保险 |
|
||||||
|
| `CollabAdminController` | 协作 |
|
||||||
|
| `AdminWorkOrderController` | 工单 |
|
||||||
|
| `AdminTeamReportController` | 团报 |
|
||||||
|
| `OrderAdvanceController` | 预付 |
|
||||||
|
| `InvoiceAdminController` / `AdminInvoiceController` | 发票 |
|
||||||
|
| `GroupBatchActionController` | 子订单撤出(`withdraw`)、转入(`transfer-in`)两个团期动作 |
|
||||||
|
|
||||||
|
#### 入参
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| orderId | Path | Long | ✅ | - | 子订单 ID |
|
||||||
|
| 请求体 | Body | Object | 视端点而定 | - | **结构本批零改动** |
|
||||||
|
| Authorization | Header | String | ✅ | Bearer token | 角色 key 由网关透传 |
|
||||||
|
|
||||||
|
#### 出参 `Result<SubOrderWithdrawRespVO>`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| data | Object | **结构零改动**;对 GROUP_BATCH_MANAGER 永远拿不到(先抛 581008) |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /v3/admin/order/group-batch/sub-order/2101506167043985410/withdraw HTTP/1.1
|
||||||
|
Host: <网关域名>
|
||||||
|
Authorization: Bearer <团期管理员的 token>
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "reason": "客户取消" }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
本组端点不返回列表,无空数据形态;对有权角色的成功响应与本批之前一致:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "code": 200, "message": "成功", "data": true, "success": true }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 581008,
|
||||||
|
"message": "无权查看此订单",
|
||||||
|
"success": false,
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ **报文文案是「无权查看此订单」,出现在写操作上会读着别扭**——这是刻意复用读侧错误码(前端对 581008 已有一套处理),不是挂错。前端在写入口上可自行换一句更贴切的提示文案,但判据仍是 581008。
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 拒绝**只针对 `GROUP_BATCH_MANAGER` 这一个角色**,与订单是不是团期子订单无关(写面不做 `groupBatchId` 分叉)。
|
||||||
|
- **对其他任何角色零行为变化**:这些写端点此前对客服 / 运营 / 财务等角色没有归属校验,本批**保持原样**,不要把本条读成「订单写面已做归属收口」。
|
||||||
|
- MQ 回放 / 定时任务 / 内部 Feign / 单测等非请求上下文无角色,放行,与既有守卫一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、契约约束与正确调用方式(接口类必写)
|
||||||
|
|
||||||
|
> 本节只写**后端接受/拒绝请求的规则**,不写 UI 渲染建议。
|
||||||
|
|
||||||
|
### ✅ 放行 / ❌ 拒绝对照(调用方角色 = GROUP_BATCH_MANAGER)
|
||||||
|
|
||||||
|
| 场景 | 结果 |
|
||||||
|
|------|------|
|
||||||
|
| ✅ `GET /v3/admin/order/{id}`,该单 `groupBatchId` 非空 | 200,正常返回 |
|
||||||
|
| ✅ `GET /v3/admin/order/{id}/itinerary`,团期子订单 | 200 |
|
||||||
|
| ✅ `GET /v3/admin/order/meal-info/list?orderId=<团期子订单>` | 200 |
|
||||||
|
| ❌ `GET /v3/admin/order/{id}`,该单 `groupBatchId` 为 NULL(散客单) | 581008 |
|
||||||
|
| ❌ `GET /v3/admin/order/{id}/finance`(哪怕是团期子订单) | 581008 |
|
||||||
|
| ❌ `GET /v3/admin/order/{orderId}/payment/list` | 581008 |
|
||||||
|
| ❌ `PUT /v3/admin/order/{id}`(修改订单) | 581008 |
|
||||||
|
| ❌ `POST /v3/admin/order/group-batch/sub-order/{orderId}/withdraw` | 581008 |
|
||||||
|
|
||||||
|
### 切换状态时的必要动作
|
||||||
|
|
||||||
|
- 前端不需要、也不应该在请求里传角色:角色 key 由网关从 JWT 透传,后端只认它。
|
||||||
|
- 判断「当前用户能不能看金额面」**不要靠试调**:按当前登录角色是否为团期管理员在前端直接分支,避免每个 Tab 打开时先吃一个 581008。
|
||||||
|
- 团期管理员登录后需**重新登录或刷新页面**才能拿到新注册的 `/order-v2/detail/:id` 路由(前端路由表在登录时一次性生成)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、数据库行为
|
||||||
|
|
||||||
|
本条不改任何业务数据结构。唯一的数据变更是**角色菜单绑定**(hl-user-service):
|
||||||
|
|
||||||
|
| 变更 | 外部可观察行为 |
|
||||||
|
|------|----------------|
|
||||||
|
| 给 `GROUP_BATCH_MANAGER` 绑定「订单详情」菜单 `/order-v2/detail/:id` | `GET /admin/menu/my` 对该角色多返回这条菜单,前端动态路由才能注册该路径 |
|
||||||
|
|
||||||
|
- 该角色**只绑「订单详情」,不绑「订单列表」**:绑列表等于给全站订单的浏览入口,超出诉求。父目录「订单管理v2」该角色此前已持有,路由父链完整。
|
||||||
|
- 菜单不走 Redis 缓存,但**前端路由表在登录时一次性生成**:已登录的会话需要重新登录或刷新页面才看到新路由。
|
||||||
|
- 在此之前,该角色从未注册过 `/order-v2/detail/:id` 路由,团期详情页点子订单「进入」会命中前端兜底路由 404,请求压根发不到后端——所以本批之前看到的 404 与后端 581008 是两回事。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、边界行为
|
||||||
|
|
||||||
|
- 未登录 → 401(网关拦截)。
|
||||||
|
- 团期管理员访问散客单的任一读端点 → 581008。
|
||||||
|
- 团期管理员访问 13 个金额面端点 → 581008。
|
||||||
|
- 团期管理员访问任一订单域写端点 → 581008。
|
||||||
|
- 房务管理员 / 房务组长访问订单详情读面 → 581045(既有行为,不变)。
|
||||||
|
- 订单不存在 → 订单不存在错误码,不 500。
|
||||||
|
|
||||||
|
### 🔴 已知缺口:端点收回 ≠ 金额不可见
|
||||||
|
|
||||||
|
本批**只收端点、不改响应体**(响应脱敏是独立设计,不在本单)。所以团期管理员打开子订单详情时,**仍会在响应里拿到金额**:
|
||||||
|
|
||||||
|
| 位置 | 仍可见的内容 |
|
||||||
|
|------|--------------|
|
||||||
|
| `GET /v3/admin/order/{id}` 的 `main` | `totalAmount`、`payableAmount`、`paidAmount`、`refundAmount`、`balanceAmount`、`depositAmount`、`singleRoomSurcharge` 共 7 个金额字段 |
|
||||||
|
| `GET /v3/admin/order/{id}/contract-insurance` | 保费 |
|
||||||
|
| `GET /v3/admin/order/{id}/itinerary` | 协议价与结算价 |
|
||||||
|
|
||||||
|
主详情之所以不一并收回,是因为它是页面入口,收了等于工单诉求落空。
|
||||||
|
|
||||||
|
**前端据此决定渲染**:若产品口径要求该角色看不到金额,需要在前端按角色隐藏上述字段的展示;后端此版不做脱敏,字段会照常下发。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.5、枚举 / 数据字典
|
||||||
|
|
||||||
|
### role_key(与本条判权相关的后台角色)
|
||||||
|
|
||||||
|
**所属字段**: 不在任何请求/响应体中——由网关从 JWT 的 `role_key` 经 `X-Admin-Role` 透传给后端 | **类型**: `String`
|
||||||
|
|
||||||
|
| 值 | 中文 | 在本条中的行为 |
|
||||||
|
|----|------|----------------|
|
||||||
|
| `GROUP_BATCH_MANAGER` | 团期管理员 | 本条唯一行为变化的角色:团期子订单读面放行,金额面 13 端点 581008,写面全域 581008 |
|
||||||
|
| `ADMIN` / `SUPER_ADMIN` | 管理员 / 超级管理员 | 恒放行,零变化 |
|
||||||
|
| `VEHICLE_MANAGER` | 车务管理员 | 读面与金额面均放行(派车需看签单与预付),零变化 |
|
||||||
|
| `ROOM_MANAGER` | 房务管理员 | 订单详情读面 581045,零变化 |
|
||||||
|
| `house_keeper_lead` | 房务组长 | 同房务管理员,零变化 |
|
||||||
|
| 其余(定制师 / 运营 / 客服 / 财务等) | - | 须为本单定制师,否则 581008,零变化 |
|
||||||
|
|
||||||
|
### 本条涉及的错误码
|
||||||
|
|
||||||
|
**所属字段**: `Result.code` | **类型**: `Integer`
|
||||||
|
|
||||||
|
| 值 | 报文 | 触发条件 |
|
||||||
|
|----|------|----------|
|
||||||
|
| `581008` | `无权查看此订单` | 团期管理员访问散客单 / 金额面端点 / 任一写端点;或其他角色非本单定制师 |
|
||||||
|
| `581045` | `房务角色无权查看订单详情,房务仅可配房` | 房务管理员 / 房务组长访问订单详情读面(既有,不变) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.6、修改前后对比
|
||||||
|
|
||||||
|
### 字段级对比
|
||||||
|
|
||||||
|
| 字段 | 改前 | 改后 |
|
||||||
|
|------|------|------|
|
||||||
|
| 全部涉及端点的请求体 | - | **逐字段不变** |
|
||||||
|
| 全部涉及端点的响应体 | - | **逐字段不变**(含主详情的 7 个金额字段,见已知缺口) |
|
||||||
|
|
||||||
|
本条零字段变更,变的只有判权结果。
|
||||||
|
|
||||||
|
### 行为级对比(调用方角色 = GROUP_BATCH_MANAGER)
|
||||||
|
|
||||||
|
| 行为 | 改前 | 改后 |
|
||||||
|
|------|------|------|
|
||||||
|
| 点团期子订单「进入」 | 前端路由未注册 → 404(请求发不出去) | 路由可注册,详情页可打开 |
|
||||||
|
| `GET /v3/admin/order/{团期子订单}` | 581008 | 200 |
|
||||||
|
| `GET /v3/admin/order/{散客单}` | 581008 | 581008 |
|
||||||
|
| 行程 / 确认清单 / 服务标准 / 状态日志 / 打印行程单 / 推送记录 / 合同保险 / 行程文档 / 用餐信息 | 581008 | 200(仅团期子订单) |
|
||||||
|
| 财务 / 发票 / 退款 / 取消预览 / 签单 / 预付 / 优惠加价 / 收款流水 / 手工收款(13 端点) | 581008 | 581008 |
|
||||||
|
| 改同行人 / 改行程 / 发起退款 / 开合同 / 改保险 / 改大交通 / 改订单主单 | 读面进不去,实际调不到 | 明确 581008 |
|
||||||
|
| 子订单撤出 / 转入 | 同上 | 明确 581008 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.7、影响评估
|
||||||
|
|
||||||
|
- **是否破坏向后兼容**: 否。零字段变更;对 `GROUP_BATCH_MANAGER` 之外的任何角色零行为变化。
|
||||||
|
- **前端是否必须同步上线**: 是。需要按角色控制入口——否则团期管理员打开子订单详情后,金额面 Tab 与写按钮仍在页面上,点一次吃一个 581008。
|
||||||
|
- **前端 workaround 清理点**:
|
||||||
|
- 若此前为「团期管理员点子订单必 404」做过前端兜底提示(例如直接禁用「进入」按钮、或点击后提示无权限),**可以撤掉**——该角色现在能正常打开团期子订单详情。
|
||||||
|
- 若此前把团期管理员当成「订单域完全无权」的角色做过整块屏蔽,需要改成**分面控制**:读面开、金额面关、写面关。
|
||||||
|
- 已登录的团期管理员账号需要重新登录或刷新页面才能拿到新注册的详情路由,前端如有路由缓存需一并处理。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、不影响范围(显式声明, 帮前端/QA 缩小排查面)
|
||||||
|
|
||||||
|
- **仅影响**: 管理后台 `GROUP_BATCH_MANAGER`(团期管理员)角色的订单域行为。
|
||||||
|
- **零影响**:
|
||||||
|
- 其他全部后台角色(超管 / 管理员 / 定制师 / 运营 / 客服 / 财务 / 车务 / 房务 / 房务组长)在上述任一端点上的权限与响应。
|
||||||
|
- 所有请求参数与响应字段结构(零字段变更)。
|
||||||
|
- 小程序端 `/v3/mp/**` 全部接口。
|
||||||
|
- 内部 Feign `/v3/internal/**` 与 MQ 回放 / 定时任务链路(非请求上下文无角色,放行,与既有守卫一致)。
|
||||||
|
- 订单写端点对其余角色的归属校验现状(存量缺口保持原样,本批不治理)。
|
||||||
|
- 房务配房链路(走 in-process 聚合器,不经本批端点)。
|
||||||
|
- 团期需求域的用车 / 用房需求接口(那批改动见同批另一份交接件)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、测试环境已验证
|
||||||
|
|
||||||
|
**网关路由**:本条零网关改动。涉及端点全部落在 `hl-gateway` 已配置的 `/v3/admin/**` 通配路由内,无新增 `/admin/` 前缀(故 `gateway_status: not_required`)。
|
||||||
|
|
||||||
|
**架构门禁**(分支 `fix/8154-group-batch-manager-order-view`):
|
||||||
|
|
||||||
|
```
|
||||||
|
GroupBatchManagerFinanceReadGuardArchTest 13 个金额端点(15 个方法)必须且只能挂财务守卫 ✓
|
||||||
|
GroupBatchManagerWriteGuardArchTest 14 个写面控制器 + 2 个团期动作方法全部纳管 ✓
|
||||||
|
RoleClaimFailOpenInventoryTest 角色缺失放行清册未被意外改动 ✓
|
||||||
|
RedLineArchTest 架构红线门禁 ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
两条金额面门禁是成对的:一条要求财务端点**必须**调财务守卫,另一条要求它们**不得**调放行团期管理员的宽松守卫——只有第一条时,两条守卫都写上仍会绿,而宽松那条在顺序靠前时会先放行。
|
||||||
|
|
||||||
|
**数据库变更**:`V20260922_154__grant_order_detail_menu_to_group_batch_manager.sql`(hl-user-service),`INSERT IGNORE` + 唯一键,重复执行零新增行;角色或菜单任一不存在时匹配 0 行、迁移仍成功。
|
||||||
|
|
||||||
|
**生产环境覆盖边界**:二期(order-v3 / fleet)尚未上线生产,本条涉及的 `/v3/admin/**` 路径在生产环境为 404;生产库 `sys_menu` 是否存在 `path = '/order-v2/detail/:id'` 这一行未查证,若不存在则该菜单迁移在生产上按设计安全跳过。这是既有事实,不是本批引入的。
|
||||||
|
|
||||||
|
**活体实测**(测试服网关 `https://api.test.1814.love:9443`,2026-09-22 13:0x–13:1x;`hl-order-service-v3` @ `f1986f996`,`hl-user-service` @ `c4321f961`)。用一个 `GROUP_BATCH_MANAGER` 角色账号,对一个团期子订单与一个散客单逐个请求。左列是本批改动**前**在同一环境实测的读数,右列是改动后:
|
||||||
|
|
||||||
|
| 端点 | 改动前 | 改动后 | 结论 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `GET /v3/admin/order/{团期子订单 id}` | 581008 | 200 | 放行 |
|
||||||
|
| `GET .../{id}/itinerary` | 581008 | 200 | 放行 |
|
||||||
|
| `GET .../{id}/status-log` | 581008 | 200 | 放行 |
|
||||||
|
| `GET .../{id}/finance` | 581008 | 581008 | 不变 |
|
||||||
|
| `GET .../{id}/invoices` | 581008 | 581008 | 不变 |
|
||||||
|
| `GET .../{id}/refund` | 581008 | 581008 | 不变 |
|
||||||
|
| `GET .../{id}/sign-voucher` | 581008 | 581008 | 不变 |
|
||||||
|
| `GET .../{id}/contract-insurance` | 581008 | 581008 | 不变 |
|
||||||
|
| `GET /v3/admin/order/{散客单 id}` | 581008 | 581008 | **阴性对照**:放行范围未扩大到散客单 |
|
||||||
|
| `PUT /v3/admin/order/{团期子订单 id}` | **放行,返 `data: true`** | **581008** | **写面缺口已堵** |
|
||||||
|
|
||||||
|
(表中的 581008 指响应体里的业务 `code`,HTTP 状态码按本仓约定一律为 200。)
|
||||||
|
|
||||||
|
最后一行是本批最有分辨力的一条读数:**改动前该角色读订单被拒、写订单却畅通**——读面有守卫挡着,写面当时没有任何团期管理员判权。它不是为放开读权而配套加的保险,它本身就是一个既有越权,本批一并收口。
|
||||||
|
|
||||||
|
**user-service 迁移已在测试环境生效**(启动日志原文):
|
||||||
|
|
||||||
|
```
|
||||||
|
Migrating schema `hl_user_service` to version "20260922.154 - grant order detail menu to group batch manager"
|
||||||
|
Successfully applied 1 migration to schema `hl_user_service`, now at version v20260922.154
|
||||||
|
```
|
||||||
|
|
||||||
|
第二个实例随后启动时读到 `Current version ... 20260922.154` / `Schema is up to date`,两实例一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、相关文档
|
||||||
|
|
||||||
|
- 关联 Issue: [wx/HL#8154](https://git.1814.love:8443/wx/HL/issues/8154)
|
||||||
|
- 同批交接件: `changelogs-v2/2026-09/22_8152_团期用车需求补车辆规格与接送机汇总-修改接口-管理后台.md`(团期需求域字段与端点变更)
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
### 链接
|
||||||
|
|
||||||
|
- **Issue**: [#8154](https://git.1814.love:8443/wx/HL/issues/8154)
|
||||||
|
- **分支**: `fix/8154-group-batch-manager-order-view`
|
||||||
|
|
||||||
|
### 联系人
|
||||||
|
|
||||||
|
- **后端负责人**: @wx
|
||||||
在新工单中引用
屏蔽一个用户