Wujies 无界平台
开始使用

快速开始

这一页只解决一件事:在开发机上从空库走到「装上第一个应用」,把数据库、后端宿主、插件运行时、前端、迁移与菜单这条链路走通。

读完之后你能回答下面这些问题:

问题本文位置权威依据
三个仓库各是什么,wujies-admin 指的到底是哪一个§1wujies-platform/AGENTS.md §0
要导哪些 SQL、按什么顺序导§2script/sql/README.md、deploy/tools/build-1panel.ps1
怎么编译、怎么启动后端(并真的加载插件)§3–§4script/prune/start-host.ps1、wujies-admin/src/main/resources/application.yml
怎么起前端,它连的是哪个后端§5plus-ui/vite.config.ts、plus-ui/.env.development
怎么装一个应用,怎么判断它真的生效了§6–§7AGENTS.md §3.2、§3.4
怎么跑测试§8根 pom.xml

本页不讲:怎么写一个应用(见 应用清单、必须遵守的不变量)、怎么打包上架(见 发布应用包)、生产部署(见 在部署里使用应用、升级)。


1. 先认清楚三个仓库

三个仓库没有共同的 git 根,各自提交、各自分支。

目录(相对工作区根)是什么怎么认出来
wujies-platform/后端(Java / Spring Boot)+ SQL 脚本有 pom.xml、wujies-admin/、script/sql/
wujies/wujies-plugins/应用本体(插件)+ 应用前端工程(pnpm workspace)+ 打包器有 package.ps1、wujies-<app>/、frontend/<app>-app/、keys/
plus-ui/宿主前端(Vue 3 / Vite)有 vite.config.ts、src/
wujies-admin 这个名字有三处含义,读到时按上下文区分
写法实际指判据
wujies-platform/wujies-admin/后端启动模块,产物 wujies-admin/target/wujies-admin.jar有 pom.xml、src/main/、Dockerfile
工作区顶层的 wujies-admin/只有 app-market-dist/,前端产物的落位目录没有 package.json、没有 src/
plus-ui/宿主前端仓库;它的 package.json 里 name 就是 wujies-admin(混淆的根源)有 vite.config.ts、src/

后端模块地图

路径内容
wujies-admin/启动模块。PluginClassPathLoader 在这里,在 Spring 之前执行
wujies-common/基础设施:wujies-common-core、-web、-mybatis、-job、-plugin-sdk 等
wujies-modules/wujies-app-market/应用市场:目录、插件运行时、上架端、包安全
wujies-modules/wujies-system/用户 / 角色 / 菜单 / 租户等底座
wujies-modules/wujies-workflow/工作流
wujies-modules/wujies-plugin-probe/插件运行时探针
wujies-extend/独立服务:调度中心、监控中心

有三样东西不存在,别去找:

  • 平台里没有任何给应用准备的契约模块。wujies-modules/ 下只有上表那几个,平台对 cn.wujies.<app>.* 的 Java 引用数量是 0。契约随插件走。
  • 没有 wujies-job / wujies-generator / wujies-demo 模块(已删除)。定时任务随应用走,宿主只提供 wujies-common-job。
  • 平台仓库里没有 plugins/ 目录。插件本体在另一个仓库。

应用长什么样

每个应用是 wujies-plugins/wujies-<app>/ 下一个自包含工程,契约与实现同处一室:

wujies-plugins/wujies-gallery/
├── app.json                       清单:code / version / assets / menus
├── pom.xml                        平台依赖一律 provided
├── migrations/01-create-gallery-tables.sql
└── src/main/
    ├── java/cn/wujies/gallery/{domain,domain/bo,domain/vo,mapper,service,service/impl,controller,open,listener}
    └── resources/mapper/gallery/*.xml     ← Mapper XML 在插件里,随 jar 走
  • 实体 / BO / VO / Mapper 接口 / Mapper XML / Service 接口与实现 / controller / open / listener / task 全在插件模块自己的 src/ 下;
  • Mapper XML 由 PluginMapperRegistrar 在插件加载时动态注册进宿主的 SqlSessionFactory,不是靠宿主扫 mapperLocations;
  • 插件之间要对方的契约时,依赖兄弟插件构件(同样 provided)。
旧说法一律作废

「契约单独放一个平台模块」「Mapper XML 必须留在平台侧」「靠宿主的 mapperLocations 扫插件里的 XML」都是 2026-09-30 之前的规则,已经不成立。看到这类描述(包括本站其它页面里的)以 AGENTS.md §2.2 为准。

应用有两种形态,都不奇怪:

形态判据例子
全栈应用有 app.json、pom.xml、后端 src/,也有 frontend/<app>-app/wujies-gallery、wujies-exam、wujies-mall
纯前端应用目录下只有 app.json,包里没有 jarwujies-finance

2. 建库:按这个顺序导 SQL

全新部署只认平台自己的初始化脚本。应用自己的表不在这里,它们随应用包走。

顺序文件内容
①wujies-platform/script/sql/platform_init.sql平台初始化:25 张 sys_* 表 + 地区 / 字典 / 参数 / 菜单 / 角色授权 / 租户 / 默认账号
②wujies-platform/script/sql/update/ 下的 7 个脚本(见下)应用市场的表结构
③wujies-platform/script/sql/app_catalog_seed.sql应用目录种子(app_info / app_version / app_category)。不导市场里什么都没有

第 ② 步的清单与顺序(不是"按文件名排序",是这份固定清单):

app_market.sql                     应用市场核心表(app_info / app_version / app_install / app_schema_history …)
app_market_app_model.sql           install_mode / audience 列
fix_app_version_unique_index.sql   UNIQUE(app_id, version_active)
app_market_home_path.sql           home_path 列
app_market_sync_icon.sql           app_info.icon 跟随菜单表
app_market_schema_history_fix.sql  迁移账本唯一键 (app_code, script)
app_market_register_modules.sql    把平台自带模块登记成应用

依据是 deploy/tools/build-1panel.ps1 里的 $appMarketScripts 数组与 script/sql/README.md 第四节 —— 这也是安装包实际导入的那 7 个。

  • script/sql/ry_job.sql 只在启用 SnailJob 调度中心时需要,它建的是调度中心自己的 sj_* 表。
  • script/sql/update/ 里其余的 platform_*.sql、app_market_<app>_migrate.sql、fix_* / cleanup_* 是存量库专用的一次性脚本,全新部署不需要。
别用上游的 ry_vue_5.X.sql 起库

它已经不在仓库里了(script/sql/ 下没有这个名字的文件)。它是上游项目的种子数据,不是本仓库的库结构:缺 82 行参数、31 个字典类型、几百行菜单、整个地区数据,列结构也是上游版本,拿它起库要一层层打补丁。

两个容易混的地方:

  • script/sql/archive-upstream/ 里是上游的版本升级脚本 update_<旧版本>-<新版本>.sql,不是种子数据,也不要执行;
  • script/sql/backup/ry-vue-*.sql 是工具链的输入(全库快照),同样不是种子数据。

本仓库的起点虽然是 RuoYi-Vue-Plus 5.X,改名为 Wujies 无界平台后已经独立演进、不跟随上游。唯一的交付库初始化是 platform_init.sql。

应用的表不要手工建

每个应用的表由包内 migrations/*.sql 在安装时自动执行。手工导入会让平台在"彻底删除"时不知道这个应用占了哪些表 —— 删表依据就是那些 CREATE TABLE 语句。

初始化是否完整,不要看 sys_config 里的 platform.init.version 这一行 —— 它不在 platform_init.sql 里, 而是后端容器首次启动、导入完成之后才写进去的标记(wujies-platform/deploy/docker/backend/entrypoint.sh, 约 L103–106,值取环境变量 PLATFORM_INIT_VERSION,插入是幂等的)。 所以手工导库时这一行必然不存在,拿它当判据会把一次成功的初始化误判成失败。

手工导库要判断初始化是否完整,去看表和数据在不在:

  • 表:sys_user / sys_menu / sys_config / app_info 是否都建出来了;
  • 行:sys_user 里有没有 user_id = 1 的默认账号、sys_menu 里有没有菜单行;
  • app_info 有没有目录种子行 —— 0 行说明第 ③ 步(app_catalog_seed.sql)没导进去,市场会是空的。

改连接串

改什么文件
MySQL / Redis / RabbitMQ 连接wujies-platform/wujies-admin/src/main/resources/application-dev.yml
应用市场上架端、形态、目录、签名公钥、多租户排除表wujies-platform/wujies-admin/src/main/resources/application.yml
application-dev.yml 里预置的地址不是本机

它的数据源 url、Redis host 指向的是仓库作者的开发环境,直接启动会连到那台机器(或者连不上)。先按你的环境改这两处再启动。


3. 编译并打包后端

所有命令的工作目录都是 wujies-platform/。

& $mvn -B -q compile -DskipTests                        # 全反应堆编译(根 pom 默认 skipTests=true,编译不跑测试)
& $mvn -pl wujies-admin -am -B install -DskipTests      # 打可运行 jar → wujies-admin/target/wujies-admin.jar
改了后端代码,必须重新打包再验证

只跑 mvn compile 不会更新 wujies-admin.jar,你启动的还是旧代码,于是很容易得出「我的改动没生效」的错误结论,然后去改本来是对的代码。

不要在别人正在跑的 JVM 上执行 mvn install

compiler 插件会先清空输出目录再全编。正在运行、按需懒解析类的 JVM 会抛 NoClassDefFoundError。先停服务,或只做 compile。


4. 启动后端宿主

手工启动(推荐,最少魔法)

java "-Dloader.path=plugins-dist" "-Dspring.profiles.active=dev" -jar wujies-admin\target\wujies-admin.jar

三个要点:

  1. 工作目录必须是 wujies-platform/。app-market.deploy.plugin-dir(plugins-dist)与 frontend-dir(../wujies-admin/app-market-dist/remote-apps)都是相对路径,见 wujies-admin/src/main/resources/application.yml。
  2. profile 要显式传。application.yml 里的 spring.profiles.active 是构建期由 Maven 属性 @profiles.active@ 替换的(根 pom.xml 里 dev 是默认激活的 profile),别依赖它:本地用 -Dspring.profiles.active=dev(读 application-dev.yml),部署用 prod。
  3. -Dloader.path 只在打包后的 jar 上生效。wujies-admin/pom.xml 配了 layout=ZIP,只有它才认这个参数;从 IDE 直接跑主类时它会被静默忽略 —— 表现为 plugins-dist 里明明有 jar,安装应用却报「当前 JVM 没有加载 xxx.jar」。
两个 -D 参数都必须加引号

PowerShell 会把 -Dloader.path=plugins-dist 拆成 /path=plugins-dist,报 ClassNotFoundException: /path=plugins-dist。

调整端口用 -Dserver.port=8087(application.yml 的默认值是 8086)。

启动脚本:先读这段再决定用不用

wujies-platform/script/prune/start-host.ps1 的意图是「先清理上一版插件 jar,再打包,再用正确的 loader.path / profile 启动」。

参数作用
-PluginDir插件落位目录,默认 plugins-dist
-ProfileSpring profile,默认 prod
-Build启动前重新打包
-SkipPrune跳过清理
-NoPlugin不加载插件(排查「没有插件时宿主是否正常」)
-Port覆盖端口
这个脚本当前的工程根推断是错的(未实测,来自读源码 + 已记录的缺陷)

脚本第 60 行是 $root = Split-Path $PSScriptRoot -Parent,即「脚本目录的上一层」;而脚本实际在 script/prune/ 下,于是推断出的根是 wujies-platform/script,不是 wujies-platform。后果是:插件目录被算成 script/plugins-dist、jar 路径被算成 script/wujies-admin/target/wujies-admin.jar(不存在 → 触发打包 → 在 script/ 下跑 Maven 失败)。同目录的 prune-plugins.ps1 有同一个问题,而且它更隐蔽 —— 目录不存在时它打印「目录不存在,无需清理」并以 0 退出,看着像成功。

docs/开发问题与解决方案.md 第 9 节把这条列为未改(建议改成"向上找 pom.xml")。我没有运行这两个脚本,本页的结论是逐行读源码得出的,未经实测。要用就先修根推断,否则按上面手工启动。

为什么要先清理旧的插件 jar

两个加载时机现在都按 active-plugins.txt 选版本(2026-10-02 起):运行期是 PluginRuntimeManager,JVM 启动瞬间是 PluginClassPathLoader.listJars —— 不在清单里的残留 jar 会被刻意跳过(启动日志打印「跳过 N 个不在 active-plugins.txt 里的 jar」),所以 「同一 appCode 留下两个版本 → 两份同样的 mapper XML → Result Maps collection already contains value」 这个老坑只在清单不可用(active-plugins.txt 缺失或为空,此时不过滤)时才复现。

清理仍然值得做:清磁盘、回收被占用的 jar 文件句柄(Windows 上不清理会导致卸载/彻底删除删不掉)。 清理依据就是这份清单:只删同一个 appCode 名下非当前生效版本的 jar,手工放置的 jar 一律不碰。


5. 启动前端

cd plus-ui
npm install
npm run dev
事实值 / 出处
dev server 端口.env.development 的 VITE_APP_PORT = 80 → 访问 http://localhost
接口代理vite.config.ts 把 /dev-api 转到 http://127.0.0.1:8086 并去掉前缀;后端换了端口要同步改这里
运行期垫片npm run dev 会先跑 predev → gen:runtime,不用手工执行
守门脚本npm run check:globals、npm run check:remote、npm run audit:menus
远程应用的落位目录,后端和 dev server 当前指向两个不同的物理目录
  • 后端落位到 app-market.deploy.frontend-dir,当前值是 ../wujies-admin/app-market-dist/remote-apps(相对 wujies-platform/,即工作区顶层那个 wujies-admin/),见 application.yml;
  • dev server 从 <plus-ui>/app-market-dist 提供 /remote-apps/*,见 plus-ui/vite/plugins/appMarketStatic.ts(它用 process.cwd() 解析,而 npm run dev 的 cwd 就是 plus-ui)。

两个目录目前都存在且内容不同(工作区根那份 10 个应用目录,plus-ui 那份 11 个)。装完应用菜单点开报「远程视图不可用」时,先比对这两个目录。 未实测:本页没有真的装一个应用来验证这条链路,以上是读源码与目录得出的结论。


6. 登录

打开 http://localhost/login:

项值
租户000000(platform_init.sql 的 sys_tenant 种子行)
账号admin(platform_init.sql 的 sys_user 种子行,user_id = 1)
密码见 platform_init.sql 的 sys_user 种子行 —— 存的是 BCrypt 哈希,明文不由仓库文件给出。本站其它页面写作 admin123,本页未核对

接口请求要同时带 Authorization 与 clientid 两个头,缺一不可 —— clientid 会被 wujies-common-security 的 SecurityConfig 与 token 里记录的值比对。前端已处理好(plus-ui/src/utils/request.ts 从 VITE_APP_CLIENT_ID 设默认头),手工调接口时注意。


7. 装一个应用,验证整条链路

登录后的菜单结构来自 platform_init.sql 的 sys_menu 种子行:

菜单pathcomponent
应用市场 → 应用市场appmarket / marketappstore/market/index
我的应用 → 已安装应用myapps / installedappstore/installed/index
我的应用 → 应用任务myapps / taskappstore/tasks/index
应用上架 → 应用管理 / 分类管理appconsole / …appstore/console/**

步骤:

  1. 应用市场里挑一个应用(例如「图床管理」),点安装;
  2. 打开 我的应用 → 应用任务,看进度、当前步骤与日志 —— 这是排查问题的第一站,步骤与日志是边跑边落库的,失败原因(含服务端异常文本)都在那里,见 任务中心与日志;
  3. 安装成功后左侧菜单出现该应用的入口。

全程不需要重启后端。

一次安装实际发生什么:

1. 生成 app_install_task 记录(任务中心看的就是它)
2. 检查本机有没有产物
   ├─ 已有 → 就地使用(同一版本重装)
   └─ 没有 / 正在升级 → 按 packageUrl 从市场拉包
        ├─ 校验 MD5 / SHA256
        ├─ 安全检查:产物白名单 + 签名校验
        ├─ 解包落位:jar → plugin-dir 根下;前端 → frontend-dir/<appCode>/
        └─ 记录 active-plugins.txt
3. 执行包内 migrations/*.sql(按文件名排序,幂等,只做 up)
4. 加载后端插件            ← 在迁移之后
5. 同步菜单与授权(按 app.json 的 menus 幂等 upsert)
6. 任务置为成功

三条值得记住的设计:

  • 迁移在加载之前是刻意的:先改表结构、再切换代码。反过来的话,新代码在启动时机读一个还没加的列就会炸。

  • 升级会强制重新落位(按 fromVersion != toVersion 判断),「文件存在就跳过」只在同版本重装时成立。

  • plugins-dist/active-plugins.txt 是运行期选版本的依据,一行一条 appCode=jarFileName:

    gallery=wujies-gallery-plugin-1.0.5.jar
    mall=wujies-mall-plugin-1.0.3.jar

    插件 jar 与前端目录都落位在部署内全局共享的位置,所以「彻底删除」受两道闸约束(别的租户还在用就不动、平台表前缀受保护),详见 生命周期。


8. 跑测试

工作目录同为 wujies-platform/:

& $mvn -pl wujies-modules/wujies-app-market -B test "-DskipTests=false" "-Dprofiles.active=dev"
新增测试必须打 @Tag

根 pom.xml 的 surefire 配了 <groups>${profiles.active}</groups>。没打 @Tag("dev") / @Tag("local") / @Tag("prod") 的测试根本不会被执行 —— 症状是 Tests run 总数不变 + BUILD SUCCESS,看着全绿,其实没跑。新增后核对一下总数。


起不来?先看这几条

症状原因处理
Result Maps collection already contains valueplugins-dist 里同一应用有两个版本的 jar,MyBatis 解析了两份同样的 XML只留 active-plugins.txt 里登记的那一个;prune-plugins.ps1 因上述根推断 bug 目前不可用
插件完全没加载只在 yml 里改了 app-market.plugin-runtime.enabled这个开关启动器读的是系统属性,它在 Spring 之前执行。用 JVM 参数切换:-Dapp-market.plugin-runtime.enabled=false
expected single matching bean but found 2宿主上下文里注册了与插件实现同类型的 bean宿主不能按类型注入插件 bean,改走 PluginServiceProvider.getPluginBean(appCode, type)
ReferenceError: useStorage is not defined / Failed to resolve component: KTable远程应用的自动导入清单与宿主不一致见 前端远程应用、本地联调;守门脚本 npm run check:globals
ClassNotFoundException: /path=plugins-distPowerShell 把 -D 参数拆了两个 -D 参数都加引号
市场里一个应用都看不到没导 app_catalog_seed.sql按 §2 补导
安装/部署被安全检查拒绝app-market.security.mode 当前是 enforce,包没有签名见 安全模式;发布时统一带 -SigningKey

更多症状见 wujies-platform/AGENTS.md 第 5 节「已知坑」与 docs/开发问题与解决方案.md;故障排查 一节的部分命令仍是改造前的写法,读时按本页的命令替换。

相关阅读

界面截图

后台登录页(截图占位)

后台登录页(截图占位)

/images/docs/quickstart-login.png

建议 1920 × 1080,截登录表单与验证码。

目录