From 52aa4a0fd0c6c9ce65bc904ad3a443a638d1d3e3 Mon Sep 17 00:00:00 2001 From: jw Date: Mon, 28 Sep 2026 17:00:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=A2=E6=9C=9F=E8=AF=A6=E6=83=85=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E5=88=97=E8=A1=A8=E5=BC=B9=E3=80=8C=E5=8F=82=E6=95=B0?= =?UTF-8?q?=20groupBatchId=20=E6=A0=BC=E5=BC=8F=E9=94=99=E8=AF=AF=E3=80=8D?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E7=BC=BA=E9=99=B7=E4=BA=A4=E6=8E=A5=EF=BC=88?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E9=A2=84=E6=A3=80=E4=BB=A5=E7=A9=BA=E5=9B=A2?= =?UTF-8?q?=E6=9C=9F=20ID=20=E9=87=8D=E5=8F=91=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #8410 在团期详情新增的 confirm-check 监听器缺 isDetailActive 与空 id 守卫, 离开页面那一拍以空串重发,拼出 group-batch//confirm-check 落进团期详情接口报 400。 后端零改动;与 28_8478 交接清单不冲突。 Co-Authored-By: Claude Opus 5.5 --- ...格式错误确认预检以空团期ID重发-前端缺陷-管理后台.md | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 changelogs-v2/2026-09/28_frontend_团期详情返回列表弹groupBatchId格式错误确认预检以空团期ID重发-前端缺陷-管理后台.md diff --git a/changelogs-v2/2026-09/28_frontend_团期详情返回列表弹groupBatchId格式错误确认预检以空团期ID重发-前端缺陷-管理后台.md b/changelogs-v2/2026-09/28_frontend_团期详情返回列表弹groupBatchId格式错误确认预检以空团期ID重发-前端缺陷-管理后台.md new file mode 100644 index 00000000..8629b0e4 --- /dev/null +++ b/changelogs-v2/2026-09/28_frontend_团期详情返回列表弹groupBatchId格式错误确认预检以空团期ID重发-前端缺陷-管理后台.md @@ -0,0 +1,182 @@ +--- +schema: hl-changelog/v2 +ticket: "frontend" +title: "团期详情「配置」节点点浏览器返回,列表页弹「参数 groupBatchId 格式错误」:确认预检监听器缺空值守卫,离开页面瞬间以空团期 ID 重发 confirm-check" +consumer: admin +author: "jw(GIT)" +change_type: "前端缺陷" +backend_status: "not_required" +gateway_status: "not_required" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "2026-09-28 jw 在 TEST(web.test.1814.love)从团期列表「进入团期」打开 jw测试产品 第1期(状态:配置),点浏览器返回,回到列表页即弹 400「参数 groupBatchId 格式错误,请检查后重试」。根因在 hl-ui:#8410(c37fd8ad)在团期详情页新增的 watch([canConfirmBatch, groupBatchId]) 没有照同页其它监听器写 isDetailActive 与空 id 两道守卫;离开详情页时路由先变、onDeactivated 后执行,中间这一拍 groupBatchId 塌缩为空串而页面仍算激活,该监听器随即调 loadConfirmCheck(),拼出 GET /v3/admin/order/group-batch//confirm-check,连续斜杠被合并后落进团期详情接口的 groupBatchId 位,confirm-check 转 Long 失败返 400。后端接口与数据均正常,契约不变。修法:该监听器补两道守卫(守卫放在清空 confirmCheck 之前),loadConfirmCheck 入口补空 id 兜底;与 28_8478 前端交接清单(保留 loadConfirmCheck)不冲突,可同批改。" +updated_at: "2026-09-28" +base: dev-v3 +--- + +# 团期详情点返回弹「参数 groupBatchId 格式错误」:确认预检用空团期 ID 重发了一次 + +> **服务**: hl-order-service-v3(**后端零改动,接口与数据均正常**) +> **页面**: 管理后台 → 团期订单 → 进入团期(团期详情)→ 离开详情页(浏览器返回 / 点顶部页签 / 点侧边栏菜单) +> **接口**: `GET /v3/admin/order/group-batch/{groupBatchId}/confirm-check`(#8410 确认预检) +> **日期**: 2026-09-28 +> **影响范围**: 仅前端。状态为「配置」的团期,每次从详情页离开都会多弹一次 400 提示;详情页签未关(页面仍在缓存)时再回到同一团期,「确认」按钮的预检置灰会失效 + +--- + +## 一、现象 + +1. 团期列表点某个「配置」状态团期的「进入团期」。 +2. 点浏览器返回,回到 `/order-v2/batch`。 +3. 页面顶部弹出:`参数 groupBatchId 格式错误,请检查后重试`。 + +- 只有**状态为「配置」**的团期会出现;招募、确认、出行等状态的团期来回进出不报。 +- 不限于浏览器返回,从这类团期详情跳到任何**非团期详情**的页面都会弹(顶部页签、侧边栏菜单、点子订单进入订单详情同理)。直接从一个团期跳到另一个团期不报。 +- 除了这一次提示,页面数据和按钮当下都正常,没有产生任何写入。 + +复现团期:jw测试产品 第1期(jw测试1期),`/order-v2/batch/detail/2101506167098511362`,状态 `RESOURCE_PREPARING`(配置)。 + +--- + +## 二、根因(对 hl-ui `origin/v2.1` = `501c4258` 源码实读) + +**① 离开详情页的那一拍,groupBatchId 会变成空串** + +`src/views/order-v2/batch/detail/index.vue`: + +```js +// :513 +const routeBatchId = computed(() => decodeURIComponent(String(route.params.code || ''))) +// :519-529 keep-alive 失活冻结(896cbbec,09-22) +const isDetailActive = ref(true) +onActivated(() => { isDetailActive.value = true }) +onDeactivated(() => { isDetailActive.value = false }) +const groupBatchId = computed(() => + isDetailActive.value ? routeBatchId.value : frozenBatchId.value +) +``` + +`route` 是全局路由。点返回后路由立刻变成 `/order-v2/batch`,`route.params.code` 为 `undefined`,`routeBatchId` 变成空串 `''`。 +09-22 的失活冻结要等 `onDeactivated` 执行后才生效,而 `onDeactivated` 在本轮渲染**之后**才跑;监听器(默认 `flush: 'pre'`)在渲染**之前**就执行了。 +所以中间有一拍:**页面仍算激活(`isDetailActive === true`),`groupBatchId` 却已经是 `''`**。 + +**② 同页其它监听器都挡住了这一拍,#8410 新加的这个没挡** + +同页主拉取监听器(`:584-596`)和 Tab 监听器(`:597`)都有两道守卫: + +```js +if (!isDetailActive.value) return +if (!id) return +``` + +#8410(`c37fd8ad`,09-27)新增的确认预检监听器(`:922-929`)一道都没有: + +```js +watch( + [canConfirmBatch, groupBatchId], + ([can]) => { + confirmCheck.value = null + if (can) loadConfirmCheck() // 这一拍 groupBatchId === '' + }, + { immediate: true } +) +``` + +`canConfirmBatch`(`:896`)= `detail.batchStatus === 'RESOURCE_PREPARING'`。离开时 store 里的详情还是这个团,所以「配置」状态的团 `can` 为 `true`,于是 `loadConfirmCheck()`(`:905`)拿空串发请求。 +其它状态的团 `can` 为 `false`,不发请求,**这就是只有「配置」团才报的原因**。 + +**③ 空串拼进路径后,落到了另一个接口上** + +`src/api/orderV2GroupBatch.js:177`: + +```js +http.get(`${BASE}/${String(groupBatchId)}/confirm-check`, …) +// groupBatchId = '' → GET /v3/admin/order/group-batch//confirm-check +``` + +`//` 在到达接口匹配前被合并成 `/`,实际命中的是团期详情接口 `GET /v3/admin/order/group-batch/{groupBatchId}`,`confirm-check` 这个字面量被当成团期 ID 去转数字,于是返回 400「参数 groupBatchId 格式错误」。 + +**④ 次生问题:回到同一团期后确认按钮不再置灰(按源码推演,未在浏览器实测)** + +离开那一拍,监听器先执行了 `confirmCheck.value = null`,空 ID 请求失败后 `catch` 不回填,预检结果就一直是 `null`。 +详情页签没关时,页面还在缓存里;再次进入**同一个**团期,`groupBatchId` 前后相同,监听器不会再触发,预检不会重拉: + +- `confirmBtnDisabled`(`:931`)在 `confirmCheck == null` 时为 `false`,本该置灰的「确认」按钮变成可点; +- 缺项横幅(`showConfirmCheckBanner`,`:936`)也不再显示。 + +点「确认」前 `onConfirmBatch`(`:939`)还会再预检一次,所以**不会误提交**,只是提前置灰这层提示失效了。刷新页面可恢复。 + +**⑤ 这是 09-22 同类问题的第四个监听器** + +09-22 的 `22_frontend_团期详情页切走后重放请求groupBatchId塌缩-前端缺陷-管理后台.md` 已按这个机制修过三个子组件(`load()` 首行补空值守卫)。 +`GroupVehicleRequirementSection.vue:455-458` 的注释写得很清楚:「空串拼进 URL 会让路径段消失/后缀滑位(400 参数类型错误或 404),空 id 直接不发请求」。 +#8410 是在那之后新加的监听器,没有带上这道守卫。 + +--- + +## 三、后端与数据侧的核查结果(均正常,供排除用) + +2026-09-28 经 `web.test.1814.love`(与浏览器同一入口)用 admin token 实测: + +| 请求 | 结果 | +|---|---| +| `GET /v3/admin/order/group-batch/2101506167098511362`(团期详情) | `200`,`batchStatus=RESOURCE_PREPARING`、`batchLabel=1`、`batchName=jw测试1期` | +| `GET …/2101506167098511362/confirm-check`(真实 ID) | `200`,`ready=false`、`statusConfirmable=true`、`checkedHouseholdCount=12` | +| `GET …/group-batch//confirm-check`(空 ID,即前端实际拼出的地址) | `code 400`「参数 groupBatchId 格式错误,请检查后重试」 | +| `GET …/group-batch/confirm-check`(单斜杠对照) | 同上,与双斜杠返回逐字一致 | +| `GET …/group-batch/undefined/confirm-check`(对照) | 同上 | + +结论:带真实 ID 时接口正常;报错只由空 ID 触发,后端不需要改。 + +--- + +## 四、修复建议 + +**① 监听器补两道守卫**(`index.vue:922-929`),守卫要放在清空 `confirmCheck` **之前**: + +```js +watch( + [canConfirmBatch, groupBatchId], + ([can, id]) => { + // 离开页面那一拍:路由已变、onDeactivated 未执行,id 为空串;失活期间也不重拉 + if (!isDetailActive.value || !id) return + confirmCheck.value = null + if (can) loadConfirmCheck() + }, + { immediate: true } +) +``` + +守卫放在前面,离开时就不会把预检结果清空,第二节第④条的次生问题也一起消失。 + +**② `loadConfirmCheck()` 入口补兜底**(`index.vue:905`),挡住以后新增的其它调用点: + +```js +async function loadConfirmCheck() { + const batchId = groupBatchId.value + if (!batchId || confirmCheckLoading.value) return + const seq = ++confirmCheckSeq + … +} +``` + +`28_8478_团期详情新增分叉进度条并替代确认缺项横幅-修改接口-管理后台.md` 第四节第 6 条要在 `onReporterSaved` 里补调 `loadConfirmCheck()`,有这道兜底后同样安全。 + +**③ 建议补的回归**(`batch/detail/__tests__/index.spec.js` 的 `#8410` 用例组): + +- 详情 `batchStatus=RESOURCE_PREPARING`、页面激活时,把 `route.params.code` 改为 `undefined`(模拟返回列表)→ 断言 `getGroupBatchConfirmCheck` 没有以 `''` 被调用; +- 再触发 `onDeactivated` / `onActivated` 回到同一 ID → 断言 `confirmCheck` 仍是离开前的结果(`ready=false` 时按钮仍置灰)。 + +现有 `#8410` 用例只覆盖了进入配置节点和点「确认」前两个时机,没有模拟路由离开,所以测不出来。 + +--- + +## 五、业务边界 + +- 与 `28_8478` 前端交接清单**不冲突**:8478 要去掉页头缺项横幅,但明确**保留** `loadConfirmCheck` 预检(确认按钮置灰靠它)。本条改的正是它的触发监听器,可以同批改。 +- 修复后预检的调用时机不变:进入配置节点时调一次,点「确认」前再调一次;非配置节点不调。 +- `confirm-check` 的入参、出参、权限码(`group-batch:confirm`)都不变,见 `27_8410_团期确认只读预检-新增接口-管理后台.md`。 +- 本条不涉及后端改动,接口契约不变。