FILE_REF_CONSUMERS
FILE_REF_CONSUMERS = ['site.logo' => '登录页与后台顶栏 Logo', 'site.favicon' => '浏览器标签页图标(favicon)']
配置 key → 功能说明(文件引用「被什么功能用」的消费点标注)。 新增 image/file 配置项并接功能时在此补充;未覆盖项回落配置定义 remark,再空则 null。
文件维护服务
职责:归属校验——列表经 DataScopeService 过滤;单个文件的读/改/删/复制操作 统一走 assertOwned()(非豁免操作者只能操作自己 user_id 的文件), 豁免语义(超管或 is_exempt 角色)与列表过滤一致。 公共目录模型——td_sys_file_folder.user_id=0 即公共目录树,全员可见; 豁免管理员拥有全部公共目录的完整维护权(is_file_maintainer 标志已废弃)。
┌────────────────── 分区导航(按此顺序阅读)──────────────────┐ 分区 1 · 列表与统计(读) list / trashList / usage / userStats / overview / duplicates / danglingRefs 分区 2 · 目录管理 folders / folderCreate / folderUpdate / folderDestroy 分区 3 · 公共目录 publicFolderCreate / publicFolderUpdate / publicFolderDestroy 分区 4 · 下载与引用(读) preview / download / refs 分区 5 · 上传与编辑 create / update / rename 分区 6 · 复制与移动 copy / batchCopy / move / batchMove 分区 7 · 回收站 trash / restore / destroyPermanently / batchTrash batchRestore / purgeTrash / cleanTrash 分区 8 · 内部辅助 attachRefCounts / buildQuery / assertVisible / assertOwned filterOwnedIds / effectiveQuota / assertQuota / quotaUsedBytes assertQuotaWithUsed / assertNotDescendant / assertFolderParent assertPublicFolderManager / assertPublicFolderParent / assertNotPublicFolder isPublicMaintainer / publicFolderIds / folderIsPublic assertFolderNameUnique / assertFolderOwned / refConsumer └────────────────────────────────────────────────────────────┘
$publicFolderIdsMemo : ?array
实例级公共目录 id 缓存(勿用 static——static 跨请求/测试进程残留会读到过期 id)
paginate(array $filters = [], int $perPage = 15) : \Illuminate\Contracts\Pagination\LengthAwarePaginator
文件列表(分页,附每条文件的引用计数 file_refs_count)。
| array | $filters | keyword / file_type / folder_id / source / user_id(豁免) / min_size / created_from / created_to / orphan / sort / order |
| int | $perPage |
overview() : array
全站文件概览聚合(运维台账顶栏数据源)。
全站口径:total_(正常文件数/总字节)、today_count(今日新增)、 trash_(回收站数量/字节)、user_count(正常文件去重上传者数)、 orphan_count(正常文件中无引用数,同 orphan 筛选语义)、 dup_group_count/dup_wasted_bytes(内容哈希重复族数/浪费字节=(族内数量-1)×族内 MAX(file_size))、 dangling_count(悬空引用去重文件 id 数:记录缺失或已在回收站)、 by_type(正常文件按 file_type 聚合 size/count,按 size 降序)。 豁免专属——非豁免抛 403;挂 sys:file:stats 缓存(依赖 td_sys_files,增删改自动失效)。
danglingRefs() : array
配置侧悬空引用清单(运维台账「悬空引用」视图数据源)。
从配置侧出发:已存值(join 定义,type image/file)+ 定义默认值两来源, 按数值型文件 id 聚族(refs 结构与 refs() 一致,consumer 带上); 逐 id 批量比对 td_sys_files:记录不存在=missing(file_name=null)、 file_status=回收站=trash(带 file_name);只返回悬空项,按 file_id 降序。 豁免专属——非豁免抛 403;挂 sys:file:stats 缓存(实际 key = sys:file:stats:dangling-refs)。
folders(int $userId) : array
目录列表(平铺,parent_id 组装由前端完成;附每目录正常文件计数)。
返回双分区(公共目录模型):personal=个人空间(一人一棵树, 豁免管理员传 userId 可看任意用户目录);public=公共空间(user_id=0 系统树, 全员可见、豁免/维护标志用户可写)。公共目录附 is_public=1 与 can_write, can_manage_public=操作者对公共空间的完整管理权(建/改/删目录 + 目录内文件写操作)。
| int | $userId |
publicFolderUpdate(\App\Models\SysFileFolder $folder, array $data) : \App\Models\SysFileFolder
更新公共目录(重命名/移父级/排序/启停)。防环与个人目录同规(user_id=0 树内判链)。 公共编排单源:updateFolderCommon。
| \App\Models\SysFileFolder | $folder | |
| array | $data |
无权管理或目标非法时抛错
preview(\App\Models\SysFile $file, string $variant = 'original') : array
图片内联预览(净化/缩略图体系配套)。
variant=thumb 取缩略图变体(无则回落原图);仅图片类允许内联(防 SVG/HTML 内联执行面, 且上传白名单本就不含 svg);其余类型引导走鉴权下载。可见性校验与下载同规。 HTTP 响应组装(StreamedResponse)归位 Controller,Service 只回文件指针信息。
| \App\Models\SysFile | $file | |
| string | $variant |
非图片/无权访问
download(\App\Models\SysFile $file) : array
鉴权下载:仅正常状态且通过**可见性**校验的文件可下载, 响应为强制附件下载(Content-Disposition: attachment),不内联渲染——私有盘无公开 URL。 共享文件所有登录用户可下载引用;操作权仍仅本人 + 豁免。 HTTP 响应组装归位 Controller,Service 只回文件指针信息。
| \App\Models\SysFile | $file |
无权访问抛 403,文件状态异常抛 400
create(\Illuminate\Http\UploadedFile $file, string $fileType, string|null $fileTarget = null, int $isShared = SysFile::SHARED_NO, string $scene = 'default', int $folderId, string $fromPath = '') : array
上传文件(凡上传必登记:场景烙印 + 逻辑目录 + 来源页面)。
| \Illuminate\Http\UploadedFile | $file | 上传文件实例 |
| string | $fileType | 文件分类 |
| string|null | $fileTarget | 文件标签 |
| int | $isShared | 共享状态(0=私有 1=共享,默认私有) |
| string | $scene | 上传场景(FileScene 注册表 key) |
| int | $folderId | 归属目录 id(0=根目录;非 0 须为本人目录) |
| string | $fromPath | 来源页面路径(前端自动捕获) |
rename(\App\Models\SysFile $file, string $fileName) : \App\Models\SysFile
重命名(修改原始文件名)。
扩展名闸门:新扩展名必须在分类白名单内(FileUploadHelper::isAllowedExtension), 堵住「上传 txt → 改名 md/html」类绕过——预览端按扩展名选渲染器, 非法扩展名可触发非预期渲染路径(存储型 XSS 面)。
| \App\Models\SysFile | $file | |
| string | $fileName |
无权操作时抛 403 / 扩展名不允许
applyVisibility(mixed $query, array $filters, int $status) : void
可见性过滤(单源:台账/回收站两态共用编排): 豁免管理员=全部,且可按上传者筛选(台账维度); 非豁免=本人上传 OR 共享(is_shared=1)OR 公共目录(公共目录文件全员可见)—— 替代原 DataScopeService 组织轴过滤,文件模块自身的可见性模型为「本人+共享+公共」; 回收站是操作区:非豁免仅见自己的(别人的共享/公共文件不会出现在你的回收站), 维护标志用户额外可见公共目录中的回收站文件(自己移入的要能恢复/清空)。
| mixed | $query | |
| array | $filters | |
| int | $status |
updateFolderCommon(\App\Models\SysFileFolder $folder, array $data, int $ownerUserId, callable $assertParent) : \App\Models\SysFileFolder
目录更新公共编排(单源:folderUpdate/publicFolderUpdate 共用)。 防环(不能移动到自身/后代下)、同级重名、白名单 update; 权限校验(归属/公共管理)由调用方先行完成。
| \App\Models\SysFileFolder | $folder | |
| array | $data | |
| int | $ownerUserId | |
| callable | $assertParent |