Backend: - permission_handler: 完善权限 CRUD 接口(列表/创建/更新/删除) - auth_handler: 修复认证处理逻辑 - router: 新增权限管理路由 - handler_test: 新增权限 handler 测试覆盖 Frontend: - permissions.ts/test.ts: 权限服务层完整实现 - profile/settings/service_tests: 服务适配器修正 - client.ts: HTTP 客户端健壮性增强 - vite.config.js: 构建配置优化 - E2E 脚本: run-playwright-cdp-e2e 大幅增强(权限流程覆盖) Docs: - REAL_PROJECT_STATUS: 状态更新 - PRODUCTION_CHECKLIST/QUALITY_STANDARD/TECHNICAL_GUIDE/PROJECT_EXPERIENCE_SUMMARY: 团队规范完善 - plans/2026-04-23: 权限浏览器 CRUD 设计方案 验证: go build 0错误
12 KiB
12 KiB
技术指南
更新时间:2026-03-25
本文件不再承载泛化培训内容,改为当前项目的技术入口索引。
1. 先读什么
建议阅读顺序:
README.mddocs/status/REAL_PROJECT_STATUS.mddocs/team/QUALITY_STANDARD.mddocs/team/PRODUCTION_CHECKLIST.mddocs/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. 常用命令
后端
go test ./... -count=1
go vet ./...
go build ./cmd/server
前端
cd frontend/admin
npm.cmd run lint
npm.cmd run build
真实浏览器
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. 改代码前的阅读顺序
开始前按以下顺序阅读:
docs/status/REAL_PROJECT_STATUS.mddocs/code-review/FULL_CODE_REVIEW_REPORT_2026-04-17.mddocs/team/QUALITY_STANDARD.mddocs/team/PROJECT_EXPERIENCE_SUMMARY.md
用途:
REAL_PROJECT_STATUS告诉你当前已经验证过的工作区真相。FULL_CODE_REVIEW_REPORT告诉你已经复核过的风险清单和任务分级。QUALITY_STANDARD告诉你当前必须遵守的工程约束。PROJECT_EXPERIENCE_SUMMARY告诉你哪些失败模式已经真实消耗过项目时间。
2. 先执行的新鲜命令
在做出任何“当前状态”判断前,先执行:
go build ./cmd/server
go vet ./...
go test ./... -count=1
cd frontend/admin
npm.cmd run lint
npm.cmd run build
如果本轮工作涉及认证、会话、路由守卫、导航、弹窗防线或用户主流程,还要执行:
cd frontend/admin
npm.cmd run e2e:full:win
3. 当前发布阻塞级关注点
在一般优化之前,优先处理这些区域:
internal/api/handler/user_handler.goUpdateUserauthorization boundaryinternal/service/auth.gopassword login MFA/device-trust enforcementinternal/service/auth.gorefresh-token revocation persistence failure handlinginternal/api/middleware/cors.gounsafe default CORS behaviorinternal/repository/user.gocursor/sort mismatch inListCursorinternal/service/password_reset.gosingle-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
DevicesPagemust keep the request cursor separate from the responsenext_cursor; otherwise the initial load can auto-chain into extra/admin/devicesrequests 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/settingsdirectdata. - 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-bootstrapmust 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.
- Check whether the failing page consumes an API whose response envelope or field names changed.
- Check whether the page state machine, pagination flow, or derived state issued unexpected follow-up requests.
- Check whether the failing assertion uses a broad text locator where route, heading, role, or labeled-control matching would be more precise.
- 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/capabilitiesreturnedpassword_reset=falseeven whenpasswordResetHandlerwas mounted and the reset routes were live.
Minimal Fix
AuthHandlernow carries the password-reset capability bit and fillscaps.PasswordResetinGetAuthCapabilities().- Router assembly now synchronizes that bit from the same
passwordResetHandler != nilcondition 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-passwordemits a real SMTP-captured reset link/api/v1/auth/password/validateand/api/v1/auth/reset-passwordcomplete 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, andcd frontend/admin && npm.cmd run buildsuccessfully.
Recovery Notes That Matter
- The permissions frontend adapter must accept raw numeric backend
typevalues, 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=0and 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/permissionscall. 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 nativecan fail the supported build path if project root resolution is implicit. The stable fix is an explicitrootinvite.config.js.
Boundary
- This snapshot proves browser-level real E2E closure with
20supported 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 backendPUT /users/:id/passwordhandler bindsold_passwordandnew_passwordonly.
Minimal Fix
frontend/admin/src/services/profile.tsnow maps the UI request to the real backend payload shape before calling the shared HTTP client.frontend/admin/scripts/run-playwright-cdp-e2e.mjsnow 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:winis green with20scenarios, includingprofile-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.