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做三件事:
- 没有根节点就建根,名称用项目名。
- 固定插入系统夹:立项资料、变更资料、验收资料、未归类。
- 读类型配置,把 schema 落成
folderKind=type的夹。
privatevoidsyncTypeFolders(LongprojectId){ProjectDocumentDOroot=documentMapper.selectRoot(projectId);ProjectLedgerDOledger=ledgerMapper.selectById(projectId);if(root==null||ledger==null||ledger.getProjectType()==null){return;}ProjectTypeConfigDOconfig=projectTypeConfigService.getTypeConfigByType(ledger.getProjectType());if(config==null||CollUtil.isEmpty(config.getFolderSchema())){return;}intsort=32;for(ProjectTypeFolderNodenode:coerceFolderSchema(config.getFolderSchema())){applyTypeFolder(projectId,root.getId(),node,sort,1);sort+=1;}}applyTypeFolder按名称找子夹,没有才 insert,深度超过 3 直接 return。改类型 schema不会删已经有文件的夹,只补缺失节点。实施可以后补「设计图纸 / 会议纪要」,已传文件还在原夹。
阶段任务(taskType=1)另走syncPhaseFolders。阶段改名会改夹名;阶段删除时,孩子被挪到「未归类」,夹本身删除。不要手删系统夹去「整理界面」。
| 夹种类 | folderKind | 谁创建 | 能不能删 |
|---|---|---|---|
| 根 | 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 三档:角色默认值,显式授权才能收紧
权限枚举只有三档,不要发明第四档「可下载不可看」:
| permission | 名称 | 能做什么 |
|---|---|---|
| 0 | 仅查看 | 预览、下载、打印 |
| 1 | 可编辑 | 替换源文件、打开 OnlyOffice 编辑 |
| 2 | 可管理 | 建夹、改 ACL、删(系统夹除外) |
resolvePermission先确认你是项目成员、PMO 或管理员,再取角色默认值和ACL 行的较大值。文件夹若被标成受限(visibility=1或祖先受限),非管理员只认 ACL,角色默认值作废。只读项目再min到查看。
privateintresolvePermission(ProjectDocumentDOdoc,AccessContextctx){if(!ctx.member&&!ctx.pmo&&!ctx.manager){returnNONE;}booleanrestricted=isRestricted(doc,ctx);intacl=resolveAclPermission(doc,ctx);introleDefault=ctx.manager?MANAGE.getPermission():roleDefaultPermission(ctx.roleType,doc,ctx);inteffective=Math.max(roleDefault,acl);if(restricted&&!ctx.manager){effective=acl;}if(ctx.readonly){effective=Math.min(effective,VIEW.getPermission());}returneffective;}角色默认值:项目经理管理、技术成员编辑、普通成员仅自己上传的能编、观察者查看。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 打开。
booleancanEdit=!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)
项目 → 基础配置 → 项目类型配置,打开一个类型,看 folder schema 是否已有两层目录。项目 → 项目台账进详情,切到资料库,核对系统夹和类型夹是否都在。- 对「设计图纸」一类夹打开权限,看三档和继承勾选。
- 点一份 docx「在线编辑」,应进
/project/document-editor,不要跳到云盘。 - 回到
项目 → 项目文档用文件名搜,确认来源列能区分上传和立项。
源码仓库: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 支持一下!