feat(files): add v2 task and metadata workflows

This commit is contained in:
yoyuzh
2026-04-09 00:42:41 +08:00
parent c5362ebe31
commit 977eb60b17
60 changed files with 5218 additions and 72 deletions

View File

@@ -457,3 +457,110 @@
- 2026-04-08 阶段 3 第三小步 API 补充:新增 `PUT /api/v2/files/upload-sessions/{sessionId}/parts/{partIndex}`,请求体为 `{ "etag": "...", "size": 8388608 }`,用于记录当前用户上传会话的 part 元数据并返回 v2 会话响应。该接口会校验 part 范围和会话状态,当前只更新 `uploadedPartsJson`,不接收或合并真实文件分片内容。
- 2026-04-08 阶段 3 第四小步 API 补充:本小步没有新增对外 API。后端新增上传会话过期清理任务只处理未完成且已过期的会话并把它们标记为 `EXPIRED`;已完成会话和旧 `/api/files/**` 上传接口响应不变。
- 2026-04-08 阶段 4 第一小步 API 补充:本小步没有新增存储策略管理 API。v2 上传会话响应新增 `storagePolicyId`,用于标识该会话绑定的默认存储策略;当前该字段只服务后续 multipart/多策略迁移,旧 `/api/files/**` 上传下载接口响应不变。
## 2026-04-08 阶段 5 文件搜索第一小步
`GET /api/v2/files/search`
说明:
- 需要登录,且只返回当前用户自己的未删除文件或目录。
- 返回 v2 envelope`data` 结构复用 `PageResponse<FileMetadataResponse>``items``total``page``size`
- 支持查询参数:`name``type``sizeGte``sizeLte``createdGte``createdLte``updatedGte``updatedLte``page``size`
- `type` 支持 `file``directory``folder``all`;时间参数使用 ISO 日期时间格式,例如 `2026-04-08T12:00:00`
- 当前搜索只基于 `StoredFile` 固定字段,不启用标签或 metadata 条件过滤;旧 `/api/files/list` 与上传下载分享接口保持不变。
## 2026-04-08 阶段 5 文件搜索第二小步
- 前端通过 `front/src/lib/file-search.ts` 接入 `GET /api/v2/files/search`,该 helper 会拼接 `name``type``sizeGte/sizeLte``createdGte/createdLte``updatedGte/updatedLte``page``size`,并复用 `apiV2Request()` 的生产端点、认证与 client id 头。
- `front/src/pages/Files.tsx` 的桌面端文件页新增独立搜索视图,搜索结果不写入 `getFilesListCacheKey(...)`,清空搜索后回到当前目录列表;移动端搜索尚未接入。
## 2026-04-08 阶段 5 分享二期后端最小骨架
`POST /api/v2/shares`
需要登录。
- 为当前用户自己的非目录文件创建分享。
- 请求字段:`fileId`,以及可选的 `password``expiresAt``maxDownloads``allowImport``allowDownload``shareName`
- 密码只保存 hash不在响应中返回。
`GET /api/v2/shares/{token}`
公开访问。
- 返回分享摘要。
- 如果分享设置了密码,在校验前不返回 `file` 详情。
`POST /api/v2/shares/{token}/verify-password`
公开访问。
- 校验分享密码,成功后返回可读分享摘要。
- 响应永不返回 `passwordHash`
`POST /api/v2/shares/{token}/import`
需要登录。
- 把分享文件导入当前用户网盘。
- 分享过期、密码错误或未提供、`allowImport=false``maxDownloads` 已耗尽时拒绝导入。
`GET /api/v2/shares/mine`
需要登录。
- 分页列出当前用户创建的分享。
`DELETE /api/v2/shares/{id}`
需要登录。
- 只删除当前用户自己的分享。
-`/api/files/share-links/**` 接口保留兼容;当前 `allowDownload` 已落库并返回,但还没有独立 v2 下载路由消费它。
## 2026-04-08 阶段 5 文件事件流最小闭环
`GET /api/v2/files/events?path=/`
说明:
- 需要登录,返回 `text/event-stream`
- 请求头支持 `X-Yoyuzh-Client-Id`
- 首次连接会先推送一个轻量 `READY` 事件
- 事件写入 `FileEvent` 表,字段包含 `userId``eventType``fileId``fromPath``toPath``clientId``payloadJson``createdAt`
- 当前后端已做同用户广播、路径前缀过滤和同 `clientId` 自身事件抑制
- 前端通过 `front/src/lib/file-events.ts` 以 fetch stream 订阅该 SSE复用鉴权与 `X-Yoyuzh-Client-Id` 请求头;桌面 `Files` 与移动 `MobileFiles` 收到变更事件后会失效当前目录缓存并刷新当前目录列表
## 2026-04-08 阶段 6 任务框架与 worker 后端最小骨架
`GET /api/v2/tasks`
需要登录。分页列出当前用户自己的后台任务。
`GET /api/v2/tasks/{id}`
需要登录。只返回当前用户自己的任务详情。
`DELETE /api/v2/tasks/{id}`
需要登录。取消当前用户自己的任务,`QUEUED` / `RUNNING` 会转为 `CANCELLED` 并写入 `finishedAt`,终态任务保持原样。
`POST /api/v2/tasks/archive`
需要登录。创建 `ARCHIVE` 类型的 `QUEUED` 任务;`fileId` 必须属于当前用户且未删除,`path` 必须匹配服务端派生逻辑路径,暂允许文件和目录;当前 worker 只做 no-op 占位完成,不执行真实压缩。
`POST /api/v2/tasks/extract`
需要登录。创建 `EXTRACT` 类型的 `QUEUED` 任务;`fileId` 必须属于当前用户且未删除,`path` 必须匹配服务端派生逻辑路径,并拒绝目录和非压缩包类文件;当前 worker 只做 no-op 占位完成,不执行真实解压。
`POST /api/v2/tasks/media-metadata`
需要登录。创建 `MEDIA_META` 类型的 `QUEUED` 任务;`fileId` 必须属于当前用户且未删除,`path` 必须匹配服务端派生逻辑路径并拒绝目录和非媒体类文件worker 会重新按 `userId + fileId` 加载文件,写入 `media:contentType``media:size`,对 ImageIO 可识别图片额外写 `media:width``media:height`。当前仍不做缩略图、视频时长或前端任务面板。
补充说明:
- worker 会定时领取少量 `QUEUED` 任务并切换为 `RUNNING`,完成后标记 `COMPLETED`,异常时标记 `FAILED` 并写入 `errorMessage`
- 已取消或其他终态任务不会被重新执行。
- 创建成功后的任务 state 使用服务端文件信息,至少包含 `fileId``path``filename``directory``contentType``size`
- 桌面端 `Files` 页面会拉取最近 10 条任务、提供 `QUEUED/RUNNING` 取消按钮,并可为当前选中文件创建 `MEDIA_META` 任务;移动端与 archive/extract 的前端入口暂未接入。

View File

@@ -453,3 +453,37 @@ Android 壳补充说明:
- 2026-04-08 `files/storage` 合并补充S3 存储实现拆出多吉云临时密钥客户端与运行期会话提供器。`S3FileContentStorage` 现在通过 `S3SessionProvider.currentSession()` 获取当前 bucket、`S3Client``S3Presigner`,避免每次操作重复内联多吉云 token 解析逻辑;测试环境可直接注入 mock S3 client/presigner。该改动没有引入 multipart仍是单对象 PUT/HEAD/GET/COPY/DELETE 路径。
- 2026-04-08 阶段 4 第二小步补充:`FileService` 在创建新的 `FileEntity.VERSION` 时会通过 `StoragePolicyService.ensureDefaultPolicy()` 写入默认 `storagePolicyId``FileEntityBackfillService` 对历史 `FileBlob` 回填新实体时也写入同一默认策略。复用已有实体时保持原策略字段不变,只增加引用计数,避免在兼容迁移阶段覆盖历史数据。
- 2026-04-08 阶段 4 第三小步补充:管理台新增只读存储策略列表。`AdminController` 暴露 `GET /api/admin/storage-policies``AdminService` 通过白名单 DTO 返回策略基础字段和结构化 `StoragePolicyCapabilities`;前端 `react-admin` 新增 `storagePolicies` 资源展示能力矩阵。该能力只做配置可视化,不改变旧上传下载路径,不暴露凭证,不启用策略编辑或 multipart。
## 2026-04-08 阶段 5 文件搜索第一小步
- 后端新增 `FileMetadata``FileMetadataRepository`,作为后续标签、缩略图状态、媒体属性和自定义属性的统一扩展表骨架。当前阶段只建表与仓储入口,不迁移现有回收站字段,也不改变旧 `/api/files/**` DTO。
- 后端新增 `FileSearchService``GET /api/v2/files/search`,按当前登录用户查询未删除的 `StoredFile`,支持文件名、文件/目录类型、大小、创建时间、更新时间和分页过滤。
- 搜索结果复用现有 `FileMetadataResponse`,因此旧网盘列表、下载、分享、回收站和上传链路不受影响;前端用户侧搜索 UI 和 metadata/tag 过滤留到后续小步接入。
## 2026-04-08 阶段 5 文件搜索第二小步
- 前端桌面端新增独立搜索模式:`front/src/lib/file-search.ts` 复用 `apiV2Request('/files/search', ...)`,并在 `front/src/lib/file-search.test.ts` 覆盖参数编码、空参数跳过和 v2 数据解包。
- `front/src/pages/Files.tsx` 同时保留目录视图和搜索结果视图,搜索结果不写入 `getFilesListCacheKey(...)`,也不影响原有目录缓存和上传主链路;移动端文件页暂未接入搜索。
## 2026-04-08 阶段 5 分享二期后端最小骨架
- 旧分享仍保留在 `/api/files/share-links/**`,用于兼容当前前端公开分享页和旧导入路径。
- 新 v2 分享位于 `com.yoyuzh.api.v2.shares``ShareV2Service``FileShareLink` 新增 `passwordHash``expiresAt``maxDownloads``downloadCount``viewCount``allowImport``allowDownload``shareName` 策略字段。
- 公开端点仅包括 `GET /api/v2/shares/{token}``POST /api/v2/shares/{token}/verify-password`;创建、导入、我的分享列表和删除仍需要登录。
- 密码分享在校验前隐藏 `file` 详情v2 导入会在复用旧导入落库链路前校验过期时间、密码、`allowImport``maxDownloads`。当前 `allowDownload` 只落库和返回,尚未接入独立 v2 下载路由。
## 2026-04-08 阶段 5 文件事件流最小闭环
- 后端新增 `FileEvent` / `FileEventType` / `FileEventRepository` / `FileEventService`,并暴露受保护的 `GET /api/v2/files/events` SSE 入口。
- 当前事件流以用户为广播边界,支持 `path` 前缀过滤和 `X-Yoyuzh-Client-Id` 自身事件抑制;首次连接会收到 `READY` 事件。
- `FileService` 只在上传、导入、复制、移动、重命名、删除、恢复这些核心变更点记录最小事件。
- 前端新增 `front/src/lib/file-events.ts`,通过 fetch stream 复用鉴权和 `X-Yoyuzh-Client-Id` 请求头,不直接使用原生 `EventSource`;桌面 `Files` 与移动 `MobileFiles` 已在当前目录订阅事件,收到变更后失效当前目录缓存并刷新列表。
## 2026-04-08 阶段 6 任务框架与 worker 后端最小骨架
- 后端新增 `BackgroundTask` / `BackgroundTaskType` / `BackgroundTaskStatus` / `BackgroundTaskRepository` / `BackgroundTaskService`,用于承载后续压缩、解压、缩略图、媒体元数据和清理类后台工作。
- 新增受保护的 `/api/v2/tasks/**``GET /api/v2/tasks``GET /api/v2/tasks/{id}``DELETE /api/v2/tasks/{id}`,以及 `POST /api/v2/tasks/archive``POST /api/v2/tasks/extract``POST /api/v2/tasks/media-metadata` 占位创建接口。
- 任务创建入口集中在 `BackgroundTaskService` 校验 `StoredFile``fileId` 必须属于当前用户且未删除,请求 `path` 必须匹配由 `StoredFile.path + filename` 派生的真实逻辑路径;`ARCHIVE` 暂允许文件和目录,`EXTRACT` 仅允许压缩包类文件,`MEDIA_META` 仅允许媒体类文件。任务 public/private state 使用服务端派生的 `fileId``path``filename``directory``contentType``size`
- 当前实现新增了 worker 最小调度:定时扫描少量 `QUEUED` 任务,通过状态条件更新完成 claim`MEDIA_META` 任务会进入独立 handler 写入基础媒体元数据与图片宽高,其余任务类型执行 no-op handler 后标记 `COMPLETED`handler 异常会标记 `FAILED` 并记录错误原因,已取消任务不会被领取。
- 当前仍不包含真实压缩、解压、缩略图、媒体元数据解析、重试/恢复策略或前端队列展示。
- 桌面端 `front/src/pages/Files.tsx` 已接入最近 10 条后台任务查看与取消入口,并可为当前选中文件创建 `MEDIA_META` 任务;移动端与 archive/extract 的前端入口仍未接入。