# 技术指南 更新时间:2026-03-25 本文件不再承载泛化培训内容,改为当前项目的技术入口索引。 ## 1. 先读什么 建议阅读顺序: 1. `README.md` 2. `docs/status/REAL_PROJECT_STATUS.md` 3. `docs/team/QUALITY_STANDARD.md` 4. `docs/team/PRODUCTION_CHECKLIST.md` 5. `docs/team/PROJECT_EXPERIENCE_SUMMARY.md` ## 2. 当前项目最重要的真实结论 - 主验收路径是 `cd frontend/admin && npm.cmd run e2e:full:win` - 当前闭环的是浏览器级真实 E2E,不是完整 OS 级自动化 - `smoke` 仅用于诊断,不是运行时依赖 - 非测试代码中的 `panic`、fake success、mock runtime provider 都应视为缺陷 ## 3. 常用命令 ### 后端 ```powershell go test ./... -count=1 go vet ./... go build ./cmd/server ``` ### 前端 ```powershell cd frontend/admin npm.cmd run lint npm.cmd run build ``` ### 真实浏览器 ```powershell cd frontend/admin npm.cmd run e2e:full:win ``` ## 4. 常见工程经验 - 如果结论依赖真实用户流程,就不要只跑单元测试。 - 如果终端出现乱码,不要把乱码文本继续复制进业务逻辑。 - 如果错误分级依赖字符串子串,后续大概率会回归;优先改成显式错误类型。 - 如果 service 依赖具体 repository 类型断言,后续替换实现或测试 mock 会变脆。 ## 5. 文档维护规则 - 状态变更:更新 `docs/status/REAL_PROJECT_STATUS.md` - 规则变更:更新 `docs/team/QUALITY_STANDARD.md` - 发布门槛变更:更新 `docs/team/PRODUCTION_CHECKLIST.md` - 阶段性经验:更新 `docs/team/PROJECT_EXPERIENCE_SUMMARY.md` ## 6. 2026-04-10 多轮 Review 实操指引 ### 6.1 如何判断“是否闭环” - 结论优先级:文档支持的主入口 > repo 内单步命令 > 局部 smoke、单个用例、`-short` 结果。 - 只要 `go test ./... -count=1` 仍被 `LL_001` 卡住,或 `npm.cmd run e2e:full:win` 仍未跑通,就不能把项目表述为“全量验证通过”。 - `go build ./cmd/server` 通过,只能证明 repo 内该命令通过;不能自动推出包装脚本里的 build 路径也稳定。 ### 6.2 如何审查 stub 转 live 的高风险改动 - 先看权限边界:调用者是否真的具备读取或修改目标资源的资格。 - 再看治理边界:是否存在 `self-action`、`last-admin`、越权枚举、越权提升等问题。 - 再看一致性:多步写操作是否在事务内;失败时是否有显式回滚。 - 最后看文档与测试:是否补了负向测试、边界测试、回滚测试,以及状态文档与规范文档。 ### 6.3 当前需要持续关注的热点 - `internal/service/scale_test.go`:`LL_001` 仍是全量 `go test ./... -count=1` 的门槛。 - `frontend/admin/scripts/run-playwright-auth-e2e.ps1`:需要优先保证文档支持的 `e2e:full:win` 入口自身稳定,而不是只验证子命令。 - `frontend/admin/src/components/common/ui-consistency.test.tsx`:原生弹窗噪声仍会污染测试结果,应继续清理。 - `internal/api/handler/user_handler.go` 与 `internal/service/user_service.go`:RBAC / 管理员治理逻辑需要持续按越权、事务、自删、最后管理员等维度审查。 ## 2026-04-18 优化修复入口 本附录是任何工程师或智能体在当前仓库状态下开启新一轮优化或修复批次时的强制入口。 ### 1. 改代码前的阅读顺序 开始前按以下顺序阅读: 1. `docs/status/REAL_PROJECT_STATUS.md` 2. `docs/code-review/FULL_CODE_REVIEW_REPORT_2026-04-17.md` 3. `docs/team/QUALITY_STANDARD.md` 4. `docs/team/PROJECT_EXPERIENCE_SUMMARY.md` 用途: - `REAL_PROJECT_STATUS` 告诉你当前已经验证过的工作区真相。 - `FULL_CODE_REVIEW_REPORT` 告诉你已经复核过的风险清单和任务分级。 - `QUALITY_STANDARD` 告诉你当前必须遵守的工程约束。 - `PROJECT_EXPERIENCE_SUMMARY` 告诉你哪些失败模式已经真实消耗过项目时间。 ### 2. 先执行的新鲜命令 在做出任何“当前状态”判断前,先执行: ```powershell go build ./cmd/server go vet ./... go test ./... -count=1 cd frontend/admin npm.cmd run lint npm.cmd run build ``` 如果本轮工作涉及认证、会话、路由守卫、导航、弹窗防线或用户主流程,还要执行: ```powershell cd frontend/admin npm.cmd run e2e:full:win ``` ### 3. 当前发布阻塞级关注点 在一般优化之前,优先处理这些区域: - `internal/api/handler/user_handler.go` `UpdateUser` authorization boundary - `internal/service/auth.go` password login MFA/device-trust enforcement - `internal/service/auth.go` refresh-token revocation persistence failure handling - `internal/api/middleware/cors.go` unsafe default CORS behavior - `internal/repository/user.go` cursor/sort mismatch in `ListCursor` - `internal/service/password_reset.go` single-use verification code consumption semantics ### 4. 修复批次工作规则 - 不要把历史绿灯当作当前证据。 - 不要在没有分别验证的情况下,把门禁刷新、安全修复和重构混在同一个“已完成”结论里。 - 修 bug 或安全问题时,没有对应回归测试,就不要把任务提升为“完成”。 - 不要让包装脚本掩盖项目正式支持主命令的失败。 ### 5. 文档更新规则 当一轮修复改变了真实结论时,必须在同一批次同步更新: - `docs/status/REAL_PROJECT_STATUS.md` - 规则变化时更新 `docs/team/QUALITY_STANDARD.md` - 产出可复用经验时更新 `docs/team/PROJECT_EXPERIENCE_SUMMARY.md` ## 0. 2026-04-23 Latest Technical Snapshot Use this section as the current workspace truth when older notes elsewhere in this file describe earlier failures. ### Main Acceptance Path - The supported browser-level gate remains `cd frontend/admin && npm.cmd run e2e:full:win`. - That gate was re-run green on 2026-04-23 after fixes in device pagination flow, backend-response envelope decoding, settings-service adapter alignment, and Playwright CDP selector and suite-retry stability. ### Recovery Notes That Matter - `DevicesPage` must keep the request cursor separate from the response `next_cursor`; otherwise the initial load can auto-chain into extra `/admin/devices` requests and trigger rate limiting. - Frontend services must decode backend envelopes by their actual fields and by the shared HTTP client contract. The recovered cases in this round were `list`, `deliveries`, `accounts`, and `/admin/settings` direct `data`. - Late-stage E2E scenarios are more stable when assertions target route, heading, and role-based locators instead of broad page text matches. - If suite retry reuses the same backend process, one-time preconditions such as `admin-bootstrap` must be refreshed from live backend capabilities before the next attempt starts. ### Boundary - This snapshot proves browser-level real E2E closure in the current workspace. - It does not by itself prove the full backend matrix, OS-level automation, or live third-party provider verification. ## 2026-04-23 Late-Suite E2E Triage Order Use this order before blaming the browser wrapper when `cd frontend/admin && npm.cmd run e2e:full:win` fails in later admin scenarios. 1. Check whether the failing page consumes an API whose response envelope or field names changed. 2. Check whether the page state machine, pagination flow, or derived state issued unexpected follow-up requests. 3. Check whether the failing assertion uses a broad text locator where route, heading, role, or labeled-control matching would be more precise. 4. Only after the first three checks stay clean, investigate CDP session lifecycle, page/context closure, or local browser startup instability. ## 2026-04-23 Password Reset And CDP Recovery Notes ### Root Cause - The password-reset browser gap came from a backend contract omission: `/api/v1/auth/capabilities` returned `password_reset=false` even when `passwordResetHandler` was mounted and the reset routes were live. ### Minimal Fix - `AuthHandler` now carries the password-reset capability bit and fills `caps.PasswordReset` in `GetAuthCapabilities()`. - Router assembly now synchronizes that bit from the same `passwordResetHandler != nil` condition that mounts the reset routes. ### Browser Flow Proof - The supported browser suite now proves the real password-reset chain end to end: - admin creates a real user - login surface exposes the forgot-password entry - `/api/v1/auth/forgot-password` emits a real SMTP-captured reset link - `/api/v1/auth/password/validate` and `/api/v1/auth/reset-password` complete through the browser - the user logs in with the new password ### Stability Rule - When headless-shell closes the last live target late in the suite, reconnect the CDP browser connection and reacquire the persistent page before declaring the whole run failed. ## 2026-04-23 Permissions CRUD And Full Matrix Technical Snapshot Use this section as the newest technical snapshot when earlier 2026-04-23 notes describe only the 19-scenario gate. ### Main Acceptance Path - The supported browser-level gate remains `cd frontend/admin && npm.cmd run e2e:full:win`. - That gate was re-run green on 2026-04-23 after adding `permissions-management-crud`, fixing permissions payload compatibility, fixing auth-header selection under concurrent refresh state, and stabilizing CDP observation for proxied permission calls. - The same branch state also re-ran `go test ./... -count=1`, `go vet ./...`, `go build ./cmd/server`, `cd frontend/admin && npm.cmd run test:run`, `cd frontend/admin && npm.cmd run lint`, and `cd frontend/admin && npm.cmd run build` successfully. ### Recovery Notes That Matter - The permissions frontend adapter must accept raw numeric backend `type` values, normalize them to the frontend string enum, and serialize writes back to the backend numeric form. - The permissions backend handler must continue accepting menu `type=0` and status payloads delivered as either numeric or string values, because real browser flows and clients can send both forms during incremental rollout. - A valid non-expired access token must still be attached to requests even when a different refresh flow is already in flight. Refresh state alone is not evidence that the current request should lose authentication. - In the permissions CRUD scenario, the page and backend were healthy even when Playwright CDP request and response observers missed the proxied `/api/v1/permissions` call. The reliable proof path was the in-page fetch diagnostic log plus the post-submit UI refresh. - Ant modal leave animations can keep intercepting clicks after the dialog is visually closing. Scenario code should wait for the modal to stop blocking interaction before the next action. - Vite 8 on Windows with `--configLoader native` can fail the supported build path if project root resolution is implicit. The stable fix is an explicit `root` in `vite.config.js`. ### Boundary - This snapshot proves browser-level real E2E closure with `20` supported scenarios in the current workspace. - It does not by itself prove OS-level automation, live third-party provider verification, or remote-repository publication status. ## 2026-04-24 Profile Security Contract Recovery ### Root Cause - The profile password form used the UI model (`current_password`, `confirm_password`) all the way through the service layer, but the real backend `PUT /users/:id/password` handler binds `old_password` and `new_password` only. ### Minimal Fix - `frontend/admin/src/services/profile.ts` now maps the UI request to the real backend payload shape before calling the shared HTTP client. - `frontend/admin/scripts/run-playwright-cdp-e2e.mjs` now couples password and TOTP fetch waits to the submit action that triggers them, so a later locator failure does not leave an orphaned background waiter that hides the real error. ### Browser Flow Proof - The targeted profile page and service regression set is green. - The supported browser-level gate `cd frontend/admin && npm.cmd run e2e:full:win` is green with `20` scenarios, including `profile-and-security`. ### Stability Rule - When a scenario uses asynchronous fetch diagnostics for proof, create the waiter in the same control flow as the triggering action and tear it down implicitly with that action path. A background waiter that survives a failed action is a runner bug because it can replace the primary failure with misleading page-closed noise. ## 2026-04-24 Scenario-Isolated Browser Gate Notes ### Main Acceptance Path - The supported browser-level gate remains `cd frontend/admin && npm.cmd run e2e:full:win`. - On 2026-04-24 that gate was re-run green after changing `frontend/admin/scripts/run-playwright-auth-e2e.ps1` to keep one backend and one frontend session alive while launching a fresh browser process for each selected Playwright scenario. - `frontend/admin/scripts/run-playwright-cdp-e2e.mjs` now supports a lightweight `E2E_LIST_SCENARIOS=1` mode so the wrapper and the runner derive the scenario order from the same source of truth. ### Current Green Evidence - The current full-gate green evidence is `21` isolated scenario runs in one end-to-end environment: - `admin-bootstrap` - `public-registration` - `email-activation` - `password-reset` - `login-surface` - `auth-workflow` - `responsive-login` - `desktop-mobile-navigation` - `user-management-crud` - `user-management-batch` - `role-management-crud` - `permissions-management-crud` - `device-management` - `login-logs` - `operation-logs` - `webhook-management` - `import-export` - `profile-management` - `profile-and-security` - `settings` - `dashboard-stats` ### Operational Knobs - `E2E_SCENARIO_ISOLATION=0` keeps the legacy whole-suite browser mode available for diagnostics. - `E2E_SCENARIO_ATTEMPTS` overrides the per-scenario retry count; otherwise the wrapper falls back to `E2E_SUITE_ATTEMPTS`. ## 2026-04-24 Resource Ownership Authorization Notes ### Recommended Implementation Pattern - For owner-scoped resources, split authorization across two layers: - the handler extracts the current actor identity and admin bit from the authenticated request context; - the service loads the target resource and re-checks `owner-or-admin` before returning or mutating it. - This prevents future handlers, background callers, or admin-route reuse from silently bypassing ownership checks by passing a raw resource ID straight into repository operations. ### Minimum Regression Pattern - Add a targeted red-green regression set for each resource family that covers: - cross-user read forbidden; - cross-user update forbidden; - cross-user delete forbidden; - cross-user state toggle forbidden; - admin positive access when the contract allows it.