SpringBoot3+Vue3+OnlyOffice 项目文档协同:folder schema、ACL 与云盘分轨怎么配
🌐 文档地址:https://ruoyioffice.com
📦 源码1·GitHub:https://github.com/yuqing2026/ruoyi-office
📦 源码2·GitCode:https://gitcode.com/zhouzhongyan/ruoyi-office
📦 源码3·Gitee:https://gitee.com/yqzy1688/ruoyi-office
💬 微信:17156169080(备注「RuoYi Office」)
立项合同在审批附件里,周报在任务评论,图纸在某个人的网盘。验收那天谁也拼不出完整资料袋。RuoYi Office 把项目资料做成一棵属于项目的树:类型配置先预制目录,过程单据自动归夹,ACL 三档决定谁能打开 OnlyOffice。它不是 OA 云盘换皮。

▲ 项目资料里点开 docx,右边就是 WPS 文字窗口在改正文;后台走 OnlyOffice,不进个人网盘
引言:过程附件为什么不能进个人云盘
OA 云盘解决「公司文件打开即编」。项目现场要的是另一件事:这份 Word 属于哪个项目、该进哪一层目录、任务关闭后还能不能找到。
| 「图纸在老王收藏夹」 | 老王离职,收藏夹权限一起走 |
| 「立项扫描件在审批附件」 | 台账详情打不开,验收要翻流程 |
| 「改个目录名,历史文件全乱」 | 没有系统夹/类型夹,全是手工文件夹 |
| 「外包能看,不能改正文」 | 分享三档对不上项目角色 |
一句话:云盘按人/组织分享;项目资料按项目空间 + 过程来源 + ACL。 OnlyOffice 只是打开方式,不是归属。
技术栈锚点:Spring Boot 3.5 + Vue3 Vben + OnlyOffice。下文写类名和方法。
一、先选入口:三页不要配成同一件事
| 项目类型配置 | /project/base-config/type-config | folder schema、自定义字段,实施时配一次 |
| 项目文档中心 · 查询统计 | /project/project-document | 跨项目检索、来源分布 |
| 项目文档中心 · 文档管理 | 同上,先选项目再切 Tab | 本项目树、上传、ACL、打开编辑 |
| 台账详情 · 项目文档 | /project/project-ledger-detail?id= | 同一套 DocumentLibrary;能力包关掉则 Tab 不出现 |
| 独立编辑页 | /project/document-editor?id=&editable=1 | 隐藏路由,OnlyOffice 画布 |
菜单在「项目 → 项目文档」「项目 → 基础配置 → 项目类型配置」。台账详情的资料库 Tab 才是日常办理页。

▲ /project/base-config/type-config 打开类型表单。目录名称可加子目录,深度锁在 3。这是预制,不是运行时随手建夹
二、folder schema:类型先画树,立项再 sync
FolderSchemaEditor 是递归组件:一层一个名称,点「添加子目录」才下钻,depth < 3 才显示按钮。存进 ProjectTypeConfigDO.folderSchema。
立项或首次打开资料库时,ProjectDocumentServiceImpl.ensureSpace 做三件事:
private void syncTypeFolders(Long projectId) {
ProjectDocumentDO root = documentMapper.selectRoot(projectId);
ProjectLedgerDO ledger = ledgerMapper.selectById(projectId);
if (root == null || ledger == null || ledger.getProjectType() == null) {
return;
}
ProjectTypeConfigDO config = projectTypeConfigService.getTypeConfigByType(ledger.getProjectType());
if (config == null || CollUtil.isEmpty(config.getFolderSchema())) {
return;
}
int sort = 32;
for (ProjectTypeFolderNode node : coerceFolderSchema(config.getFolderSchema())) {
applyTypeFolder(projectId, root.getId(), node, sort, 1);
sort += 1;
}
}
applyTypeFolder 按名称找子夹,没有才 insert,深度超过 3 直接 return。改类型 schema 不会删已经有文件的夹,只补缺失节点。实施可以后补「设计图纸 / 会议纪要」,已传文件还在原夹。
阶段任务(taskType=1)另走 syncPhaseFolders。阶段改名会改夹名;阶段删除时,孩子被挪到「未归类」,夹本身删除。不要手删系统夹去「整理界面」。
| 根 | root | ensureSpace | 否 |
| 立项 / 变更 / 验收 / 未归类 | apply / change / acceptance / uncategorized | ensureSpace | 否 |
| 阶段 | phase | 同步阶段任务 | 随阶段删,孩子进未归类 |
| 类型预制 | type | syncTypeFolders | 不随 schema 自动删 |
| 手建 | custom | 有管理权的人 | 可以 |
三、多来源归集:过程件进树,不要再传一遍
ProjectDocumentSourceType 把来源写成六种:upload 资料库上传,task 任务,execution 执行,progress 进度,apply 立项,change 变更。后五种 isProcess=true。
立项/变更审批通过后,indexBillAttachments 把单据附件索引进对应系统夹。任务、执行、进度各自挂钩 taskId / executionId。文档中心的「查询」Tab 按来源、分类、文件名搜;「资料库」Tab 才是树。

▲ /project/project-document 的「查询统计」:来源分布 + 文件表。这是唯一列表形态;树在「文档管理」Tab
分类字典 project_doc_category:资料 / 图纸 / 规范 / 会议 / 报表 / 其他。分类是检索标签,不能替代 folder schema。图纸既要进「设计图纸」夹,也可以打 drawing 标签。
单文件上限 100MB。超了在 insertFile 抛业务异常,不要靠浏览器卡死当门禁。
四、ACL 三档:角色默认值,显式授权才能收紧
权限枚举只有三档,不要发明第四档「可下载不可看」:
| 0 | 仅查看 | 预览、下载、打印 |
| 1 | 可编辑 | 替换源文件、打开 OnlyOffice 编辑 |
| 2 | 可管理 | 建夹、改 ACL、删(系统夹除外) |
resolvePermission 先确认你是项目成员、PMO 或管理员,再取 角色默认值 和 ACL 行 的较大值。文件夹若被标成受限(visibility=1 或祖先受限),非管理员只认 ACL,角色默认值作废。只读项目再 min 到查看。
private int resolvePermission(ProjectDocumentDO doc, AccessContext ctx) {
if (!ctx.member && !ctx.pmo && !ctx.manager) {
return NONE;
}
boolean restricted = isRestricted(doc, ctx);
int acl = resolveAclPermission(doc, ctx);
int roleDefault = ctx.manager ? MANAGE.getPermission()
: roleDefaultPermission(ctx.roleType, doc, ctx);
int effective = Math.max(roleDefault, acl);
if (restricted && !ctx.manager) {
effective = acl;
}
if (ctx.readonly) {
effective = Math.min(effective, VIEW.getPermission());
}
return effective;
}
角色默认值:项目经理管理、技术成员编辑、普通成员仅自己上传的能编、观察者查看。PMO 但不是成员时默认查看。
ACL 主体三种:0 用户、1 部门、2 岗位。给用户授权时,对方必须已是项目成员,否则 PROJECT_DOCUMENT_ACL_NOT_MEMBER。inheritPermission 默认 true:子节点向上走最多 30 层,祖先行若关掉继承就跳过。
saveAcl 只有当前有效权限 ≥ 管理才能写。写完会 markRestricted,避免「配了外包查看,角色默认值又把编辑加回来」。

▲ 先选项目再切「文档管理」。树是系统夹 + 类型夹;有管理权才能开权限。台账详情同名 Tab 受能力包控制,文档中心这条更稳
部门可见 deptVisible=1 时,非管理员还必须在上传部门的组织线上,否则直接 NONE。它和 ACL 是两道门,不要只配一道。
五、OnlyOffice 分轨:bizType 不是云盘
独立页 /project/document-editor 在 core.ts 里注册为隐藏路由,不进菜单。ProjectDocumentOnlyOfficeServiceImpl.BIZ_TYPE = "project_document"。下载和回调都挂 /admin-api/project/document/onlyoffice/*。
打开前先 checkPermission。历史版本强制 editable=false。扩展名不在可编名单就抛「类型不支持」,不要把 PDF 当 Word 打开。
boolean canEdit = !historyView
&& editable
&& ProjectDocumentAclPermissionEnum.canOnlineEdit(permission)
&& FileOnlineEditUtils.isEditableExtension(
StrUtil.blankToDefault(doc.getFileExt(), doc.getFileName()));
documentKey 用 project_document + 文档id + 版本 + fileUrl + 本次 openId 做种子,避免和 OA 云盘同一文件 URL 抢同一把 key。回调回写走 updateFileUrlAfterEdit,归档件直接 return,不会覆盖。
| 把项目 Word 丢进 OA 云盘再分享 | ACL 与项目角色脱节,验收搜不到来源 |
| 用云盘 JWT 调项目回调 | bizType 对不上,回写落空 |
| 观察者角色却指望打开即编 | 默认只有查看,要显式 ACL=1 |
| 把编辑页当菜单项 | 没有项目上下文,树和权限都丢 |
云盘文讲 documentKey、双 JWT、forcesave。本文只要求你记住:项目资料的打开即编认资料库 ACL,认 project_document。

▲ /project/document-editor?id=&editable=1。画布是 Word/Excel,不是资料树。未启文档服务器或无 docx 时会失败,不要用云盘编辑器充数
六、设计决策:为什么不复用云盘目录
| 空间粒度 | 一项目一棵树 | 验收、权限、检索都以项目为界 |
| 目录从哪来 | 系统夹 + 类型 schema + 阶段 | 实施配类型,业务少手建 |
| 过程件 | 索引进树,不复制二进制 | 审批附件和资料库同一 URL |
| 权限 | 角色默认 ∪ ACL,受限则只认 ACL | 外包目录必须收得住 |
| 在线编 | 独立 bizType | 和云盘版本链、组织分享隔离 |
| 阶段删除 | 孩子进未归类 | 不丢文件,不留空阶段夹 |
前端按钮看 canEdit / canDelete。归档后两者都假。系统夹即使你是管理者也不能删。
七、核心实现再拆两刀
7.1 首次进入一定先 ensureSpace
文档中心和台账资料库打开时都会确保空间存在。只调上传、不调 ensure,会把文件插到不存在的 parent。类型后补 schema,下一次进入会 syncTypeFolders 补夹,不必重建项目。
7.2 替换源文件也要编辑权
replaceSource 与在线编辑同一档权限。有人用「重新上传」绕过 OnlyOffice,权限门必须同一把。所有者在非受限树上可以编辑自己的文件,但不能改 ACL。
7.3 查询 Tab 与资料库 Tab
查询面向「文件名 / 来源 / 分类」扁平检索,适合找某张扫描件。资料库面向树和权限。实施培训时先带人走类型配置 → 台账资料库,再教跨项目检索。不要把查询结果当权限模型讲。
八、数据结构(够用即可)
主表 project_document:项目、父节点、节点类型(夹/文件)、folderKind、来源、版本号、归档、可见性、部门可见。ACL 表 project_document_acl:documentId、targetType、targetId、permission、inheritPermission。版本表只服务历史预览,不改当前指针——和云盘「恢复即新版本」是不同模块,不要混引用。
类型配置 folderSchema 是 JSON 数组,节点只有 name + children。不要在 schema 里写权限,权限永远落在运行时树上。
九、技术亮点
| 类型预制目录 | folderSchema ≤3 层,sync 补齐 | 同类项目资料袋长一样 |
| 系统夹不可删 | ROOT/APPLY/CHANGE/… | 验收路径稳定 |
| 过程件索引 | sourceType + sourceId | 审批附件和树能对上 |
| 阶段删不丢文件 | 孩子进未归类 | 改 WBS 不怕丢图纸 |
| 角色 ∪ ACL | max;受限只认 ACL | 外包目录可收紧 |
| ACL 继承 | 向上 30 层,可关 inherit | 少配叶子 |
| OnlyOffice 分轨 | bizType=project_document | 不和云盘抢 key |
| 独立编辑路由 | /project/document-editor | 画布全屏,树还在台账 |
十、快速体验
在线演示:https://ruoyioffice.com/web/(账号 admin / admin123)
源码仓库:GitHub:https://github.com/yuqing2026/ruoyi-office | GitCode:https://gitcode.com/zhouzhongyan/ruoyi-office | Gitee:https://gitee.com/yqzy1688/ruoyi-office
本地:后端单体启动模块(默认 48080),前端 pnpm dev:antd,OnlyOffice 按基础设施在线文档配置启用。没启文档服务器时,资料树和 ACL 仍可配,只是编辑页会失败。
常见问题(FAQ)
项目文档和 OA 云盘哪个用来改合同正文?
公司制度、模板、跨项目制度进云盘。属于某个项目的过程件进项目资料库。合同模块还有自己的起草/模板链。三套不要互相当备份盘。
folder schema 改了,已经开工的项目会怎样?
下次 ensureSpace / syncTypeFolders 只补缺失夹,不删已有夹,也不搬已有文件。改名不会自动重命名运行时夹,要在资料树里手工处理。
观察者为什么打不开「编辑」?
角色默认是查看。要编辑必须 ACL≥1,且文件未归档、扩展名可编。只把云盘分享开成「可编辑」对项目树无效。
立项附件为什么资料库看不见?
看流程有没有走到会调用 indexBillAttachments 的节点,以及项目是否已 ensureSpace。附件还在审批单上,只是没索引进树。
PMO 能改 ACL 吗?
仅 PMO、不是成员、也不是管理员时,默认查看。要管理目录,进项目成员并给经理角色,或对那一夹显式 ACL=2。
结语
项目文档协同要配的不是「再接一个在线编辑器」,而是三件事选对:类型 schema 决定资料袋长什么样;来源索引决定过程件去哪一夹;ACL 三档决定谁能打开 OnlyOffice。云盘继续管组织文件。两套 bizType,两套权限,不要图省事并成一个网盘。
读者能带走的三句:目录在类型上配;过程件靠索引;打开即编认资料库 ACL。你们现在验收找图纸,是进项目树,还是问「谁电脑上有」?评论区对照一下。
相关阅读:同目录企业云盘全景讲组织分享与版本;甘特图文讲任务条,不讲资料树。那些文章不替代本页的 folder schema。
💡 想要体验 RuoYi Office 的强大功能?
🌐 在线演示:https://ruoyioffice.com/web/(账号 admin / admin123)
📦 源码仓库:GitHub:https://github.com/yuqing2026/ruoyi-office | GitCode:https://gitcode.com/zhouzhongyan/ruoyi-office | Gitee:https://gitee.com/yqzy1688/ruoyi-office
💬 技术咨询:添加微信 17156169080,备注「RuoYi Office」
⭐ 如果觉得不错,请给个 Star 支持一下!





