--- schema: "hl-changelog/v2" ticket: "8410" title: "团期「配置 → 确认」新增只读预检 confirm-check:前端据 ready 提前置灰「确认」按钮,团期级五项与逐户未满足项可见" consumer: "admin" author: "jw(GIT)" change_type: "新增接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "pending" frontend_owner: "" frontend_ref: "" target_release: "v2.1" verified_at: "2026-09-27" status_note: "新增 GET /v3/admin/order/group-batch/{groupBatchId}/confirm-check(权限码 group-batch:confirm,与确认写口同码)。返回此刻点「确认」能不能过(ready)、状态是否允许确认(statusConfirmable)、团期级五项(房 / 车 / 导游领队 / 摄影 / 物资,每项 passed + 未通过文案)、逐户未满足项(待支付 / 定制中的在团户,每户 orderNo + items[code, text])与 gateMessage(与此刻点确认拿到的 589556 message 逐字相同)。判据与写口同源;主报账人按「确认时会先补齐人员副本」投影;零写入。状态不是 RESOURCE_PREPARING 时不做逐户预检,checkedHouseholdCount 为 null。前端需在团期详情「配置」节点进入时与点「确认」前调用,据 ready 置灰按钮、据 batchItems / unmetHouseholds 列缺项。" updated_at: "2026-09-27" base: "dev-v3" --- # 团期确认: 新增只读预检 confirm-check,按钮可提前置灰(管理后台) > **服务**: hl-order-service-v3(端口 8086/8186) > **PR**: #8411 > **Issue**: #8410 > **日期**: 2026-09-27 > **影响范围**: 管理后台团期详情「配置」节点的「确认」按钮(置灰与缺项提示);确认写口本身契约不变 --- ## ⚠️ 关键变化 1. **新增只读端点** `GET .../{groupBatchId}/confirm-check`:点「确认」之前就能知道能不能过、差什么。 2. **逐户未满足项第一次对前端可见**。#8339 起确认门含逐户预检(订金、出行人、户级房车、合同模板、主报账人),但团期详情只有团期级五个标记位,前端据此置灰会漏掉逐户这一半;以后以本端点的 `ready` 为准。 3. `gateMessage` 与点确认拿到的 589556 `message` 逐字相同,可直接展示。 --- ## 一、背景 团期「配置 → 确认」(#8268,`POST .../confirm`)的门 = 房 / 车 / 导游领队 / 摄影四项配齐 + 物资已确认,#8339 又加了逐户预检。不满足时整单返回 589556,`message` 里列出未满足项,但**没有结构化数据**,团期详情也只透出团期级五个标记位。于是出现「五项全绿、某户没付订金时按钮是亮的,点下去才报错」。需求确认与订房确认早就各有配对的只读预检(`requirement/confirm-check`、`room-plans/confirm-check`),团期确认补齐同一形态。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 团期确认预检 | GET | `/v3/admin/order/group-batch/{groupBatchId}/confirm-check` | 新增接口 | 只读;与 `POST .../confirm` 同门、同报文、同权限码 | 网关无改动(在既有 `/v3/admin/order/group-batch` 前缀下)。 --- ## 三、接口详情 ### 1. 团期确认预检 `GET /v3/admin/order/group-batch/{groupBatchId}/confirm-check` **VO**: `GroupBatchConfirmCheckRespVO` #### 使用场景 团期详情「配置」节点:进入页面时与点「确认」前调用。`ready=false` 时置灰「确认」按钮,用 `batchItems` 展示团期级五项的勾叉,用 `unmetHouseholds` 逐户列出还差什么,或直接展示 `gateMessage`。`statusConfirmable=false`(不在配置节点)时按钮应隐藏或置灰,不要提示缺项。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期主键 | 不存在返 589500 | #### 出参 | 字段 | 类型 | 说明 | |------|------|------| | groupBatchId | String | 团期主键(雪花 id,按字符串返回) | | batchStatus | String | 团期状态码 | | batchStatusName | String | 团期状态中文名 | | ready | Boolean | 此刻点「确认」能否通过:`statusConfirmable=true` 且五项全过且 `unmetHouseholds` 为空 | | statusConfirmable | Boolean | 状态是否允许确认(仅 `RESOURCE_PREPARING`);false 时点确认会是 589501 | | batchItems | Array | 团期级五项,固定顺序、全量返回(含已通过项),任何状态下都有值 | | batchItems[].code | String | `HOTEL_READY` / `VEHICLE_READY` / `GUIDE_READY` / `PHOTOGRAPHER_READY` / `MATERIAL_CONFIRMED`,与团期详情同名布尔字段对应 | | batchItems[].name | String | 房 / 车 / 导游领队 / 摄影 / 物资 | | batchItems[].passed | Boolean | 是否通过 | | batchItems[].unmetText | String | 未通过时的文案(与 589556 里的逐字相同),通过时 null | | checkedHouseholdCount | Integer | 参与逐户预检的户数(待支付 + 定制中的在团户);`statusConfirmable=false` 时不做逐户预检,为 null(null = 没查,0 = 查了没有需确认的户) | | unmetHouseholds | Array | 不满足的户,按 orderId 升序;全满足或未做逐户预检时为空数组 | | unmetHouseholds[].orderId | String | 子订单 id(字符串) | | unmetHouseholds[].orderNo | String | 子订单号 | | unmetHouseholds[].orderStatus | String | `PENDING_PAY` / `CUSTOMIZING` | | unmetHouseholds[].items | Array | 该户未满足项,顺序同 589556 | | unmetHouseholds[].items[].code | String | 见「六.5、枚举」 | | unmetHouseholds[].items[].text | String | 文案,与 589556 里的逐字相同 | | gateMessage | String | 此刻点确认会拿到的 589556 `message`(逐字相同);`ready=true` 或 `statusConfirmable=false` 时为 null | #### 请求示例 ```http GET /v3/admin/order/group-batch/2097250563497385985/confirm-check HTTP/1.1 Host: api.test.1814.love Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "success": true, "data": { "groupBatchId": "2097250563497385985", "batchStatus": "RESOURCE_PREPARING", "batchStatusName": "资源准备中", "ready": false, "statusConfirmable": true, "batchItems": [ { "code": "HOTEL_READY", "name": "房", "passed": true, "unmetText": null }, { "code": "VEHICLE_READY", "name": "车", "passed": true, "unmetText": null }, { "code": "GUIDE_READY", "name": "导游领队", "passed": true, "unmetText": null }, { "code": "PHOTOGRAPHER_READY", "name": "摄影", "passed": true, "unmetText": null }, { "code": "MATERIAL_CONFIRMED", "name": "物资", "passed": false, "unmetText": "物资未确认" } ], "checkedHouseholdCount": 3, "unmetHouseholds": [ { "orderId": "2097260000000000001", "orderNo": "GT-26-0085", "orderStatus": "CUSTOMIZING", "items": [ { "code": "PAYMENT_OK", "text": "未付订金" }, { "code": "PRIMARY_REPORTER_MISSING", "text": "未指定主报账人" } ] } ], "gateMessage": "团期尚不满足确认条件:物资未确认;订单 GT-26-0085:未付订金、未指定主报账人" } } ``` #### 空数据 / 降级响应 全部满足:`ready=true`,`unmetHouseholds=[]`,`gateMessage=null`。不在配置节点(如招募中、已确认):`statusConfirmable=false`、`ready=false`、`checkedHouseholdCount=null`、`unmetHouseholds=[]`、`gateMessage=null`,`batchItems` 照常返回五项。 ```json { "code": 200, "success": true, "data": { "groupBatchId": "2097250563497385985", "batchStatus": "MATERIAL_PREPARING", "batchStatusName": "物料准备中", "ready": false, "statusConfirmable": false, "batchItems": [ { "code": "HOTEL_READY", "name": "房", "passed": true, "unmetText": null }, { "code": "VEHICLE_READY", "name": "车", "passed": true, "unmetText": null }, { "code": "GUIDE_READY", "name": "导游领队", "passed": true, "unmetText": null }, { "code": "PHOTOGRAPHER_READY", "name": "摄影", "passed": true, "unmetText": null }, { "code": "MATERIAL_CONFIRMED", "name": "物资", "passed": true, "unmetText": null } ], "checkedHouseholdCount": null, "unmetHouseholds": [], "gateMessage": null } } ``` #### 错误响应 团期不存在: ```json { "code": 589500, "message": "团期不存在", "success": false, "data": null } ``` 无权限(角色未授 `group-batch:confirm`): ```json { "code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "success": false, "data": null } ``` #### 业务边界 - 权限码 `group-batch:confirm`,与确认写口同码(授 `GROUP_BATCH_MANAGER` / `ADMIN`);看不到「确认」按钮的角色不需要调本端点。 - 判据与 `POST .../confirm` 同源:同一阶段判断、同一张五项表、同一套逐户判据、同一个 589556 构造;`gateMessage` 就是那条异常的 message。 - 预检只是预检:点确认时写口按当时数据重判一遍,两次调用之间数据变了以写口为准。 - 主报账人按「确认时会先补齐团期人员副本」投影:团期层已设主报账人、订单层副本滞后的户,预检不报「未指定主报账人」(点确认时写口会先补齐再判)。 - 零写入:不补齐副本、不写时间线、不改状态、不发事件。 - 逐户预检的开销与点一次确认相当(每户读一次确认清单),不要轮询调用。 --- ## 四、契约约束与正确调用方式 ### ✅ 正确 / ❌ 错误调用顺序 | 场景 | 调用 | |------|------| | ✅ 置灰「确认」按钮 | 进入配置节点 → `GET confirm-check` → 按 `ready` 置灰 | | ✅ 点「确认」 | `GET confirm-check`(`ready=true`)→ `POST confirm` | | ❌ 只用详情的五个标记位判断能否确认 | 漏掉逐户预检,点下去仍可能 589556 | | ❌ 自己拼 589556 文案 | 直接用 `gateMessage` 或 `batchItems[].unmetText` / `items[].text` | ### ready 与 statusConfirmable `statusConfirmable=false` 表示「不在能确认的节点」(按钮不该出现或应置灰且不提示缺项);`statusConfirmable=true` 且 `ready=false` 表示「在配置节点但还有缺项」(置灰并展示缺项)。 --- ## 六、边界行为 - 未登录 → 401(网关拦截)。 - 团期不存在 → 589500;无权限 → 589507(不进业务逻辑)。 - 在团户里已是待出行 / 出行中 / 已完成的户不参与逐户预检(与写口一致);已取消户不在「在团」口径内。 - 库里若出现认不出的团期状态值,预检返回 `statusConfirmable=false`;确认写口同步改为返回 589501(此前是 500)。正常数据不可达。 --- ## 六.5、枚举 / 数据字典 ### 团期级五项(batchItems[].code) **所属字段**: `batchItems[].code` | **类型**: `String` | 值 | 中文 | 未通过文案 | |----|------|------------| | `HOTEL_READY` | 房 | 房未配齐 | | `VEHICLE_READY` | 车 | 车未配齐 | | `GUIDE_READY` | 导游领队 | 导游领队未配齐 | | `PHOTOGRAPHER_READY` | 摄影 | 摄影未配齐 | | `MATERIAL_CONFIRMED` | 物资 | 物资未确认 | ### 逐户未满足项(unmetHouseholds[].items[].code) **所属字段**: `unmetHouseholds[].items[].code` | **类型**: `String` | 值 | 中文 | 说明 | |----|------|------| | `PENDING_PAY` | 待支付(须先付订金或取消) | 订单仍是待支付;此时不再重复列 `PAYMENT_OK` | | `PAYMENT_OK` | 未付订金 | 团期口径付订金即可(沿用单户确认清单编码) | | `TRAVELER_COMPLETE` | 出行人信息 | 文案取单户确认清单的失败原因 | | `HOTEL_DONE` | 房型安排 | 同上 | | `VEHICLE_DONE` | 用车安排 | 同上 | | `CONTRACT_TEMPLATE_OK` | 合同方案配置 | 同上 | | `PRIMARY_REPORTER_MISSING` | 未指定主报账人 | 按「确认时先补齐副本」投影后仍无主报账人 | --- ## 七、不影响范围 - **仅影响**: 新增一个只读端点。 - **零影响**: - `POST .../confirm` 的入参、出参、错误码与门条件(内部改为与预检共用判据;唯一可观察差异是库里出现非法状态值时由 500 改 589501,正常数据不可达) - 团期详情、需求确认预检、订房确认预检 - 小程序端 - 零数据库变更、零配置变更、零权限种子变更(复用 #8268 的 `group-batch:confirm`)。 --- ## 八、测试环境已验证 部署:hl-order-service-v3 = dev-v3 @ ecc92b95c(2026-09-27 10:22);取证时 TEST 检出为 dev-v3 @ 57199b539(含 ecc92b95c),经网关 `https://api.test.1814.love` 真实鉴权实测(2026-09-27 10:45–10:56),order-v3 取证期间未被重部署;工单 #8410 已验收关单。 | # | 场景 | 结果 | |---|---|---| | 1 | ADMIN / GROUP_BATCH_MANAGER / SUPER_ADMIN 调 confirm-check | 200 | | 2 | CUSTOMIZER 调 confirm-check | 589507,前后三表无写入 | | 3 | 不存在的团期 / 不带 token | 589500 / 401 | | 4 | RESOURCE_PREPARING 团期(房 / 车 / 物资未过,12 户定制中) | `batchItems` 五项顺序正确,与详情五个布尔、库值三方一致;`checkedHouseholdCount=12`,12 户逐户列出(TRAVELER_COMPLETE / HOTEL_DONE / VEHICLE_DONE / PRIMARY_REPORTER_MISSING) | | 5 | 同一团期调 `POST .../confirm` 对照 | 589556,`message` 与预检 `gateMessage` 逐字相同(914 字);确认被拒无写入 | | 6 | 团期层临时设主报账人、订单层副本滞后(NONE) | 预检不再报「未指定主报账人」(按确认时先补齐副本投影),预检不补齐副本;还原后恢复原状 | | 7 | 订单层副本是 PRIMARY 但团期层为 NONE(将被补齐降级) | 预检仍报「未指定主报账人」,预检不改该行;还原后恢复原状 | | 8 | 连续四次调预检 | `order_group_batch` / `group_batch_status_log` / `order_staff_assignment` / 子订单状态逐字段无变化 | | 9 | RECRUITING / MATERIAL_PREPARING / CANCELLED 团期 | `statusConfirmable=false`、`ready=false`、`checkedHouseholdCount=null`、`unmetHouseholds=[]`、`gateMessage=null`,五项照返 | --- ## 九、相关历史 PR | PR | Issue | 说明 | 是否仍有效 | |----|-------|------|------------| | #8302 | #8268 | 团期人工确认写口(五项门、589556) | ✅ | | #8346 / #8349 | #8339 | 确认门逐户预检、确认前补齐人员副本 | ✅(本端点投影的就是这次补齐) | | — | #7210 | 需求确认预检 `requirement/confirm-check`(同形先例) | ✅ | | **本 PR #8411** | **#8410** | 团期确认只读预检 | ✅ 最新 | --- ## 十、相关文档 - 关联 Issue: [wx/HL#8410](https://git.1814.love/wx/HL/issues/8410) - 关联 PR: [wx/HL#8411](https://git.1814.love/wx/HL/pulls/8411) - 写口条目:`24_8268_团期人工确认端点与确认后才出合同保险-新增接口-管理后台.md` ## 关联 / 联系人 ### 链接 - **Issue**: [#8410](https://git.1814.love/wx/HL/issues/8410) - **PR**: [#8411](https://git.1814.love/wx/HL/pulls/8411) - **Merge commit**: [ecc92b95c](https://git.1814.love/wx/HL/commit/ecc92b95ca2cc9c5801f2cfab5f29456f0419875) ### 联系人 - **后端负责人**: @jw