From 7b9b60b9acc0b5a5cd9ebe95d8cb1a4f50612d47 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Sun, 6 Sep 2026 16:11:30 +0800 Subject: [PATCH] =?UTF-8?q?changelog(7149):=20=E6=8C=89=20CHANGELOG=5FTEMP?= =?UTF-8?q?LATE=20=E9=87=8D=E5=86=99=E2=80=94=E2=80=94=E4=B8=89=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E8=87=AA=E5=90=AB=E5=85=A5=E5=8F=82/=E5=87=BA?= =?UTF-8?q?=E5=8F=82/=E7=A4=BA=E4=BE=8B/=E9=94=99=E8=AF=AF/=E8=BE=B9?= =?UTF-8?q?=E7=95=8C=EF=BC=8C=E8=A1=A5=E5=AF=B9=E6=AF=94=E4=B8=8E=E5=BD=B1?= =?UTF-8?q?=E5=93=8D=E8=AF=84=E4=BC=B0=EF=BC=88=E9=97=A8=E7=A6=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01XYL5S9SsBtkg7aGyFAbrrQ --- ...€求-物资准备起冻结-团单房型间数必填-修改接口-管理后台.md | 391 ++++++++++++------ 1 file changed, 264 insertions(+), 127 deletions(-) diff --git a/changelogs-v2/2026-09/06_7149_团期子订单支付后即可提房车需求-物资准备起冻结-团单房型间数必填-修改接口-管理后台.md b/changelogs-v2/2026-09/06_7149_团期子订单支付后即可提房车需求-物资准备起冻结-团单房型间数必填-修改接口-管理后台.md index 7b80e305..7863e019 100644 --- a/changelogs-v2/2026-09/06_7149_团期子订单支付后即可提房车需求-物资准备起冻结-团单房型间数必填-修改接口-管理后台.md +++ b/changelogs-v2/2026-09/06_7149_团期子订单支付后即可提房车需求-物资准备起冻结-团单房型间数必填-修改接口-管理后台.md @@ -12,270 +12,407 @@ frontend_owner: "mmg" frontend_ref: "" target_release: "" verified_at: "2026-09-06" -status_note: "后端已合 dev-v3 并部署测试服、网关实测通过;前端需改团期子订单提需求弹窗:房型大类必填+两个新错误码展示+招募中即显示提需求入口。" +status_note: "后端已合 dev-v3 并部署测试服、网关实测通过;前端需改团期子订单提需求弹窗:房型大类必填、两个新错误码直接展示 message、招募中即显示提需求入口、物料准备中入口置灰。" updated_at: "2026-09-06" base: "dev-v3" --- -# 团期模块:子订单支付后即可提房车、物资准备起冻结、房型间数必填 +# 团期模块:子订单支付后即可提房车需求、物资准备起冻结、团单房型与间数必填 > **服务**: `hl-order-service-v3` > **Issue**: #7149 -> **PR**: #7177 +> **PR**: #7177(squash 合入 dev-v3 `a52365278`) > **日期**: 2026-09-06 -> **影响范围**: 团期子订单房型与用车需求提交/修改、团期冻结逻辑、业务允许入口 +> **影响范围**: 管理后台团期子订单详情「住宿安排 / 用车安排」提需求弹窗与「订单调整」提交 ## ⚠️ 关键变化 -团期子订单定制师的需求操作权限、冻结时机与校验规则同时调整: +三条行为改造,请求 / 响应结构一律不变: -1. **支付后即可提需求**:团期子订单(`product_batch_id` 非空)在定制师端已支付(`orderStatus=CUSTOMIZING`)后,团期处于招募中(`RECRUITING`)或资源准备中(`RESOURCE_PREPARING`)即可提交/修改房车需求,无需等待团期成团。前端需在招募中即显示提需求入口。 +1. **支付后即可提需求**:团期子订单(详情里 `groupOrder=true` / `productBatchId` 非空)在客户已支付(`orderStatus=CUSTOMIZING`)后即可提 / 改用房、用车需求,团期处于「招募中 `RECRUITING`」或「资源准备中 `RESOURCE_PREPARING`」都放行,不再等团期成团。改前团期非 `RESOURCE_PREPARING` 一律 589501「团期状态不允许当前操作」。 +2. **物资准备起冻结**:团期进入「物料准备中 `MATERIAL_PREPARING`」及之后(`PENDING_DEPARTURE / TRAVELLING / REVIEWING / SETTLED`)提 / 改需求被拒,新错误码 **589536「团期已进入物资准备,需求已冻结,请联系团期管理员」**(文案里的「物资准备」就是状态芯片的「物料准备中」,同义)。唯一例外:该户该资源最新一版需求被团期管理员打回(`REJECTED_TO_CONSULTANT`)时可以重提一次,重提后再改仍 589536。团期 `CANCELLED` / 查不到团期仍 589501。 +3. **团单房型大类与房间数必填**:团期子订单每个非自订晚(`customerSelfBooked` 非 `true`)的每段,按段首候选 `candidates[0].rooms[]` 逐行要求 `roomCategory` 非空且 `roomCount ≥ 1`(旧结构无 `rooms[]` 时按段级 `roomCategory` + `roomCount` 判),缺失返回新错误码 **582099「团期订单第{N}晚第{M}段需填写房型大类与房间数」**(M 从 1 起),拒绝时零副作用。核心订单不受影响,房型仍选填。**团单不再接受「加晚次空白占位」段**——每晚要么填齐房型行,要么标 `customerSelfBooked=true`。 -2. **物资准备起冻结**:团期进入物料准备中(`MATERIAL_PREPARING`)及之后后,定制师不得再提交或修改房车需求。新增错误码 **589536**;唯一例外:最新版需求被打回可重提一次。前端需在物料准备中后置灰入口或弹提示。 - -3. **房型与间数必填**:团期子订单的每个非自订晚的每段,房型大类(`roomCategory`)与房间数(`roomCount`)必填且≥1。新增错误码 **582099**;核心订单无此约束。前端需改房型大类为必填。 +前端要做:① 团期子订单提需求弹窗把「房型大类」改必填并提示;② 589536 / 582099 直接展示后端 `message`;③ 团期子订单在招募中即显示「提交房型需求 / 用车需求」入口;进入物料准备中后入口置灰或提示已冻结(被打回的户除外);④ 团单去掉「先提交空白占位」交互。 ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|---|---|---|---|---| -| 1 | 提交/修改房型需求 | PUT | `/v3/admin/order/{id}/hotel-requirement` | 行为与守卫修改 | 团期子订单新增权限闸门+房型间数必填校验 | -| 2 | 调整订单统一提交 | POST | `/v3/admin/order/{id}/adjustment/submit` | 行为与守卫修改 | hotelRequirement与vehicleRequirement走同一闸门 | -| 3 | 提交/修改用车需求 | PUT | `/v3/admin/order/{id}/vehicle-requirement` | 行为与守卫修改 | 团期子订单新增权限闸门 | -| 4 | 团期汇总查询 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement-summary` | 复用不改 | 响应roomCategory不再出现未知 | - -## 变更接口 - -(同上) +| 1 | 提交 / 修改用房需求(兼容期 @Deprecated) | PUT | `/v3/admin/order/{id}/hotel-requirement` | 行为修改 | 团期闸门放宽 + 冻结 + 团单房型间数必填 | +| 2 | 订单调整统一提交 | POST | `/v3/admin/order/{id}/adjustment/submit` | 行为修改 | `updates.hotelRequirement` / `updates.vehicleRequirement` 走同一闸门与必填校验 | +| 3 | 提交 / 修改用车需求 | PUT | `/v3/admin/order/{id}/vehicle-requirement` | 行为修改 | 团期闸门放宽 + 冻结,不加必填 | ## 三、接口详情 -### 1. 提交/修改房型需求 `PUT /v3/admin/order/{id}/hotel-requirement` +### 1. 提交 / 修改用房需求 `PUT /v3/admin/order/{id}/hotel-requirement` -**VO**: HotelRequirementReqVO转HotelRequirementRespVO +**VO**: `HotelRequirementReqVO → HotelRequirementRespVO`(`hl-order-service-v3/src/main/java/com/hulalv/order/requirement/controller/admin/vo/`) #### 使用场景 -定制师提交/修改/调整房型需求。后端按是否存在active行与当前状态自动三分支:无active→INIT_SUBMIT、PENDING→PENDING_EDIT、DONE→DONE_ADJUST。本端点为@Deprecated兼容期。 +定制师在子订单详情「住宿安排」提交或修改用房需求。后端按当前生效版本自动分支:无生效版本 → `INIT_SUBMIT`(新版本);生效版本为 `PENDING / PENDING_REVIEW` → `PENDING_EDIT`(同版本覆盖);`DONE` 等 → `DONE_ADJUST`(版本 +1)。团期子订单新版本状态恒为 `PENDING_REVIEW`(等团期管理员确认),不进房务抢单池。本端点为兼容期入口(#4515 标 @Deprecated),新客户端走接口 2。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| -| id | Path | Long | 是 | 正整数 | 订单ID | -| days | Body | Array | 是 | 长度等于tripNights | 每晚用房安排 | -| days[].dayNumber | Body | Integer | 是 | 1到tripNights | 第几晚 | -| days[].customerSelfBooked | Body | Boolean | 否 | true/false/null | 客户自订标记 | -| days[].segments | Body | Array | 条件必填 | 非自订晚≥1段 | 分住段列表 | -| days[].segments[].roomCategory | Body | String | 条件必填 | 字典room_category;团期必填 | 房型大类 | -| days[].segments[].roomCount | Body | Integer | 条件必填 | 大于0;团期必填 | 房间数 | -| days[].segments[].candidates | Body | Array | 是 | ≥1家 | 候选酒店列表 | -| specialTags | Body | Array | 否 | 字典值 | 特殊诉求标签 | -| remark | Body | String | 否 | 最长500字 | 备注 | +| `id` | Path | String(Long) | 是 | 订单 ID | 团期子订单 `productBatchId` 非空 | +| `days` | Body | Array | 是 | 长度 = `tripNights`,`dayNumber` 1..tripNights 不重复 | 逐晚安排 | +| `days[].dayNumber` | Body | Integer | 是 | 1..tripNights | 第几晚 | +| `days[].customerSelfBooked` | Body | Boolean | 否 | `true` = 客人自订 | 自订晚不校验房型,`segments` 可空 | +| `days[].segments` | Body | Array | 非自订晚 ≥1 | 缺段 582098 | 当晚分住段 | +| `days[].segments[].candidates` | Body | Array | 是(≥1) | 候选酒店,房控择一 | `hotelId / hotelName` 可空(无酒店候选) | +| `days[].segments[].candidates[].rooms` | Body | Array | 新结构 | 房型行 | 团单按 `candidates[0].rooms` 逐行校验 | +| `…rooms[].roomCategory` | Body | String | **团单是** | 字典 `room_category`(STANDARD/SINGLE/TWIN/QUEEN/KING/SUITE/FAMILY/YURT/SPECIAL) | 核心订单选填不变 | +| `…rooms[].roomCount` | Body | Integer | **团单是,≥1** | 有行即 >0(582016) | 房数 | +| `…rooms[].roomTypeId / roomTypeName / protocolPrice / remark` | Body | Long / String / Decimal / String | 否 | — | 真实房型与快照,可空 | +| `days[].segments[].roomCategory / roomCount` | Body | String / Integer | 兼容 | 旧结构无 `rooms[]` 时团单必填 | 段级兼容字段 | +| `days[].segments[].budget / remark` | Body | Decimal / String(≤200) | 否 | 预算由后端按协议价覆盖 | — | +| `specialTags` | Body | String[] | 否 | 字典 `house_special_demand` | 特殊诉求 | +| `remark` | Body | String(≤500) | 否 | — | 备注 | -#### 出参 +#### 出参 `Result` | 字段 | 类型 | 说明 | |---|---|---| -| data.requirementId | String | 需求行ID | -| data.version | Integer | 版本号 | -| data.status | String | 需求状态 | -| data.submittedAt | String | 提交时间 | +| `data.requirementId` | String | 需求行 ID | +| `data.version` | Integer | 版本号(INSERT-only 单调递增) | +| `data.isActive` | Boolean | 是否当前生效版本 | +| `data.status` | String | 团单恒 `PENDING_REVIEW`;核心为 `PENDING` | +| `data.submittedAt` | String | 首提时间 | +| `data.claimerId / claimerName / claimedAt` | String / String / String | 团单恒 null(不进抢单池) | +| `data.branchTaken` | String | `INIT_SUBMIT / PENDING_EDIT / DONE_ADJUST` | +| `data.previousVersion` | Integer | `DONE_ADJUST` 时上一版本号,否则 null | +| `data.assignmentDeletedCount` | Integer | `DONE_ADJUST` 时软删配房行数,否则 null | #### 请求示例 -提交房型需求,第1晚TWIN房2间,第2晚自订。应答会返回requirementId和version。 +```json +{ + "days": [ + { "dayNumber": 1, "segments": [ { "remark": "第1晚", "candidates": [ { "hotelName": "网关实测酒店", "rooms": [ { "roomTypeName": "测试房型", "roomCategory": "KING", "roomCount": 1 } ] } ] } ] }, + { "dayNumber": 2, "segments": [ { "remark": "第2晚", "candidates": [ { "hotelName": "网关实测酒店", "rooms": [ { "roomTypeName": "测试房型", "roomCategory": "TWIN", "roomCount": 2 } ] } ] } ] } + ], + "remark": "#7149 网关实测" +} +``` #### 响应示例 -成功返回code=200,data包含requirementId、version、status=PENDING_REVIEW。 +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "requirementId": "2096504560393551873", + "version": 1, + "isActive": true, + "status": "PENDING_REVIEW", + "submittedAt": "2026-09-06 15:44:21", + "claimerId": null, + "claimerName": null, + "claimedAt": null, + "branchTaken": "INIT_SUBMIT", + "previousVersion": null, + "assignmentDeletedCount": null + } +} +``` + +#### 空数据 / 降级响应 + +本接口无空列表语义;`days` 为空数组或长度与 `tripNights` 不符返回 582011「天数长度与订单住宿晚数不一致」。第 2 晚 `customerSelfBooked=true` 且无 `segments` 时正常返回 200(自订晚不进房务分母,团单房型校验跳过)。 #### 错误响应 -冻结场景返回code=589536,消息「团期已进入物资准备,需求已冻结,请联系团期管理员」。房型缺失返回code=582099,消息「团期订单第X晚第Y段需填写房型大类与房间数」。 +```json +{ "code": 582099, "message": "团期订单第2晚第1段需填写房型大类与房间数", "success": false, "data": null } +``` + +```json +{ "code": 589536, "message": "团期已进入物资准备,需求已冻结,请联系团期管理员", "success": false, "data": null } +``` + +其它沿用:589501「团期状态不允许当前操作」(团期 `CANCELLED` / 查不到)、582098「第{N}晚缺少用房需求,请填写房间需求或标记为客户自订」、582016「房间数必须大于 0」、582019「候选方案必须至少含 1 个房型行」、582017「订单状态不允许提交需求」(未支付)。HTTP 始终 200,错误在 `code` / `message`。 #### 业务边界 -- 权限守卫:允许RECRUITING/RESOURCE_PREPARING,拒绝MATERIAL_PREPARING及之后(589536)、CANCELLED(589501)。 -- 房型间数必填:仅团期子订单非自订晚,缺失返582099,DB零副作用。 -- 自订晚customerSelfBooked=true允许segments为空,不受约束。 -- 打回例外:被打回后可重提一次,重提后再改仍返589536。 +- 团期闸门(房车共用):`RECRUITING / RESOURCE_PREPARING` 放行;`MATERIAL_PREPARING / PENDING_DEPARTURE / TRAVELLING / REVIEWING / SETTLED` 及未知状态 589536;`CANCELLED` / 查不到团期 589501;核心订单不查团期。 +- 冻结期例外:该户用房需求最新一版为 `REJECTED_TO_CONSULTANT`(团期管理员打回)时放行重提,新版本回到 `PENDING_REVIEW`;之后再改仍 589536,要再改只能再次被打回。 +- 团单房型间数必填按「段首候选 rooms 行」判,与全团需求汇总 `requirement-summary` 同口径;旧结构(无 `rooms[]`)按段级 `roomCategory + roomCount` 合成一行判。 +- 校验顺序:结构校验(582098 / 582016 / 582019)→ 订单状态(582017)→ 团期闸门(589536 / 589501)→ 团单房型间数(582099);任一拒绝均在写库之前,无新版本、`room_control_status` 不变。 +- 成功后 `order_main.room_control_status=PENDING_REVIEW`(既有行为),团期成团时定制师不再产生「房型需求 · 待提交」待办。 -### 2. 调整订单统一提交 `POST /v3/admin/order/{id}/adjustment/submit` +### 2. 订单调整统一提交 `POST /v3/admin/order/{id}/adjustment/submit` -**VO**: AdjustmentSubmitReqVO转AdjustmentSubmitRespVO +**VO**: `AdjustmentSubmitReqVO → AdjustmentSubmitRespVO`(`hl-order-service-v3/src/main/java/com/hulalv/order/adjustment/controller/admin/vo/`) #### 使用场景 -前端内存收集改动后一次性提交,后端单事务原子应用。房、车需求通过updates.hotelRequirement和updates.vehicleRequirement传入,走同一权限守卫。 +管理端子订单详情「订单调整」一次性提交各子域改动;本次只涉及 `updates.hotelRequirement`(用房需求完整新版本)与 `updates.vehicleRequirement`(用车需求完整新版本),两者与接口 1 / 接口 3 走同一团期闸门与团单房型间数校验。其它子域(人数 / 日期 / 出行人等)本次不变、省略。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| -| id | Path | Long | 是 | 正整数 | 订单ID | -| updates.hotelRequirement | Body | Object | 否 | 完整新版本 | 房需求(days/specialTags/remark) | -| updates.vehicleRequirement | Body | Object | 否 | 完整新版本 | 车需求(fleet/specialTags/remark) | +| `id` | Path | String(Long) | 是 | 订单 ID | — | +| `updates` | Body | Object | 是 | 至少一个子域非空 | 各子域修改内容 | +| `updates.hotelRequirement` | Body | `HotelRequirementBodyVO` | 否 | 结构同接口 1 的 `days / specialTags / remark` | 用房需求完整新版本,团单房型间数必填规则同接口 1 | +| `updates.vehicleRequirement` | Body | `VehicleRequirementBodyVO` | 否 | 结构同接口 3 的 `fleet / specialTags / pickupRequired / dropoffRequired / remark` | 用车需求完整新版本 | -#### 出参 +#### 出参 `Result` | 字段 | 类型 | 说明 | |---|---|---| -| data.success | Boolean | true表示提交成功 | +| `data.success` | Boolean | 提交成功恒 `true` | #### 请求示例 -包含updates.hotelRequirement和updates.vehicleRequirement两个可选字段,其结构同对应的两个直接提交接口。 +```json +{ + "updates": { + "hotelRequirement": { + "days": [ + { "dayNumber": 1, "segments": [ { "candidates": [ { "hotelName": "网关实测酒店", "rooms": [ { "roomCategory": "KING", "roomCount": 1 } ] } ] } ] }, + { "dayNumber": 2, "segments": [ { "candidates": [ { "hotelName": "网关实测酒店", "rooms": [ { "roomCategory": "TWIN", "roomCount": 1 } ] } ] } ] } + ], + "remark": "#7149 网关实测 调整入口" + } + } +} +``` #### 响应示例 -成功返回code=200,data.success=true。 +```json +{ "code": 200, "message": "成功", "success": true, "data": { "success": true } } +``` + +#### 空数据 / 降级响应 + +`updates` 各子域全空时沿用既有「无有效变更」拒绝;`hotelRequirement` 提交成功但版本号不变(`PENDING_EDIT` 覆盖同版本)属正常,前端以订单详情重新拉取为准。 #### 错误响应 -冻结返回code=589536;房型缺失返回code=582099。 +```json +{ "code": 589536, "message": "团期已进入物资准备,需求已冻结,请联系团期管理员", "success": false, "data": null } +``` + +```json +{ "code": 582099, "message": "团期订单第2晚第1段需填写房型大类与房间数", "success": false, "data": null } +``` #### 业务边界 -- 房、车改动走同一闸门:RECRUITING/RESOURCE_PREPARING允许,MATERIAL_PREPARING及之后冻结。 -- 房型间数必填校验同PUT /hotel-requirement。 -- 单事务原子应用。 +- 调整入口绕过「订单须为定制中」的普通提交闸,但**不绕**团期闸门与团单房型间数校验,行为与接口 1 / 3 一致。 +- 团期冻结期内该入口同样 589536,被打回户例外同接口 1。 +- 拒绝发生在事务内任何写操作之前,其它子域改动一并回滚。 -### 3. 提交/修改用车需求 `PUT /v3/admin/order/{id}/vehicle-requirement` +### 3. 提交 / 修改用车需求 `PUT /v3/admin/order/{id}/vehicle-requirement` -**VO**: VehicleRequirementReqVO转VehicleRequirementRespVO +**VO**: `VehicleRequirementReqVO → VehicleRequirementRespVO`(`hl-order-service-v3/src/main/java/com/hulalv/order/requirement/controller/admin/vo/`) #### 使用场景 -定制师提交/修改/调整用车需求。后端按是否存在active行与当前状态自动三分支。 +定制师在子订单详情「用车安排」提交或修改用车需求。本次只改团期闸门(放宽 + 冻结 + 打回例外),**不加任何必填**;车型 / 座位联动校验、容量校验、派单展开等既有逻辑不变。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| -| id | Path | Long | 是 | 正整数 | 订单ID | -| fleet | Body | Array | 是 | 至少1项 | 车辆类型与需求行 | -| fleet[].vehicleType | Body | String | 是 | SUV/MPV/大巴/轿车 | 车型大类 | -| fleet[].seats | Body | Integer | 是 | 大于0 | 每辆座位数 | -| fleet[].count | Body | Integer | 是 | 大于0 | 该类车数量 | -| specialTags | Body | Array | 否 | 字典vehicle_special_demand | 特殊诉求标签 | -| remark | Body | String | 否 | 最长500字 | 备注 | +| `id` | Path | String(Long) | 是 | 订单 ID | — | +| `fleet` | Body | Array | 是(≥1) | 缺 582021 | 用车明细 | +| `fleet[].vehicleType` | Body | String | 是 | 车务车型大类 key(582022) | 车型大类 | +| `fleet[].seats` | Body | Integer | 是 | 该车型可选座位数(582024) | 座位数 | +| `fleet[].count` | Body | Integer | 是 | >0(582023) | 车辆数 | +| `specialTags` | Body | String[] | 否 | 字典 `vehicle_special_demand`(582025) | 特殊诉求 | +| `pickupRequired / dropoffRequired` | Body | Boolean | 否 | — | 接 / 送机 | +| `remark` | Body | String | 否 | — | 备注 | -#### 出参 +#### 出参 `Result` | 字段 | 类型 | 说明 | |---|---|---| -| data.requirementId | String | 需求ID | -| data.version | Integer | 版本号 | -| data.status | String | 需求状态 | -| data.submittedAt | String | 提交时间 | +| `data.requirementId` | String | 需求行 ID | +| `data.version` | Integer | 版本号 | +| `data.isActive` | Boolean | 是否当前生效版本 | +| `data.status` | String | 团单恒 `PENDING_REVIEW` | +| `data.submittedAt` | String | 首提时间 | +| `data.branchTaken` | String | `INIT_SUBMIT / PENDING_EDIT / DONE_ADJUST` | #### 请求示例 -提交MPV车2辆,座位数7,包含wifi特殊诉求。 +```json +{ "fleet": [ { "vehicleType": "SUV", "seats": 7, "count": 1 } ] } +``` #### 响应示例 -成功返回code=200,data包含requirementId、version、status=PENDING_REVIEW。 +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "requirementId": "2096504700000000001", "version": 1, "isActive": true, "status": "PENDING_REVIEW", "submittedAt": "2026-09-06 15:46:02", "branchTaken": "INIT_SUBMIT" } +} +``` + +#### 空数据 / 降级响应 + +`fleet` 为空返回 582021「用车需求数组不能为空」;车队车型库不可用返回 582091「车队车型库不可用,无法校验座位数选项」(既有降级,本次不变)。 #### 错误响应 -冻结返回code=589536;团期不存在返回code=589501。 +```json +{ "code": 589536, "message": "团期已进入物资准备,需求已冻结,请联系团期管理员", "success": false, "data": null } +``` + +其它沿用:589501(团期 `CANCELLED` / 查不到)、582022 / 582024 / 582023 / 582025(车型 / 座位 / 数量 / 诉求校验)。 #### 业务边界 -- 权限守卫同房需求:允许RECRUITING/RESOURCE_PREPARING,拒绝MATERIAL_PREPARING(589536)/CANCELLED(589501)。 -- 房、车需求用同一闸门判。 +- 团期闸门与接口 1 完全一致(同一方法),放行 / 冻结 / 打回例外按**用车需求**自身的最新一版判。 +- 车侧不加房型类必填;`fleet` 结构与既有校验不变。 +- 成功后 `order_main.vehicle_control_status=PENDING_REVIEW`(既有行为)。 -## 四、契约约束与正确调用方式 +## 四、契约约束与正确调用方式(接口类必写) -### 正确 payload 对照 +### ✅ 正确 / ❌ 错误 payload 对照 -团期子订单第1晚填房型(TWIN 2间),第2晚自订:应答返回200和PENDING_REVIEW状态。 +| 场景 | payload 要点 | 结果 | +|---|---|---| +| ✅ 团单每晚房型行齐全 | `candidates[0].rooms[]` 每行 `roomCategory` + `roomCount ≥ 1` | 200,`status=PENDING_REVIEW` | +| ✅ 团单某晚客人自订 | `{ "dayNumber": 2, "customerSelfBooked": true }`,无 `segments` | 200,该晚跳过校验 | +| ✅ 旧结构团单 | 无 `rooms[]`,段级 `roomCategory` + `roomCount` 齐全 | 200 | +| ❌ 团单房型行缺 `roomCategory` | `rooms: [ { "roomCount": 1 } ]` | 582099 | +| ❌ 团单旧结构缺段级 `roomCategory` | 段级只有 `roomCount`,候选带 `roomTypeId` | 582099 | +| ❌ 团单「加晚次空白占位」段 | 候选与段全空 | 582099(核心订单该形态仍放行) | +| ❌ 团期物料准备中提交 / 修改 | 任何合法 payload | 589536(该户最新需求被打回除外) | +| ❌ 子订单未支付 | 任何 payload | 582017「订单状态不允许提交需求」 | -### 错误 payload 对照 +### 切换状态时的必要动作 -第2晚缺房型大类:返回582099;物料准备中提交:返回589536;空白占位段(仅团单):返回582099。 +- 团期从招募中推进到物料准备中后,前端应把提需求入口置灰并以 589536 文案提示;管理员打回某户后该户入口恢复一次。 +- 团单弹窗保存前在前端做房型大类必填校验,减少 582099 往返;后端仍以 582099 兜底。 -## 五、数据库行为 +## 五、数据库行为(涉及写操作时必写) -- 无表结构变更。 -- 提交成功时order_hotel_requirement新版本status=PENDING_REVIEW、version++。 -- 冻结拒绝时零副作用。 +- 无表结构变更、无 Flyway。 +- 提交成功:`order_hotel_requirement` / `order_vehicle_requirement` 写入新版本(`PENDING_EDIT` 为同版本换行,其它版本 +1),`status=PENDING_REVIEW`;`order_main.room_control_status` / `vehicle_control_status` 回写 `PENDING_REVIEW`(既有行为)。 +- 被拒(589536 / 582099 / 589501 / 结构校验):无任何写入。 ## 六、边界行为 -- **自订晚处理**:customerSelfBooked=true允许segments为空,不受房型必填约束;不计入配房分母。 -- **打回例外与重提限制**:被打回后可重提一次,重提后再改仍返589536;被打回配房行释放库存。 -- **旧客户端兼容**:rooms数组为空时回退段级roomCategory+roomCount;房型必填校验对新旧结构同样适用。 -- **团期状态枚举**:RECRUITING/RESOURCE_PREPARING允许,MATERIAL_PREPARING及之后冻结,CANCELLED拒绝。 +- **团单不再接受「加晚次空白占位」段**:核心订单允许先提交全空的候选 / 段占位,团期子订单的非自订晚一律按缺房型拒绝(582099「第 N 晚第 M 段」);团单每晚要么填齐 `rooms[]`,要么标 `customerSelfBooked=true`。 +- **文案「物资准备」= 团期状态芯片「物料准备中」(`MATERIAL_PREPARING`)**:589536 文案沿用代码里的「物资准备」叫法,与状态枚举文案同义,前端直接展示 `message` 即可。 +- **冻结期打回只给一次重提机会**:重提后最新版变 `PENDING_REVIEW`,再改回到 589536;这是有意设计(改需求须经管理员再次打回)。 +- **自订晚**:`customerSelfBooked=true` 的晚不做房型校验;前端标记自订后应清空该晚 `segments`,避免残留段进入全团汇总。 +- **未知团期状态**按冻结处理(fail closed),不会误放行。 -## 六.5、枚举 / 数据字典 +## 六.5、枚举 / 数据字典(接口出现枚举时必写) -| 字段 | 值 | 中文 | +### roomCategory(字典 `room_category`) + +| 值 | 含义 | +|---|---| +| STANDARD | 标准间 | +| SINGLE | 单人间 | +| TWIN | 双床房 | +| QUEEN | 大床房 | +| KING | 特大床房 | +| SUITE | 套房 | +| FAMILY | 家庭房 | +| YURT | 帐篷 / 毡房 | +| SPECIAL | 特色房 | + +### 团期状态(`com.hulalv.order.groupbatch.enums.GroupBatchStatus`) + +| 值 | 芯片文案 | 提 / 改需求 | |---|---|---| -| roomCategory | STANDARD/SINGLE/TWIN/QUEEN/KING/SUITE/FAMILY/YURT/SPECIAL | 房型大类 | -| GroupBatchStatus | RECRUITING | 招募中 | -| GroupBatchStatus | RESOURCE_PREPARING | 资源准备中 | -| GroupBatchStatus | MATERIAL_PREPARING | 物料准备中 | -| GroupBatchStatus | PENDING_DEPARTURE等 | 已出发或更后 | -| GroupBatchStatus | CANCELLED | 已取消 | +| RECRUITING | 招募中 | 放行 | +| RESOURCE_PREPARING | 资源准备中 | 放行 | +| MATERIAL_PREPARING | 物料准备中 | 589536(打回户例外) | +| PENDING_DEPARTURE | 待出发 | 589536(打回户例外) | +| TRAVELLING | 出行中 | 589536(打回户例外) | +| REVIEWING | 核单中 | 589536(打回户例外) | +| SETTLED | 已结算 | 589536(打回户例外) | +| CANCELLED | 已取消 | 589501 | -## 六.6、修改前后对比 +### 需求状态(`com.hulalv.order.requirement.enums.RequirementStatus`,`data.status` / `room_control_status`) + +| 值 | 含义 | +|---|---| +| PENDING_REVIEW | 待团期管理员审核(团单提交后) | +| REJECTED_TO_CONSULTANT | 已打回定制师(冻结期可重提一次) | +| PENDING / PROCESSING / DONE | 核心订单 / 管理员提交房务后的房务侧状态,本次不变 | + +## 六.6、修改前后对比(修改/删除类接口必写,新增跳过) ### 字段级对比 -无字段增删;roomCategory和roomCount既有字段,仅约束条件变。 +无字段增删;`rooms[].roomCategory`、`rooms[].roomCount`(及旧结构段级同名字段)由「选填」改为「团期子订单必填」,核心订单不变。 ### 行为级对比 | 行为 | 修改前 | 修改后 | |---|---|---| -| 团期子订单提需求权限 | 成团后 | 招募中或资源准备中 | -| 物料准备中操作 | 允许 | 冻结(589536) | -| 房型与间数 | 选填 | 必填(582099) | +| 团期子订单提 / 改需求时机 | 团期须为 `RESOURCE_PREPARING`(成团后),否则 589501 | 支付后即可:`RECRUITING / RESOURCE_PREPARING` 放行 | +| 团期物料准备及之后 | 589501 | 589536(新码,文案明确「已冻结」),最新需求被打回的户可重提一次 | +| 团单缺房型大类 / 房数 | 放行,全团汇总出现「未知」房型 | 582099 拒绝,零副作用 | +| 团单空白占位段 | 放行 | 582099 拒绝 | +| 用车需求 | 同 589501 闸 | 同新闸门,不加必填 | -## 六.7、影响评估 +## 六.7、影响评估(修改/删除类必写) -- **破坏向后兼容**:是;权限、冻结时机、校验规则均变。 -- **前端必须同步上线**:是。 -- **前端workaround清理点**:删除「等待成团」逻辑;删除「物料准备中允许」分支;房型改必填;直接展示589536和582099。 +- 破坏向后兼容:**部分**——请求 / 响应结构不变,但团单缺房型或空白占位段的旧调用会从 200 变 582099,招募中的调用从 589501 变 200,物料准备中从 589501 变 589536。 +- 前端是否必须同步上线:**建议同步**——不同步时功能可用但用户会收到 582099 / 589536 提示且入口显示时机不准。 +- 前端需清理的分支:「未成团不显示提需求入口」「物料准备中仍允许提交」「团单空白占位先提交」。 -## 七、不影响范围 +## 七、不影响范围(显式声明, 帮前端/QA 缩小排查面) -- 核心订单房型仍选填。 -- 其他接口逻辑不变。 -- DB表结构无变更。 +- 核心订单(`productBatchId` 为空)的提需求、房型选填、抢单池、房务配房全部不变。 +- 团期管理员确认 / 打回 / 提交房务(`…/hotel-requirement/reject`、`…/dispatch`、`group-batch/{groupBatchId}/requirement/*`)本次不改;打回后重提的版本号规则不变。 +- `GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary` 结构不变,只是按本次提交的团单不再出现 `roomCategory="未知"` 项(历史数据仍可能出现)。 +- 网关路由、权限码、表结构均无改动。 ## 八、测试环境已验证 -**环境**:TEST,api.test.1814.love:9443,2026-09-06 15:44,admin/1001 +**环境**:TEST 网关 `https://api.test.1814.love:9443`,order-v3 dev-v3 `a52365278` 两实例 2026-09-06 15:43 滚动部署,登录 `admin`(adminId 1001,SUPER_ADMIN,且为两张测试单的定制师),实测 15:44–15:47。 -**网关实测**:招募中提房型→200,PENDING_REVIEW;缺房型大类→582099;资源准备中可提→200;物料准备中冻结→589536;被打回户可重提→200,version=2;重提后再改→589536;用车同样冻结→589536;自订晚不受约束→200。 +| 场景 | 请求 | 结果 | +|---|---|---| +| 招募中(`RECRUITING`)子订单 2096412454488612866 提完整房型 | `PUT …/hotel-requirement` | 200,`status=PENDING_REVIEW` | +| 招募中,第 2 晚房型行缺 `roomCategory` | `PUT …/hotel-requirement` | 582099「团期订单第2晚第1段需填写房型大类与房间数」,DB 无新版本 | +| 招募中,旧结构段级缺 `roomCategory`(候选带 `roomTypeId`) | `PUT …/hotel-requirement` | 582099,同上 | +| 资源准备中完整提交 | `PUT …/hotel-requirement` | 200,`version=1 INIT_SUBMIT PENDING_REVIEW`;`order_main.room_control_status=PENDING_REVIEW` | +| 资源准备中经调整入口改需求 | `POST …/adjustment/submit` | 200,`data.success=true`(同版本 `PENDING_EDIT` 换行) | +| 第 2 晚 `customerSelfBooked=true` 无段 | `PUT …/hotel-requirement` | 200 | +| 团期 2089713777065832450 子订单 2096029450184347649 资源准备中提交 | `PUT …/hotel-requirement` | 200 | +| 团期管理员打回该户 | `POST …/hotel-requirement/reject` | 200,需求行 `REJECTED_TO_CONSULTANT`、`is_active=0` | +| 团期改为 `MATERIAL_PREPARING` 后被打回户重提 | `PUT …/hotel-requirement` | 200,`version=2 PENDING_REVIEW`(冻结期例外) | +| 重提后再改 | `PUT …/hotel-requirement` | 589536「团期已进入物资准备,需求已冻结,请联系团期管理员」 | +| 物料准备中经调整入口改需求 | `POST …/adjustment/submit` | 589536 | +| 物料准备中提用车需求 | `PUT …/vehicle-requirement` | 589536 | -**单测**:BUILD SUCCESS 396全绿。 - -**部署**:a52365278已合dev-v3并部署。 - -## 验证证据 - -上述测试环境已验证的8个场景与单测全绿。 +**单测**:`RequirementServiceTest` 244 / `OrderTodoServiceTest` 15 / `RequirementGroupBatchErrorCodeRangeTest` 3 + 5 个 ArchTest(RedLine / MapperBoundary / HouseModuleBoundary / DashboardLayer / LocalCacheVetting)全绿,`BUILD SUCCESS` 396 用例。 ## 十、相关文档 -- [Issue #7149](https://git.1814.love:8443/wx/HL/issues/7149) -- [PR #7177](https://git.1814.love:8443/wx/HL/pulls/7177) -- 团期房务方案:docs/group/团期房务实现方案-v1.0.html +- 团期模块接口文档 `docs/group/团期模块接口文档-v2.0.html` §0C.11.1(提需求时机)/ §0C.11.2(房型间数必填)/ GB-ADM-011: +- 团期房务实现方案 `docs/group/团期房务实现方案-v1.0.html` §3.12.1 / §3.12.2 +- 后续工单(本次不做):管理员确认 / 打回改造(M1 / M2)、房务团期看板与按日订房、定制师 ↔ 团期管理员站内会话 ## 关联 / 联系人 ### 链接 -- **Issue**: [#7149](https://git.1814.love:8443/wx/HL/issues/7149) -- **PR**: [#7177](https://git.1814.love:8443/wx/HL/pulls/7177) -- **Merge commit**: [a52365278](https://git.1814.love:8443/wx/HL/commit/a52365278) +- Issue: +- PR: +- 合并提交: `a52365278`(dev-v3) ### 联系人 -- **后端**: @wx -- **前端**: @mmg +- 后端:wx +- 前端(hl-ui):mmg