16 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 7347 | 整单核单 finalize 新增团期住宿户级闸门,未安排好住宿不允许结算(新增错误码 584130) | admin | wx(GIT) | 修改接口 | deployed | not_required | not_required | mmg | not_required 2026-09-10 mmg: 仅 finalize 错误码集合扩大新增 584130,HTTP 恒 200 判 code,请求/成功响应/方法/路径一字未改;后端自答前端唯一事=给 584130 直接展示后端 message(不二次包装)。实证该诉求已被既有通用业务码分支覆盖:request.js:512-528 未知码取 data.message 原样弹(errorBus message.error),finalize 经 settlementService.js 调用无 silentError、catch 不改写 message,后端 message 自动原样弹出,新增映射反违「不二次包装」。旧口径清理点实证无(财务核单无按住宿派生行推断能否结算逻辑)。 | 2026-09-10 | dev-v3 |
整单核单 finalize 新增团期住宿户级闸门(修改接口)
服务: hl-order-service-v3 Issue: #7347 日期: 2026-09-10 影响范围: 既有端点
POST /v3/admin/order/{orderId}/settlement/finalize(完成核单)的错误码集合扩大;不新增/不删除端点,请求体、成功响应体结构、方法、路径、网关路由一字未改
⚠️ 关键变化
- 团期子订单完成核单(finalize)新增一道硬阻断:该订单需要配房(
needsHotel=true)但其住宿需求还没被房务推到DONE时,finalize 直接拒绝,新增业务错误码584130。改前这种情况能直接结算通过(尤其是"一条住宿派生行都没有"的团期子订单,改前完全不受阻拦)。 - HTTP 状态码恒为 200,闸门以
Result.code = 584130的业务错误体返回,前端必须判code,不能按 HTTP 状态识别。 - 免房户(
needsHotel=false)与非团期订单(productBatchId为空)完全不受影响,行为与改前逐字节一致。 - 管理者已对测试库执行存量统计 SQL,结果为 0(团期子订单里没有一单会被新闸拦住),因此全量生效,不做灰度,没有需要提前处理的存量数据。
- 前端需要做的唯一事情:给
584130加提示文案,见下方"前端提示文案建议"。不涉及任何请求/响应字段改动。
一、背景
团期房务批次(#7322–#7328)把住宿的行级确认接进了核单:某户某晚没分平,那一行就不能被确认(#7327 的 584129)。但整单核单完成(finalize)此前对住宿没有任何硬性闸门——一个团期子订单哪怕住宿完全没安排好,只要没有已录入的住宿行,照样能走完 finalize 结算掉(既有的住宿检查落在 SettlementCategoryCheckService,条件是 rowCount > 0,0 行时天然放行)。
本单在 performSubmitBlockingChecks(既有 finalize 阻断检查方法)里新增一道户级闸门:只要该团期子订单还需要配房,就必须等房务把户级住宿需求推到 DONE 才允许结算。闸门用户级谓词而不是团级 order_group_batch.hotel_ready——团级标志只要有一户没推平就是 false,会把整团所有户的结算一起冻住,违反"不让一户卡整团"的既有原则;户级谓词与房务侧完成判定 finalizeHotelRequirementDone 写的是同一个字段,两侧口径天然对齐。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 完成核单(finalize) | POST | /v3/admin/order/{orderId}/settlement/finalize |
错误码集合扩大 | 新增可能返回 584130;请求体(无)、成功响应体结构、方法、路径均未改;网关路由零改动 |
三、接口详情
1. 完成核单 POST /v3/admin/order/{orderId}/settlement/finalize
VO: 无请求体(Path 参数 orderId)→ Result<SettlementSubmitRespVO>(成功响应结构本单未改,字段集合不重复列出)
使用场景
核单员在核单页点击"完成核单",前端调用本端点。团期子订单在此新增一道住宿完成校验;本节判定入口与触发条件表见下方"业务边界"前的说明表。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
orderId |
Path | Long | 是 | 大于 0 | 订单 ID;本单未改 |
判定入口与触发条件(前端可直接照此做提示判定表):新闸位于 SettlementService.assertGroupHotelReadyForFinalize(OrderInfo),在既有的司机结算完成检查之后、CASH_PAID 缺凭证软预警之前触发(硬阻断,非软预警):
| 条件 | 判定结果 |
|---|---|
productBatchId == null(非团期订单) |
不受本单影响,行为与改前完全一致 |
productBatchId != null 且 needsHotel != true(团期免房户) |
放行,行为与改前一致 |
productBatchId != null 且 needsHotel == true,该户最新一条住宿需求 status == DONE |
放行 |
productBatchId != null 且 needsHotel == true,住宿需求不存在(一条派生行都没有,改前的漏洞场景) |
抛 584130 |
productBatchId != null 且 needsHotel == true,住宿需求存在但 status 为 PENDING / PROCESSING / PENDING_REVIEW / REJECTED_TO_CONSULTANT / REJECTED_TO_ADMIN(即非 DONE) |
抛 584130 |
该户住宿派生行已被撤销转为 GROUP_BATCH_PLAN_REVOKED,但住宿需求本身仍非 DONE |
抛 584130(REVOKED 行不构成"已安排好"的证据) |
判定只读户级住宿需求的 status 字段,不看有没有派生行、也不解析日期判断"是否自助订房"——这些口径统一由房务侧的完成判定负责写 DONE,本闸只读结果。
出参
成功响应结构本单未改,Result<SettlementSubmitRespVO> 字段集合与改前完全一致(不重复列出全部字段,仅摘录与本单相关的部分):
| 字段 | 类型 | 说明 |
|---|---|---|
data.orderId |
Long | 订单 ID;本单未改 |
data.finalSnapshotStatus |
String | 核单终态快照状态;本单未改 |
data.warnings |
List<WarningItemVO> |
软预警列表(如 CASH_PAID 缺凭证);本单未改,584130 不进这个列表,而是走错误响应 |
请求示例
POST /v3/admin/order/1000123/settlement/finalize
(无请求体,仅 Path 参数 orderId;本单未改请求形态)
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"orderId": 1000123,
"finalSnapshotStatus": "CONFIRMED",
"warnings": []
}
}
(成功路径响应结构本单未改,仅摘录关键字段)
空数据 / 降级响应
不适用:本闸不产生空数据/降级语义,判定只有"放行"或"抛 584130"两种结果。
错误响应
{
"code": 584130,
"message": "团期子订单 GB202609090001 的住宿尚未安排完成(住宿需求当前状态:PROCESSING),请等房务配房完成后再提交核单",
"success": false,
"data": null
}
message 模板(SettlementErrorCode.SETTLEMENT_GROUP_HOTEL_NOT_READY,SettlementErrorCode.java:339-342)为:
团期子订单 {0} 的住宿尚未安排完成(住宿需求当前状态:{1}),请等房务配房完成后再提交核单
两个占位符:{0} = 订单号(orderNo),{1} = 住宿需求当前状态。住宿需求行完全不存在时,{1} 填固定文案 未提交住宿需求(常量 SettlementService.GROUP_HOTEL_REQUIREMENT_ABSENT,逐字抄自源码,不是状态枚举值),而不是某个状态码。
业务边界
- 闸门只对
productBatchId != null(团期子订单)生效,核心订单的房控闸是另一套逻辑(OrderService.needsHotel != true → 视为房控通过),本单不动它。 - 闸门读取的是"该户最新一条住宿需求"(
RequirementService.getLatestHotelRequirementByOrderId),走既有 Service 公共只读方法,未直连 Mapper。 - 与既有"已录入住宿行全部 CONFIRMED"的行级检查(
SettlementCategoryCheckService)并列不重叠:旧检查看的是已录入行的确认状态(行级),新闸看的是住宿需求status(户级),两者判定顺序上新闸更早触发(performSubmitBlockingChecks早于SettlementReportFlowService.prepareFinalizationInCurrentTransaction)。
四、契约约束与正确调用方式
- 必须按响应体
code == 584130识别,不能按 HTTP 状态码识别——HTTP 恒为 200。 - 建议直接展示后端
message,其中已包含订单号与住宿需求当前状态,无需前端自行拼接。 - "该团期子订单是否可以完成核单"以 finalize 实际返回结果为准,前端不应该通过"是否存在住宿派生行"自行推断能否结算——这正是改前的漏洞场景(0 行时误以为可以结算)。
切换状态时的必要动作
无。本单不涉及任何需要调用方额外置空/切换的字段,闸门完全由后端根据现有数据判定。
五、数据库行为
584130在阻断检查阶段抛出,属于既有 finalize 阻断检查的一部分,抛出后不产生任何写入(不写核单快照、不写车辆冻结、不写日志表)。- 判定只读
order_main(productBatchId/needsHotel)与住宿需求表(status),无表结构变更、无 Flyway。
六、边界行为
- 团期免房户(
needsHotel=false):放行,不查住宿需求。 - 团期需房户,住宿需求
status=DONE:放行。 - 团期需房户,住宿需求缺失(零派生行):拒绝,
584130,message中状态位为"未提交住宿需求"。 - 团期需房户,住宿需求非 DONE(
PENDING/PROCESSING/PENDING_REVIEW/REJECTED_TO_CONSULTANT/REJECTED_TO_ADMIN):拒绝,584130,message中状态位为该实际状态值。 - 该户住宿派生行已转
GROUP_BATCH_PLAN_REVOKED,但需求status未到DONE:仍拒绝,584130(REVOKED行不算完成证据)。 - 非团期订单:完全不受影响,无论住宿情况如何都走改前逻辑。
- 全自订户(每一晚都自助订房)的
needsHotel依然为true,其住宿需求需先由房务侧完成判定推到DONE才能通过本闸;这条口径由另一单负责写DONE,本单只读结果、不重复判定。
六.5、枚举 / 数据字典
新增错误码(SettlementErrorCode,hl-order-service-v3/src/main/java/com/hulalv/order/settlement/errorcode/SettlementErrorCode.java:339-342)
| code | 常量名 | message 模板 | 触发条件 |
|---|---|---|---|
584130 |
SETTLEMENT_GROUP_HOTEL_NOT_READY |
团期子订单 {0} 的住宿尚未安排完成(住宿需求当前状态:{1}),请等房务配房完成后再提交核单 |
团期子订单需要配房,且住宿需求当前状态不是 DONE(含需求缺失) |
住宿需求状态枚举(RequirementStatus,本单只读不改,供理解 {1} 占位取值)
| value | label | 是否放行本闸 |
|---|---|---|
PENDING |
待房务配 | 否 |
PROCESSING |
配房中 | 否 |
DONE |
配房完成 | 是(唯一放行值) |
PENDING_REVIEW |
待审核 | 否 |
REJECTED_TO_CONSULTANT |
驳回 | 否 |
REJECTED_TO_ADMIN |
驳回 | 否 |
| (需求行不存在) | —— | 否,message 状态位显示"未提交住宿需求" |
前端提示文案建议
- 直接展示
message原文即可(已含订单号 + 当前状态),无需二次包装。 - 若需要更醒目的引导,可在
message之外追加一句操作指引,例如:"请前往房务模块查看该团期订单的住宿安排进度,配房完成后再回来提交核单。" - 不建议把
584130与其他 40xxxx/58xxxx 段错误码合并成同一个通用提示——该码语义明确(住宿未完成),合并展示会丢失"该去催房务"这条关键信息。
六.6、修改前后对比
行为级对比
| 场景 | 改前 | 改后 |
|---|---|---|
| 团期需房户,一条住宿派生行都没有 | 可以直接结算通过(改前存在的漏洞) | 拒绝,584130 |
团期需房户,住宿需求非 DONE(有派生行但未分平) |
可能继续核单(仅受行级 CONFIRMED 检查约束,0 行时不受约束) |
拒绝,584130 |
团期需房户,住宿需求 DONE |
可核单 | 行为不变,仍可核单 |
| 团期免房户 | 可核单 | 行为不变 |
| 非团期订单 | 可核单(受核心订单自己的房控闸约束) | 行为不变,核心房控闸逻辑本单未动 |
该户住宿派生行已被撤销为 GROUP_BATCH_PLAN_REVOKED |
若无行级检查约束可能通过 | 拒绝,584130(除非需求 status 已是 DONE) |
字段级对比
无字段变化。请求体(无)、成功响应体 SettlementSubmitRespVO 的字段集合均未改动,仅错误响应的 code 取值范围新增了 584130。
六.7、影响评估
- 是否破坏向后兼容:否。端点方法/路径/请求体/成功响应体结构均未变化;免房户、
DONE户、非团期订单的行为与改前逐字节一致。 - 前端是否必须同步上线:需要新增
584130的提示文案,否则该错误会被前端当成未知错误码兜底展示(用户体验较差,但不会导致请求失败或数据错误)。 - 前端 workaround 清理点:如果前端此前依赖"住宿派生行是否存在"来判断该团期子订单是否可结算,需要清理——这个判断口径本身就是改前的漏洞,不应再使用;一律以 finalize 实际返回结果为准。
七、不影响范围
- 仅影响:
POST /v3/admin/order/{orderId}/settlement/finalize端点在团期子订单(productBatchId != null)且需要配房(needsHotel=true)时的错误码集合。 - 零影响:
- 无新增/修改/删除任何其他端点;网关路由零改动。
- 免房户(
needsHotel=false)与非团期订单(productBatchId为空)的 finalize 行为与改前完全一致。 - 成功响应体
SettlementSubmitRespVO字段集合未变。 - 不读取团级
order_group_batch.hotel_ready字段,不会出现"一户卡整团"的情况。 - 不涉及表结构变更、Flyway、数据迁移。
- 既有的住宿"已录入行全部 CONFIRMED"行级检查(
SettlementCategoryCheckService)逻辑未改,新闸与它并列而非替代。
八、测试环境已验证
- 存量影响评估:管理者已对测试库执行存量统计 SQL(统计"团期子订单中会被新闸拦住"的数量),结果为 0——没有任何存量团期子订单处于会被新闸拦截的状态。因此本单全量生效,不做灰度,无需推平存量数据。
- 部署状态:测试服已部署 HEAD
18c1df126,该 HEAD 即本单的实现提交本身(PR #7433)。 - 网关验证:端点路径/方法未变,无需新增网关路由配置;沿用既有
/v3/admin/order/**路由规则。 - 兼容性结论:端点结构不变,仅扩展业务错误码集合,前端只需新增对
584130的分支处理。
十、相关文档
- 关联 Issue: wx/HL#7347
- 前置/关联依赖:
#7327(团期住宿行级确认,584129 与本单 584130 相邻但语义不同)、#7325(户级住宿完成判定,本单读取其写入的status字段)
关联 / 联系人
链接
- Issue: #7347
- PR: 尚未创建(合并后回填 #N)
- Merge commit: 尚未产生(合并后回填 https://git.1814.love:8443/wx/HL/commit/sha)