环境准备与启动
适用端:后端 + Web | 类型:操作
从零把后端 ainb 与 Web 前端 ainb-ui-admin-vue3 在本地跑起来,并给出「跑通了没有」的判定方法。本页所有命令与输出都在 Windows 10 + Git Bash 环境实测过。
移动端不在这页
移动端 ainb-ui-admin-uniapp 的启动路径(H5 :9000 / 微信小程序 / App)、env/.env* 叠加顺序与「只能 pnpm」的约束单独成页:移动端环境准备与启动。它共用本页第一步的同一个后端与同一个库,所以先把后端跑起来再去看那页。
前置阅读:手册导读(先搞清楚三个仓库分别是什么)。
它解决什么问题
本地开发链路只有四段,任何一段没对上都会表现为「前端白屏」或「接口 401」:
前端不走 Vite proxy
vite.config.ts 的 server.proxy 整段是注释掉的(注释原文:「server 端已经支持跨域」)。浏览器直连 http://localhost:48080/admin-api,base_url 由 .env.local 的 VITE_BASE_URL + VITE_API_URL 拼成。所以「后端换了端口」只需要改 .env.local,不要去 vite.config.ts 里找 proxy。
需要装什么
| 组件 | 版本要求 | 出处 / 实测 |
|---|---|---|
| JDK | 编译目标 17 | 根 pom.xml 的 <java.version>17</java.version>;实测用 BellSoft Liberica JDK 21 编译通过(maven.compiler.target=17) |
| Maven | 3.6+ | 实测 3.9.16。仓库没有 mvnw,只能用 PATH 上的 mvn |
| Node.js | ≥ 20.19.0 | 前端 package.json 的 engines.node |
| pnpm | ≥ 8.6.0 | 前端 package.json 的 engines.pnpm。只能用 pnpm,npm i / yarn 会破坏 lockfile |
| MySQL | 8.x | script/docker/docker-compose.yml 用 mysql:8;sql/tools/docker-compose.yaml 用 mysql:8.0.33 |
| Redis | 6+ | docker-compose.yml 用 redis:6-alpine;spring.cache.type: REDIS 是硬依赖,没有 Redis 起不来 |
不需要装的东西(容易白折腾):Nacos(全仓 pom 零命中)、XXL-Job(定时任务用的是 Quartz,且 local profile 直接 exclude 了 QuartzAutoConfiguration)、对象存储(infra 文件服务可用本地磁盘/数据库)、RocketMQ / Kafka(WebSocket 的 sender-type: local)。
第一步:准备数据库
库名是 ainb-professional-init(application-local.yaml:53 与 application-dev.yaml:53 里写死的),字符集 utf8mb4。库名带连字符,连接串里原样写着,命令行里要引号。
prod profile 还指着旧库名
application-prod.yaml:54,75 仍然是 ruoyi-vue-pro,没有跟着 local / dev 一起改(local 由 5b955cb8a7 改、dev 由 c3dccc1a82 改)。按 prod 配置部署时,那个库里没有 system_nav_node,「App菜单」页与 GET /system/nav-node/tree 会直接报表不存在。部署前先确认目标库名。
ainb/sql/mysql/ 下现在只有 4 个 .sql,外加一个 update/ 目录(实测 2026-10-25):
| 文件 | 大小 | 建表数 | 作用 |
|---|---|---|---|
ainb-professional-init.sql | 39 MB | 211 | 专业版全量快照,Source Schema: ainb-professional-init。含 agent_ 16 / ai_ 13 / bpm_ 12 / infra_ 24 / biz_ 9 / test_ 39 / act_ 39 / qrtz_ 11。唯一能把平台跑起来的一份 |
ainb-init.sql | 13 MB | 95 | 开源版全量快照,Source Schema: ainb-public-init。只有 system_ 34 / infra_ 7 / act_ 39 / flw_ 6 / sys_ 7 / graph_ 2 |
ruoyi-vue-pro.sql | 1.9 MB | 48 | 上游芋道基线(system_ 32 / infra_ 11 / 示例表 5),只作对照,不要导 |
quartz.sql | 42 KB | 11 | Quartz 表。专业版快照里已含 qrtz_ 11 张,不需要再导 |
update/*.sql | 6 个 | — | 增量迁移脚本,见下文导入顺序 |
两份 dump 在 nav-node 这件事上正好是反的
ainb-init.sql(开源版)有system_nav_node的建表与 9 行「App菜单」菜单/权限,但没有infra_form_design.app_nav_node_id列,且缺 64 张代码里@TableName声明了的表 —— 包括infra_codegen_table/infra_codegen_column,连代码生成器自己都不能用;AI、Agent、工作流、低代码全部起不来。ainb-professional-init.sql(专业版)平台表齐全,但完全没有 nav-node 相关的任何东西:既没有system_nav_node建表,也没有那 9 行菜单,只有一处手工加的app_nav_node_id列(:14270),而它自己的注释还引用着那张不存在的表。
所以「导开源版」和「导专业版」都不能一步到位。
导入(Git Bash,在 ainb/ 目录下执行):
mysql --default-character-set=utf8mb4 -h127.0.0.1 -uroot -p123456 \
-e "CREATE DATABASE IF NOT EXISTS \`ainb-professional-init\` DEFAULT CHARACTER SET utf8mb4"
mysql --default-character-set=utf8mb4 -h127.0.0.1 -uroot -p123456 ainb-professional-init \
< sql/mysql/ainb-professional-init.sql
# 再按【依赖顺序】补 nav-node 那 6 个增量脚本。
# 🔴 不能写 for f in sql/mysql/update/*.sql —— 通配按字母序展开,会让
# menu-manage-split-web-app.sql 跑在它自己声明的前置之前、
# nav-node-prune-undelivered.sql 跑在它要裁的 seed 之前。
U=sql/mysql/update
for f in $U/2026-10-24-system-nav-node.sql \
$U/2026-10-24-system-nav-node-seed.sql \
$U/2026-10-24-nav-node-prune-undelivered.sql \
$U/2026-10-24-system-nav-node-menu.sql \
$U/2026-10-24-menu-manage-split-web-app.sql \
$U/2026-10-24-form-design-app-nav.sql; do
mysql --default-character-set=utf8mb4 -h127.0.0.1 -uroot -p123456 ainb-professional-init < "$f"
done这 6 个脚本都写成幂等的(按语义键先删后插,或用 CREATE TABLE IF NOT EXISTS),可以重复执行。没有 Flyway / Liquibase / spring.sql.init,全靠人工跑一次。跑完后端要重启:GrantDecisionService#resourceChainCache 是进程级缓存且没有 TTL,sys_resource 与菜单的迁移不重启不生效。
已经有跑着的库就别重导
全量快照重导等于把演示数据、菜单授权全部打回快照状态。本手册的截图与示例数据都建立在现有库上,重导后需要重新跑 xpy-blog/tools/seed-*.mjs 系列夹具才能复现。
第二步:确认后端配置
配置文件在 enlev-server/src/main/resources/,一共 5 个:application.yaml(公共)+ application-{local,dev,prod}.yaml + logback-spring.xml。application.yaml 里 spring.profiles.active: local,所以本地开发只需要看 application-local.yaml。
需要确认的四处:
| 配置项 | local 的值 | 说明 |
|---|---|---|
spring.datasource.dynamic.datasource.master.url | jdbc:mysql://127.0.0.1:3306/ainb-professional-init?... | 库名 / 地址 / 账号密码在这里改 |
spring.datasource.dynamic.datasource.slave | lazy: true,同库同账号 | 多数据源用 dynamic-datasource,primary: master;slave 是懒加载,不连也不报错 |
spring.data.redis | host: 127.0.0.1 / port: 6379 / database: 0 | password 行是注释掉的 |
server.port | 48080 | 注意:不在 application.yaml,三个 profile 文件各写一份 |
local profile 还关掉了几个碍事的东西(都是本项目本地开发的默认取向,照抄即可):
spring:
autoconfigure:
exclude: # 排除 Quartz 与向量库自动配置,本地起不来也不需要
- org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration
enlev:
captcha:
enable: false # 关图片验证码,方便脚本登录
security:
mock-enable: false # 模拟登录,三个 profile 都关着(见下文「免登录调试」)
access-log:
enable: false第三步:起后端
IDE 里直接跑主类 enlev-server/src/main/java/cn/enlev/ainb/server/EnlevServerApplication.java(@SpringBootApplication(scanBasePackages = {"${enlev.info.base-package}.server", "${enlev.info.base-package}.module"}),enlev.info.base-package = cn.enlev.ainb)。
命令行则用 Maven:
cd ainb
mvn -pl enlev-server -am -DskipTests spring-boot:run只想验证某一层能不能编过,用增量编译(实测 18.6 秒,本地仓库已暖):
mvn -q -pl enlev-module-system -am -DskipTests compile-pl <模块> -am 会连带编译它依赖的上游模块。注意业务模块只有 5 个(enlev-module-{system,infra,bpm,ai,agent}),上游的 enlev-module-im、支付、商城、CRM 等已连目录一起删除,-pl enlev-module-im 必然报「Could not find the selected project in the reactor」。
第四步:起前端
cd ainb-ui-admin-vue3
pnpm install
pnpm devpnpm dev 实际是 vite --mode env.local,加载 .env + .env.local,端口 VITE_PORT=80,host: 0.0.0.0,open: true(自动开浏览器)。访问 http://localhost,登录页默认已填好租户「启凌智搭」与账号 admin(来自 .env 的 VITE_APP_DEFAULT_LOGIN_*),本地验证码已关闭。
pnpm dev-server 连的不是你的后端
dev-server = vite --mode dev,.env.dev 里 VITE_BASE_URL='http://api-dashboard.enlev.cn'——那是远端环境。用它验证本地后端改动,看到的是别人的数据。
验证跑通了
| 检查 | 命令 | 期望 |
|---|---|---|
| 后端活着 | curl -s http://127.0.0.1:48080/admin-api/system/auth/get-permission-info | {"code":401,"msg":"账号未登录"}(401 说明服务通了,只是没带 token) |
| 接口文档 | 浏览器打开 http://127.0.0.1:48080/doc.html | knife4j 页面,HTTP 200 |
| 前端活着 | 浏览器打开 http://localhost | 登录页 |
| 全链路 | 用 admin / admin123 登录 | 进首页,左侧菜单完整 |
接口文档的分组是按模块的 GroupedOpenApi bean 来的,实测 http://127.0.0.1:48080/v3/api-docs/swagger-config 返回:
{"urls":[{"url":"/v3/api-docs/ai","name":"ai"},{"url":"/v3/api-docs/all","name":"all"},
{"url":"/v3/api-docs/bpm","name":"bpm"},{"url":"/v3/api-docs/infra","name":"infra"},
{"url":"/v3/api-docs/system","name":"system"}]}没有 agent 分组
enlev-module-agent 是自研模块,没有写 AgentWebConfiguration,因此 97 个 /admin-api/agent/** 接口不会出现在独立分组里,只能在 all 组找到(实测 all 组含 99 条 agent 路径)。这不是接口没注册,是文档分组缺一个 bean——补法见接口约定与 CRUD 骨架。
免登录调试:mock token
enlev-spring-boot-starter-security 里有一套 mock 登录:TokenAuthenticationFilter#mockLoginUser 校验 token 以 mockSecret 开头,截掉前缀当 userId,租户取请求头 tenant-id。开着的时候,Authorization: Bearer test + 用户编号 就能直接冒充任意用户,无需走登录接口:
curl -s -H "Authorization: Bearer test1" -H "tenant-id: 1" \
http://127.0.0.1:48080/admin-api/system/user/profile/get
# {"code":0,"msg":"","data":{"id":1,"username":"admin","nickname":"超级管理员",...}}三个 profile 现在都是 mock-enable: false
application-{local,dev,prod}.yaml 全部显式关掉了(476b4b2637,2026-10-18)。上面那条 curl 现在会返回 401,本手册的 tools/ 脚本也全走真实登录取 token。 原因是这个口子的代价太大:SecurityProperties 的 mockSecret 默认值就是 "test",而 mockEnable 默认 false 只说明「不配就不开」,一旦有人图省事打开、又忘了配 mock-secret,任何能连到 48080 的人都能用 Bearer test1 拿到超级管理员身份 —— prod 曾长期处在这个状态。TokenAuthenticationFilter#mockLoginUser 的方法注释原文是「注意,在线上环境下,一定要关闭该功能!!!」。 真要临时用:把 mock-enable 改 true 的同时必须配一个不可猜的 mock-secret,且只在 local 改、不要提交。
常用命令速查
| 目的 | 命令 | 在哪执行 |
|---|---|---|
| 编译单模块(含依赖) | mvn -q -pl enlev-module-system -am -DskipTests compile | ainb/ |
| 打可执行 jar | mvn -DskipTests package → enlev-server/target/enlev-server.jar(fat jar,340 MB;全量无改动实测 12.9 秒 BUILD SUCCESS,24 个模块) | ainb/ |
| 跑全部单测 | mvn test | ainb/ |
| 前端开发 | pnpm dev(:80,连本地 48080) | ainb-ui-admin-vue3/ |
| 前端类型检查 | pnpm ts:check(脏了用 pnpm ts:check:clean) | 同上 |
| 前端单测 | pnpm test:unit(vitest,59 个 src/**/*.spec.ts,1280 用例 / 4.14 s) | 同上 |
| 前端生产构建 | pnpm build:prod → dist-prod/ | 同上 |
| 前端 lint | pnpm lint(eslint + stylelint + prettier 三连检查) | 同上 |
坑与注意
.env.local是纳入 git 跟踪的(不合常理但确实如此)。不要加进.gitignore,也不要把里面的值搬到别处——它是本地联调的唯一事实来源。src/types/auto-*.d.ts只由 dev server 生成(build/vite/index.ts里写的是dts: !isBuild && '...'),且被.gitignore的auto-*.d.ts规则忽略。新增全局组件后要先跑一次pnpm dev再跑pnpm ts:check,否则报「找不到组件」。pnpm test:e2e是坏的:playwright.config.ts与tests/e2e/都不存在,别运行也别尝试补全。- 三个仓库是各自独立的 git 仓库,
C:\code\xpy本身不是仓库。跨端改动要分别提交(一次改动可能要在两三个仓库各提交一次),执行 git 命令前先cd进对应子目录。 - 端口占用:后端 48080、前端 80。Windows 上 80 端口容易被 IIS / 其它服务占,改
.env的VITE_PORT即可(前端直连后端,改端口不影响接口)。 - 后端启动失败先按顺序排:MySQL 通不通 → 库名是不是
ainb-professional-init→ Redis 通不通 → 48080 是否被占。这四条覆盖了绝大多数情况。 - 起来了但页面报「表不存在」,多半是只导了全量快照、没跑
sql/mysql/update/那 6 个增量脚本;表齐了但菜单/权限不对,则是没重启后端(resourceChainCache无 TTL)。
→ 本节是贴着上下文的提醒;按症状查的全域清单见坑位索引。
相关页面
- 仓库结构与改名约定:三个仓库的目录布局,以及从芋道改名过来留下了什么;
- 移动端环境准备与启动:H5 :9000 / 微信小程序 / App 三条启动路径;
- 后端架构与模块依赖:模块怎么分、依赖为什么是单向的;
- 前端工程结构:
src/各目录职责与自动导入机制; - 构建与部署:打包产物与部署方式。
