--- schema: "hl-changelog/v2" ticket: "7535" title: "团期接口返回值整改·非破坏批(opsStage / 负责人 / 报账人 / 审批行补全 / 枚举中文配对 / 命名统一)" consumer: "admin" author: "jw(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "not_required" frontend_status: "not_required" frontend_owner: "mmg" frontend_ref: "" target_release: "" verified_at: "2026-09-12" status_note: "19 个团期读端点纯加响应字段,既有 key 的名称/类型/取值/null 语义一行未动,前端零改动即可继续运行。零 DDL、零错误码、零网关改动。四组旧字段已标 @Deprecated 但本批不删,请新代码逐步切到新名。 前端 2026-09-13 闭环 not_required:前端无严格 schema 校验多余 key 静默忽略,五组 @Deprecated 旧字段本批仍返回不 break,改读新名为非强制改进项本批不动。零业务代码改动。" updated_at: "2026-09-13" base: "dev-v3" --- # order-v3: 团期接口返回值整改·非破坏批 **服务**: hl-order-service-v3 **PR**: #7583 **Issue**: #7535 --- ## ⚠️ 关键变化 🟢 **本批纯加字段,前端零改动即可继续运行。** 19 个端点只多出响应 key,**既有 key 的名称、类型、取值与 null 语义一行没动**。新字段按需取用。 🔴 **`opsStage` 以后端八桶为准,原型侧的 stage 取值需要改**(wx 2026-09-11 定案)。后端八值: `RECRUIT` 招募中 / `FORMED` 已成团 / `PENDING_TRIP` 待出行 / `TRAVELLING` 出行中 / `TRIP_FINISHED` 出行完毕 / `AUDITING` 核团中 / `CHECKED` 已验团 / `DISBANDED` 已流团。 **`FORMED` 是复合桶**(`RESOURCE_PREPARING` + `MATERIAL_PREPARING` 两个九态都落它)。 **前端不要再自己复刻九态 → 八桶的折叠逻辑,直接读 `opsStage`。** 🔴 **四个 `Text` / `Label` 后缀字段已标 `@Deprecated`**,本批**不删**、值不变,请新代码改用同值的 `Name` 字段,删除时间另行通知: | 旧字段(仍返回) | 新字段(同值) | 所在响应 | |---|---|---| | `batchStatusLabel` | `batchStatusName` | 团期看板行 | | `productBatchStatusLabel` | `productBatchStatusName` | 团期分页项 | | `statusText` | `statusName` | 芯片逐户行 | | `bizTypeText` | `bizTypeName` | 审批中心统一列表行 | 🔴 **审批中心统一列表的 `orderNo` / `customerName` / `departDate` 只在 `bizType=WITHDRAW` 行有值**,`DISBAND` 行恒 `null`(与已有的 `orderId` 同一条规则)。前端**按行类型判断再渲染**,否则 DISBAND 行会出现三个空列。 🔴 **`checkedResourceTypes` 恒 `["HOTEL"]`**:需求确认预检**只检查住宿、不检查用车**,`ready=true` **不代表**用车需求齐备。 🔴 **联系人 key 统一为 `customerName`**:芯片逐户 / 合同逐户 / 行程汇总预警行 / 行程下钻逐户**四处**新增 `customerName`,与既有 `contactName` **逐字同值**;`contactName` 已标 `@Deprecated`,本期仍返回、**计划下一期删除**。 报名清单「联系人」列显示 `—` 是**前端读错 key**(后端一直给的是客户姓名),请改读 `customerName`。 🔴 **团期主键 key 统一为 `groupBatchId`**:名额调整 / 芯片详情 / 合同面板三处响应新增 `groupBatchId`,与既有 `batchId` **逐字同值、同为字符串形态**;`batchId` 已标 `@Deprecated`。 staff 的 `PUT .../staff` 与 `GET .../staff/candidates` 响应新增 `productBatchId` + `groupBatchId`—— ⚠️ **候选端点的 `groupBatchId` 在团期尚未创建时为 `null`**(staff 允许在成团前先配,该端点**不报错**),前端**不得**把它当必有主键去拼请求,否则会打出 `/group-batch/null/...`。 `GET .../staff` 返回**裸数组、没有顶层信封**,因此**不带**这两个 ID。 🔴 **芯片逐户行与合同逐户行新增 `teamNo`**(纯加)。⚠️ **逐行各不相同**,不是页头那一个团期编号的重复;⚠️ **订金支付成功后才生成,未付订金的户是 `null`**(与是不是团期订单无关)。后端**不做任何兜底**——不返空串、不回退成订单号、不拿团期编号顶替,前端请按 `null` 渲染「—」。 另:「报名清单 / 财务 / 预支」三张表的同名字段由**破坏批那张单(#7536)**交付,两批的字段名、类型与 null 语义完全一致,前端可按同一套逻辑渲染。 🔴 **应收 / 已收 / 待收命名统一**:团期详情新增 `receivableAmount` / `receivedAmount`(与既有 `totalReceivable` / `totalReceived` 同值,旧名已 `@Deprecated`、计划下一期删除);团期分页项新增 `unpaidAmount` = `max(0, 应收 − 已收)`,**恒非负、恒非 null**。⚠️ 与财务 tab 的同名字段**同名不同源**,权威待收值仍以财务 tab 为准。财务 tab 那 12 个金额字段的 JSON 形态**本批不变**,其变更由另一张单交接。 🔴 **芯片逐户新增房务负责人三件套** `claimerId` / `claimerName` / `claimerSource`,**六个芯片端点统一给**。 `claimerSource` 取值 `BATCH`(团级指针)/ `LEGACY`(历史旧户的户级归属)/ `null`(真实无人认领,此时另两个字段也为 `null`)——让前端不必猜这个名字是团级还是户级。 🔴 **十个裸 code 字段补齐了中文配对**,统一兜底口径:**code 为 `null` → `Name` 为 `null`;code 翻不出 → `Name` 回落原 code**(不抛异常、不返空串)。 --- ## 一、背景 团期模块的响应体长期存在四类不一致,都是前端一眼能看见、但后端没人系统清过的: 1. **八桶只能筛、不能读**:`GET /v3/admin/order/group-batch` 的**入参**早就支持按八桶 `opsStage` 筛选,**出参却没有这个字段**——前端只能拿九态 `batchStatus` 自己再折一遍桶,折叠规则一分叉就和服务端筛选结果对不上。 2. **中文名后缀三套并存**:同一个 `batchStatus` 在分页项叫 `batchStatusName`、在看板行叫 `batchStatusLabel`;芯片叫 `statusText`、审批叫 `bizTypeText`。同时还有 10 个裸 code 字段根本没有中文配对,前端只能自己维护映射表。 3. **同义字段各叫各的**:联系人在团期域 4 个 VO 里叫 `contactName`、在其余 8 个 VO 里叫 `customerName`;团期主键在 3 个 VO 里叫 `batchId`、其余一律 `groupBatchId`;整团应收/已收在三个端点有三套名字。 4. **该有的字段缺位**:芯片逐户看不到房务负责人、逐户行看不到团号、审批中心统一列表的退单户行看不到订单号与客户姓名、staff 名册看不到报账人等级、预检响应不告诉前端它到底检查了什么。 本单**只加不改**:19 个端点新增 47 个响应 key,旧字段一律同值保留并打 `@Deprecated`(本批不删)。 **路径、方法、请求参数、既有字段的名称/类型/取值/null 语义一律不变**,向后兼容纯增。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 团期分页列表(A1) | GET | `/v3/admin/order/group-batch` | 修改 | 响应行新增 `opsStage` / `opsStageName` / `unpaidAmount` / `productBatchStatusName` | | 2 | 团期看板 | GET | `/v3/admin/order/group-batch/board` | 修改 | 响应行新增 `opsStage` / `opsStageName` / `batchStatusName` | | 3 | 团期详情(A2) | GET | `/v3/admin/order/group-batch/{groupBatchId}` | 修改 | 新增 `opsStage` / `opsStageName` / `receivableAmount` / `receivedAmount` / `thresholdSourceName` | | 4 | 团期配房逐户明细 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/hotel` | 修改 | 信封 +2、逐户 +6 | | 5 | 团期配车逐户明细 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/vehicle` | 修改 | 同上 | | 6 | 团期配导游逐户明细 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/guide` | 修改 | 同上 | | 7 | 团期配摄影逐户明细 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/photo` | 修改 | 同上 | | 8 | 团期合同逐户明细 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/contract` | 修改 | 同上 | | 9 | 团期保险逐户明细 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/insurance` | 修改 | 同上 | | 10 | 团期「合同保险」面板 | GET | `/v3/admin/order/group-batch/{groupBatchId}/contracts` | 修改 | 信封 +1、逐户 +2 | | 11 | 团期行程逐日汇总 | GET | `/v3/admin/order/group-batch/{groupBatchId}/itinerary` | 修改 | 天数预警行新增 `customerName` | | 12 | 团期行程某项逐户下钻 | GET | `/v3/admin/order/group-batch/{groupBatchId}/itinerary/nodes/{nodeKey}` | 修改 | 逐户行新增 `customerName` | | 13 | 审批中心统一列表 | GET | `/v3/admin/order/group-batch/approvals/page` | 修改 | 新增 `orderNo` / `customerName` / `departDate` / `approvalStatusName` / `bizTypeName` | | 14 | 退单审批列表 | GET | `/v3/admin/order/group-batch/withdraw/page` | 修改 | 新增 `refundModeName` / `approvalStatusName` | | 15 | 整团确认需求缺失预检 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check` | 修改 | 外层 +2、缺失项 +2 | | 16 | 调整团期满团名额 | PUT | `/v3/admin/order/group-batch/{groupBatchId}/capacity` | 修改 | 新增 `groupBatchId` | | 17 | 团期人员配置列表 | GET | `/v3/admin/group-batch/{productBatchId}/staff` | 修改 | 每行新增 `staffRoleName` / `reporterRank` / `reporterRankName` | | 18 | 团期人员配置全量保存 | PUT | `/v3/admin/group-batch/{productBatchId}/staff` | 修改 | 顶层 +2、每行 +3 | | 19 | 团期人员配置候选列表 | GET | `/v3/admin/group-batch/{productBatchId}/staff/candidates` | 修改 | 每项新增 6 个 key | --- ## 三、接口详情 ### 1. 团期分页列表(A1) `GET /v3/admin/order/group-batch` **VO**: `PageResult` #### 使用场景 团期看板的分页列表。**本单起每行多出 4 个 key**:运营八桶 `opsStage`/`opsStageName`、整团待收 `unpaidAmount`、以及与 `productBatchStatusLabel` 同值的 `productBatchStatusName`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | pageNo / pageSize / productId / scope / batchStatus / opsStage / month / keyword / deadlineFrom / deadlineTo | Query | — | — | 本单**一个入参都没改** | 含同名的**入参筛选** `opsStage`,取值集与新出参 `opsStage` 完全一致 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | opsStage | String | **本单新增**。运营八桶:`RECRUIT` 招募中 / `FORMED` 已成团 / `PENDING_TRIP` 待出行 / `TRAVELLING` 出行中 / `TRIP_FINISHED` 出行完毕 / `AUDITING` 核团中 / `CHECKED` 已验团 / `DISBANDED` 已流团。由 `batchStatus` 九态折叠(`FORMED` 是复合桶 = `RESOURCE_PREPARING` + `MATERIAL_PREPARING`)。⚠️ `batchStatus` 为 `null` 或不在九态内(历史脏数据)时本字段为 `null` | | opsStageName | String | **本单新增**。八桶中文名;与 `opsStage` **同生同灭**(一起为 `null`) | | unpaidAmount | String | **本单新增**。整团待收 = `max(0, receivableAmount − receivedAmount)`,**恒非负、恒非 `null`**(两个被减数任一为 `null` 按 0 计;已收多于应收的历史脏数据返 `0`,不透负数)。JSON **字符串**形态。⚠️ 与财务 tab 的同名字段**同名不同源**,权威待收以财务 tab 为准 | | productBatchStatusName | String | **本单新增**。与 `productBatchStatusLabel` **逐字同值同源**(产品域 Feign 给出,order 域不再映射一次) | | productBatchStatusLabel | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `productBatchStatusName` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch?pageNo=1&pageSize=20 Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "records": [ { "groupBatchId": "2096412454643802114", "batchStatus": "RESOURCE_PREPARING", "batchStatusName": "资源准备中", "opsStage": "FORMED", "opsStageName": "已成团", "productBatchStatus": "FINISHED", "productBatchStatusLabel": "已结束", "productBatchStatusName": "已结束", "receivableAmount": "321750.00", "receivedAmount": "55000.00", "unpaidAmount": "266750.00" } ], "total": 28, "page": 1, "pageSize": 20 }, "success": true } ``` #### 空数据 / 降级响应 库里存在九态之外的脏 `batch_status` 时,**只影响那一行**:该行 `opsStage` 与 `opsStageName` 同为 `null`,`batchStatus`/`batchStatusName` 照旧(后者回落原 code),整页**不会 500**。未建团行 `opsStage=RECRUIT`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | 400 | `pageNo` / `pageSize` 非法 | | 不变 | `GROUP_BATCH_PRODUCT_FETCH_FAILED`(589515) | `productId` 场景拉产品域班期失败 | ```json { "code": 589515, "message": "产品域班期获取失败", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - **前端不要再自己复刻九态 → 八桶的折叠逻辑**,直接读 `opsStage`。后端八桶是唯一真源(`GroupBatchStageBuckets`)。 - `unpaidAmount` 是本行应收/已收的**现算值**,与财务 tab 的同名字段算法不同,某些历史脏数据下可能差一点。 - `productBatchStatusName` / `productBatchStatusLabel` 在 `productId` 缺省的老调用里**都是 `null`**(不拉产品域),这一条与改前一致。 ### 2. 团期看板 `GET /v3/admin/order/group-batch/board` **VO**: `List` #### 使用场景 团期看板的行列表。**本单起每行多出 3 个 key**:`opsStage`/`opsStageName`,以及与 `batchStatusLabel` 同值的 `batchStatusName`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | productId | Query | Long | ✅ | 产品 ID | 本单不变 | | scope | Query | String | ❌ | 班期范围 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | opsStage | String | **本单新增**。运营八桶:`RECRUIT` 招募中 / `FORMED` 已成团 / `PENDING_TRIP` 待出行 / `TRAVELLING` 出行中 / `TRIP_FINISHED` 出行完毕 / `AUDITING` 核团中 / `CHECKED` 已验团 / `DISBANDED` 已流团。由 `batchStatus` 九态折叠(`FORMED` 是复合桶 = `RESOURCE_PREPARING` + `MATERIAL_PREPARING`)。⚠️ `batchStatus` 为 `null` 或不在九态内(历史脏数据)时本字段为 `null` | | opsStageName | String | **本单新增**。八桶中文名;与 `opsStage` **同生同灭**(一起为 `null`) | | batchStatusName | String | **本单新增**。与 `batchStatusLabel` **逐字同值**(同一次装配取同一个变量,不会分叉) | | batchStatusLabel | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `batchStatusName` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/board?productId=2044306857534636034 Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": [ { "groupBatchId": "2096412454643802114", "batchStatus": "RESOURCE_PREPARING", "batchStatusLabel": "资源准备中", "batchStatusName": "资源准备中", "opsStage": "FORMED", "opsStageName": "已成团" } ], "success": true } ``` #### 空数据 / 降级响应 未命中行(产品有班期、订单侧未建团)固定 `batchStatus=RECRUITING`,走同一条折桶得 `opsStage=RECRUIT`/招募中,**不特判**。脏状态行两个 opsStage 字段同为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | 400 | `productId` 缺失 | ```json { "code": 400, "message": "productId 不能为空", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 同一团期在 A1 分页、本端点、A2 详情三处的 `opsStage` **必然相同**(同一个 resolve)。 - `batchStatusLabel` 与 `batchStatusName` 永远同值,前端二选一即可。 ### 3. 团期详情(A2) `GET /v3/admin/order/group-batch/{groupBatchId}` **VO**: `GroupBatchDetailRespVO` #### 使用场景 团期详情。**本单起多出 5 个 key**:`opsStage`/`opsStageName`、与旧名同值的 `receivableAmount`/`receivedAmount`、以及 `thresholdSource` 的中文名 `thresholdSourceName`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | opsStage | String | **本单新增**。运营八桶:`RECRUIT` 招募中 / `FORMED` 已成团 / `PENDING_TRIP` 待出行 / `TRAVELLING` 出行中 / `TRIP_FINISHED` 出行完毕 / `AUDITING` 核团中 / `CHECKED` 已验团 / `DISBANDED` 已流团。由 `batchStatus` 九态折叠(`FORMED` 是复合桶 = `RESOURCE_PREPARING` + `MATERIAL_PREPARING`)。⚠️ `batchStatus` 为 `null` 或不在九态内(历史脏数据)时本字段为 `null` | | opsStageName | String | **本单新增**。八桶中文名;与 `opsStage` **同生同灭**(一起为 `null`) | | receivableAmount | String | **本单新增**。与 `totalReceivable` **逐字同值、同一来源不重算**,字段名与 A1 分页项 / 财务端点统一。JSON **字符串**形态 | | receivedAmount | String | **本单新增**。与 `totalReceived` **逐字同值、同一来源不重算**。JSON **字符串**形态 | | totalReceivable / totalReceived | BigDecimal | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `receivableAmount` / `receivedAmount` | | thresholdSourceName | String | **本单新增**。`PRODUCT_REALTIME` → 产品域实时值;`SNAPSHOT_FALLBACK` → 建团快照回退。`thresholdSource` 为 `null` 时为 `null`,取值不在枚举内时**回落原 code** | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2096412454643802114 Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "groupBatchId": "2096412454643802114", "batchStatus": "RESOURCE_PREPARING", "batchStatusName": "资源准备中", "opsStage": "FORMED", "opsStageName": "已成团", "totalReceivable": "321750.00", "totalReceived": "55000.00", "receivableAmount": "321750.00", "receivedAmount": "55000.00", "thresholdSource": "PRODUCT_REALTIME", "thresholdSourceName": "产品域实时值" }, "success": true } ``` #### 空数据 / 降级响应 `thresholdSource` 为 `null` 时 `thresholdSourceName` 也为 `null`;脏 `batch_status` 时两个 opsStage 字段同为 `null`。均不报错。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - `thresholdSourceName` 是**派生只读字段**(由 `thresholdSource` 现算),因此与它**不可能分叉**。 - 新旧两对金额字段永远同值,前端二选一即可;旧名下一期删除。 ### 4. 团期配房逐户明细 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/hotel` **VO**: `GroupBatchChipDetailVO` #### 使用场景 团期看板「配房」芯片下钻的逐户明细(**六个芯片端点同构**)。本单起信封多出 `groupBatchId`/`aggregateStatusName`,逐户行多出 `teamNo`/`customerName`/`statusName` 与三个负责人字段。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号(`order_main.team_no`,形如 `26-0001`)。⚠️ **逐户各不相同**,不是页头那一个团期编号的重复;⚠️ **订金支付成功后才生成,未付订金的户为 `null`**(与是不是团期订单无关)。后端**不做任何兜底**——不返空串、不回退成 `orderNo`、不拿团期编号顶替 | | customerName | String | **本单新增**。与 `contactName` **逐字同值**(同一来源 `order_main.customer_name`) | | contactName | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `customerName` | | statusName | String | **本单新增**。与 `statusText` **逐字同值**(同一次装配直接对拷) | | statusText | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `statusName` | | claimerId | String | **本单新增**。该户房务负责人 adminId(字符串形态);整团与该户均无人认领时为 `null` | | claimerName | String | **本单新增**。负责人姓名;与 `claimerId` 同生同灭 | | claimerSource | String | **本单新增**。`BATCH` 取自团级指针 `order_group_batch.house_claimer_id`(绝大多数情况)/ `LEGACY` 该户在历史旧户冻结名单内、取自该户 `order_hotel_requirement.claimer_id` / `null` 真实无人认领(此时另两个字段也为 `null`) | | aggregateStatusName | String | **本单新增**(信封层)。整团聚合态中文名:待开始 / 进行中 / 已完成 / 异常。`aggregateStatus` 为 `null` 时为 `null` | | groupBatchId | String | **本单新增**(信封层)。与 `batchId` **逐字同值**,同为字符串形态 | | batchId | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `groupBatchId` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2097498512387104770/chips/hotel Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "batchId": "2097498512387104770", "groupBatchId": "2097498512387104770", "chipLabel": "配房", "aggregateStatus": "TODO", "aggregateStatusName": "待开始", "totalCount": 4, "doneCount": 0, "items": [ { "orderId": "2098372769329598466", "orderNo": "HL20260911192708661", "teamNo": "26-9069", "contactName": "何书禾", "customerName": "何书禾", "peopleCount": 2, "status": "PENDING", "statusText": "待房务配", "statusName": "待房务配", "needsIt": true, "updateTime": null, "claimerId": "30001", "claimerName": "张三", "claimerSource": "BATCH" } ] }, "success": true } ``` #### 空数据 / 降级响应 团期无活跃子订单时 `items` 为空数组、三格计数为 0,新增字段**不出现在任何行里**(没有行)。整团未认领时逐户三个负责人字段全为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - **六个芯片端点的响应结构完全一致**,本单 6 处一起加,不因芯片类型特判——「这个团归谁」与看哪张芯片无关。 - **负责人默认读团级指针**:整团认领只写团级、不回写户级,只读户级会让「已被正常认领并配房的新团」全团显示空负责人。 - 超管接管后 CAS 落空的残留户级归属属**待清理脏数据**,本字段仍如实给 `BATCH`——`claimerSource` 就是给前端/运营分辨用的。 - 一个团里有的户有 `teamNo`、有的户是 `null` 是**正常态**,不是后端漏填。 ### 5. 团期配车逐户明细 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/vehicle` **VO**: `GroupBatchChipDetailVO` #### 使用场景 团期看板「配车」芯片下钻的逐户明细(**六个芯片端点同构**)。本单起信封多出 `groupBatchId`/`aggregateStatusName`,逐户行多出 `teamNo`/`customerName`/`statusName` 与三个负责人字段。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号(`order_main.team_no`,形如 `26-0001`)。⚠️ **逐户各不相同**,不是页头那一个团期编号的重复;⚠️ **订金支付成功后才生成,未付订金的户为 `null`**(与是不是团期订单无关)。后端**不做任何兜底**——不返空串、不回退成 `orderNo`、不拿团期编号顶替 | | customerName | String | **本单新增**。与 `contactName` **逐字同值**(同一来源 `order_main.customer_name`) | | contactName | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `customerName` | | statusName | String | **本单新增**。与 `statusText` **逐字同值**(同一次装配直接对拷) | | statusText | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `statusName` | | claimerId | String | **本单新增**。该户房务负责人 adminId(字符串形态);整团与该户均无人认领时为 `null` | | claimerName | String | **本单新增**。负责人姓名;与 `claimerId` 同生同灭 | | claimerSource | String | **本单新增**。`BATCH` 取自团级指针 `order_group_batch.house_claimer_id`(绝大多数情况)/ `LEGACY` 该户在历史旧户冻结名单内、取自该户 `order_hotel_requirement.claimer_id` / `null` 真实无人认领(此时另两个字段也为 `null`) | | aggregateStatusName | String | **本单新增**(信封层)。整团聚合态中文名:待开始 / 进行中 / 已完成 / 异常。`aggregateStatus` 为 `null` 时为 `null` | | groupBatchId | String | **本单新增**(信封层)。与 `batchId` **逐字同值**,同为字符串形态 | | batchId | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `groupBatchId` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2097498512387104770/chips/vehicle Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "batchId": "2097498512387104770", "groupBatchId": "2097498512387104770", "chipLabel": "配房", "aggregateStatus": "TODO", "aggregateStatusName": "待开始", "totalCount": 4, "doneCount": 0, "items": [ { "orderId": "2098372769329598466", "orderNo": "HL20260911192708661", "teamNo": "26-9069", "contactName": "何书禾", "customerName": "何书禾", "peopleCount": 2, "status": "PENDING", "statusText": "待房务配", "statusName": "待房务配", "needsIt": true, "updateTime": null, "claimerId": "30001", "claimerName": "张三", "claimerSource": "BATCH" } ] }, "success": true } ``` #### 空数据 / 降级响应 团期无活跃子订单时 `items` 为空数组、三格计数为 0,新增字段**不出现在任何行里**(没有行)。整团未认领时逐户三个负责人字段全为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - **六个芯片端点的响应结构完全一致**,本单 6 处一起加,不因芯片类型特判——「这个团归谁」与看哪张芯片无关。 - **负责人默认读团级指针**:整团认领只写团级、不回写户级,只读户级会让「已被正常认领并配房的新团」全团显示空负责人。 - 超管接管后 CAS 落空的残留户级归属属**待清理脏数据**,本字段仍如实给 `BATCH`——`claimerSource` 就是给前端/运营分辨用的。 - 一个团里有的户有 `teamNo`、有的户是 `null` 是**正常态**,不是后端漏填。 ### 6. 团期配导游逐户明细 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/guide` **VO**: `GroupBatchChipDetailVO` #### 使用场景 团期看板「配导游」芯片下钻的逐户明细(**六个芯片端点同构**)。本单起信封多出 `groupBatchId`/`aggregateStatusName`,逐户行多出 `teamNo`/`customerName`/`statusName` 与三个负责人字段。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号(`order_main.team_no`,形如 `26-0001`)。⚠️ **逐户各不相同**,不是页头那一个团期编号的重复;⚠️ **订金支付成功后才生成,未付订金的户为 `null`**(与是不是团期订单无关)。后端**不做任何兜底**——不返空串、不回退成 `orderNo`、不拿团期编号顶替 | | customerName | String | **本单新增**。与 `contactName` **逐字同值**(同一来源 `order_main.customer_name`) | | contactName | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `customerName` | | statusName | String | **本单新增**。与 `statusText` **逐字同值**(同一次装配直接对拷) | | statusText | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `statusName` | | claimerId | String | **本单新增**。该户房务负责人 adminId(字符串形态);整团与该户均无人认领时为 `null` | | claimerName | String | **本单新增**。负责人姓名;与 `claimerId` 同生同灭 | | claimerSource | String | **本单新增**。`BATCH` 取自团级指针 `order_group_batch.house_claimer_id`(绝大多数情况)/ `LEGACY` 该户在历史旧户冻结名单内、取自该户 `order_hotel_requirement.claimer_id` / `null` 真实无人认领(此时另两个字段也为 `null`) | | aggregateStatusName | String | **本单新增**(信封层)。整团聚合态中文名:待开始 / 进行中 / 已完成 / 异常。`aggregateStatus` 为 `null` 时为 `null` | | groupBatchId | String | **本单新增**(信封层)。与 `batchId` **逐字同值**,同为字符串形态 | | batchId | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `groupBatchId` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2097498512387104770/chips/guide Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "batchId": "2097498512387104770", "groupBatchId": "2097498512387104770", "chipLabel": "配房", "aggregateStatus": "TODO", "aggregateStatusName": "待开始", "totalCount": 4, "doneCount": 0, "items": [ { "orderId": "2098372769329598466", "orderNo": "HL20260911192708661", "teamNo": "26-9069", "contactName": "何书禾", "customerName": "何书禾", "peopleCount": 2, "status": "PENDING", "statusText": "待房务配", "statusName": "待房务配", "needsIt": true, "updateTime": null, "claimerId": "30001", "claimerName": "张三", "claimerSource": "BATCH" } ] }, "success": true } ``` #### 空数据 / 降级响应 团期无活跃子订单时 `items` 为空数组、三格计数为 0,新增字段**不出现在任何行里**(没有行)。整团未认领时逐户三个负责人字段全为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - **六个芯片端点的响应结构完全一致**,本单 6 处一起加,不因芯片类型特判——「这个团归谁」与看哪张芯片无关。 - **负责人默认读团级指针**:整团认领只写团级、不回写户级,只读户级会让「已被正常认领并配房的新团」全团显示空负责人。 - 超管接管后 CAS 落空的残留户级归属属**待清理脏数据**,本字段仍如实给 `BATCH`——`claimerSource` 就是给前端/运营分辨用的。 - 一个团里有的户有 `teamNo`、有的户是 `null` 是**正常态**,不是后端漏填。 ### 7. 团期配摄影逐户明细 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/photo` **VO**: `GroupBatchChipDetailVO` #### 使用场景 团期看板「配摄影」芯片下钻的逐户明细(**六个芯片端点同构**)。本单起信封多出 `groupBatchId`/`aggregateStatusName`,逐户行多出 `teamNo`/`customerName`/`statusName` 与三个负责人字段。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号(`order_main.team_no`,形如 `26-0001`)。⚠️ **逐户各不相同**,不是页头那一个团期编号的重复;⚠️ **订金支付成功后才生成,未付订金的户为 `null`**(与是不是团期订单无关)。后端**不做任何兜底**——不返空串、不回退成 `orderNo`、不拿团期编号顶替 | | customerName | String | **本单新增**。与 `contactName` **逐字同值**(同一来源 `order_main.customer_name`) | | contactName | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `customerName` | | statusName | String | **本单新增**。与 `statusText` **逐字同值**(同一次装配直接对拷) | | statusText | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `statusName` | | claimerId | String | **本单新增**。该户房务负责人 adminId(字符串形态);整团与该户均无人认领时为 `null` | | claimerName | String | **本单新增**。负责人姓名;与 `claimerId` 同生同灭 | | claimerSource | String | **本单新增**。`BATCH` 取自团级指针 `order_group_batch.house_claimer_id`(绝大多数情况)/ `LEGACY` 该户在历史旧户冻结名单内、取自该户 `order_hotel_requirement.claimer_id` / `null` 真实无人认领(此时另两个字段也为 `null`) | | aggregateStatusName | String | **本单新增**(信封层)。整团聚合态中文名:待开始 / 进行中 / 已完成 / 异常。`aggregateStatus` 为 `null` 时为 `null` | | groupBatchId | String | **本单新增**(信封层)。与 `batchId` **逐字同值**,同为字符串形态 | | batchId | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `groupBatchId` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2097498512387104770/chips/photo Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "batchId": "2097498512387104770", "groupBatchId": "2097498512387104770", "chipLabel": "配房", "aggregateStatus": "TODO", "aggregateStatusName": "待开始", "totalCount": 4, "doneCount": 0, "items": [ { "orderId": "2098372769329598466", "orderNo": "HL20260911192708661", "teamNo": "26-9069", "contactName": "何书禾", "customerName": "何书禾", "peopleCount": 2, "status": "PENDING", "statusText": "待房务配", "statusName": "待房务配", "needsIt": true, "updateTime": null, "claimerId": "30001", "claimerName": "张三", "claimerSource": "BATCH" } ] }, "success": true } ``` #### 空数据 / 降级响应 团期无活跃子订单时 `items` 为空数组、三格计数为 0,新增字段**不出现在任何行里**(没有行)。整团未认领时逐户三个负责人字段全为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - **六个芯片端点的响应结构完全一致**,本单 6 处一起加,不因芯片类型特判——「这个团归谁」与看哪张芯片无关。 - **负责人默认读团级指针**:整团认领只写团级、不回写户级,只读户级会让「已被正常认领并配房的新团」全团显示空负责人。 - 超管接管后 CAS 落空的残留户级归属属**待清理脏数据**,本字段仍如实给 `BATCH`——`claimerSource` 就是给前端/运营分辨用的。 - 一个团里有的户有 `teamNo`、有的户是 `null` 是**正常态**,不是后端漏填。 ### 8. 团期合同逐户明细 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/contract` **VO**: `GroupBatchChipDetailVO` #### 使用场景 团期看板「合同」芯片下钻的逐户明细(**六个芯片端点同构**)。本单起信封多出 `groupBatchId`/`aggregateStatusName`,逐户行多出 `teamNo`/`customerName`/`statusName` 与三个负责人字段。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号(`order_main.team_no`,形如 `26-0001`)。⚠️ **逐户各不相同**,不是页头那一个团期编号的重复;⚠️ **订金支付成功后才生成,未付订金的户为 `null`**(与是不是团期订单无关)。后端**不做任何兜底**——不返空串、不回退成 `orderNo`、不拿团期编号顶替 | | customerName | String | **本单新增**。与 `contactName` **逐字同值**(同一来源 `order_main.customer_name`) | | contactName | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `customerName` | | statusName | String | **本单新增**。与 `statusText` **逐字同值**(同一次装配直接对拷) | | statusText | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `statusName` | | claimerId | String | **本单新增**。该户房务负责人 adminId(字符串形态);整团与该户均无人认领时为 `null` | | claimerName | String | **本单新增**。负责人姓名;与 `claimerId` 同生同灭 | | claimerSource | String | **本单新增**。`BATCH` 取自团级指针 `order_group_batch.house_claimer_id`(绝大多数情况)/ `LEGACY` 该户在历史旧户冻结名单内、取自该户 `order_hotel_requirement.claimer_id` / `null` 真实无人认领(此时另两个字段也为 `null`) | | aggregateStatusName | String | **本单新增**(信封层)。整团聚合态中文名:待开始 / 进行中 / 已完成 / 异常。`aggregateStatus` 为 `null` 时为 `null` | | groupBatchId | String | **本单新增**(信封层)。与 `batchId` **逐字同值**,同为字符串形态 | | batchId | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `groupBatchId` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2097498512387104770/chips/contract Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "batchId": "2097498512387104770", "groupBatchId": "2097498512387104770", "chipLabel": "配房", "aggregateStatus": "TODO", "aggregateStatusName": "待开始", "totalCount": 4, "doneCount": 0, "items": [ { "orderId": "2098372769329598466", "orderNo": "HL20260911192708661", "teamNo": "26-9069", "contactName": "何书禾", "customerName": "何书禾", "peopleCount": 2, "status": "PENDING", "statusText": "待房务配", "statusName": "待房务配", "needsIt": true, "updateTime": null, "claimerId": "30001", "claimerName": "张三", "claimerSource": "BATCH" } ] }, "success": true } ``` #### 空数据 / 降级响应 团期无活跃子订单时 `items` 为空数组、三格计数为 0,新增字段**不出现在任何行里**(没有行)。整团未认领时逐户三个负责人字段全为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - **六个芯片端点的响应结构完全一致**,本单 6 处一起加,不因芯片类型特判——「这个团归谁」与看哪张芯片无关。 - **负责人默认读团级指针**:整团认领只写团级、不回写户级,只读户级会让「已被正常认领并配房的新团」全团显示空负责人。 - 超管接管后 CAS 落空的残留户级归属属**待清理脏数据**,本字段仍如实给 `BATCH`——`claimerSource` 就是给前端/运营分辨用的。 - 一个团里有的户有 `teamNo`、有的户是 `null` 是**正常态**,不是后端漏填。 ### 9. 团期保险逐户明细 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/insurance` **VO**: `GroupBatchChipDetailVO` #### 使用场景 团期看板「保险」芯片下钻的逐户明细(**六个芯片端点同构**)。本单起信封多出 `groupBatchId`/`aggregateStatusName`,逐户行多出 `teamNo`/`customerName`/`statusName` 与三个负责人字段。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号(`order_main.team_no`,形如 `26-0001`)。⚠️ **逐户各不相同**,不是页头那一个团期编号的重复;⚠️ **订金支付成功后才生成,未付订金的户为 `null`**(与是不是团期订单无关)。后端**不做任何兜底**——不返空串、不回退成 `orderNo`、不拿团期编号顶替 | | customerName | String | **本单新增**。与 `contactName` **逐字同值**(同一来源 `order_main.customer_name`) | | contactName | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `customerName` | | statusName | String | **本单新增**。与 `statusText` **逐字同值**(同一次装配直接对拷) | | statusText | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `statusName` | | claimerId | String | **本单新增**。该户房务负责人 adminId(字符串形态);整团与该户均无人认领时为 `null` | | claimerName | String | **本单新增**。负责人姓名;与 `claimerId` 同生同灭 | | claimerSource | String | **本单新增**。`BATCH` 取自团级指针 `order_group_batch.house_claimer_id`(绝大多数情况)/ `LEGACY` 该户在历史旧户冻结名单内、取自该户 `order_hotel_requirement.claimer_id` / `null` 真实无人认领(此时另两个字段也为 `null`) | | aggregateStatusName | String | **本单新增**(信封层)。整团聚合态中文名:待开始 / 进行中 / 已完成 / 异常。`aggregateStatus` 为 `null` 时为 `null` | | groupBatchId | String | **本单新增**(信封层)。与 `batchId` **逐字同值**,同为字符串形态 | | batchId | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `groupBatchId` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2097498512387104770/chips/insurance Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "batchId": "2097498512387104770", "groupBatchId": "2097498512387104770", "chipLabel": "配房", "aggregateStatus": "TODO", "aggregateStatusName": "待开始", "totalCount": 4, "doneCount": 0, "items": [ { "orderId": "2098372769329598466", "orderNo": "HL20260911192708661", "teamNo": "26-9069", "contactName": "何书禾", "customerName": "何书禾", "peopleCount": 2, "status": "PENDING", "statusText": "待房务配", "statusName": "待房务配", "needsIt": true, "updateTime": null, "claimerId": "30001", "claimerName": "张三", "claimerSource": "BATCH" } ] }, "success": true } ``` #### 空数据 / 降级响应 团期无活跃子订单时 `items` 为空数组、三格计数为 0,新增字段**不出现在任何行里**(没有行)。整团未认领时逐户三个负责人字段全为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - **六个芯片端点的响应结构完全一致**,本单 6 处一起加,不因芯片类型特判——「这个团归谁」与看哪张芯片无关。 - **负责人默认读团级指针**:整团认领只写团级、不回写户级,只读户级会让「已被正常认领并配房的新团」全团显示空负责人。 - 超管接管后 CAS 落空的残留户级归属属**待清理脏数据**,本字段仍如实给 `BATCH`——`claimerSource` 就是给前端/运营分辨用的。 - 一个团里有的户有 `teamNo`、有的户是 `null` 是**正常态**,不是后端漏填。 ### 10. 团期「合同保险」面板 `GET /v3/admin/order/group-batch/{groupBatchId}/contracts` **VO**: `GroupBatchContractBoardVO` #### 使用场景 合同保险面板一屏读。本单起信封多出 `groupBatchId`,逐户卡片多出 `teamNo` 与 `customerName`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | groupBatchId | String | **本单新增**(信封层)。与 `batchId` **逐字同值**,同为字符串形态 | | batchId | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `groupBatchId` | | teamNo | String | **本单新增**(逐户)。口径与芯片逐户行**完全一致**:`order_main.team_no`,逐户各不相同,未付订金为 `null`,无任何兜底 | | customerName | String | **本单新增**(逐户)。与 `contactName` **逐字同值** | | contactName | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `customerName` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2097498512387104770/contracts Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "batchId": "2097498512387104770", "groupBatchId": "2097498512387104770", "issuable": false, "totalCount": 4, "items": [ { "orderId": "2098372769329598466", "orderNo": "HL20260911192708661", "teamNo": "26-9069", "contactName": "何书禾", "customerName": "何书禾", "contractState": "NOT_ISSUED", "contractStateText": "未出" } ] }, "success": true } ``` #### 空数据 / 降级响应 本期无活跃子订单时 `items` 为空数组、三格分子分母均 0。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 本端点与芯片 `contract` 端点的 `teamNo` **同一取值来源、同一 null 语义**,两处必然一致。 - 面板三格计数由 `items` 自算,本单**未改一行**计数逻辑。 ### 11. 团期行程逐日汇总 `GET /v3/admin/order/group-batch/{groupBatchId}/itinerary` **VO**: `GroupBatchItineraryRespVO` #### 使用场景 团期行程汇总。本单起「天数不一致户」预警行多出 `customerName`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | dayCountOutliers[].customerName | String | **本单新增**。与同一项的 `contactName` **逐字同值** | | dayCountOutliers[].contactName | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `customerName` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2096412454643802114/itinerary Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "dayCount": 3, "dayCountConsistent": false, "dayCountOutliers": [ { "orderId": "2096655509804187650", "orderNo": "HL20260907014322086", "contactName": "沈怀妍", "customerName": "沈怀妍", "dayCount": 1 } ] }, "success": true } ``` #### 空数据 / 降级响应 各户天数一致时 `dayCountOutliers` 为空数组,新增字段不出现(没有行)。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 本单只加字段,**行程一致性判定逻辑一行未改**。 ### 12. 团期行程某项逐户下钻 `GET /v3/admin/order/group-batch/{groupBatchId}/itinerary/nodes/{nodeKey}` **VO**: `GroupBatchItineraryNodeDetailVO` #### 使用场景 行程某个节点的逐户下钻。本单起逐户行多出 `customerName`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | | nodeKey | Path | String | ✅ | 节点键(由汇总端点给出) | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | items[].customerName | String | **本单新增**。与同一项的 `contactName` **逐字同值** | | items[].contactName | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `customerName` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2096412454643802114/itinerary/nodes/ Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "nodeName": "礼仪接机", "householdCount": 55, "items": [ { "orderId": "2096655509804187650", "orderNo": "HL20260907001741975", "contactName": "蒋岚妍", "customerName": "蒋岚妍", "has": true } ] }, "success": true } ``` #### 空数据 / 降级响应 `nodeKey` 无人命中时 `items` 仍返回全部活跃户(`has=false`),新增字段照常有值。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 未命中该节点的户 `has=false`,但 `customerName` 与 `contactName` 照常有值。 ### 13. 审批中心统一列表 `GET /v3/admin/order/group-batch/approvals/page` **VO**: `PageResult` #### 使用场景 流团与退单户共用的审批中心列表。本单起每行多出 5 个 key:退单户三件套 `orderNo`/`customerName`/`departDate`,以及 `approvalStatusName` 与 `bizTypeName`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | pageNum / pageSize / bizType / approvalStatus / groupBatchId | Query | — | — | 本单**一个入参都没改** | — | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | orderNo | String | **本单新增**。⚠️ **只在 `bizType=WITHDRAW` 行有值,`DISBAND` 行恒 `null`**(与已有的 `orderId` 同一条规则);订单查不到(已软删)时也为 `null` | | customerName | String | **本单新增**。同上规则 | | departDate | String | **本单新增**。团期出发日 `yyyy-MM-dd`,与退单专用列表 `GET .../withdraw/page` **同源同格式、逐字相同**。同上规则 | | approvalStatusName | String | **本单新增**。待审批 / 已通过 / 已取消退单。`approvalStatus` 为 `null` 时为 `null`,取值不在枚举内时**回落原 code** | | bizTypeName | String | **本单新增**。与 `bizTypeText` **逐字同值**(流团 / 退单户) | | bizTypeText | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `bizTypeName` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/approvals/page?pageNum=1&pageSize=20 Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "records": [ { "approvalId": "2096029515502243842", "bizType": "WITHDRAW", "bizTypeText": "退单户", "bizTypeName": "退单户", "approvalStatus": "APPROVED", "approvalStatusName": "已通过", "orderId": "2096029450184347649", "orderNo": "HL20260905081537435", "customerName": "退单复测-7100", "departDate": "2026-10-31" }, { "approvalId": "2097963098638876674", "bizType": "DISBAND", "bizTypeText": "流团", "bizTypeName": "流团", "approvalStatus": "APPROVED", "approvalStatusName": "已通过", "orderId": null, "orderNo": null, "customerName": null, "departDate": null } ], "total": 38 }, "success": true } ``` #### 空数据 / 降级响应 页内**一行 WITHDRAW 都没有**时,后端**一次订单查询都不发**(空集合短路)。订单或团期已软删的 WITHDRAW 行三个新字段为 `null`,**该行照常返回、不漏行、不报错**。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589507, "message": "无操作权限", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 🔴 **前端必须按行类型判断再渲染**:三个新字段只在 `bizType=WITHDRAW` 行有值,`DISBAND` 行恒 `null`。按「字段存在即渲染」写会出现空列。 - 整页**一次**批量取单(防 N+1),不是每行一次。 ### 14. 退单审批列表 `GET /v3/admin/order/group-batch/withdraw/page` **VO**: `PageResult` #### 使用场景 退单户专用审批列表。本单起每行多出 `refundModeName` 与 `approvalStatusName` 两个中文名。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | pageNum / pageSize / approvalStatus / … | Query | — | — | 本单**一个入参都没改** | — | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | refundModeName | String | **本单新增**。按政策退 / 订金全额退 / 已付全额退 / 部分退。`refundMode` 为 `null` 时为 `null`,取值不在枚举内时**回落原 code** | | approvalStatusName | String | **本单新增**。待审批 / 已通过 / 已取消退单。同上兜底口径 | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/withdraw/page?pageNum=1&pageSize=20 Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "records": [ { "approvalId": "2096029515502243842", "orderNo": "HL20260905081537435", "customerName": "退单复测-7100", "departDate": "2026-10-31", "refundMode": "FULL_DEPOSIT", "refundModeName": "订金全额退", "approvalStatus": "APPROVED", "approvalStatusName": "已通过" } ], "total": 2 }, "success": true } ``` #### 空数据 / 降级响应 无数据时 `records` 为空数组。两个 code 为 `null` 时对应的 `Name` 也为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无权限 | ```json { "code": 589507, "message": "无操作权限", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 退单详情 `GET .../withdraw/{approvalId}` 的响应体继承自本 VO,**同样多出这两个字段**(向后兼容纯增)。 - 本端点与审批中心统一列表对同一张审批单的 `orderNo`/`customerName`/`departDate` **逐字相同**。 ### 15. 整团确认需求缺失预检 `GET /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check` **VO**: `GroupBatchRequirementCheckRespVO` #### 使用场景 点「整体确认需求」之前的预检。本单起外层多出 `checkedResourceTypes` 与 `batchStatusName`,缺失项多出 `customerName` 与 `reasonName`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | checkedResourceTypes | String[] | **本单新增**。本期**恒 `["HOTEL"]`**,由服务端常量给出。🔴 预检**只检查住宿、不检查用车**——`ready=true` **不代表**用车需求齐备 | | batchStatusName | String | **本单新增**。团期状态中文名;`batchStatus` 为 `null` 时为 `null`,不在九态内时回落原 code | | missing[].customerName | String | **本单新增**。客户姓名(`order_main.customer_name`),运营据此直接认出是哪一户 | | missing[].reasonName | String | **本单新增**。未提报 / 缺房型或房数 / 需求结构异常 / 住宿晚数对不上 / 晚序号异常。`reason` 为 `null` 时为 `null`,不在五值内时回落原 code | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2096412454643802114/requirement/confirm-check Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "groupBatchId": "2096412454643802114", "batchStatus": "RESOURCE_PREPARING", "batchStatusName": "资源准备中", "ready": false, "checkedResourceTypes": ["HOTEL"], "missing": [ { "orderId": "2096655509804187650", "orderNo": "HL20260907014322086", "customerName": "沈怀妍", "consultantId": "1001", "consultantName": "admin", "reason": "NOT_SUBMITTED", "reasonName": "未提报" } ] }, "success": true } ``` #### 空数据 / 降级响应 无缺失户时 `missing` 为空数组、`ready` 由阶段判定;`checkedResourceTypes` **无论如何都是 `["HOTEL"]`**(不随团期配置变化)。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `GROUP_BATCH_NOT_FOUND`(589500) | 团期不存在 / 非团期 / 已软删 | | 不变 | `GROUP_BATCH_PERMISSION_DENIED`(589507) | 无 `group-batch:view` 权限 | ```json { "code": 589500, "message": "团期不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 🔴 `checkedResourceTypes` 存在的**全部理由**就是让前端知道「`ready=true` 只说明住宿齐了」。扩到用车是另一张单,届时本字段变成 `["HOTEL","VEHICLE"]`。 - 本单**不扩大检查范围**,预检行为一行未改。 ### 16. 调整团期满团名额 `PUT /v3/admin/order/group-batch/{groupBatchId}/capacity` **VO**: `GroupBatchCapacityRespVO` #### 使用场景 调整满团户数。本单起响应多出 `groupBatchId`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期聚合主键 | 本单不变 | | capacityDelta | Body | Integer | ✅ | 净增量(户),不得为 0 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | groupBatchId | String | **本单新增**。与 `batchId` **逐字同值**,同为字符串形态。⚠️ 它是**派生只读字段**(由 `batchId` 现算),因此两者**不可能分叉** | | batchId | String | **值不变**,本单起标 `@Deprecated`,本批**不删**;新代码请改读 `groupBatchId` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http PUT /v3/admin/order/group-batch/2097498512387104770/capacity Content-Type: application/json {"capacityDelta": 1} Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "batchId": "2097498512387104770", "groupBatchId": "2097498512387104770", "beforeMaxRooms": 4, "maxRooms": 5, "capacityDelta": 1, "enrolledRooms": 4, "remainRooms": 1 }, "success": true } ``` #### 空数据 / 降级响应 失败时 `data` 为 `null`(与改前一致),新增字段随之不出现。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | 589538 | 非招募中阶段不可调 | | 不变 | 589539 | 名额调整量不能为 0 | | 不变 | 589509 | 新满团户数低于已报名户数或为负 | | 不变 | 589540 | 产品域库存同步失败 | ```json { "code": 589539, "message": "名额调整量不能为 0", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 本单**只加返回字段**,名额调整的权限门、阶段门、库存同步逻辑一行未改。 ### 17. 团期人员配置列表 `GET /v3/admin/group-batch/{productBatchId}/staff` **VO**: `List` #### 使用场景 团期已配人员名册(**裸数组、无顶层信封**,本单不改其形态)。本单起每行多出 `staffRoleName` 与报账人两字段。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | productBatchId | Path | Long | ✅ | 产品侧班期 ID | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | staffRoleName | String | **本单新增**。角色中文名(领队/司机/导游/摄影/其他/导游助理/研学老师/生活老师);`staffRole` 为 `null` 时为 `null`,不在枚举内时回落原 code | | reporterRank | String | **本单新增**。报账人等级 `PRIMARY` 主 / `SECONDARY` 次 / `NONE` 非报账人。**本端点恒非 `null`**(历史空值归一为 `NONE`) | | reporterRankName | String | **本单新增**。主报账人 / 次报账人 / 非报账人;与 `reporterRank` 同生同灭 | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/group-batch/2052935476557328386/staff Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": [ { "id": "2098667039013941249", "staffId": 1002, "staffRole": "GUIDE", "staffRoleName": "导游", "staffName": "李雪梅", "staffPhone": "138****1002", "sortOrder": 0, "reporterRank": "PRIMARY", "reporterRankName": "主报账人" } ], "success": true } ``` #### 空数据 / 降级响应 未配置任何人员时返回空数组 `[]`(与改前一致)。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | 400 | `productBatchId` 非法 | ```json { "code": 400, "message": "参数校验失败", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 🔴 本端点返回**裸数组、没有顶层信封**,因此**不带** `productBatchId` / `groupBatchId`。包信封是结构变更(破坏性),另有工单跟进。 - `reporterRank` 由 `PUT .../staff/{staffId}/reporter-rank` 设置,本单未改那个写端点一行。 ### 18. 团期人员配置全量保存 `PUT /v3/admin/group-batch/{productBatchId}/staff` **VO**: `BatchStaffConfigRespVO` #### 使用场景 全量保存团期人员配置。本单起响应顶层多出 `productBatchId`/`groupBatchId`,`staffList[]` 每项多出 `staffRoleName` 与报账人两字段。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | productBatchId | Path | Long | ✅ | 产品侧班期 ID | 本单不变 | | staffList | Body | Array | ✅ | 全量配置(传空数组 = 清空) | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | productBatchId | String | **本单新增**(顶层)。直接回显路径参数,**恒非 `null`** | | groupBatchId | String | **本单新增**(顶层)。由 `productBatchId` 反查得到。⚠️ **本端点的 200 响应中恒非 `null`**——入口第一道守卫就是成团校验(未建团 589553 / 未成团或已流团 589552),团期不存在时根本返不到这里 | | staffList[].staffRoleName / reporterRank / reporterRankName | String | **本单新增**,口径同上一个端点;新配的人 `reporterRank` 为 `NONE` | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http PUT /v3/admin/group-batch/2052935476557328386/staff Content-Type: application/json {"staffList": [{"staffId": 1002, "staffRole": "GUIDE", "sortOrder": 0}]} Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "productBatchId": "2052935476557328386", "groupBatchId": "2096412454643802114", "staffList": [ { "id": "2098667039013941249", "staffId": 1002, "staffRole": "GUIDE", "staffRoleName": "导游", "staffName": "李雪梅", "reporterRank": "NONE", "reporterRankName": "非报账人" } ], "affectedOrderCount": 55 }, "success": true } ``` #### 空数据 / 降级响应 传空数组 = 清空配置,`staffList` 返回空数组,两个顶层 ID 照常有值。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | 589553 | 团期尚未创建(该班期还没有任何订单) | | 不变 | 589552 | 团期未成团或已流团 | | 不变 | 589582 | 请求体内 `staffId` 重复 | | 不变 | 582114 | 角色与人员类型不符 | ```json { "code": 589553, "message": "团期尚未创建(该班期还没有任何订单),请先建团并完成成团后再操作", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 🔴 **未建团时本端点返 589553,不是 200**——这是工单 #7287 T5 定的**既有守卫**,本单未改。想在成团前拿团期 ID,用候选端点。 - 反查团期用的是**入口守卫已经查过的那一次**结果,**新增查询数为 0**。 ### 19. 团期人员配置候选列表 `GET /v3/admin/group-batch/{productBatchId}/staff/candidates` **VO**: `List` #### 使用场景 「配置导游 / 配置摄影」弹窗的人员资源库。本单起每项多出 6 个 key。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | productBatchId | Path | Long | ✅ | 产品侧班期 ID | 本单不变 | | role | Query | String | ✅ | `GUIDE` 导游位 / `PHOTOGRAPHER` 摄影位 | 本单不变 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | productBatchId | String | **本单新增**。直接回显路径参数,**恒非 `null`** | | groupBatchId | String | **本单新增**。由 `productBatchId` 反查。🔴 **团期尚未创建时为 `null`**(staff 允许在成团前先配,本端点**不报错**、返 200)。**前端不得把它当必有主键去拼后续请求**,否则会打出 `/group-batch/null/...` | | staffTypeName | String | **本单新增**。人员类型中文名;`staffType` 为 `null` 时为 `null`,不在枚举内时回落原 code | | assignedRoleName | String | **本单新增**。已选角色中文名;`assignedRole` 为 `null` 时为 `null` | | reporterRank | String | **本单新增**。⚠️ **未进入本团期名册的候选为 `null`,不是 `NONE`**——`NONE` 的语义是「已在名册里、但不是报账人」,两者必须能区分 | | reporterRankName | String | **本单新增**。与 `reporterRank` 同生同灭 | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型、取值与 null 语义 | #### 请求示例 ```http GET /v3/admin/group-batch/2052935476557328386/staff/candidates?role=GUIDE Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": [ { "productBatchId": "2052935476557328386", "groupBatchId": "2096412454643802114", "staffId": 1002, "staffName": "李雪梅", "staffPhone": "138****1002", "staffType": "GUIDE", "staffTypeName": "导游", "assigned": true, "assignedRole": "GUIDE", "assignedRoleName": "导游", "reporterRank": "PRIMARY", "reporterRankName": "主报账人" }, { "productBatchId": "2052935476557328386", "groupBatchId": "2096412454643802114", "staffId": 1010, "staffType": "LEADER", "staffTypeName": "领队", "assigned": false, "assignedRole": null, "assignedRoleName": null, "reporterRank": null, "reporterRankName": null } ], "success": true } ``` #### 空数据 / 降级响应 资源域无可用人员时返回空数组。团期尚未创建时 `groupBatchId` 为 `null`、HTTP 200、`code=200`(**不返 589500**)。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | 582116 | `role` 不是 `GUIDE` / `PHOTOGRAPHER` | | 不变 | `STAFF_INFO_FETCH_FAILED` | 资源域不可达 | ```json { "code": 582116, "message": "候选角色非法", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 🔴 **`reporterRank` / `assignedRole` 是【本团期名册】维度,`assigned` 是【本配置位】维度,两者口径不同。** 一个被配到**另一个位**上的人,在本位会是 `assigned=false` 但 `reporterRank` 非 `null`——这是对的,不是 bug。 - 整批**只反查一次**团期,不是每个候选一次。 --- ## 四、契约约束与正确调用方式 1. **不需要改任何调用代码即可继续工作**。本批是纯增,未消费新字段的页面行为完全不变。 2. **`opsStage` 直接读,不要自己折桶**。服务端八桶的唯一真源是 `GroupBatchStageBuckets`;前端复刻一份,规则一改就和**入参筛选** `opsStage` 的结果对不上。 3. **同值字段对二选一即可**,不要两个都渲染: `batchStatusLabel`↔`batchStatusName`、`productBatchStatusLabel`↔`productBatchStatusName`、 `statusText`↔`statusName`、`bizTypeText`↔`bizTypeName`、`contactName`↔`customerName`、 `batchId`↔`groupBatchId`、`totalReceivable`↔`receivableAmount`、`totalReceived`↔`receivedAmount`。 服务端保证每一对**在同一次响应里逐字相同**(同一表达式/同一变量赋值,或派生只读字段)。 4. **`teamNo` 按 `null` 判空即可**,不必区分 `""`——后端永远不返空串。也**不要**用 `teamNo != null` 判断是不是团期单,那会两个方向都判错。 5. **审批中心统一列表按 `bizType` 分支渲染**:`DISBAND` 行的 `orderId` / `orderNo` / `customerName` / `departDate` 恒 `null`。 6. **staff 候选端点的 `groupBatchId` 可能为 `null`**,拿它拼后续请求前必须判空。 --- ## 五、数据库行为 **本单零数据库变更**:无新增 / 修改表、无新增列、无新增索引、**无 Flyway 迁移脚本**、无新增 Mapper 方法。 清单里的两个写端点(16 `PUT .../capacity`、18 `PUT .../staff`)**写库行为一行未改**,本单只是在它们返回前多填了展示字段: - `PUT .../capacity` 的 `groupBatchId` 是**派生只读字段**,由已有的 `batchId` 现算,不读库; - `PUT .../staff` 的两个顶层 ID 复用**入口成团守卫已经查过的那一次** `getByProductBatchId` 结果,**新增查询数为 0**。 唯一的 Mapper 改动是给**两条既有列受限投影**各加一个已有列: `OrderInfoMapper.selectChipProjectionByProductBatchIds` 与 `selectContractPanelProjectionByProductBatchIds` 各加一个 `OrderInfo::getTeamNo`(映射 `order_main.team_no`,**非加密列**,不触发逐行解密)。 `git diff` 上该文件**只有两行新增、无第三个 hunk**。 新增字段的取数全部**零新增查询**: - 团号来自上述两条**已有**的批量投影; - 芯片负责人的团级指针来自 `chipDetail` 里**已在手**的团期实体; - 冻结名单每次请求读 1 次(按 `groupBatchId` 单表命中),**名单为空时短路掉**户级需求那次查询; - 审批列表整页**一次**批量取单,页内无 `WITHDRAW` 行时**一次都不取**; - staff 报账人来自**已有**的名册查询。 --- ## 六、边界行为 | 场景 | 行为 | |---|---| | `batch_status` 是九态之外的脏值 / 为 `null` | 该行 `opsStage` 与 `opsStageName` **同为 `null`**,`batchStatus` / `batchStatusName` 照旧;**整页不会 500** | | 未建团行(产品有班期、订单侧无团期) | `batchStatus=RECRUITING` → `opsStage=RECRUIT`/招募中,走同一条折桶、不特判 | | 整团未认领(`house_claimer_id IS NULL`) | 芯片逐户 `claimerId`/`claimerName`/`claimerSource` **三者全为 `null`**,不是空串、不是 0 | | 该户在历史旧户冻结名单内 | 读该户 `order_hotel_requirement.claimer_id`,`claimerSource=LEGACY`;户级也为空则三者整体 `null`(**不回落团级**) | | 超管接管后 CAS 落空、户级残留旧人 | 该户不在冻结名单里,按定案显示**团级新接管人** + `claimerSource=BATCH`。这是对的:团级指针是唯一真源,户级残留是待清理脏数据 | | 订金未支付成功 | `teamNo` 为 `null`(**不是空串、不回退成 `orderNo`**);同团其他已付订金的户照常有值 | | 审批行是 `DISBAND` | `orderNo` / `customerName` / `departDate` 恒 `null`,该行**照常返回、不漏行、不报错** | | 审批行是 `WITHDRAW` 但订单/团期已软删 | 三个字段为 `null`,该行照常返回;**统一列表与退单专用列表给出相同的 `null`** | | 页内一条 `WITHDRAW` 都没有 | 后端**一次订单查询都不发** | | 任一 code 为 `null` | 对应的 `Name` 为 `null`(**不造空串**) | | 任一 code 不在枚举内 | 对应的 `Name` **回落原 code**(不抛异常、不返空串);`opsStage` 是唯一例外——它回落 `null`,因为八桶取值集是前端页签,透一个不存在的值出去会渲染出不存在的页签 | | 应收 / 已收任一为 `null` | 按 `0` 参与计算,`unpaidAmount` 恒非 `null` | | 已收 > 应收(退款/多收的历史脏数据) | `unpaidAmount` 返回 `0`,**不透负数** | | staff 候选:团期尚未创建 | `groupBatchId` 为 `null`,HTTP 200 + `code=200`(**不返 589500**) | | staff 候选:人已被配到**另一个位** | `assigned=false`(本配置位口径)但 `assignedRole` / `reporterRank` **非 `null`**(本团期名册口径)——两者口径不同,不是 bug | | staff 候选:人还没进本团期名册 | `reporterRank` 为 `null`(**不是 `NONE`**) | | staff 名册:`reporter_rank` 历史空值 | 归一为 `NONE` / 非报账人(名册端点**恒非 `null`**) | ## 六.6、修改前后对比 | 维度 | 改前 | 改后 | |---|---|---| | 团期分页项 `GroupBatchPageItemRespVO` | 27 个 key | **31 个 key**(+`opsStage`/`opsStageName`/`unpaidAmount`/`productBatchStatusName`) | | 团期看板行 `GroupBatchBoardItemRespVO` | 27 | **30**(+`opsStage`/`opsStageName`/`batchStatusName`) | | 团期详情 `GroupBatchDetailRespVO` | 43 | **48**(+`opsStage`/`opsStageName`/`receivableAmount`/`receivedAmount`/`thresholdSourceName`) | | 芯片信封 `GroupBatchChipDetailVO` | 6 | **8**(+`aggregateStatusName`/`groupBatchId`) | | 芯片逐户 `GroupBatchChipItemRespVO` | 8 | **14**(+`teamNo`/`customerName`/`statusName`/`claimerId`/`claimerName`/`claimerSource`) | | 合同面板 `GroupBatchContractBoardVO` | 10 | **11**(+`groupBatchId`) | | 合同逐户 `GroupBatchContractItemVO` | 15 | **17**(+`teamNo`/`customerName`) | | 预检 `GroupBatchRequirementCheckRespVO` | 4 | **6**(+`checkedResourceTypes`/`batchStatusName`;`missing[]` 另 +2) | | 统一审批行 `GroupBatchApprovalItemRespVO` | 14 | **19**(+`orderNo`/`customerName`/`departDate`/`approvalStatusName`/`bizTypeName`) | | 退单行 `WithdrawApprovalItemRespVO` | 16 | **18**(+`refundModeName`/`approvalStatusName`) | | staff 候选 `StaffCandidateRespVO` | 7 | **13**(+6) | | **团期子订单项 `GroupBatchOrderItemRespVO`(A3)** | 29 | **29 —— 零变化** | | 路径 / 方法 / 请求参数 | — | **完全不变** | | 既有字段的名称 / 类型 / 取值 / null 语义 | — | **完全不变**(源码 diff 中零条字段删除或改名) | | 错误码 | — | **无新增、无调整** | | 数据库 / Flyway / 网关路由 | — | **零改动** | ## 六.7、影响评估 - **兼容性**:响应体**纯增**字段,未消费新 key 的前端页面**无需任何改动即可继续工作**。 - **需要前端动的**: 1. 团期列表 / 看板的「阶段」页签改读 `opsStage`,**删掉自己那套九态→八桶折叠**; 2. 报名清单「联系人」列改读 `customerName`(此前显示 `—` 是读错 key); 3. 芯片下钻逐户行可加「负责人」与「团号」两列; 4. 审批中心统一列表可加订单号 / 客户姓名 / 出发日三列(**按 `bizType` 分支渲染**); 5. 新代码逐步把四组 `Text`/`Label`/`batchId`/`total*` 旧名切到新名(**本批不强制,删除时间另行通知**)。 - **性能**:新增开销只有「每次芯片请求多读一次冻结名单(按 `groupBatchId` 单表命中,名单为空时到此为止)」与「审批列表整页一次批量取单(页内无 WITHDRAW 行则零次)」。 团号与负责人全部复用**已有**的批量投影与已在手的团期实体,**零新增查询**。 - **响应体积**:团期分页每行多 4 个 key、芯片逐户每户多 6 个 key。芯片明细端点返回全量逐户、没有分页,**几百户的团会多出上千个字段**,仍在 KB 量级、不触任何网关限制。 - **连带生效(无需单独对接)**:退单审批详情 `GET .../withdraw/{approvalId}` 的响应体继承自 `WithdrawApprovalItemRespVO`,**同样多出** `refundModeName` / `approvalStatusName`。 - **两个只读派生字段**:`GroupBatchCapacityRespVO.groupBatchId` 与 `GroupBatchDetailRespVO.thresholdSourceName` 是**只读派生 getter**(分别由 `batchId` 与 `thresholdSource` 现算)。对前端没有任何差别;对后端的意义是这两对 key **在结构上不可能分叉**。 --- ## 七、不影响范围 - **团期子订单列表(A3)`GroupBatchOrderItemRespVO` 一个字段都没加**,改前改后 29 个 key 完全一致——它的 5 个裸 code 与 `roomTypeName` 归 #7536。 - **财务 tab 的 12 个金额字段 JSON 形态本批不变**(`GroupBatchFinanceRespVO` / `GroupBatchFinanceItemVO` 零改动),其变更由 #7536 交接。 - **「报名清单 / 财务 / 预支」三张表逐行的 `teamNo` 不在本批**,归 #7536;两批的字段名、类型与 null 语义完全一致。 - **`GET /v3/admin/group-batch/{productBatchId}/staff` 仍返回裸数组、不包顶层信封**(包信封属结构变更,另有工单)。 - **预检不扩到用车**:本批只是把「只检查住宿」这个事实透出成 `checkedResourceTypes`,检查行为一行未改。 - **小程序端(mp 域)全部 VO 不动**;**`/v3/internal/` 端点不动**。 - **零改动**:Controller 方法体、Entity、Flyway、错误码、hl-gateway 配置、其他微服务。 --- ## 八、测试环境已验证 **部署**:`hl-order-service-v3` @ `dev-v3` / `bb68be409`(PR #7583 合并提交),双实例滚动重启完成。 **全部实测经真实网关 `api.test.1814.love:9443` + Bearer 鉴权。** 本单**零新增 Controller、零网关路由改动**,网关无需重滚。 | 验证点 | 结果 | |---|---| | 八桶三端点一致 | 团期 `2096412454643802114`(`RESOURCE_PREPARING`)在 A1 分页 / A2 详情 / 看板三处均 `opsStage=FORMED`、`opsStageName=已成团` | | `FORMED` 复合桶 | `RESOURCE_PREPARING` 与 `MATERIAL_PREPARING` 两个团期同为 `FORMED`/已成团 | | 芯片负责人(已认领) | 六个芯片端点逐户全部 `claimerId=30001` / `claimerName=验收房务甲` / `claimerSource=BATCH` | | 芯片负责人(未认领) | 三个字段全为 `null` | | 报账人 | 置 `PRIMARY` 前名册读到 `NONE`/非报账人,置后读到 `PRIMARY`/主报账人 | | 候选 `reporterRank` | 未被本团期选中的 5 人全部 `null`(**不是 `NONE`**);已选未任报账人的返 `NONE`/非报账人 | | 审批统一列表 vs 退单列表 | 同一审批单 `orderNo`/`customerName`/`departDate` **三项逐字相同**(2 条正向 + 1 条降级态同为 `null`) | | DISBAND 行 | 一页 38 行(34 DISBAND / 4 WITHDRAW),**34 行三字段全 `null`、零漏行、零报错** | | 预检 | 4 个团期均返 `checkedResourceTypes=["HOTEL"]`,`missing[]` 每项带 `customerName` 与 `reasonName` | | 后缀四处同值 | 芯片 55 户 `statusText==statusName`;A1 17 行 `productBatchStatusLabel==productBatchStatusName`;看板与审批同理 | | 联系人四处同值 | 芯片 / 合同 / 行程下钻实测逐户 `customerName==contactName` | | 团期 ID 三处同值 | 名额调整 / 芯片详情 / 合同面板 `groupBatchId==batchId`,**均为字符串形态** | | 金额 | 详情两对逐字同值;A1 全 28 行 `unpaidAmount == max(0, 应收−已收)` **零例外** | | `teamNo` 正负 | 同一团期一户 `26-9069`、三户 `null`,芯片与合同两端点一致;`orderNo` 同一对象里照常非空 | | staff 候选未建团 | `groupBatchId=null`,HTTP 200 + `code=200`(**不返 589500**) | | **零破坏(真·双部署对拍)** | 同一台 TEST 上先后部署 `93bd823b4`(合入前)与 `bb68be409`(合入后),逐端点抓取响应并摊平成规范化字段路径做集合对拍:**20 个端点删除字段路径 = 0**,新增 +90;另用同 commit 重跑一趟作对照组,键集漂移 = 0。A3 `GET .../{groupBatchId}/orders` **新增 0 / 删除 0 / 值变 0** | **本地全量**: - 第一遍(排除 `*IT` / `*IntegrationTest` / `*MysqlTest` / `*MysqlMigrationTest`):`Tests 9800 / Failures 0 / Errors 52 / Skipped 8` - 第二遍(只跑上面排除的类,每类独立 JVM):`Tests 308 / Failures 0 / Errors 9 / Skipped 41` - 两遍 61 个 Errors **全部既存或环境所致**(`SignVoucherServiceTest` 48 例是 `dev-v3` 上既存的测试间污染;其余 13 例全是 `Could not find a valid Docker environment`)。与 `dev-v3` 基线逐类一致,**本单净增 249 例、零新增失败**。 - ArchUnit 全绿:`HouseModuleBoundaryArchTest` 4/0(本单新引入中立包依赖的专项)、`RedLineArchTest` 12/0、`MapperBoundaryArchTest` 26/0、`LayerEnforcementTest` 5/0。 --- ## 十、相关文档 - Issue:https://git.1814.love:8443/wx/HL/issues/7535 - PR:https://git.1814.love:8443/wx/HL/pulls/7583 - API-SPEC §17.1 / §17.2 字段清单已同步:`docs/order-v3/api/API-SPEC.html` - 团期接口文档「已落地清单」已登记:`docs/group/团期模块接口文档-v2.0.html` §0A.10 - 八桶唯一真源:`hl-order-service-v3/src/main/java/com/hulalv/order/groupbatch/helper/GroupBatchStageBuckets.java` - 团号生成器:`hl-order-service-v3/src/main/java/com/hulalv/order/core/service/GroupCodeService.java` --- ## 关联 / 联系人 - 后端:jw - 前端(hl-ui 管理后台):mmg - 需求定案:wx(2026-09-11:「原型上有的必须有;可以多给;不能加离谱的字段」「后缀统一为 Name」「opsStage 以后端八桶为准,原型侧改」) - ⚠️ **待 mmg 确认**:原型侧的 8 个 stage 取值与后端八桶的重合度是 wx 口述、撰写工单时未核对原型文件。本批以后端八桶为准,原型侧需按上表 8 个取值调整。