From bb6df1469a20daba444cb6ed98737b6d0dfa44a4 Mon Sep 17 00:00:00 2001 From: yaosutu <770858045@qq.com> Date: Sun, 4 Oct 2026 14:23:12 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20=E8=AE=A2=E6=AD=A3=2004=5F87?= =?UTF-8?q?14=20=E5=9B=A2=E6=9C=9F=E6=A0=B8=E5=8D=95=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E9=97=A8=E7=A6=81=E5=A5=91=E7=BA=A6=EF=BC=88#8783=20PR=20#8784?= =?UTF-8?q?=EF=BC=89=EF=BC=9Aconfirm=20blocking=20=E9=9D=9E=E7=A9=BA?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=AB=AF=E7=A1=AC=E6=8B=A6=20589568=E3=80=81?= =?UTF-8?q?=E8=B4=9F=E9=87=91=E9=A2=9D=20400=20=E6=94=B9=E6=8A=9B=20589753?= =?UTF-8?q?=E3=80=81=E9=9B=B6=E5=80=BC=E9=87=91=E9=A2=9D=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=200.00=EF=BC=88=E4=BF=AE=E6=94=B9=E6=8E=A5=E5=8F=A3=C2=B7?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=90=8E=E5=8F=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ..._团期核单确认门禁订正-修改接口-管理后台.md | 242 ++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 changelogs-v2/2026-10/04_8783_团期核单确认门禁订正-修改接口-管理后台.md diff --git a/changelogs-v2/2026-10/04_8783_团期核单确认门禁订正-修改接口-管理后台.md b/changelogs-v2/2026-10/04_8783_团期核单确认门禁订正-修改接口-管理后台.md new file mode 100644 index 00000000..b82ca37e --- /dev/null +++ b/changelogs-v2/2026-10/04_8783_团期核单确认门禁订正-修改接口-管理后台.md @@ -0,0 +1,242 @@ +--- +schema: "hl-changelog/v2" +ticket: "8783" +title: "团期核单确认门禁订正:blocking 非空服务端硬拦 589568 + 负金额改抛 589753 + 零值金额统一 0.00" +consumer: "admin" +author: "yst" +change_type: "修改接口" +backend_status: "merged" +gateway_status: "not_required" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "订正 04_8714 团期核单重做 changelog 的契约行为描述:confirm 在 blocking 非空时此前仅 panel 透出前端置灰、服务端 200 放行,现服务端硬拦 589568;行内负金额由此前 400 参数校验改为契约错误码 589753;panel/tab 空集合金额统一序列化为 0.00 两位小数。已合 dev-v3 未部署测试服。" +updated_at: "2026-10-04" +base: "dev-v3" +--- + +# order-v3 groupbatch:团期核单确认门禁订正(管理后台)—— 订正 04_8714 + +> ⚠️ **本文是订正 changelog**,修订《04_8714_团期核单重做8类tab明细+公摊拆账-修改接口-管理后台.md》中三处契约行为描述。原文件保留不改动;两处描述不一致时**以本文为准**。接口路径、入参结构、出参结构均无变化,变化只在**服务端行为 / 错误码 / 金额序列化格式**三处。 + +## 1. 接口背景 + +#8714 团期核单重做落地后,#8783(PR #8784)补齐三处契约缺口: + +1. **确认门禁漏拦**:原契约 panel 透出 `blockingOrderIds` 供前端把确认按钮置灰,但服务端 confirm 端点并未校验——前端绕过置灰直接调 `POST …/settlement/panel/confirm` 时,团内仍有子订单第 1 层核单未定稿也能 200 确认成功。现服务端补上硬校验,blocking 非空即拒绝。 +2. **负金额错误码不合契约**:行内金额/单价/数量为负时,此前由 Bean Validation(`@DecimalMin`)拦截返通用 400,不属于核单域错误码体系。现删除字段级 `@DecimalMin`,改在 Service 层 resolveSplits 入口统一校验,按契约抛 **589753**。 +3. **零值金额格式不统一**:panel 各分类合计、户视图已摊成本/收入、tab `allocatedTotal` 等空集合金额,此前部分路径序列化为 `"0"`(无小数位),与 tab 两位小数口径不一致。现统一为 `"0.00"`。 + +## 2. 变更清单 + +| # | 接口 | 变更点 | 类型 | +|---|------|--------|------| +| 1 | `POST /v3/admin/order/group-batch/{groupBatchId}/settlement/panel/confirm` | **新增服务端门禁**:在团子订单第 1 层核单未全部定稿(blocking 非空)时拒绝确认,抛 **589568**(此前 200 放行,仅 panel 透出 blockingOrderIds 供前端置灰) | 🔧 行为变更 | +| 2 | `PUT …/settlement/{8 个 tab}` / `POST …/settlement/lines` / `POST …/settlement/alloc-preview` | 行内金额/单价/数量为负,由 Bean Validation **400**(通用参数错误)改为契约错误码 **589753**;涉及字段见 §4 | ⚠️ 错误码变化 | +| 3 | `GET …/settlement/panel` / `GET …/settlement/{tab}` | 空集合金额(分类合计 / 户 allocatedCost/allocatedIncome / tab allocatedTotal 等)序列化由可能为 `"0"` 统一为 **`"0.00"`**(两位小数) | 🔧 出参格式统一 | + +路径前缀统一为 `/v3/admin/order/group-batch/{groupBatchId}/settlement`,下文用 `…` 代指。接口签名(路径 / 入参字段 / 出参字段)零变化。 + +## 3. 接口详情 + +| 项 | 说明 | +|---|---| +| 服务 | hl-order-service-v3(端口 8086) | +| 路径前缀 | `/v3/admin/order/group-batch/{groupBatchId}/settlement` | +| 使用场景 | 管理后台 → 团期详情 → 核单 Tab:确认核单、明细行金额录入 | +| 认证 | 管理后台登录态(JWT);网关既有路由 `/v3/admin/**`,无新增网关配置 | +| 权限码 | `group-batch:audit:allocate`(确认核单);`group-batch:audit:edit`(tab PUT / lines 新增);缺码一律 589507 | +| 幂等性 | 与 04_8714 一致:写口不加 `@Idempotent`,由 main 主行锁 + expectedVersion CAS 兜底(过期 589573) | +| 限流 | 无特殊限流 | + +## 4. 接口入参 + +入参结构与 04_8714 §4 完全一致,此处只列**本次行为变化涉及的字段**(GroupSettleLineSaveReqVO 及其 splits[]): + +| 字段 | 类型 | 必填 | 原约束(@DecimalMin,删) | 现约束(Service 校验) | +|------|------|------|---------------------------|------------------------| +| budgetAmount | number | 可空 | ≥0,违例 400 | **为负抛 589753** | +| actualAmount | number | ✅ | ≥0,违例 400 | **为负抛 589753** | +| unitPrice | number | 可空 | ≥0,违例 400 | **为负抛 589753**(住宿/餐食/其他收入) | +| ticketUnitPrice | number | 可空 | ≥0,违例 400 | **为负抛 589753**(门票·游玩) | +| quantity | number | 可空 | ≥0,违例 400 | **为负抛 589753**(餐食/其他收入) | +| dailyPrice | number | 可空 | ≥0,违例 400 | **为负抛 589753**(车辆) | +| perDayFee | number | 可空 | ≥0,违例 400 | **为负抛 589753**(导游/摄影师) | +| splits[].ratio | number | 可空 | ≥0,违例 400 | **为负抛 589753** | +| splits[].amount | number | 可空 | ≥0,违例 400 | **为负抛 589753** | + +`POST …/settlement/panel/confirm` 入参不变: + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| expectedVersion | int | ✅ | 乐观锁版本(GET panel/tab 返回的 `version` 原样回传) | + +## 5. 出参字段 + +出参结构与 04_8714 §5 完全一致。**唯一变化是零值金额的序列化格式**: + +| 字段 | 原来(空集合时可能) | 现在(统一) | +|------|----------------------|--------------| +| panel `categories[].actualAmount` / `allocatedAmount` / `unallocatedAmount` | `"0"` | `"0.00"` | +| panel `households[].allocatedCost` / `allocatedIncome` / `grossProfit` | `"0"` | `"0.00"` | +| tab `allocatedTotal` / `actualTotal` | `"0"` | `"0.00"` | + +非零金额不受影响(此前已是两位小数)。金额仍为 JSON 字符串,前端按字符串处理即可;若有对 `"0"` 的等值判断 / 解析分支需放宽为兼容 `"0.00"`(建议统一 `Number(x)` 或字符串比较改为数值比较)。 + +`GET …/settlement/panel` 中与本门禁相关的字段(不变,重述便于对照): + +| 字段 | 类型 | 说明 | +|------|------|------| +| readyToAllocate | boolean | 各户第 1 层核单是否全部定稿;false 时 confirm **必吃 589568** | +| blockingOrderIds | string[] | 第 1 层核单未定稿的子订单 ID;**非空时 confirm 必吃 589568** | + +## 6. 枚举 / 数据字典 + +本次无枚举 / 字典变化。 + +## 7. 错误码 + +| 错误码 | 文案({0} 为动态定位串) | 触发场景 | 本次变化 | +|--------|--------------------------|----------|----------| +| 589568 | 整团核单当前状态不允许该操作:{0} | ① CONFIRMED 后任何写操作(原有);② **确认核单时在团子订单第 1 层核单未全部定稿**——{0} 带未定稿订单号清单,「、」分隔,与 panel 的 blockingOrderIds 一一对应 | ⚠️ **新增触发场景②**(此前 confirm 不校验,200 放行) | +| 589753 | 核单拆账入参非法:{0} | 金额/单价/数量为负、未知费用类别等入参层非法;{0} 指明具体字段与值 | ⚠️ **负金额场景由此前 400 改为 589753**(删 @DecimalMin,改 Service 层 resolveSplits 入口校验) | + +其余错误码(589507 / 589567 / 589571 / 589572 / 589573 / 589750 / 589751 / 589752 / 589754 / 589755)与 04_8714 §7 一致,无变化。 + +**前端适配点**: +- 589568 的 toast / 弹窗需覆盖「确认时 blocking 非空」场景——message 里已带未定稿订单号清单,可直接展示;建议同时刷新 panel 更新 blockingOrderIds。 +- 负金额入参不再走 400 分支,改走 589753 分支;若前端此前对 400 做通用「参数错误」提示,现在会收到带具体字段定位的 589753 message,体验更好。 + +## 8. 示例 + +### 8.1 典型:确认核单时有子订单第 1 层核单未定稿,被 589568 拒绝 + +请求: + + POST /v3/admin/order/group-batch/1934567890123456789/settlement/panel/confirm + Content-Type: application/json + +```json +{ "expectedVersion": 5 } +``` + +响应(业务失败,团内订单 HL20261001001、HL20261001003 第 1 层核单未定稿): + +```json +{ + "code": 589568, + "message": "整团核单当前状态不允许该操作:以下订单未完成核单:HL20261001001、HL20261001003" +} +``` + +对照:`GET …/settlement/panel` 此时返回 `"readyToAllocate": false`、`"blockingOrderIds": ["1934567890123450001", "1934567890123450003"]`。**订正前**:同样的请求返回 200 确认成功(服务端不拦);**订正后**:服务端硬拦,前端置灰只是体验层,不再是唯一防线。 + +### 8.2 边界:明细行金额为负,抛 589753(不再是 400) + +请求: + + PUT /v3/admin/order/group-batch/1934567890123456789/settlement/hotels + Content-Type: application/json + +```json +{ + "expectedVersion": 3, + "lines": [ + { + "lineId": null, + "allocMode": "SHARED", + "allocRule": "PER_HEAD_AVG", + "actualAmount": -100.00, + "hotelName": "图嘎营地", + "roomCount": 5, + "unitPrice": 380.00 + } + ] +} +``` + +响应: + +```json +{ + "code": 589753, + "message": "核单拆账入参非法:actualAmount 不能为负(-100.00)" +} +``` + +**订正前**:同样请求由 Bean Validation 拦截,返回 HTTP 400 / 通用参数校验错误(不在核单域错误码体系内);**订正后**:统一走契约错误码 589753,message 带字段名与具体值。splits[].amount / splits[].ratio / dailyPrice / perDayFee 等其余 §4 列出的字段为负时同样抛 589753。 + +### 8.3 业务失败:expectedVersion 过期(589573,既有行为不变,供对照) + +请求: + + POST /v3/admin/order/group-batch/1934567890123456789/settlement/panel/confirm + +```json +{ "expectedVersion": 3 } +``` + +(库中当前 version 已为 5) + +响应: + +```json +{ + "code": 589573, + "message": "整团核单数据已被他人修改(当前版本 5,提交版本 3),请刷新后重试" +} +``` + +处理方式不变:重新 GET panel 读回全量(含新 version)再重试,不能本地 version+1。 + +## 9. 业务边界 + +**适用**: +- 团期核单确认(panel/confirm)的服务端兜底校验——所有在团子订单第 1 层核单定稿后才允许确认。 +- 明细行金额录入的合法性校验——全部金额/单价/数量/比例字段不允许为负。 + +**不适用**: +- 逐子订单第 1 层核单本身的确认流程(那是子订单核单域的事,本门禁只消费其结果)。 +- 确认后修改:CONFIRMED 不可逆(与 04_8714 一致)。 + +**特殊边界**: +- **置灰 ≠ 防线**:panel `blockingOrderIds` / `readyToAllocate` 仍正常透出,前端应继续据此置灰确认按钮提升体验;但服务端现在会真拦,前端**必须**处理 589568 回执(toast message + 刷新 panel),不能假设按钮置灰后用户永远触发不到。 +- **零值金额判断**:前端若有 `amount === "0"` 之类的字符串等值判断,需改为数值比较或兼容 `"0.00"`。 + +## 10. 修改前后对比 + +### 行为级对比 + +| 行为 | 原来(04_8714 描述 / 订正前实现) | 现在(#8784 订正后) | +|------|-----------------------------------|----------------------| +| confirm 时 blocking 非空 | 服务端**不校验**,200 放行确认成功;blockingOrderIds 只在 panel 透出供前端置灰 | 服务端**硬拦**,抛 589568,message 带未定稿订单号清单(「、」分隔) | +| 行内金额/单价/数量为负 | Bean Validation `@DecimalMin` 拦截,HTTP **400** 通用参数错误 | Service 层 resolveSplits 入口校验,契约错误码 **589753**,message 带字段定位 | +| 空集合金额序列化 | 部分路径输出 `"0"`(无小数位),与 tab 两位小数口径不一致 | 统一 `"0.00"`(两位小数) | + +### 字段级对比 + +无字段增删 / 改名 / 类型变化;仅 §4 列出字段的**校验位置与错误码**变化(@DecimalMin 注解删除,改 Service 校验)。 + +## 11. 影响评估 / 回滚 + +- **破坏兼容**:轻微。接口签名零变化;行为变化三处——① confirm 新增拒绝场景(原先能确认成功的请求现在被拒,属安全收紧,前端需新增 589568 处理分支);② 负金额错误码 400 → 589753(前端若按 400 特判需改判 589753);③ 零值金额 `"0"` → `"0.00"`(前端若有字符串等值判断需放宽)。 +- **前端必须同步上线**:否(后端已合 dev-v3 未部署测试服;前端适配可在联调窗口内完成)。前端原有「blocking 置灰」逻辑**保留**,只是不再唯一依赖它。 +- **workaround 清理点**:若前端此前因服务端不拦而自行做了 confirm 前的 blocking 预校验弹窗,可保留(体验层);若有对负金额 400 的特判文案,改挂 589753。 +- **回滚方案**:后端回滚 = revert PR #8784 即可(纯代码改动,无 DDL / 无数据迁移)。 + +## 12. 注意事项 + +1. **本文优先于 04_8714**:两份 changelog 对 confirm 门禁 / 负金额错误码 / 零值金额格式的描述不一致时,以本文为准。04_8714 的其余内容(接口清单 / 出入参结构 / 枚举字典 / 拆账规则 / CAS 语义)继续有效。 +2. **589568 message 可直接展示**:未定稿订单号清单已内嵌 message(「、」分隔),前端无需自行查 blockingOrderIds 拼装;但建议同时刷新 panel 让置灰状态与服务端一致。 +3. **负金额提示走 589753**:不要再期待 400;message 带字段名与非法值,可直接 toast。 +4. **金额仍按字符串处理**:`"0.00"` 统一后前端解析逻辑应天然兼容,只需清理对 `"0"` 的硬编码等值判断。 + +## 13. 关联 / 联系人 + +- Issue:https://git.1814.love/wx/HL/issues/8783 +- PR:https://git.1814.love/wx/HL/pulls/8784 +- Commit(squash merge):https://git.1814.love/wx/HL/commit/d414d7d00705 +- 被订正的原 changelog:`changelogs-v2/2026-10/04_8714_团期核单重做8类tab明细+公摊拆账-修改接口-管理后台.md`(同仓同目录) +- 后端负责人:@yst