Files
workspace/code/g3soft-erp/CODEBUDDY.md
T
2026-10-14 17:25:47 +08:00

8.2 KiB
Raw Blame History

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 的用例也收进来。

免登录是怎么做的

  1. e2e/support/global-setup.ts:整轮测试只登录一次,调 POST /api/auth/login, 会话落到 e2e/.auth/session.json(已 gitignore,里面有可用 token)。
  2. e2e/support/fixtures.ts:覆盖内置 context fixture,用 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(key g3erp:login,pinia-plugin-persistedstate 写的), 所以浏览器每个新会话/新 context 都是未登录态 —— 这正是自动登录要解决的问题。
  • docs/ 目录当前不存在(提交 15e0ae5b 删掉了)。文档写进 README.md / server/README.md 或本文件,别再新增指向 docs/ 的链接。