diff --git a/changelogs-v2/2026-09/01_6905_团期看板-4-接口对齐补全-GB-ADM-000-001-002-003-修改接口-管理后台.md b/changelogs-v2/2026-09/01_6905_团期看板-4-接口对齐补全-GB-ADM-000-001-002-003-修改接口-管理后台.md index 587fa281..101e5f75 100644 --- a/changelogs-v2/2026-09/01_6905_团期看板-4-接口对齐补全-GB-ADM-000-001-002-003-修改接口-管理后台.md +++ b/changelogs-v2/2026-09/01_6905_团期看板-4-接口对齐补全-GB-ADM-000-001-002-003-修改接口-管理后台.md @@ -10,7 +10,7 @@ gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "f6b849eb" -target_release: "" +target_release: "hl-ui@f6b849eb" verified_at: "2026-09-02" status_note: "regionText 由上游 product-v2 提供后透传,当前测试环境返回 null 属预期;001 排序保持 create_time DESC 未变(depart_date ASC 变更待 wx 确认);#6929 已实现:003 totalPrice 应收字段 + birthdayInTrip 跨年修正(详见 §十一)" updated_at: "2026-09-20" diff --git a/changelogs-v2/2026-09/20_7972_接送机用车的免车闸与结算闸改为非对称判据-修改接口-管理后台.md b/changelogs-v2/2026-09/20_7972_接送机用车的免车闸与结算闸改为非对称判据-修改接口-管理后台.md new file mode 100644 index 00000000..1ec50a82 --- /dev/null +++ b/changelogs-v2/2026-09/20_7972_接送机用车的免车闸与结算闸改为非对称判据-修改接口-管理后台.md @@ -0,0 +1,538 @@ +--- +schema: "hl-changelog/v2" +ticket: "7972" +title: "接送机用车的免车闸与结算闸改为非对称判据" +consumer: "admin" +author: "wx(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "not_required" +frontend_status: "not_required" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "本次改动为后端行为调整(错误码判定条件变化),网关无需变更。gateway_status not_required 意味着网关侧无需部署(网关只转发,错误码处理由后端定义)。" +updated_at: "2026-09-20" +base: "dev-v3" +--- + +# order-v3: 接送机用车的免车闸与结算闸改为非对称判据 + +> **存放目录**: changelogs-v2/2026-09/ +> +> **服务**: hl-order-service-v3 +> **PR**: #7976 +> **Issue**: #7972 +> **日期**: 2026-09-20 +> **影响范围**: 管理后台团期用车需求编辑、整团确认、订单结算门禁 + +--- + +## ⚠️ 关键变化 + +免车判据从「**两种需求(TRAVEL+TRANSFER)任一已进行就拒**」改为「**只看 TRAVEL,TRANSFER 独立判**」。 +- **前端以为**:只有 TRANSFER(接送机)、没有 TRAVEL(团车)的户被 809114 拒绝免车 +- **实际现在**:809114 放行这类户;584131 只针对 TRANSFER 本身判(不存在/DONE 放行,其余拒) + +**结算闸新增硬阻**:有 TRANSFER 且未完工的户在 finalize 时被 584131 拒绝,且**存量 PENDING 的 TRANSFER 会在本次上线后对应户 finalize 变 584131**(非回归,是改动预期行为)。 + +--- + +## 一、背景 + +**只订接送机、不订团车** 是合法的在团户(存在真实订单),但被原判据的两支 OR 条件挡住。 +免车意在「本团无需用车」,而只有 TRANSFER 的户仍有接送机需求,整团免车不能替接送机放行。 +改为非对称判据后:整团免车只豁免 TRAVEL,接送机必须逐户推进到 DONE。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 保存团期用车需求 | PUT | `/admin/group-batch/{groupBatchId}/vehicle-requirement` | 行为变化 | 809114 放宽(不挡仅 TRANSFER 户) | +| 2 | 整团确认 | POST | `/admin/group-batch/{groupBatchId}/vehicle-requirement/confirm` | 行为变化 | 同上 | +| 3 | 订单结算 | POST | `/admin/order/{orderId}/submit` | 行为变化 | 新增 584131 拒绝条件 | +| 4 | 读团期用车需求 | GET | `/admin/group-batch/{groupBatchId}/vehicle-requirement` | 行为变化 | 只读,无副作用 | + +--- + +## 三、接口详情 + +### 1. 保存团期用车需求 `PUT /admin/group-batch/{groupBatchId}/vehicle-requirement` + +**VO**: `GroupVehicleRequirementSaveReqVO → GroupVehicleRequirementRespVO` + +#### 使用场景 + +管理员在团期用车需求编辑页保存草稿或点「整团免车」。 +本次改动只影响点「整团免车」按钮时的 809114 门禁判定:只有 TRANSFER(接送机)、没有 TRAVEL(团车)的户现在能通过。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期聚合主键 | +| reason | Body | String | ✅ | 不含空格、不超 200 字 | 免车原因(写入备注留痕) | +| operatorId | 上下文 | String | ✅ | 取决于登录用户 | 操作人 ID(管理员工号) | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| requirementId | Long | 正式需求聚合主键 | +| status | String | 状态(DRAFT/CONFIRMED/DISPATCHED/DONE) | +| groups | Array | 乘车分组列表,免车声明时为空数组 | +| version | Integer | 乐观锁版本号 | +| confirmedBy | String | 确认人 ID(免车时为当前操作人) | +| confirmedAt | LocalDateTime | 确认时刻 | + +#### 请求示例 + +```json +{ + "reason": "只订接送机,不需要团车" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "requirementId": 7200000000000001, + "groupBatchId": 7100000000000001, + "status": "CONFIRMED", + "groups": [], + "version": 2, + "confirmedBy": "admin001", + "confirmedAt": "2026-09-20 10:30:00", + "remark": "[2026-09-19 15:22:00 撤回] 改房型; [2026-09-20 10:30:00 免车] 只订接送机,不需要团车" + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无(必返回需求对象)。 + +#### 错误响应 + +```json +{ + "code": 809114, + "message": "车务已开工(子订单 5100000000000001:PROCESSING),不能再声明整团免车", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- **809114 新判据**(仅 TRAVEL 分支):检查全部在团户的 active TRAVEL(非 TRANSFER)需求,若任一已进入 PENDING/PROCESSING/DONE,整团免车被拒 +- **TRANSFER 不挡免车**:即使某户有 active TRANSFER 处于上述状态,809114 不再拦住,转由结算闸(584131)独立硬阻 +- **幂等**:整团已经是免车态(零分组已确认版本)时重复点免车返回 200,不抛错、不推版本 +- **无副作用**:本端点不调 fleet,不修改户级需求状态,只操作团期层面的正式需求聚合根 + +--- + +### 2. 整团确认 `POST /admin/group-batch/{groupBatchId}/vehicle-requirement/confirm` + +**VO**: `无请求体 → GroupVehicleRequirementRespVO` + +#### 使用场景 + +整团确认页面完成预检后点确认按钮。整团确认作为保存需求的上游,同样需要过 809114 门禁(只看 TRAVEL)。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期聚合主键 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| requirementId | Long | 正式需求聚合主键 | +| status | String | 状态(DRAFT/CONFIRMED/DISPATCHED/DONE) | +| groups | Array | 乘车分组列表 | +| version | Integer | 乐观锁版本号 | +| confirmedBy | String | 确认人 ID | +| confirmedAt | LocalDateTime | 确认时刻 | + +#### 请求示例 + +```json +{} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "requirementId": 7200000000000001, + "status": "CONFIRMED", + "groups": [ + { + "groupCode": "BUS", + "memberCount": 4, + "days": [ + { "tripDate": "2026-10-01", "headcount": 4 } + ] + } + ], + "version": 1, + "confirmedBy": "admin002", + "confirmedAt": "2026-09-20 11:00:00" + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无。 + +#### 错误响应 + +```json +{ + "code": 809114, + "message": "车务已开工(子订单 5100000000000002:DONE),不能再声明整团免车", + "success": false +} +``` + +#### 业务边界 + +- **与保存草稿共用同一把团级锁**:concurrency 写入时后到者抛 809102 +- **状态转移**:确认后正式需求进入 CONFIRMED,版本 +1 + +--- + +### 3. 订单结算 `POST /admin/order/{orderId}/submit` + +**VO**: `无独立请求体 → SettlementSubmitRespVO` + +#### 使用场景 + +管理员在订单结算页面点「提交核单」。本次改动新增 584131 硬阻:有 TRANSFER(接送机)且未完工的户无法 finalize。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| orderId | Path | Long | ✅ | - | 子订单 ID | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| orderId | Long | 子订单 ID | +| status | String | finalize 后的订单状态 | + +#### 请求示例 + +```json +{} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "orderId": 5100000000000001, + "status": "SETTLED" + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无。 + +#### 错误响应(新增 584131) + +```json +{ + "code": 584131, + "message": "接送机:PENDING(团期已声明整团免车,跳过用车户级 TRAVEL 闸;但接送机未安排完成)", + "success": false +} +``` + +#### 业务边界 + +**新增判据(584131,非对称)**: +- **TRAVEL 分支**:若整团已声明免车(zero-group CONFIRMED 版本),TRAVEL 判定被短路,跳过需求检查 +- **TRANSFER 分支(独立)**: + - 不存在 active TRANSFER(未订接送机)→ 放行 + - TRANSFER 状态 = DONE → 放行 + - TRANSFER 状态 = PENDING/PROCESSING/其他 → 硬阻 584131,不受整团免车影响 + +**存量数据行为**:production 中停在 `PENDING` 的 TRANSFER 需求行(9 条已于 2026-09-19 走真实端点 reject 清掉),所属户的 finalize 在本次上线后会从 200 变 584131——这是本次改动的**预期行为**,不是回归。 + +--- + +### 4. 读团期用车需求 `GET /admin/group-batch/{groupBatchId}/vehicle-requirement` + +**VO**: `无请求体 → GroupVehicleRequirementRespVO` + +#### 使用场景 + +编辑页首次打开或重新加载时回填当前需求。本接口是配车刷新状态在 admin 侧唯一的**无副作用观测口**(#7988),不取锁、不进事务、无任何写操作。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期聚合主键 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| requirementId | Long | 正式需求聚合主键 | +| status | String | 状态(DRAFT/CONFIRMED/DISPATCHED/DONE) | +| groups | Array | 乘车分组列表 | +| version | Integer | 乐观锁版本号 | +| confirmedBy | String | 确认人 ID | +| confirmedAt | LocalDateTime | 确认时刻 | + +#### 请求示例 + +```json +{} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "requirementId": 7200000000000001, + "status": "CONFIRMED", + "groups": [], + "version": 2, + "confirmedBy": "admin001", + "confirmedAt": "2026-09-20 10:30:00", + "planRefreshStatus": "FAILED", + "planRefreshFailReason": "fleet timeout" + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +```json +{ + "code": 200, + "message": "成功", + "data": null, + "success": true +} +``` + +#### 错误响应 + +```json +{ + "code": 589500, + "message": "团期不存在", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- **零写入**:不锁、不改库、不进事务、不调 fleet、不推版本 +- **存在性**:返回 null 表示「尚未形成正式需求」(正常),不区分「本该有但丢了」 + +--- + +## 四、契约约束与正确调用方式 + +### ✅ 正确 / ❌ 错误 payload 对照 + +| 场景 | 端点 | payload | 结果 | +|------|------|---------|------| +| ✅ 仅 TRANSFER,免车通过 | PUT `/admin/group-batch/{id}/vehicle-requirement` | `{"reason":"只订接送机"}` | 200,状态 CONFIRMED,groups=[] | +| ✅ 仅 TRAVEL,整团确认通过 | POST `/admin/group-batch/{id}/vehicle-requirement/confirm` | (无体) | 200,状态 CONFIRMED,groups=[{BUS}] | +| ❌ 仅 TRANSFER,TRAVEL 已 PROCESSING,免车拒绝 | PUT `/admin/group-batch/{id}/vehicle-requirement` | `{"reason":"..."}` | 409114「车务已开工」 | +| ✅ 有 TRANSFER 已 DONE,finalize 通过 | POST `/admin/order/{id}/submit` | (无体) | 200 | +| ❌ 有 TRANSFER 未完工,finalize 拒绝 | POST `/admin/order/{id}/submit` | (无体) | 584131「接送机未完成」 | + +### 前后端协议 + +- **809114 的「拒绝户」变了**:从「TRAVEL 或 TRANSFER 任一进行」改为「仅看 TRAVEL 进行」。前端如有针对 809114 的兜底提示,需复查文案准确性 +- **584131 是新条件**:不是对 809114 的替代,而是针对 TRANSFER 独立的硬阻(结算端) +- **两道分离**:免车判定(团期层,809114)与结算判定(订单层,584131)是两套独立的门禁,分别对 TRAVEL 与 TRANSFER 行为 + +--- + +## 五、数据库行为 + +### 免车声明 + +| 前置条件 | 结果 | +|----------|------| +| 用户A: TRAVEL=PENDING, TRANSFER=null | 809114 拒绝(TRAVEL 已进行) | +| 用户B: TRAVEL=null, TRANSFER=PENDING | 809114 放行(TRANSFER 不挡) → 免车声明成功 | +| 用户C: TRAVEL=DONE, TRANSFER=DONE | 809114 拒绝(TRAVEL 已进行) | + +### finalize 闸门 + +| 前置条件 | 结果 | +|----------|------| +| TRANSFER=null | 584131 放行 | +| TRANSFER=DONE | 584131 放行 | +| TRANSFER=PENDING | 584131 拒绝(新判据) | + +### 版本化 + +- 整团免车时若目标版本是「带分组已确认」,会先失活旧版本(CAS),再插入一条「零分组已确认」的新版本 +- `version` 字段自动 +1,乐观锁冲突返 809102 + +--- + +## 六、边界行为 + +- 未登录 → 401(网关拦截) +- 团期不存在 → 589500 +- 正式需求不存在 → 809100 +- 下游 fleet 超时 → 返 200,不阻断 finalize(结算与车配解耦) +- 团期被锁(并发写) → 等待后重试 + +--- + +## 六.5、枚举 + +### VehicleRequirementStatus(用车需求状态) + +**所属字段**: `GroupVehicleRequirementRespVO.status` | **类型**: `String` + +| 值 | 中文 | 说明 | +|-----|------|------| +| `DRAFT` | 草稿 | 编辑中 | +| `CONFIRMED` | 已确认 | 整团确认/免车声明后 | +| `DISPATCHED` | 已派发 | fleet 配车完成 | +| `DONE` | 已完成 | 订单 finalize 后 | +| `PENDING_RECONFIRM` | 待重确认 | 被打回重新编辑后的中间态 | + +### VehicleRequirementKind(需求类型) + +**所属字段**: 内部枚举,不直接暴露给前端 | **类型**: `String` + +| 值 | 中文 | 说明 | +|-----|------|------| +| `TRAVEL` | 团车 | 整团用车需求 | +| `TRANSFER` | 接送机 | 机场接送 | + +--- + +## 六.6、修改前后对比 + +### 行为级对比 + +| 行为 | 改前 | 改后 | +|------|------|------| +| 809114 判定 | 检查 TRAVEL 和 TRANSFER,任一进行则拒 | 只检查 TRAVEL,TRANSFER 不挡 | +| 接送机只户的免车 | 被 809114 拒绝 | 809114 放行(若 TRANSFER 未完工则被 584131 拒) | +| finalize 对 TRANSFER | 无专项检查 | 新增 584131:不存在/DONE 放行,其余拒 | +| 整团免车对 TRANSFER | 整户豁免 | 不豁免,TRANSFER 仍需逐户到 DONE | +| flow 推进(行程清单) | 只看 TRAVEL | 追加 TRANSFER 检查:即使整团免车也要看 TRANSFER | + +--- + +## 六.7、影响评估 + +- **是否破坏向后兼容**:是(行为变化) +- **前端是否必须同步上线**:否(后端改动前兼容,后端改动后错误码体验改善但不影响流程) +- **前端 workaround 清理点**: + - 若有针对 809114「TRANSFER 已进行」的兜底提示,改成只提「TRAVEL 已进行」 + - 若有假设「整团免车豁免一切需求」的逻辑,补充 TRANSFER 的检查(与结算闸 584131 同口径) + +--- + +## 七、不影响范围 + +- **仅影响**:管理后台团期车需求编辑、整团确认、订单结算三处门禁 +- **零影响**: + - 小程序(MP 端点) + - 户级用车需求的创建/更新(只改 validation 判定,不改流程) + - 价格、保险、酒店等其他模块 + - 一期业务(dev 分支无此改动) + +--- + +## 八、测试环境已验证 + +部署版本: `8cdb9ad97`(包含提交 572dc4037) + +``` +✓ PUT /admin/group-batch/7100000000000001/vehicle-requirement + {"reason":"只订接送机"} + → 200 + status=CONFIRMED + groups=[] + +✓ POST /admin/group-batch/7100000000000001/vehicle-requirement/confirm + → 200 + CONFIRMED + +✓ POST /admin/order/5100000000000001/submit + (TRANSFER=DONE) + → 200 + +✓ POST /admin/order/5100000000000002/submit + (TRANSFER=PENDING) + → 584131 (新判据) + +✓ GET /admin/group-batch/7100000000000001/vehicle-requirement + → 200 + 当前需求 +``` + +--- + +## 九、相关历史 PR + +| PR | Issue | 说明 | 是否仍有效 | +|----|-------|------|----------| +| #7763 | #7441 PR-3 | 首次引入 584131 硬拦「缺团车需求」 | ✅ 有效(本次改动收紧范围,只看 TRANSFER) | +| #8033 | #7988 | 补只读观测口(本端点) | ✅ 有效 | +| **本 PR #7976** | **#7972** | 收口 809114/584131 的 kind 口径,非对称判定 | ✅ 最新 | + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#7972](https://git.1814.love:8443/wx/HL/issues/7972) +- 关联 PR: [wx/HL#7976](https://git.1814.love:8443/wx/HL/pulls/7976) +- Merge commit: [wx/HL@572dc4037](https://git.1814.love:8443/wx/HL/commit/572dc4037) + +--- + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#7972](https://git.1814.love:8443/wx/HL/issues/7972) +- **PR**: [#7976](https://git.1814.love:8443/wx/HL/pulls/7976) +- **Merge commit**: [572dc4037](https://git.1814.love:8443/wx/HL/commit/572dc4037) + +### 联系人 + +- **后端负责人**: @wx