From 637fad2394f3c6289212e0a0ed2ee1870a61d799 Mon Sep 17 00:00:00 2001 From: jw Date: Sat, 12 Sep 2026 15:12:28 +0800 Subject: [PATCH] =?UTF-8?q?docs(order-v3):=20=E5=9B=A2=E6=9C=9F=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E8=BF=94=E5=9B=9E=E5=80=BC=E6=95=B4=E6=94=B9=C2=B7?= =?UTF-8?q?=E9=9D=9E=E7=A0=B4=E5=9D=8F=E6=89=B9=20changelog=EF=BC=88#7535?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 19 个团期读端点纯加响应字段,47 个新 key,既有 key 的名称/类型/取值/null 语义 一行未动,前端零改动即可继续运行。零 DDL、零错误码、零网关改动。 前端要点:opsStage 直接读不要自己折桶;联系人改读 customerName; 审批中心统一列表三个新字段只在 WITHDRAW 行有值;teamNo 逐户各不相同且未付订金为 null; staff 候选的 groupBatchId 在团期未创建时为 null,不得当必有主键用。 四组旧字段(Text/Label 后缀、contactName、batchId、total*)已标 @Deprecated, 本批不删、值不变,删除时间另行通知。 --- ...Ÿ接口返回值整改非破坏批-修改接口-管理后台.md | 1982 +++++++++++++++++ 1 file changed, 1982 insertions(+) create mode 100644 changelogs-v2/2026-09/12_7535_团期接口返回值整改非破坏批-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/12_7535_团期接口返回值整改非破坏批-修改接口-管理后台.md b/changelogs-v2/2026-09/12_7535_团期接口返回值整改非破坏批-修改接口-管理后台.md new file mode 100644 index 00000000..8fd20bb1 --- /dev/null +++ b/changelogs-v2/2026-09/12_7535_团期接口返回值整改非破坏批-修改接口-管理后台.md @@ -0,0 +1,1982 @@ +--- +schema: "hl-changelog/v2" +ticket: "7535" +title: "团期接口返回值整改·非破坏批(opsStage / 负责人 / 报账人 / 审批行补全 / 枚举中文配对 / 命名统一)" +consumer: "admin" +author: "jw(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "not_required" +frontend_status: "pending" +frontend_owner: "mmg" +frontend_ref: "" +target_release: "" +verified_at: "2026-09-12" +status_note: "19 个团期读端点纯加响应字段,既有 key 的名称/类型/取值/null 语义一行未动,前端零改动即可继续运行。零 DDL、零错误码、零网关改动。四组旧字段已标 @Deprecated 但本批不删,请新代码逐步切到新名。" +updated_at: "2026-09-12" +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**) | +| **零破坏** | 逐端点 live key 集与基线 VO 字段集对照,**12 个 VO 全部零 key 删除**;A3 子订单项 **29 → 29 零变化** | + +**本地全量**: +- 第一遍(排除 `*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 个取值调整。