开发手册导读
本手册面向 二次开发者与平台维护者:讲清楚启凌智搭的代码怎么组织、低代码链路怎么跑通、权限体系怎么决策、自研 Agent 模块怎么扩展,以及「新增一个功能要动哪些文件」。
组织方式参照按域切分:不追求覆盖上游开源框架的每一行,而是把 本项目定制的部分讲透,上游部分只讲约定与扩展点。
三个入口,按你的问题选
| 你的问题是 | 走这里 |
|---|---|
| 我要做某件事,翻哪页? | 任务索引 —— 按「要动的那件事」查,直接跳到目标页的那一节 |
| 我该按什么顺序读? | 下面的按角色的阅读路线 |
| 这个症状是哪来的? | 坑位索引 —— 按「现象 → 根因 → 怎么办」查(编号 A1–F6),全部带实测证据;移动端专属的另有一页移动端坑位(编号 M1–M24) |
侧栏是按「你要动哪一端」分组的,不是按目录编号,原因见章节地图下面那条警告。
读者与前置知识
| 你是谁 | 需要先知道 | 建议入口 |
|---|---|---|
| 后端开发 | Java 17、Spring Boot 3、Maven 多模块、MyBatis-Plus、Dynamic Datasource | 环境准备与启动 → 后端架构与模块依赖 |
| Web 前端开发 | Vue 3、Vite、TypeScript、Element Plus、Form Create(开源版) | 前端工程结构 → 路由与权限机制 |
| 移动端开发 | Vue 3、uni-app、TypeScript、wot-design-uni、UnoCSS、只能用 pnpm | 移动端总览 → 移动端环境准备与启动 |
| 低代码平台开发 | 以上任一 + 平台的模型 / 表单 / 菜单三件套概念 | 低代码平台开发总览 |
| Agent 能力开发 | Spring AI、Spring AI Alibaba | 架构总览 |
先读平台使用手册再看代码
开发手册默认你已经会用这套系统。不了解「数据模型 / 表单设计 / 资源授权」在界面上长什么样,直接读代码会非常吃力——请先过一遍使用手册的 02 基础设施与 01 系统管理对应章节。
移动端是个例外:使用手册目前没有移动端篇(截图流水线只跑 Web 端 :80)。要看移动端在界面上长什么样,只能起 H5 :9000 自己点,或者看移动端仓库 README.md 里的演示图。
三个仓库与技术栈
| 仓库 | 内容 | 关键版本 | 本地端口 |
|---|---|---|---|
ainb | 后端,Maven 多模块单体 | Java 17、Spring Boot 3.5、MyBatis-Plus、Redisson | 48080(/admin-api) |
ainb-ui-admin-vue3 | 后台前端(Web) | Vue 3、Vite、TypeScript、Element Plus、pnpm(Node ≥ 20.19) | 80 |
ainb-ui-admin-uniapp | 移动端(H5 / 小程序 / App 同一套代码) | Vue 3.4、uni-app 3.0、Vite 5.2、TypeScript 5.8、wot-design-uni 2.2、UnoCSS 66、z-paging 2.8、pnpm(Node ≥ 20) | H5 9000 |
三者共用同一套后端、同一套权限体系与租户。移动端不是「另一个后台」,而是同一平台的第二个运行时宿主:工作台 / 审批 / 消息 / 我的 + 在线表单的移动端运行时,工作台菜单由服务端 system_nav_node 下发、不在前端写死。它不走 Vite proxy,直连后端 :48080(Web 端同样直连,vite.config.ts 的 server.proxy 整段是注释掉的)。
上游归属各不相同,别混为一谈:
| 仓库 | 直接上游 |
|---|---|
ainb | 芋道 ruoyi-vue-pro |
ainb-ui-admin-vue3 | 芋道 yudao-ui-admin-vue3 |
ainb-ui-admin-uniapp | 芋道 yudao-ui-admin-uniapp(业务页底座),而它本身又派生自 unibest(工程骨架) |
三者都是改名分支:包名 cn.iocoder.yudao → cn.enlev.ainb,构件前缀 yudao- → enlev-,产品名「启凌智搭」。仓库里残留的 yudao 字样与上游文档只作参考,不要改回上游命名。
与上游最大的结构差异:模块是扁平 jar
上游把每个业务模块拆成 -api / -biz 两个构件,本项目没有拆——api 只是同一构件里的一个 package。所有代码生成器模板、模块依赖、启动类扫描都按扁平结构写好。把上游的拆分方式「改回来」会直接破坏代码生成与编译。
模块依赖关系
依赖是单向的:agent → {ai, infra, bpm, system},没有任何模块反向依赖 agent。这意味着给 agent 加工具时可以直接调用其他模块的 service;反过来,其他模块要用 agent 能力,只能通过接口或事件解耦。这条纪律由 AgentArchitectureTest#testOnlyDependOnApiLayer 钉住,反向依赖当场编译不过。
enlev-module-im 已被整体删除
ls -d enlev-module-* 只剩 agent ai bpm infra system 5 个;根 pom.xml 里连注释掉的那行也没了。-pl enlev-module-im 必然失败——不是因为被注释,是因为不存在(删除于 12347efcd1,305 个文件)。移动端仓里那 11 个 pages-{crm,erp,im,iot,mall,...} 分包编译进包但用户点不到,正是因为后端这些模块已删。实测清单与 reactor 的 24 个模块怎么数见后端架构与模块依赖。
章节地图
侧栏的 8 个分组就是本手册的全部结构:
| 分组 | 这一组回答什么问题 | 页数 | 从哪进 |
|---|---|---|---|
| 开始 | 怎么跑起来、代码归属怎么判断、要干某件事翻哪页 | 4 | 任务索引 |
| 后端 | 模块怎么分、一条请求怎么走、表怎么建;含子分组「上游域」 | 7 | 后端架构与模块依赖 |
| Web 管理端 | 前端工程怎么组织、路由与权限怎么串、请求怎么发、有哪些现成组件与 hook | 4 | 前端工程结构 |
| 移动端 ★ | 同一套后端的第二个运行时宿主:约定式路由、双 token、服务端下发的工作台、在线表单的移动端渲染器、三端产物 | 9 | 移动端总览 |
| 低代码平台 | 模型 / 接口 / 表单 / 控件 / 取号 / 门户这五个域怎么在运行时被解释执行 | 8 | 低代码平台开发总览 |
| 权限体系 | 三层权限怎么在一次请求里叠加、配了为什么不生效 | 6 | 权限体系总览 |
| Agent 模块 ★ | 全仓唯一完全自研的模块:工具、ReAct、工作流、HITL、安全、计量、SSE | 8 | 架构总览 |
| 工程实践 | 端到端做一件事、怎么写测试、怎么打包部署、有哪些已知坑 | 4 | 新增一个菜单功能端到端 |
页数含本页与任务索引这两个导航页,合计 50。要复核就数侧栏,别数目录(05-mobile/ 恰好一组一目录,其余都不是)。
侧栏分组 ≠ 目录编号,这是刻意的
分组按读者要动哪一端组织,目录编号是历史 ID。所以 00-intro/ 的 9 页被拆进了「开始 / 后端 / Web 管理端」三组,04-upstream/ 挂在「后端」组下作子分组——文件都没动,只有归组变了。
不要「顺手对齐」编号与分组:那要么得改 36 个已进 AI 知识库的 URL(tools/gen-kb-url-map.mjs 会与站点路由逐条对账、不符即 exit 1),要么得把「按端找页」退回成「按编号猜页」。
同理,本页不维护逐页清单——那份权威在 docs/.vitepress/sidebar.mjs,而且它有门禁(pnpm check:pages 会校验侧栏每条文字与页面 H1 逐字一致、每个链接都有对应文件)。以前这里有一份逐页表,那是第二份拷贝、没有任何门禁,已经在漂移。下面各节只讲「这一组为什么值得读、有什么反直觉的地方」,不重复列页面。
开始 · 后端 · Web 管理端
这三组的正文页都在 00-intro/ 目录下,是按端拆开的:跑起来与代码归属放「开始」,后端三页放「后端」,前端四页放「Web 管理端」。
它们是入门材料,但不是软材料:00-intro/ 里的每一页都带着实测出来的反直觉结论,比如「岗位代码在 dept 包不在 post 包」「v-hasPermi 传字符串会直接 throw」「auto-*.d.ts 只由 dev server 生成」。第一次读建议按阅读路线走,之后当参考查。
移动端 ★
这一组是本轮新加的,也是三仓里最容易被误判的一个:它看着像「Web 端的缩小版」,实际是同一套后端的第二个运行时宿主——共用 /admin-api、共用权限判定与租户,但工程约定几乎处处与 Web 端不同(只能 pnpm、约定式路由、easycom 自动注册、不走 Vite proxy、没有单测)。拿 Web 端的习惯去套,第一小时就会撞墙。
三件反直觉的事,先记住再读:
- 工作台菜单不在前端写死。上游那套 267 项手写
menu.json被换成了库里的system_nav_node(terminal=app)自引用树,由/system/nav-node/tree下发。这条链路横跨三仓(后端建表 + Web 仓的「移动端导航」编辑器与「App菜单」页 + 移动端渲染),动任何一端都要先看服务端驱动导航。 - 约一半产物是用户点不到的。11 个
pages-{crm,erp,im,iot,mall,...}分包编译进包,但后端对应模块早已删除、导航树不建节点,实测占 H5 产物体积的 52%。这是历史遗留而非 bug,别去「修好」它,详见移动端总览。 mode与command不是一回事。非 H5 端连pnpm dev:app的 command 都是build(只有 mode 是development)。所以「现在是不是生产」只能问 mode /NODE_ENV,问 command 会得到相反的答案——本仓已经因此长出过一个把凭据内联进生产包的缺陷。
这一组有两条「致命」级坑,都在构建与发布链路上
M9:env/.env 是所有 mode 的公共基底,而 Vite 会把 VITE_* 静态内联进产物——写在这里的默认账号口令会原样出现在生产包里,且运行时没有任何开关能关掉(已修,但机制还在)。 M18:VITE_WX_APPID / VITE_UNI_APPID / VITE_BAIDU_MAP_KEY 三个身份标识仍是上游的值,照着文档上传小程序会传到上游账号下(未修,需要你先定值)。
这一组没有测试网
移动端仓库没有单测,只有 pnpm type-check(vue-tsc)、pnpm lint、pnpm check:color(品牌色硬编码守卫)。所以本组页面的结论全部来自实测源码、脚本量测与产物字面量,而不是「跑一遍测试就知道了」。改完之后要验证,只能起 H5 :9000 手点。
低代码平台
五个域(数据建模、表单设计、数据接口、门户、取号管理)上游 yudao 一个都没有。上游只有代码生成器——生成 Java/Vue 源码,产物脱离平台运行;本项目的低代码是运行时解释:配置存在库里,由平台自己解释执行。
这个区别决定了读法:代码生成器你只要看模板,运行时解释你得看解释器。所以这一组的每页都在讲「配置怎么被读出来、怎么被校验、怎么被执行」,而不是「产物长什么样」。
权限体系
sys_ 前缀的 7 张表、决策引擎、行级规则、字段模式、记录共享、权限诊断——整套都是本项目自研。上游 yudao 只有 system_role_menu(角色 ↔ 菜单的多对多)加一个 @DataScope 注解;它的判定就一句 roleMenuMapper.selectListByUserIdAndMenuIds 判非空——没有 DENY、没有动作维度、没有继承、没有理由。
这一组的坑密度是全手册最高的
权限体系的缺陷形态不是报错,而是「配了、保存成功了、发布成功了、界面上一切正常,但就是没生效」。所以每一页都有一节很长的「坑与注意」,而诊断与排错把全域已知的静默失效汇成了一张「症状 → 根因 → 去哪查 → 报错吗」的表。只读一页的话读它。
另外:「塌缩」这一件事在三页里各写了一面——权限资源(开发视角)是写库侧、授权与决策引擎是判定侧、诊断与排错是检测侧,必须连着看。
Agent 模块 ★
enlev-module-agent 是本仓唯一一个从第一行起就完全自研的业务模块。上游 yudao 只有「对话 + 绘图」,没有「让模型去建表、发布表单、部署流程、发起审批」这条链路——所以这一组讲的东西在上游找不到任何参照物,别拿对 yudao 的记忆去推。
它也是全仓测试网最密的地方,实测(2026-10-25;口径同 ai 域开发 · 测试:数 src/test 下全部 .java,注解按行首锚定):
| 模块 | 测试文件 | @Test | @Disabled |
|---|---|---|---|
enlev-module-agent | 161 | 2,225 | 0 |
enlev-module-ai | 43 | 187 | 86(46%) |
对照很说明问题:管着所有厂商模型调用的 ai 模块,近一半测试是关掉的;而自研的 agent 一个都没关。改 ai 域时别指望测试网兜住你。
⚠️ 这两个「测试文件」数与测试规范那张表的「测试类」数(agent 160、ai 13)不是同一个口径,两张表都对、别互相「订正」:那边只数 *Test.java,这边数 src/test 下全部 .java——ai 那 43 个里有 29 个是整体禁用的 *Tests.java(联网测试)加 1 个 fixture VendorApiKeys.java。86 个 @Disabled 的逐条归类见 ai 域开发 · 测试。
这一组有全平台唯一一处匿名可达的入口
POST /admin-api/agent/webhook/{path} 是整个 agent 模块唯一的 @PermitAll 端点(注解全模块只有一处,在 AgentWebhookController)——不要 token、不要登录态,只靠「IP × path 两维限流 + 验签 + body 整体闸」挡着,而每一次命中都是一次无头拉起流程:烧 token、写库、占图执行线程。动它之前先读安全与提示注入防护。
两页对「发布」的定义不一样
工作流引擎讲的是引擎侧的生效,Plan 预览确认与 HITL讲的是闸门侧的放行。改工作流时两页必须连着看,否则会以为发出去了其实没发、或以为被拦了其实是生效了。
上游域(挂在「后端」组下)
四页讲的都是上游 yudao 的模块,但四个的定制方式完全不同,读法也不同。先看这张实测的归属占比(@author 走进新时代 的文件数 / 该模块 src/main/java 下的 .java 总数):
| 域 | 本项目怎么定制它 | 自研占比 |
|---|---|---|
system | 在旁边加了一整套自研权限体系(7 张 sys_* 表 + service/permission/ 一整包) | 101 / 575 ≈ 17.6% |
infra | 低代码引擎整套挂在它身上:formdesign / datamodel / datainterface / billtemplate / portal | 199 / 469 ≈ 42.4% |
bpm | 直接改上游的类:改 SimpleModelUtils、改 BpmModelServiceImpl、在事件基类里加一道守卫 | 39 / 265 ≈ 14.7% |
ai | 只改了 14 个文件,但这 14 个管着所有厂商模型调用 | 14 / 199 ≈ 7.0% |
⚠️ 这四个数会随提交漂移(system 与 infra 已经各漂过一次)。要复核就现场量,别信本页:
cd ainb && for m in system infra bpm ai; do
b=enlev-module-$m/src/main/java
echo "$m $(grep -rl '@author 走进新时代' $b --include=*.java | wc -l) / $(find $b -name '*.java' | wc -l)"
done所以每页开头都有一张实测规模表:先看那张表,分清哪些是你不能乱动的底座、哪些是本项目自己的东西。判断单段代码归属最省事的办法是看 @author——「芋道源码」是上游,「走进新时代」是本项目。
01-lowcode 讲「它怎么工作」,04-upstream 讲「它挂在哪个模块的哪个包里」——要找低代码的某一层,先在 infra 域定位子域,再回低代码平台组看机制。
工程实践
这一组不按概念组织,按你要干的那件事组织。四页里坑位索引是被引用最多的一页:48 个正文页里有 45 个带「坑与注意」节,而这 45 个节的末尾都有一行指回它(剩下 3 页是仓库结构与改名约定与两份坑位索引自己——索引页本身就是清单,不再指回自己)。
坑位索引现在分两处,别只查一处:跨端通用的 29 条(编号 A1–F6)在坑位索引,移动端专属的 24 条(编号 M1–M24)在移动端坑位。前者的 G 节把后者标 🔴 的那几条指了过来,所以「上线前过一遍」这件事仍然能在一个页面里做完——条目正文只有一份,不重复。
上线前只看一页的话
看坑位索引,把它索引表里标 🔴 的条目过一遍,再顺着 G 节过移动端那几条——尤其是 A1(免登录 mock token 曾长期在生产 profile 里开着,Bearer test1 就是超级管理员;已修,但代码路径一行没删,开关一开就复活)、A2(Druid 控制台曾无鉴权暴露 JDBC URL 与全部已执行 SQL;已修,但改的是 yaml,不重启不生效)与 M18(小程序身份标识仍是上游的,未修)。
条数这里刻意不写:它是索引表的一个筛选结果,写死一个数字就是又造一处会漂移的拷贝——本页上一版写的是 11,而表里当时已经有 12 条。
按角色的阅读路线
每条路线都是有序的,读完就能动手;不在这个角色上的章节先跳过。
1. 第一次跑起来(谁都先走这条)
- 环境准备与启动 —— 装什么、SQL 怎么导、后端与 Web 端怎么起、怎么验证起来了。这一页不含移动端:三端共用同一个后端与同一个库,所以第 1 步照走,之后起 H5
:9000的部分转移动端环境准备与启动 - 仓库结构与改名约定 —— 三个仓库、
yudao→enlev改名规律、怎么判断一段代码是谁写的 - 任务索引 —— 之后按你要做的事查,不必通读
第 1 步的 SQL 有个真陷阱
sql/mysql/ 下两份全量 dump 都不能单独用:ainb-professional-init.sql(211 张表)平台表齐全但没有 system_nav_node;ainb-init.sql(95 张表)有 system_nav_node 却缺 64 张代码里 @TableName 声明的表。正确路径是「导专业版快照 → 按依赖顺序跑 update/ 那 6 个增量脚本 → 重启后端」。顺序、原因、以及为什么不能图省事用 update/*.sql 通配,都写在环境准备与启动里。
2. 只改后端
- 后端架构与模块依赖 —— 模块依赖图、starter、请求生命周期、
/admin-api前缀怎么来 - 接口约定与 CRUD 骨架 —— 以「岗位」为样本走完 Controller→VO→Service→Mapper→DO
- 数据库约定与表总览 ——
BaseDO、逻辑删除、多租户、建表最小清单 - 新增一个菜单功能端到端 —— 两条路线都实测走查过(手写五件套 / 代码生成器)
- 要动上游模块时,再进「后端 → 上游域」那四页
3. 只改 Web 管理端
- 前端工程结构 —— 目录职责、环境变量与 mode、alias 顺序陷阱、自动导入覆盖范围
- 路由与权限机制 —— 登录到建路由的完整链路、三层权限、
v-hasPermi的实现取舍 - axios 封装与 API 约定 —— 拦截器七分支、401 无感刷新、api 层标准写法
- 通用组件与 hooks —— 全局组件名怎么来、定制组件、hook 全清单、哪些是死代码
4. 只改移动端
- 移动端总览 —— 这个仓库是什么、17 个页面目录里哪些真交付了、与 Web 端的能力差
- 移动端环境准备与启动 —— 只能 pnpm、
init-baseFiles、env 叠加顺序、H5:9000怎么确认真跑通了 - 工程结构与约定式路由 ——
pages.json是产物不是源码、分包划分、easycom 的^yd-(.*)规则 - 请求封装与登录态 —— 拦截器七步、双 token、401 静默刷新与重放队列、加解密开关的真实语义
- 移动端页面开发范式 —— 一个列表页的完整最小样例,z-paging / wot-ui / UnoCSS 的既定写法
- 再按你要动的那件事挑一页:工作台菜单 →服务端驱动导航;在线表单 →在线表单在移动端;出包上线 →多端构建与发布
- 移动端坑位 —— 按症状查(编号
M1–M24),动手前先扫标 🔴 的那几条
第一小时最容易撞的两堵墙
别跳过 pnpm install 直接 pnpm dev:pages.json / manifest.json 由 init-baseFiles 与插件生成,仓库里没有,缺了就是「找不到模块」而不是「请先安装」。别手改 src/pages.json:它是产物,改完会被下一次生成覆盖,要改路由去改 pages.config.ts 与各页的 definePage,而且改完必须重启 dev server。
5. 只排查问题
- 坑位索引 —— 按症状查;上线前只过标 🔴 的条目 + G 节指过来的移动端那几条
- 权限类:诊断与排错 —— 末尾那张「症状 → 根因 → 去哪查 → 报错吗」总表
- Agent 类:ReAct 循环与流式对话(先确定走的哪条路由)→ 计量与配额(那次调用有没有记账)
- 移动端类:移动端坑位 —— 按症状查(编号
M1–M24)。「列表页一直转圈」先看 M3,「点了没反应、只提示页面不存在」先看 M5 - 确认了机制再回到对应组的正文页
排查时最贵的一个错误
用超管账号自测。super_admin 走短路,压根不查授权——「我这儿一切正常」在权限问题上没有任何信息量。见坑位索引 D3。
页面骨架:每页怎么读
48 个正文页共用一套骨架,节名固定、顺序固定,所以你知道该跳到哪一节:
| 节 | 覆盖率 | 它回答 | 什么时候直接跳过来 |
|---|---|---|---|
| 它解决什么问题 | 46 / 48 | 这东西为什么存在、边界在哪 | 第一次接触一个域 |
| 代码在哪 | 44 / 48 | 文件与包的路径清单 | 已经知道要改什么,只想定位 |
| 核心机制 | 44 / 48 | 它怎么工作(含 Mermaid 图) | 要改行为、或要判断改动的影响面 |
| 数据模型 | 33 / 48 | 表结构、字段语义 | 要写 SQL、要加字段 |
| 接口清单 | 30 / 48 | 端点 + 权限串 | 要调接口、要给角色授权 |
| 怎么扩展 | 43 / 48 | 「加一个 X」的步骤 | 🎯 最常直接跳来的一节,任务索引里的链接大多指向它 |
| 坑与注意 | 45 / 48 | 贴着上下文的提醒 | 改之前扫一眼,改完对一遍 |
| 测试 | 32 / 48 | 这块有哪些测试、怎么跑 | 要加测试、或要判断某个结论可不可信 |
| 未证实 | 35 / 48 | 作者没能证实的推断 | 🔴 别把这节的内容当结论引用 |
| 相关页面 | 48 / 48 | 出边 | 读完一页往下走 |
覆盖率是实测的,分母是 48 个正文页——本页与任务索引是导航页,不套这套骨架;两份坑位索引(06-practice/pitfalls、05-mobile/pitfalls)是清单页,也刻意不套(它们本身就是「按症状查」,不需要「它解决什么问题」)。复核命令:
cd xpy-blog && for s in 它解决什么问题 代码在哪 核心机制 数据模型 接口清单 怎么扩展 坑与注意 测试 未证实 相关页面; do
printf '%s %s\n' "$s" "$(find docs/develop -name '*.md' ! -name index.md ! -name tasks.md \
-exec grep -l "^## $s" {} \; | wc -l)"
done「未证实」节是刻意存在的
本手册的纪律是结论必须实测,但实测有时做不到(要长跑、要真机、要生产流量)。这类内容不删、也不伪装成结论,而是集中放进「未证实」节并写明为什么没证实。看到它就当作「一个有依据的猜测」。
页头那行「适用端 / 类型」
48 个正文页的 H1 后面都有一行元信息,例:
> **适用端**:后端 + Web | **类型**:机制它回答的是**「我该不该看这页」**——在一个 48 页的手册里,读者最先要判断的不是内容对不对,而是「这页讲的是不是我要动的那一端」。
- 适用端 取
后端/Web/移动端,跨端用+连接。实测分布:后端 + Web16 页、后端13、Web8、移动端7、三端都涉及 3(仓库结构与改名约定、服务端驱动导航、坑位索引)、Web + 移动端1(在线表单在移动端)。 - 类型就是下面那四种口味,实测分布:机制 30、参考 8、操作 7、排错 3。
🔴 这行由 tools/check-pages.cjs 守着(pnpm check:pages):缺行、位置不在 H1 后第一行、或类型/适用端取了词表外的值,都会 exit 1。新增页必须带上它——48 页是脚本一次性注入的,没有门禁的话下一页就会漏,而漏了不报错,正是本仓其它守卫要消灭的那类静默漂移。词表若要扩,改 check-pages.cjs 的 KINDS / SCOPES 并同步改本节。
四种口味
- 机制 / 解释 —— 讲「为什么是这样」,篇幅长,适合通读(如 行过滤规则、安全与提示注入防护、服务端驱动导航)
- 操作 / 怎么做 —— 讲「照着做」,步骤化,适合跳读(如 新增一个菜单功能端到端、移动端环境准备与启动、多端构建与发布)
- 参考 / 清单 —— 表格为主,适合查(如 数据库约定与表总览、后端架构与模块依赖、四个「上游域」页的接口清单)
- 排错 —— 症状驱动,适合倒查(诊断与排错 + 两份坑位索引:全域、移动端)
⚠️ 两份坑位索引在这里归排错,不归参考。它们确实表格很多、确实适合查,但入口是「我遇到了什么症状」而不是「我要查某个值」——本页旧版本把它们同时列进两栏,读起来像是自相矛盾,已收进一栏。
写作与引用规范
- 代码定位用「文件路径 + 类名」,行号只标关键锚点。行号会随迭代漂移,路径与类名不会;
- 代码片段 ≤ 30 行,超出部分给路径让读者自己去看,不整段粘贴;
- 表结构、API 清单、权限串一律脚本生成(
tools/gen-*.mjs→docs/.generated/),不手抄,避免与代码不一致; - 图用 Mermaid,架构图 / 时序图 / ER 图全部代码化,便于随代码更新;
- 定制功能页首加
::: tip 定制功能,与上游自带能力区分; - 结论必须实测。「应该是」「大概会」这类推测不进正文,进「未证实」节;
- 不维护第二份目录:逐页清单只存在于
sidebar.mjs(有门禁),本页只写「每组为什么值得读」; - 正文不记行数:行数是漂移最快的一类数字,写进去就成了下一次的错。章节地图只记页数(改一次侧栏就能对齐),要量行数就现场量。上面那张自研占比表是个例外——它 drift 过两次,所以旁边直接附了复核命令。
本手册的事实来源
| 来源 | 用途 |
|---|---|
ainb/** 源码 | 类名、路径、注解、错误码原文 |
ainb-ui-admin-vue3/** 源码 | 组件路径、API 封装、权限指令 |
ainb-ui-admin-uniapp/** 源码 | 移动端页面与分包、easycom 组件、请求封装 |
数据库 ainb-professional-init | system_menu / system_nav_node 菜单清单、表结构、权限串、租户与账号数据 |
| 运行中系统(Web 前端 80 / 移动端 H5 9000 / 后端 48080) | 截图、真实响应、真实报错文案 |
别读旧库 ruoyi-vue-pro
本地还留着一个 ruoyi-vue-pro 库,它已停在 nav-node 特性之前——没有 system_nav_node,system_menu 里也没有「App菜单」。拿它生成菜单 / 表结构清单会得到过期结果。后端 application-local.yaml 与 application-dev.yaml 现在都指向 ainb-professional-init(application-prod.yaml 尚未跟随,仍是 ruoyi-vue-pro)。tools/lib/db.mjs 导出的 DB 常量是文档侧唯一口径,改库名只改那一处。
文档语料同步:root-path 已改为必填环境变量(它曾被配成 /,把库冻结在 115 篇)
后端 AgentDocProperties(enlev.agent.doc.*)早已不做向上探测了——第一版靠标记 ainb-ui-admin-vue3/docs 逐级向上找,现在 root-path 必须显式配,没配就抛 AGENT_DOC_ROOT_PATH_MISSING、不是目录则抛 AGENT_DOC_ROOT_PATH_INVALID。
🔴 而 c3dccc1a82 refactor(系统):区分专业版和开源版 曾把三个 profile 的 root-path 全改成 /(同一次提交还删掉了那段解释「为什么必填」的注释,于是配置里再没有任何线索说明 / 是错的)。这个值能通过校验 —— / 确实是个目录(Windows 下解析成当前盘根)—— 可 directories 默认是 ["guide", "develop"],/guide 与 /develop 都不存在,于是 planSync 对每个子目录各记一条「子目录不存在,已跳过」warning、continue 掉,最终语料扫出 0 篇,接口照样返回 200。
✅ 已修:三个 profile 现在都是 root-path: ${AGENT_DOC_ROOT_PATH:...}——local 的默认值是本机手册仓库位置(本站的 docs/guide 与 docs/develop 正好对上那两个子目录名),dev/prod 默认留空:没设环境变量就在第一次调用同步接口时抛 AGENT_DOC_ROOT_PATH_MISSING,而不是静默扫 /。base-url 三个 profile 都是 https://doc.enlev.cn,与前端引用白名单(ainb-ui-admin-vue3/src/utils/url.ts 的 MANUAL_DOC_HOSTS)是同一个事实的两份拷贝,改一边必须改另一边。
但改配置本身不会更新知识库:库里实测仍是 115 篇(root-path 还正确的那次同步灌进去的),本轮新增的 9 页移动端与 tasks.md 还没灌进去、改名的 4 页也还没删掉。实测差异是「语料 125 / 库里 115 / 对上 111」,跑 tools/gen-kb-url-map.mjs 复核。
🔴 而重跑同步也不足以让引用链接可用——线上站点本身还是坏的:托管端把 cleanUrls 的回落写成了 SPA 式 /index.html,对任意路径都返回 200 + 同一份首页,实测 125 条链接里 122 条落在首页(只有 /guide/、/develop/ 这类目录索引页因为命中 $uri/ 而幸免);且线上产物比语料旧(还有 05-practice,没有 06-practice 与 05-mobile)。所以「知识库的引用能点开正确的页」要三件都到位:① nginx 回落改回 try_files $uri $uri.html $uri/ =404;;② 重新构建并上传 dist/;③ 后端跑一次 doc-sync。验证一律用 pnpm check:live(比对渲染出的 <h1>,不看状态码——那台机器上什么都是 200),细节见构建与部署。
🔴 重跑同步曾经是破坏性的:planSync 的 REMOVED 差集判据是「库里有、这次没扫到」,doSync 对 REMOVED 一律删除并连带清掉段落与向量。差集的右项一旦因为 root-path 配错变成空集,左项就整个变成待删清单——在那个状态下跑一次 doc-sync,那 115 篇会被全部删掉,而向量库写入不在事务里、删掉补不回来。
✅ 4093e19c7d(2026-10-25)加了硬守卫:扫到 0 篇即中止整个同步、连知识库都不建,并把「拦下了几篇的删除」写进 failures。所以配错 root-path 的最坏后果已从「清空内置知识库」降级为「同步报错、什么都没做」。
但**「先 plan 再 sync」这一步不能省**:守卫只拦「一篇都没扫到」,拦不住「扫到一半」——例如 root-path 指到一个只 checkout 了一半的仓库,此时 develop 整个目录会被判成待删,而那是守卫刻意放行的(拦了就等于「真的删掉整个目录」永远传播不到库里)。doc-sync-plan 会如实显示 removedCount 与「子目录不存在,已跳过」的 warning,那是唯一能看出「这次扫描不完整」的地方。机制详见架构总览 · 坑与注意第 1 条。
