结论先行
文档上传页面最容易被低估。很多人觉得做个 dropzone、接个上传接口、列表里刷新一下就够了。企业版 website_documents 告诉你完全不是:拖拽上传只是最表面的动作,真正决定这条链稳定性的,是文档所属网站、public/portal 路由分流,以及 member token 的访问边界。
第一层:入口或表面动作
模型层先守第一道闸。website_documents.documents_document._compute_website_id() 会根据 company 和 website 自动推导文档所属站点;_check_website_id() 则禁止把文档挂到不属于当前公司可见范围的网站上。也就是说,网站文档并不是“普通 document 多一个 website_id 字段”,而是多了站点与公司的一致性约束。
第二层:真正的业务护栏
前端列表层也体现出这个边界。documents_list_controller.js 只是把 website_id 加进 internal-only columns,说明界面可以编辑/查看站点字段,但真正的权限判断不在 JS。浏览器负责呈现,最终裁决在模型与路由。
第三层:状态落点与边界
路由层更关键。documents.controllers.documents.documents_home() 会根据 access_token、当前用户是否 public、是否携带 member_signup_token 决定走 public view、portal view,还是重定向去 signup;_documents_render_public_view() 与 _documents_render_portal_view() 又分别输出完全不同的页面能力。再往下,documents_access._get_member_from_token() / _get_signup_url() 用成员 token 把“知道链接”和“被允许作为成员访问”区分开。于是一个文档链接在浏览器里能打开什么,不是前端自己推断的,而是路由按 token 和身份严格分层。
为什么这套设计更稳
所以“上传”这件事只是入口。真正的链路是:上传/创建文档 → 绑定正确 website/company → 通过分享链接或成员 token 暴露 → public/portal 界面按权限降级展示。你如果只做一个拖拽组件,把 access token 直接暴露给任何用户,马上就会踩到多公司、站点错绑、匿名访问越权这些坑。
实战启示
企业版这套实现很适合前端分类,因为它提醒我们:用户看见的是一个上传页面,系统维护的却是一条权限中继链。拖进去的是文件,接住它的是网站、公司、token 和路由规则。
DISCUSSION
评论区