# workbench **Repository Path**: guocc1982/workbench ## Basic Information - **Project Name**: workbench - **Description**: No description available - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-09 - **Last Updated**: 2026-09-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Workbench — 集团私有化多租户低代码页面工作台 可视化的页面动态配置工作台:设计器编排页面区块(文本 / 指标卡 / 图表 / 表格 / 查询表单),对接外部数据源(HTTP/GraphQL/数据库直连/文件/消息流),面向**大型集团私有化部署**(多租户数据隔离、组织/SSO 对接、分级授权)。 - 单体 monorepo:后端模块 `backend/`(Spring Boot 4.1 + Java 17)、前端模块 `frontend/`(Vue 3 + Vite 8 + Element Plus + ECharts)。 - 认证:JWT(OAuth2 resource server)——**生产无登录页**,token 由集团统一身份 / 网关注入;本分支另含一个**默认关闭**的临时 dev 登录页(`/login`,仅本地调试用,**交付前移除**,见 `docs/security-note.md`)。本地调试也可用 `VITE_DEV_BEARER_TOKEN` 由 vite dev 代理注入调试 token(见下)。 - 数据库:**本地默认、全部后端测试与生产/容器统一为 MySQL 8**(H2 与 SQLite 均已移除);schema 由 **Flyway** 版本化管理(`V1__baseline.sql`),`ddl-auto=none`。 ## 仓库结构 ``` backend/ Spring Boot 4.1 后端(多租户/RBAC/数据代理/监控/日志/审计) frontend/ Vue 3 前端(页面渲染/设计器/外壳(主题·登录态)/国际化 zh-en) docker-compose.yml 容器化一键部署(mysql8 + backend + frontend,见 docs/ops/deployment.md) .env.example 部署配置模板(密钥占位;.env 不入库) docs/ superpowers/specs/ 各特性设计文档(日期-主题-design.md) superpowers/plans/ 各特性实现计划(日期-主题.md) ops/ 运维手册:db-migration / monitoring / logging / deployment security-note.md 安全说明(凭据加密、SSRF 防护与剩余纵深防御) ``` ## 快速开始(本地开发) 前置:JDK 17、Node 22+、Docker。**本地默认数据源与全部后端测试都依赖一个可用的 MySQL 实例**(H2 已移除,无内存库兜底)。 ```powershell # 1) 本地 MySQL(8.4,宿主机端口 3307;库 workbench / 用户 wb) # 容器名统一用 wb-mysql-dev(= scripts\dev.ps1 的 -Container 默认值;容器名不同时用 -Container '<名字>' 覆盖) docker run -d --name wb-mysql-dev -e MYSQL_ROOT_PASSWORD=<强口令> -e MYSQL_DATABASE=workbench ` -e MYSQL_USER=wb -e MYSQL_PASSWORD=<强口令> -p 3307:3306 -v wb-mysql-dev-data:/var/lib/mysql mysql:8.4 # 1.1) 一次性:建**独立测试库**并授权 ——「后端测试」也跑在 MySQL 上,且测试库不得用开发库 workbench。 # 必须用**特权账号**执行:wb 用户没有建库权限,实测会报 # "ERROR 1044 (42000): Access denied for user 'wb'@'%' to database 'workbench_test'"。 docker exec -i wb-mysql-dev mysql -uroot -p<强口令> -e "CREATE DATABASE IF NOT EXISTS workbench_test CHARACTER SET utf8mb4; GRANT ALL ON workbench_test.* TO 'wb'@'%'; FLUSH PRIVILEGES;" # 注:正因为 wb 无建库权限,JDBC URL 里**不要**写 createDatabaseIfNotExist=true —— 该参数在此前提下 # 不成立(同样会因权限被拒),测试库只能由特权账号按上面两条语句预先建好。 # 2) 后端(默认连 jdbc:mysql://127.0.0.1:3307/workbench;口令经环境变量注入,勿写进仓库) cd backend $env:SPRING_DATASOURCE_USERNAME = 'wb' # 已配为用户 wb;默认值即 wb,显式设置更清晰 $env:SPRING_DATASOURCE_PASSWORD = '<强口令>' # **必填**:application.yml 默认留空,须由环境变量提供 .\mvnw.cmd spring-boot:run # http://localhost:8080 (actuator/health 可探活) # 也可一键起本地环境(自动拉起 MySQL 容器 + 取口令 + 起后端;在仓库根执行,需先 cd backend; .\mvnw.cmd -DskipTests package 出 jar): # .\scripts\dev.ps1 -Password '<强口令>' # 口令默认取该参数(其次环境变量 SPRING_DATASOURCE_PASSWORD); # # -Port(默认 8080)/ -Container(默认 wb-mysql-dev)可覆盖,加 -Frontend 同时起前端 # 环境变量 SPRING_DATASOURCE_URL / _USERNAME / _PASSWORD / _DRIVER_CLASS_NAME 会自动覆盖 # application.yml 的 spring.datasource.*(容器/PaaS 注入同一套变量即可,无需改配置)。 # 要走临时 dev 登录页须带 dev profile(等价于 --spring.profiles.active=dev): # .\mvnw.cmd spring-boot:run "-Dspring-boot.run.profiles=dev" # 3) 前端 cd frontend; npm install npm run dev # http://localhost:5173(/api 自动代理) # 取会话两种方式,二选一: # A) 用临时 dev 登录页(本地调试推荐):浏览器打开 http://localhost:5173/login,填租户 ID + 用户名即可 # —— 后端须带 dev profile 启动(见上面 2))。⚠️ 用登录页时**不要设** VITE_DEV_BEARER_TOKEN: # vite dev 代理会为该变量注入/覆盖 Authorization 头,与登录页叠加会出现「登录成功但请求仍用旧身份」的误导现象。 # B) 或注入调试 token(模拟网关;token 由后端密钥签发,签发脚本见「本地演示数据」小节末尾) $env:VITE_DEV_BEARER_TOKEN = '' npm run dev # /api 代理自动附加 Authorization # 4) 后端测试(同样需要第 1/1.1 步的 MySQL;测试 URL 指向独立测试库 workbench_test, # 见 backend/src/test/resources/application.properties,防污染开发库) # ⚠️ 跑测试前不要导出指向开发库 workbench 的 SPRING_DATASOURCE_URL:OS 环境变量优先级高于测试属性, # 会把测试直接写进开发库;一旦发生,DatasourceIsolationGuardTest 会立即变红。 # 用 clean test 而非 test:从旧提交切换过来时 target/classes 可能残留多份 V1, # Flyway 会报 "Found more than one migration with version 1"(clean 前先停掉跑在 8080 的 dev 服务: # 它从 target 下的 jar 启动会锁住该文件,导致 clean 直接 BUILD FAILURE)。 cd backend; .\mvnw.cmd clean test ``` > 裸开发库无租户/角色(设计如此:租户开通时播种 TENANT_ADMIN 全权限)。本地调试可先用下面的 > **本地演示数据**小节播种演示租户/组织/用户与角色-权限映射。 ### 本地演示数据(可选) 真实环境里**用户与组织来自组织同步(HR/OA)**,实例级授权由「分级授权」或**创建即授权**产生——**生产无登录页**,token 由集团统一身份/网关注入。裸开发库既无租户也无角色,需先播种演示数据,才能走通「新建 → 编辑/发布」闭环: > **本地怎么拿到会话**:本分支含一个**默认关闭**的临时 dev 登录页 `/login`(交付前移除,见 `docs/security-note.md`)——后端以 `--spring.profiles.active=dev` 启动后,在 `http://localhost:5173/login` 填租户 ID + 用户名即可登录(下面的 `u1` 播种完就能直接登)。**用登录页时不要设 `VITE_DEV_BEARER_TOKEN`**:vite dev 代理会为该变量注入/覆盖 `Authorization` 头,两者叠加会出现「登录成功但请求仍用旧身份」的误导现象。不用登录页时,按本小节末尾的签发脚本注入调试 token(二者择一)。 - **权限码目录** `sys_permission` 由后端启动时幂等播种(`PermissionBootstrap`),无需手工插入。 - **角色-权限映射** 正常由租户开通(`POST /api/tenants`)自动播种该租户的 `TENANT_ADMIN`(全权限码)与 `USER`;但该接口自身要求 `tenant:create` 权限码,而权限码来自租户内已存在的角色映射——空库存在先有鸡还是先有蛋,故首次引导用下面 SQL。 - **实例级授权**:创建页面/数据源后,创建者所属组织自动获得标准动作集(页面 `view,edit,publish,offline,delete`;数据源 `view,edit,delete`),因此同一 token 可继续查看/编辑/发布,且列表能看到刚建的资源;删除资源时其授权一并清理。 最小播种(租户 1 + 根组织 + 用户 `u1`(orgId=根组织) + `TENANT_ADMIN` 角色-权限映射): ```powershell # 用容器内的 mysql 客户端进库(口令即上面 MYSQL_PASSWORD) docker exec -it wb-mysql-dev mysql -uwb -p workbench # 也可用本机客户端(端口为宿主机映射端口): # mysql -h127.0.0.1 -P3307 -uwb -p workbench ``` ```sql -- 1) 租户 1 INSERT INTO sys_tenant (id, name, status, created_at, updated_at) VALUES (1, '演示租户', 'active', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); -- 2) 根组织(parent_id 为空 = 组织链根;授权沿 parent_id 向上继承) INSERT INTO sys_organization (tenant_id, name, level, parent_id) VALUES (1, '演示集团总部', 1, NULL); -- 3) 演示用户 u1,归属根组织(实例授权按「用户所属组织」粒度授予) INSERT INTO sys_user (tenant_id, username, display_name, org_id, enabled) SELECT 1, 'u1', '演示用户', o.id, TRUE FROM sys_organization o WHERE o.tenant_id = 1 AND o.parent_id IS NULL; -- 4) TENANT_ADMIN 角色 + 角色-权限映射(TENANT_ADMIN 默认拥有全部权限码, -- 故直接取启动时播种的 sys_permission 目录;等效于租户开通时的自动播种) INSERT INTO sys_role (tenant_id, code, name) VALUES (1, 'TENANT_ADMIN', '租户管理员'); INSERT INTO sys_role_permission (role_id, permission_code) SELECT r.id, p.code FROM sys_role r CROSS JOIN sys_permission p WHERE r.tenant_id = 1 AND r.code = 'TENANT_ADMIN'; ``` > 上面按「空库首次引导」编写:同一库重复执行会命中 `sys_tenant`/`sys_role`/`sys_user` 的唯一约束 > (MySQL 是持久库,重启不清空;要重新演练请先清理相关行,或用特权账号 `DROP DATABASE workbench` 后重建)。 播种后用同款调试 token 演练(`subject=u1`、`roles=[TENANT_ADMIN]`、`tenant_id=1`,签发脚本见下): 签发脚本(Node 22+,前置要求之一;HS256 密钥默认取 `application.yml` 的 `workbench.security.jwt-secret`, 生产/容器环境用环境变量 `WORKBENCH_SECURITY_JWT_SECRET` 覆盖)。存为**仓库根目录** `token.js`,`node token.js` 输出的串即调试 token (脚本用 CommonJS `require`,请勿放进 `frontend/`——该目录 `package.json` 为 ESM): ```javascript const crypto = require('node:crypto'); const secret = process.env.WORKBENCH_SECURITY_JWT_SECRET || '7f3a9c1e5b8d2f6a4c0e9d7b3f1a6c8e'; const b64 = (o) => Buffer.from(JSON.stringify(o)).toString('base64url'); const iat = Math.floor(Date.now() / 1000); const h = b64({ alg: 'HS256', typ: 'JWT' }); const p = b64({ sub: 'u1', roles: ['TENANT_ADMIN'], tenant_id: 1, iat, exp: iat + 3600 }); console.log(`${h}.${p}.` + crypto.createHmac('sha256', secret).update(`${h}.${p}`).digest('base64url')); ``` ```powershell $env:VITE_DEV_BEARER_TOKEN = '' # 前端「页面列表 → 新建」→ 立即能打开/编辑/发布刚建的页面(创建即授权),删除后其授权一并回收 ``` > **演示局限**:token 的 `subject` 在 `sys_user` 里没有对应行时(例如只按 `tenant_id` 合成的调试 token), > 系统无从判断创建者组织,**不会**产生授权(设计如此,不报错)——此时实例级接口(`GET /api/pages/{id}` 等)仍返回 > 403,列表也为空。要演练「新建 → 编辑/发布」,务必先播种用户行(上面的 `u1`)。 > **存量局限**:「创建即授权」只对**今后新建**的页面/数据源生效——修复前已存在的资源不做授权回填, > 非平台创建者访问这些历史资源仍返回 403(需要时按一次性回填脚本处理,见 `docs/ops/backlog.md`)。 测试与构建: ```powershell # 后端测试同样需要第 1/1.1 步的 MySQL 与 workbench_test(先设好 SPRING_DATASOURCE_PASSWORD; # 勿导出指向开发库的 SPRING_DATASOURCE_URL,理由与 clean 的必要性见「快速开始」第 4 步) cd backend; .\mvnw.cmd clean test # 后端全量(现 165) cd frontend; npm test -- --pool=threads # 前端全量(vitest 必须 threads 池) npm run build # vue-tsc + vite 产物 ``` ## 容器化部署与升级 一键部署、升级/回滚、备份命令见 **`docs/ops/deployment.md`**(含 2026-09-08 真机验证记录:镜像构建、MySQL8 真机联调、compose 全栈 healthy、Flyway 幂等)。 ## 协作约定(多人开发建议) - **分支模型**:功能在 `feat/` 分支开发(本仓库历史即此风格),完成并经评审后**合并回 `master`**;master 保持可发布。大改动建议在 Gitee 走 Pull Request + 评审。 - **Commit**:Conventional Commits(`feat:`/`fix:`/`docs:`/`test:`/`chore:`)+ 简洁中文描述(本仓库既有历史同款)。 - **测试门槛**:合入前后端 `.\mvnw.cmd clean test`、前端 `npm test -- --pool=threads` 与 `npm run build` 全绿;测试驱动(先红后绿)与逐任务评审为默认流程。 - **环境与密钥**:开发默认密钥见 `backend/src/main/resources/application.yml`(仅本地/联调);生产一律经 `.env`/环境变量注入且**必须替换**(`.env` 已 gitignore,模板 `.env.example` 入库);`docs/security-note.md` 为安全基线。 - **数据迁移**:新表/列/约束一律新增 Flyway 迁移 `V{n}__desc.sql`,**永不修改已发布迁移**(规范见 `docs/ops/db-migration.md`)。 ## 文档索引 - 特性设计与计划:`docs/superpowers/{specs,plans}/` - 运维手册:`docs/ops/db-migration.md`、`docs/ops/monitoring.md`、`docs/ops/logging.md`、`docs/ops/deployment.md` - 安全:`docs/security-note.md` - 后续增强/技术债:`docs/ops/backlog.md`(含优先级与来源)