增加企微旧包刷新护栏(07-27-yang-jun-reimbursement-readiness)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-07-31 17:40:25 +08:00
父节点 39c3e808a3
当前提交 e123d50f86
共修改 9 个文件,包含 308 行新增和 1 行删除
@@ -1764,6 +1764,90 @@ onNeedRefresh() {
}
```
## Scenario: Live WebView Build Freshness
### 1. Scope / Trigger
- Trigger: a production browser, PWA or Enterprise WeChat WebView can remain
alive across deployment and continue running an old SPA bundle even though
public `index.html`, service worker and immutable chunks are current.
- A public bundle containing new copy proves deployment, not that every live
WebView has loaded that bundle.
### 2. Signatures
- Client build: `document.documentElement.dataset.buildSha`, sourced from
`VITE_BUILD_SHA` and normalized to 12 hexadecimal characters.
- Server check: `GET /ready` -> `{ data: { ok: true, buildSha: string } }`.
- Resume event: document `visibilitychange` with `visibilityState="visible"`.
- Prompt event: `hyb-oa:pwa-update-ready` with `{ applyUpdate }`.
### 3. Contracts
- Check only when a loaded page returns to the foreground; do not add a timer
or poll continuously.
- Request `/ready` with `cache: "no-store"`; compare only valid hexadecimal
build identifiers and never display the identifiers to ordinary users.
- A mismatch dispatches the existing update-ready event once per page
lifecycle. The logged-in shell shows `发现新版本` with an explicit
`点击刷新`; it must not auto-reload an approval, form or auth input.
- The refresh handler clears known PWA runtime/precache caches and then reloads
the current URL. Existing auth-page update protection remains in force.
- This monitor does not replace deployment build checks or real-device
verification; it closes the live-client freshness gap after deployment.
- The first deployment of this monitor cannot notify a WebView that is still
executing an older bundle without the monitor. That rollout still requires
an explicit manual refresh instruction; later deployments gain the guard.
### 4. Validation & Error Matrix
- Client and server builds match -> no prompt.
- Valid builds differ -> show one prompt.
- `/ready` is unavailable, non-2xx, malformed or missing `buildSha` -> stay
silent and preserve the current page.
- Client build is `development`, `unknown`, empty or malformed -> stay silent.
- Repeated foreground events after a mismatch -> no duplicate request or
prompt in the same page lifecycle.
### 5. Good/Base/Bad Cases
- Good: a manager returns to an old approval WebView, sees one refresh prompt,
chooses refresh and then receives the deployed approval-detail bundle.
- Base: the deployed and client builds match, so returning to the app adds no
UI or network loop beyond the single readiness check.
- Bad: infer that a missing button is a permission problem when adjacent UI
from the same unconditional JSX block proves an old bundle is running.
- Bad: force-refresh every foreground event or build a general update center.
### 6. Tests Required
- Focused Playwright with synthetic builds covers mismatch, match and failed
`/ready` responses.
- Mismatch coverage asserts one prompt, no duplicate check in the same page,
user-triggered reload, a 44px refresh target and no horizontal overflow at
375px portrait and phone landscape widths.
- Run the full affected login/portal UX file, frontend typecheck and build.
- Production acceptance still requires the representative Enterprise WeChat
WebView to refresh and visibly receive the new business entry.
### 7. Wrong vs Correct
#### Wrong
```ts
setInterval(() => window.location.reload(), 60_000);
```
#### Correct
```ts
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") {
void checkBuildFreshness();
}
});
```
## Scenario: Auth Page Layout And Login Entry Hierarchy
### 1. Scope / Trigger