8.2 KiB
CODEBUDDY.md
给 AI 协作者的项目约定。人看的话直接读 README.md(前端)与 server/README.md(后端)。
项目速览
前后端同仓、独立构建、独立版本,唯一契约是 HTTP 接口(前端不读后端源码,后端不管前端怎么调)。
| 目录 | 是什么 | 端口 / 入口 |
|---|---|---|
web/ |
前端(Vue 3 + Vite + Pinia),唯一有 package.json 的包 |
dev server 5090,/api 代理到 127.0.0.1:8088 |
server/ |
后端(Spring Boot 4.1 + Java),多机构 SQL Server | 8088,context-path /api |
sql/ |
工作流模块 SQL 脚本与演示数据 | 执行顺序见 sql/README.md |
根目录没有 package.json,前端命令一律在 web/ 下跑。
启动
# 1) 组件库必须先有产物(@g3soft/ui 的 exports 指向 dist)
# 需要 g3soft-libs 与 g3soft-erp 平级
cd ../g3soft-libs && pnpm --filter @g3soft/tokens build && pnpm --filter @g3soft/ui build
# 2) 前端
cd ../g3soft-erp/web && pnpm dev # http://localhost:5090
# 3) 后端(连只支持 TLS 1.0 的老 SQL Server 时必须这么起,见 server/config/dbconfigs/README.md)
cd ../g3soft-erp/server
mvn "-Dspring-boot.run.jvmArguments=-Djava.security.properties=./config/legacy-tls.security" spring-boot:run
改完组件库要重启 dev server(构建会清空 dist/,dev server 会崩或白屏),详见根 README.md。
开发期免登录(省掉每次填账号 + 拖滑块)
web/.env.development 里配好了测试账号,pnpm dev 打开页面已是登录态:
| 变量 | 值 | 说明 |
|---|---|---|
VITE_DEV_AUTO_LOGIN |
1 |
设为 0 关闭(例如要调登录页本身) |
VITE_DEV_ORG |
G3HD |
机构码,需对应 server/config/dbconfigs/{机构码}.properties |
VITE_DEV_ACCOUNT |
demo_zhangwei |
实测可登录,但只有审批中心权限(见下) |
VITE_DEV_PASSWORD |
demo_zhangwei |
演示账号密码=账号名 |
实现见 web/src/dev/auto-login.ts:调 POST /api/auth/login 拿 token 写进 store,
只在 import.meta.env.DEV 下生效,生产构建里被 tree-shake。
滑块验证码是纯前端校验(不发请求),所以走接口登录不需要它。
覆盖面:入口是 /login 时不自动登录;已有 token 时不重复登录。
个人覆盖用 web/.env.development.local(已被 gitignore,优先级更高)。
命令行取 token(例如要用 curl 直接打业务接口):
node scripts/dev-login.mjs --token-only # 只输出 token 一行
node scripts/dev-login.mjs # 人看的输出 + curl 示例
E2E(Playwright)
cd web
pnpm test:e2e # 全量,headless
pnpm test:e2e:ui # 带 UI 的调试模式
前置条件:后端已在 8088 起来(前端 dev server 由配置里的 webServer 负责,已经在跑就复用)。
后端没起会在 globalSetup 阶段直接报「连不上后端」,不会跑到用例里。
与 Vitest 的分工:pnpm test 跑 tests/**(happy-dom 单元测试),pnpm test:e2e 跑 e2e/**(真浏览器 + 真后端)。
单元测试放哪儿:统一放 web/tests/,目录镜像 src/ 结构
(src/components/biz-tree/BizTree.vue → tests/components/biz-tree/BizTree.spec.ts)。
不要贴源放进组件目录;vitest.config.ts 的 include 只收 tests/**,src 下误放的 spec 不会被执行。
测试里引用被测文件一律用 @/ 别名(如 @/components/biz-tree/BizTree.vue),别写相对路径。
playwright.config.ts 的 testDir 必须限定 ./e2e,否则 Playwright 会把 vitest 的用例也收进来。
免登录是怎么做的
e2e/support/global-setup.ts:整轮测试只登录一次,调POST /api/auth/login, 会话落到e2e/.auth/session.json(已 gitignore,里面有可用 token)。e2e/support/fixtures.ts:覆盖内置contextfixture,用addInitScript在应用脚本 执行之前把sessionStorage['g3erp:login']种好,于是守卫直接放行,用例一进站就是登录态。
为什么不是 storageState:token 存在 sessionStorage,而 Playwright 的 storageState
只序列化 cookie + localStorage,种不进去。addInitScript 是唯一可靠的办法。
为什么必须只登录一次:后端 ActiveSessionRegistry 按 (机构码, 账号) 只保留最新会话,
重复登录会互踢(业务码 40101)。并发 worker 各登录一次就会互相把对方踢下线。
所以:不要在用例里提交登录表单。要测登录流程本身,得用一个专用账号, 并把
VITE_DEV_AUTO_LOGIN设为0(否则应用自己会先把你登录了)。
账号沿用 web/.env.development 的 VITE_DEV_*,可用 E2E_ORG / E2E_ACCOUNT / E2E_PASSWORD
覆盖;E2E_BASE_URL 换前端地址,E2E_API_TARGET 换后端地址,E2E_CHANNEL=msedge|chrome
改用系统已装的浏览器(Chromium 下载不便时)。
写用例时注意
- 落点别选
/dashboard:它没有任何权限点(sql/20_registration/14_dashboard_menu.sql明确「不建权限点」),只有超管可见;默认账号demo_zhangwei进它会被守卫送到/403。e2e/smoke.spec.ts用/approval/todo,演示账号和超管都能进。 - 断言用
toHaveURL(/\/approval\/todo$/)这种正向断言,别用not.toHaveURL(/\/login/):goto之后 URL 立刻就是目标值,否定断言会在重定向发生之前就通过,等于什么都没断言。 - dev server 是按需转译的,某页第一次访问要现编依赖图,实测 5-11s。 配置里已经把 expect 超时放宽到 15s —— 别在用例里写死更短的值。
测试账号
| 账号 | 密码 | 权限 | 用途 |
|---|---|---|---|
demo_zhangwei / demo_liqiang / demo_wangfang / demo_liumin |
同账号名 | 只授了 menu.approval% 与 action.v_b_othercompany.%(sql/90_mock/02_*.sql:186) |
已实测可登录;侧栏只有「审批中心」,其他页面会被守卫拦到 /403 |
g3soft |
未知 | 前后端唯一超管,免查 s_power(permission.ts:61、SqlPermissionService.ADMIN_ACCOUNT) |
测全部页面得用它 |
机构码统一 G3HD。
g3soft 的密码待确认:2026-10-14 实测 G3HD/g3soft/空密码 返回 code=401
(AuthServiceTests 里「超管空密码」是纯 mock,不代表 G3HD 库的真实数据)。
拿到真实密码后填进 web/.env.development 的 VITE_DEV_PASSWORD 即可——
一次配置,不必每次查库。另外 g3soft 在 b_user 里是否存在也需确认:
本仓库的 sql/ 没有任何脚本创建它(只建 demo_*),假定它来自 G3HD 核心库。
账号密码不用去查库。库里没有的账号靠 sql/ 下的脚本建(执行顺序见 sql/README.md);
要给 demo_* 放开更多菜单,用 sql/README.md 里「可选:给普通账号授权」那段。
踩坑备忘
- 同一账号重复登录会互踢:每次登录都刷新服务端 session(
ActiveSessionRegistry.activate), 上一个会话的下一次请求会拿到业务码40101并被踢回登录页。所以别和正在用g3soft的人共用一个账号—— 否则双方会在「登录 → 被踢 → 重新登录」之间来回。需要并行时用.env.development.local给 AI 换一个账号(demo_*权限窄,测非审批页要另开权限)。 - 鉴权失败是 HTTP 200 +
body.code=401,不是 HTTP 401,判状态码会漏。 - 菜单是动态的(读
s_menu表):新增页面必须同时加前端静态路由 +s_menu记录, 缺一个就表现为「点菜单进 404」。而 Vite 对任意路径都返回 HTTP 200,网络面板里看不出 404, 判据是路由是否匹配。 - 权限是白名单语义:
s_power没授权 → 侧栏不显示该菜单 + 守卫拦到/403。 - token 存
sessionStorage(keyg3erp:login,pinia-plugin-persistedstate 写的), 所以浏览器每个新会话/新 context 都是未登录态 —— 这正是自动登录要解决的问题。 docs/目录当前不存在(提交15e0ae5b删掉了)。文档写进README.md/server/README.md或本文件,别再新增指向docs/的链接。