116 lines
5.7 KiB
Markdown
116 lines
5.7 KiB
Markdown
# Shared File Blob Storage Implementation Plan
|
||
|
||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** 将网盘文件模型改造成 `StoredFile -> FileBlob` 引用关系,让分享导入与网盘复制复用同一份底层对象,而不是再次写入物理文件。
|
||
|
||
**Architecture:** 新增独立 `FileBlob` 实体承载真实对象 key、大小和内容类型,`StoredFile` 只保留逻辑目录元数据并引用 `FileBlob`。网盘上传为每个新文件创建新 blob,分享导入和文件复制直接复用 blob;删除时按引用是否归零决定是否删除底层对象。补一个面向旧 `portal_file.storage_name` 数据的一次性回填路径,避免线上旧数据在新模型下失联。
|
||
|
||
**Tech Stack:** Spring Boot 3.3.8, Spring Data JPA, Java 17, Maven, H2 tests, local filesystem storage, S3-compatible object storage
|
||
|
||
---
|
||
|
||
### Task 1: Define Blob Data Model
|
||
|
||
**Files:**
|
||
- Create: `backend/src/main/java/com/yoyuzh/files/FileBlob.java`
|
||
- Create: `backend/src/main/java/com/yoyuzh/files/FileBlobRepository.java`
|
||
- Modify: `backend/src/main/java/com/yoyuzh/files/StoredFile.java`
|
||
- Modify: `backend/src/main/java/com/yoyuzh/files/StoredFileRepository.java`
|
||
|
||
- [ ] **Step 1: Write the failing tests**
|
||
|
||
Add/update backend tests that expect file copies and share imports to preserve a shared blob id rather than a per-user storage name.
|
||
|
||
- [ ] **Step 2: Run test to verify it fails**
|
||
|
||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileServiceTest,FileShareControllerIntegrationTest`
|
||
Expected: FAIL because `StoredFile` does not yet expose blob references and current logic still duplicates file content.
|
||
|
||
- [ ] **Step 3: Write minimal implementation**
|
||
|
||
Add `FileBlob` entity/repository, move file-object ownership from `StoredFile.storageName` to `StoredFile.blob`, and add repository helpers for blob reference counting / physical size queries.
|
||
|
||
- [ ] **Step 4: Run test to verify it passes**
|
||
|
||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileServiceTest,FileShareControllerIntegrationTest`
|
||
Expected: PASS for data-model expectations.
|
||
|
||
### Task 2: Refactor File Storage Flow To Blob Keys
|
||
|
||
**Files:**
|
||
- Modify: `backend/src/main/java/com/yoyuzh/files/FileService.java`
|
||
- Modify: `backend/src/main/java/com/yoyuzh/files/storage/FileContentStorage.java`
|
||
- Modify: `backend/src/main/java/com/yoyuzh/files/storage/LocalFileContentStorage.java`
|
||
- Modify: `backend/src/main/java/com/yoyuzh/files/storage/S3FileContentStorage.java`
|
||
- Test: `backend/src/test/java/com/yoyuzh/files/FileServiceTest.java`
|
||
- Test: `backend/src/test/java/com/yoyuzh/files/FileServiceEdgeCaseTest.java`
|
||
|
||
- [ ] **Step 1: Write the failing tests**
|
||
|
||
Add/update tests for:
|
||
- upload creates a new blob
|
||
- share import reuses the source blob and does not store bytes again
|
||
- file copy reuses the source blob and does not copy bytes again
|
||
- deleting a non-final reference keeps the blob/object alive
|
||
- deleting the final reference removes the blob/object
|
||
|
||
- [ ] **Step 2: Run test to verify it fails**
|
||
|
||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileServiceTest,FileServiceEdgeCaseTest`
|
||
Expected: FAIL because current service still calls `readFile/storeImportedFile/copyFile/moveFile/renameFile` for ordinary files.
|
||
|
||
- [ ] **Step 3: Write minimal implementation**
|
||
|
||
Refactor file upload/download/import/copy/delete to operate on blob object keys. Keep directory behavior metadata-only. Ensure file rename/move/copy no longer trigger physical object mutations, and deletion only removes the object when the last `StoredFile` reference disappears.
|
||
|
||
- [ ] **Step 4: Run test to verify it passes**
|
||
|
||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileServiceTest,FileServiceEdgeCaseTest`
|
||
Expected: PASS.
|
||
|
||
### Task 3: Backfill Old File Rows Into Blob References
|
||
|
||
**Files:**
|
||
- Create: `backend/src/main/java/com/yoyuzh/files/FileBlobBackfillService.java`
|
||
- Modify: `backend/src/main/java/com/yoyuzh/files/StoredFileRepository.java`
|
||
- Test: `backend/src/test/java/com/yoyuzh/files/FileShareControllerIntegrationTest.java`
|
||
|
||
- [ ] **Step 1: Write the failing test**
|
||
|
||
Add an integration-path expectation that persisted file rows still download/import correctly after the blob model change.
|
||
|
||
- [ ] **Step 2: Run test to verify it fails**
|
||
|
||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileShareControllerIntegrationTest`
|
||
Expected: FAIL because legacy `portal_file` rows created without blobs can no longer resolve file content.
|
||
|
||
- [ ] **Step 3: Write minimal implementation**
|
||
|
||
Add a startup/on-demand backfill that creates `FileBlob` rows for existing non-directory files using their legacy object key and attaches them to `StoredFile` rows with missing blob references.
|
||
|
||
- [ ] **Step 4: Run test to verify it passes**
|
||
|
||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileShareControllerIntegrationTest`
|
||
Expected: PASS.
|
||
|
||
### Task 4: Update Docs And Full Verification
|
||
|
||
**Files:**
|
||
- Modify: `memory.md`
|
||
- Modify: `docs/architecture.md`
|
||
- Modify: `docs/api-reference.md` (only if API semantics changed)
|
||
|
||
- [ ] **Step 1: Document the new storage model**
|
||
|
||
Record that logical file metadata now points to shared blobs, that share import and copy reuse blobs, and that physical keys are global rather than user-path keys.
|
||
|
||
- [ ] **Step 2: Run full backend verification**
|
||
|
||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test`
|
||
Expected: PASS.
|
||
|
||
- [ ] **Step 3: Summarize migration requirement**
|
||
|
||
In the final handoff, call out that old production data must be backfilled to `FileBlob` rows before or during rollout; otherwise pre-existing files cannot resolve blob references under the new model.
|