Files
my_site/docs/superpowers/plans/2026-04-09-multi-user-platform-upgrade-phase-2.md

447 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Multi User Platform Upgrade Phase 2 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:** 在当前“邀请制多用户个人盘”稳定后,把系统升级到最小可用的“空间 + 成员角色 + 文件 ACL + 站内共享 + 审计”平台模型,同时不破坏现有个人网盘、公开分享、快传和管理台主链路。
**Architecture:** 继续保留现有 `portal_user + StoredFile + FileBlob/FileEntity` 主模型,不推翻个人网盘语义,而是在其上增加 `Space``SpaceMember``FilePermissionEntry``AuditLog` 这四层扩展。个人网盘先通过“每个用户自动拥有一个 personal space”兼容到新模型桌面端和管理台先接入空间与权限入口移动端只保兼容、不在本阶段新增完整协作 UI。
**Tech Stack:** Spring Boot 3.3.8, Spring Data JPA, H2/MySQL, React 19, TypeScript, Vite 6, Tailwind CSS v4, existing `/api/v2` patterns, existing `mvn test`, `npm run test`, `npm run lint`, @test-driven-development.
---
## Scope And Sequencing
- 本计划默认在当前 `2026-04-08-cloudreve-inspired-upgrade-and-refactor.md` 的既定升级、尤其是阶段 6 和全站前端重设计完成并合入后执行。
- 本计划只覆盖桌面 Web、现有管理台、后端模型/API、现有测试体系不在本阶段实现移动端协作页、WebDAV 权限联动、组织/部门层级、团队回收站、第三方 OAuth scope 细化。
- 所有新能力优先挂在 `/api/v2/**`;旧 `/api/files/**``/api/admin/**` 只做必要兼容,不强制一次性重写。
- 执行中始终使用 @test-driven-development先写失败测试再补最小实现再跑最小验证最后补全集验证。
### Task 1: Add Space Domain Skeleton And Default Personal Space
**Files:**
- Create: `backend/src/main/java/com/yoyuzh/files/space/Space.java`
- Create: `backend/src/main/java/com/yoyuzh/files/space/SpaceType.java`
- Create: `backend/src/main/java/com/yoyuzh/files/space/SpaceRole.java`
- Create: `backend/src/main/java/com/yoyuzh/files/space/SpaceMember.java`
- Create: `backend/src/main/java/com/yoyuzh/files/space/SpaceRepository.java`
- Create: `backend/src/main/java/com/yoyuzh/files/space/SpaceMemberRepository.java`
- Create: `backend/src/main/java/com/yoyuzh/files/space/SpaceService.java`
- Modify: `backend/src/main/java/com/yoyuzh/auth/User.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/StoredFile.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/StoredFileRepository.java`
- Create: `backend/src/test/java/com/yoyuzh/files/space/SpaceServiceTest.java`
- Modify: `backend/src/test/java/com/yoyuzh/files/FileServiceTest.java`
- [ ] **Step 1: Write the failing space-domain tests**
Run: `cd backend && mvn test -Dtest=SpaceServiceTest,FileServiceTest`
Expected: FAIL because `Space`, `SpaceMember`, and `StoredFile.space` support do not exist yet.
- [ ] **Step 2: Add the minimal schema and repository layer**
Implement:
- `Space` with `PERSONAL` and `COLLABORATIVE` types
- `SpaceMember` with `OWNER`, `MANAGER`, `EDITOR`, `VIEWER`
- `StoredFile.space` as nullable-to-required migration target
- repository methods for “default personal space by user” and “space membership by user”
- [ ] **Step 3: Add default personal-space bootstrap**
Implement `SpaceService.ensurePersonalSpace(User)` so:
- existing users can lazily receive a personal space
- new users automatically get one after registration/bootstrap
- files created through current private flows can still resolve to a valid space
- [ ] **Step 4: Re-run the targeted backend tests**
Run: `cd backend && mvn test -Dtest=SpaceServiceTest,FileServiceTest`
Expected: PASS for personal-space bootstrap and `StoredFile` compatibility assertions.
- [ ] **Step 5: Commit**
```bash
git add backend/src/main/java/com/yoyuzh/files/space backend/src/main/java/com/yoyuzh/auth/User.java backend/src/main/java/com/yoyuzh/files/StoredFile.java backend/src/main/java/com/yoyuzh/files/StoredFileRepository.java backend/src/test/java/com/yoyuzh/files/space/SpaceServiceTest.java backend/src/test/java/com/yoyuzh/files/FileServiceTest.java
git commit -m "feat(files): add space domain skeleton"
```
### Task 2: Route Existing File Ownership Through Spaces Without Breaking Private Disk
**Files:**
- Modify: `backend/src/main/java/com/yoyuzh/files/FileService.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/FileSearchService.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/ShareV2Service.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/BackgroundTaskService.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/StoredFileRepository.java`
- Create: `backend/src/test/java/com/yoyuzh/files/StoredFileSpaceCompatibilityTest.java`
- Modify: `backend/src/test/java/com/yoyuzh/files/FileSearchServiceTest.java`
- Modify: `backend/src/test/java/com/yoyuzh/files/BackgroundTaskServiceTest.java`
- [ ] **Step 1: Write failing compatibility tests for old personal-disk flows**
Run: `cd backend && mvn test -Dtest=StoredFileSpaceCompatibilityTest,FileSearchServiceTest,BackgroundTaskServiceTest`
Expected: FAIL because current create/search/task flows still assume “user owns everything directly”.
- [ ] **Step 2: Thread `spaceId` through internal file creation and lookup**
Implement the smallest compatibility rules:
- private files default to current user personal space
- current search/list/task flows keep returning the same personal files as before
- share import, archive/extract, upload complete, and metadata tasks preserve the owning space
- [ ] **Step 3: Keep old API semantics stable**
Do not change:
- existing personal path semantics
- public share token behavior
- anonymous transfer behavior
- current admin file listing shape
Only add internal `space` routing needed for future collaborative flows.
- [ ] **Step 4: Re-run targeted compatibility tests**
Run: `cd backend && mvn test -Dtest=StoredFileSpaceCompatibilityTest,FileSearchServiceTest,BackgroundTaskServiceTest`
Expected: PASS with no regression on existing personal-disk behavior.
- [ ] **Step 5: Commit**
```bash
git add backend/src/main/java/com/yoyuzh/files/FileService.java backend/src/main/java/com/yoyuzh/files/FileSearchService.java backend/src/main/java/com/yoyuzh/files/ShareV2Service.java backend/src/main/java/com/yoyuzh/files/BackgroundTaskService.java backend/src/main/java/com/yoyuzh/files/StoredFileRepository.java backend/src/test/java/com/yoyuzh/files/StoredFileSpaceCompatibilityTest.java backend/src/test/java/com/yoyuzh/files/FileSearchServiceTest.java backend/src/test/java/com/yoyuzh/files/BackgroundTaskServiceTest.java
git commit -m "refactor(files): route private disk through personal spaces"
```
### Task 3: Add V2 Space APIs And Membership Management
**Files:**
- Create: `backend/src/main/java/com/yoyuzh/api/v2/spaces/SpaceV2Controller.java`
- Create: `backend/src/main/java/com/yoyuzh/api/v2/spaces/SpaceResponse.java`
- Create: `backend/src/main/java/com/yoyuzh/api/v2/spaces/SpaceMemberResponse.java`
- Create: `backend/src/main/java/com/yoyuzh/api/v2/spaces/CreateSpaceRequest.java`
- Create: `backend/src/main/java/com/yoyuzh/api/v2/spaces/AddSpaceMemberRequest.java`
- Create: `backend/src/main/java/com/yoyuzh/api/v2/spaces/UpdateSpaceMemberRoleRequest.java`
- Modify: `backend/src/main/java/com/yoyuzh/config/SecurityConfig.java`
- Modify: `backend/src/main/java/com/yoyuzh/auth/UserDetailsServiceImpl.java`
- Create: `backend/src/test/java/com/yoyuzh/api/v2/spaces/SpaceV2ControllerIntegrationTest.java`
- [ ] **Step 1: Write failing API tests for creating and listing spaces**
Run: `cd backend && mvn test -Dtest=SpaceV2ControllerIntegrationTest`
Expected: FAIL because `/api/v2/spaces/**` endpoints and security wiring do not exist.
- [ ] **Step 2: Implement the minimal V2 space endpoints**
Implement:
- `POST /api/v2/spaces`
- `GET /api/v2/spaces`
- `GET /api/v2/spaces/{id}`
- `POST /api/v2/spaces/{id}/members`
- `PATCH /api/v2/spaces/{id}/members/{userId}`
- `DELETE /api/v2/spaces/{id}/members/{userId}`
Rules:
- only authenticated users can list their spaces
- only `OWNER`/`MANAGER` can manage members
- personal spaces cannot remove the owner or add arbitrary members
- [ ] **Step 3: Return DTOs only**
Do not expose JPA entities directly. Keep API outputs small:
- space identity
- type
- display name
- current user role
- member summaries
- [ ] **Step 4: Re-run targeted V2 API tests**
Run: `cd backend && mvn test -Dtest=SpaceV2ControllerIntegrationTest`
Expected: PASS for create/list/member add/remove/role change rules.
- [ ] **Step 5: Commit**
```bash
git add backend/src/main/java/com/yoyuzh/api/v2/spaces backend/src/main/java/com/yoyuzh/config/SecurityConfig.java backend/src/main/java/com/yoyuzh/auth/UserDetailsServiceImpl.java backend/src/test/java/com/yoyuzh/api/v2/spaces/SpaceV2ControllerIntegrationTest.java
git commit -m "feat(api): add v2 space and member management"
```
### Task 4: Add File ACL Entries And Permission Evaluation
**Files:**
- Create: `backend/src/main/java/com/yoyuzh/files/permission/FilePermissionEntry.java`
- Create: `backend/src/main/java/com/yoyuzh/files/permission/FilePermissionRole.java`
- Create: `backend/src/main/java/com/yoyuzh/files/permission/FilePermissionSubjectType.java`
- Create: `backend/src/main/java/com/yoyuzh/files/permission/FilePermissionEntryRepository.java`
- Create: `backend/src/main/java/com/yoyuzh/files/permission/FilePermissionService.java`
- Create: `backend/src/main/java/com/yoyuzh/files/permission/FileAccessEvaluator.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/FileService.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/FileSearchService.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/ShareV2Service.java`
- Create: `backend/src/test/java/com/yoyuzh/files/permission/FilePermissionServiceTest.java`
- Modify: `backend/src/test/java/com/yoyuzh/files/FileServiceEdgeCaseTest.java`
- [ ] **Step 1: Write failing ACL tests**
Run: `cd backend && mvn test -Dtest=FilePermissionServiceTest,FileServiceEdgeCaseTest`
Expected: FAIL because file access is still governed only by owner/admin checks.
- [ ] **Step 2: Implement the minimal ACL model**
Implement:
- subject types: `USER`, `SPACE`
- permission roles: `VIEWER`, `EDITOR`, `MANAGER`
- inheritance flag for directory-to-descendant lookup
Rules:
- owner/personal-space owner always has full access
- collaborative space role provides the default baseline
- explicit file ACL can grant additional access to a user
- [ ] **Step 3: Enforce ACL at service boundaries**
Apply permission checks to:
- list directory
- upload into folder
- rename/move/copy/delete
- create public share
- import into target folder
- start background tasks on a file
- [ ] **Step 4: Re-run the ACL and edge-case tests**
Run: `cd backend && mvn test -Dtest=FilePermissionServiceTest,FileServiceEdgeCaseTest`
Expected: PASS with clear denials for users outside the owning space or without explicit grants.
- [ ] **Step 5: Commit**
```bash
git add backend/src/main/java/com/yoyuzh/files/permission backend/src/main/java/com/yoyuzh/files/FileService.java backend/src/main/java/com/yoyuzh/files/FileSearchService.java backend/src/main/java/com/yoyuzh/files/ShareV2Service.java backend/src/test/java/com/yoyuzh/files/permission/FilePermissionServiceTest.java backend/src/test/java/com/yoyuzh/files/FileServiceEdgeCaseTest.java
git commit -m "feat(files): add acl-based permission evaluation"
```
### Task 5: Add In-App Sharing And “Shared With Me” V2 Views
**Files:**
- Create: `backend/src/main/java/com/yoyuzh/api/v2/files/ShareFileToUserV2Request.java`
- Create: `backend/src/main/java/com/yoyuzh/api/v2/files/SharedFileV2Response.java`
- Create: `backend/src/main/java/com/yoyuzh/api/v2/files/FilePermissionsV2Controller.java`
- Modify: `backend/src/main/java/com/yoyuzh/api/v2/files/FileSearchV2Controller.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/permission/FilePermissionService.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/StoredFileRepository.java`
- Create: `backend/src/test/java/com/yoyuzh/api/v2/files/FilePermissionsV2ControllerIntegrationTest.java`
- Modify: `backend/src/test/java/com/yoyuzh/files/FileShareControllerIntegrationTest.java`
- [ ] **Step 1: Write failing tests for in-app share and shared-with-me listing**
Run: `cd backend && mvn test -Dtest=FilePermissionsV2ControllerIntegrationTest,FileShareControllerIntegrationTest`
Expected: FAIL because there is no station-internal share grant endpoint or “shared with me” query.
- [ ] **Step 2: Add minimal in-app sharing endpoints**
Implement:
- `GET /api/v2/files/{fileId}/permissions`
- `PUT /api/v2/files/{fileId}/permissions`
- `DELETE /api/v2/files/{fileId}/permissions/{entryId}`
- `GET /api/v2/files/shared-with-me`
Rules:
- only `MANAGER` or owner can grant/revoke
- station-internal share writes ACL entries, not public share tokens
- `shared-with-me` excludes files already owned through the current users own personal space
- [ ] **Step 3: Keep public sharing separate**
Do not merge station-internal sharing into `FileShareLink`.
Continue using:
- `FileShareLink` for public token shares
- `FilePermissionEntry` for logged-in user-to-user sharing
- [ ] **Step 4: Re-run targeted integration tests**
Run: `cd backend && mvn test -Dtest=FilePermissionsV2ControllerIntegrationTest,FileShareControllerIntegrationTest`
Expected: PASS for grant, revoke, and `shared-with-me` listing behavior.
- [ ] **Step 5: Commit**
```bash
git add backend/src/main/java/com/yoyuzh/api/v2/files backend/src/main/java/com/yoyuzh/files/permission/FilePermissionService.java backend/src/main/java/com/yoyuzh/files/StoredFileRepository.java backend/src/test/java/com/yoyuzh/api/v2/files/FilePermissionsV2ControllerIntegrationTest.java backend/src/test/java/com/yoyuzh/files/FileShareControllerIntegrationTest.java
git commit -m "feat(files): add in-app sharing and shared-with-me"
```
### Task 6: Add Cross-Cutting Audit Logs And Admin Audit Read API
**Files:**
- Create: `backend/src/main/java/com/yoyuzh/common/audit/AuditLogEntry.java`
- Create: `backend/src/main/java/com/yoyuzh/common/audit/AuditAction.java`
- Create: `backend/src/main/java/com/yoyuzh/common/audit/AuditLogEntryRepository.java`
- Create: `backend/src/main/java/com/yoyuzh/common/audit/AuditLogService.java`
- Create: `backend/src/main/java/com/yoyuzh/admin/AdminAuditLogResponse.java`
- Modify: `backend/src/main/java/com/yoyuzh/admin/AdminController.java`
- Modify: `backend/src/main/java/com/yoyuzh/admin/AdminService.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/FileService.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/space/SpaceService.java`
- Modify: `backend/src/main/java/com/yoyuzh/files/permission/FilePermissionService.java`
- Modify: `backend/src/test/java/com/yoyuzh/admin/AdminControllerIntegrationTest.java`
- Create: `backend/src/test/java/com/yoyuzh/common/audit/AuditLogServiceTest.java`
- [ ] **Step 1: Write failing audit tests**
Run: `cd backend && mvn test -Dtest=AuditLogServiceTest,AdminControllerIntegrationTest`
Expected: FAIL because no audit entity/service exists and admin cannot query audit logs.
- [ ] **Step 2: Implement the smallest useful audit model**
Persist at least:
- actor user id
- action type
- target type
- target id
- summary text
- created at
Log these events:
- space create/member change
- file permission grant/revoke
- public share create/delete
- file delete/restore
- [ ] **Step 3: Add admin read API only**
Implement a read-only admin endpoint:
- `GET /api/admin/audit-logs?page=0&size=20&query=...`
Do not add end-user audit UI in this phase.
- [ ] **Step 4: Re-run audit tests**
Run: `cd backend && mvn test -Dtest=AuditLogServiceTest,AdminControllerIntegrationTest`
Expected: PASS for audit persistence and admin listing.
- [ ] **Step 5: Commit**
```bash
git add backend/src/main/java/com/yoyuzh/common/audit backend/src/main/java/com/yoyuzh/admin/AdminController.java backend/src/main/java/com/yoyuzh/admin/AdminService.java backend/src/main/java/com/yoyuzh/files/FileService.java backend/src/main/java/com/yoyuzh/files/space/SpaceService.java backend/src/main/java/com/yoyuzh/files/permission/FilePermissionService.java backend/src/test/java/com/yoyuzh/admin/AdminControllerIntegrationTest.java backend/src/test/java/com/yoyuzh/common/audit/AuditLogServiceTest.java
git commit -m "feat(admin): add audit log backbone"
```
### Task 7: Add Desktop-Web Space, Permission, And Shared-With-Me UI
**Files:**
- Modify: `front/src/lib/types.ts`
- Modify: `front/src/lib/api.ts`
- Create: `front/src/lib/spaces.ts`
- Create: `front/src/lib/spaces.test.ts`
- Create: `front/src/lib/file-permissions.ts`
- Create: `front/src/lib/file-permissions.test.ts`
- Modify: `front/src/App.tsx`
- Modify: `front/src/components/layout/Layout.tsx`
- Modify: `front/src/pages/files/FilesDirectoryRail.tsx`
- Modify: `front/src/pages/files/FilesToolbar.tsx`
- Modify: `front/src/pages/files/useFilesDirectoryState.ts`
- Create: `front/src/pages/Spaces.tsx`
- Create: `front/src/pages/SharedWithMe.tsx`
- Create: `front/src/pages/files/SpaceMembersDialog.tsx`
- Create: `front/src/pages/files/FilePermissionsDialog.tsx`
- Create: `front/src/pages/spaces-state.ts`
- Create: `front/src/pages/spaces-state.test.ts`
- Create: `front/src/pages/shared-with-me-state.ts`
- Create: `front/src/pages/shared-with-me-state.test.ts`
- [ ] **Step 1: Write failing frontend helper tests**
Run: `cd front && npm run test -- src/lib/spaces.test.ts src/lib/file-permissions.test.ts src/pages/spaces-state.test.ts src/pages/shared-with-me-state.test.ts`
Expected: FAIL because the new API helpers and page state modules do not exist.
- [ ] **Step 2: Add typed API helpers first**
Implement the smallest helpers for:
- list/create spaces
- list/update members
- get/update file permissions
- list shared-with-me files
Do not start with JSX-heavy pages before helpers and tests exist.
- [ ] **Step 3: Add desktop-only navigation and management UI**
Implement:
- `Spaces` page for list/create/member management
- `SharedWithMe` page for station-internal shares
- file page dialogs for members and permissions
- a space switcher in the file rail or toolbar
Rules:
- keep current private disk route working
- personal space remains default landing target
- mobile app can stay unchanged in this phase
- [ ] **Step 4: Re-run targeted frontend tests**
Run: `cd front && npm run test -- src/lib/spaces.test.ts src/lib/file-permissions.test.ts src/pages/spaces-state.test.ts src/pages/shared-with-me-state.test.ts`
Expected: PASS with helpers and page state stabilized before broad UI verification.
- [ ] **Step 5: Commit**
```bash
git add front/src/lib/types.ts front/src/lib/api.ts front/src/lib/spaces.ts front/src/lib/spaces.test.ts front/src/lib/file-permissions.ts front/src/lib/file-permissions.test.ts front/src/App.tsx front/src/components/layout/Layout.tsx front/src/pages/files/FilesDirectoryRail.tsx front/src/pages/files/FilesToolbar.tsx front/src/pages/files/useFilesDirectoryState.ts front/src/pages/Spaces.tsx front/src/pages/SharedWithMe.tsx front/src/pages/files/SpaceMembersDialog.tsx front/src/pages/files/FilePermissionsDialog.tsx front/src/pages/spaces-state.ts front/src/pages/spaces-state.test.ts front/src/pages/shared-with-me-state.ts front/src/pages/shared-with-me-state.test.ts
git commit -m "feat(front): add spaces and shared-with-me ui"
```
### Task 8: Full Verification And Documentation Handoff
**Files:**
- Modify: `docs/architecture.md`
- Modify: `docs/api-reference.md`
- Modify: `memory.md`
- Modify only if verification reveals gaps: `backend/**`, `front/**`
- [ ] **Step 1: Run full backend verification**
Run: `cd backend && mvn test`
Expected: PASS with no regressions in auth, files, tasks, shares, admin, and API v2 tests.
- [ ] **Step 2: Run full frontend verification**
Run: `cd front && npm run test`
Expected: PASS with no regressions in files, transfer, admin, and new spaces/shared state tests.
- [ ] **Step 3: Run frontend type/lint verification**
Run: `cd front && npm run lint`
Expected: PASS with no TypeScript errors.
- [ ] **Step 4: Update project memory and architecture docs**
Document:
- space model
- member roles
- ACL rules
- in-app sharing vs public share split
- admin audit log availability
- mobile deferred scope
- [ ] **Step 5: Final commit**
```bash
git add docs/architecture.md docs/api-reference.md memory.md
git commit -m "docs: record multi-user platform phase 2 architecture"
```
## Deferred Explicitly
- 移动端 `MobileFiles` / `MobileOverview` / `MobileApp` 的空间协作 UI
- 组织/部门/用户组层级,不在本计划混入
- WebDAV、OAuth scope、桌面同步客户端权限联动
- 团队回收站、空间级生命周期策略、空间级存储策略切换
- 文件评论、审批流、在线协作文档
## Success Criteria
- 现有个人网盘用户登录后仍能像今天一样使用自己的私有文件空间。
- 每个用户可看到自己的 personal space且可创建 collaborative space。
- collaborative space 支持最小成员角色与目录/文件 ACL。
- 站内用户可通过 ACL 被共享文件,并在 “Shared With Me” 中看到结果。
- 审计日志能覆盖空间、权限、分享、删除/恢复等关键动作。
- 旧公开分享、快传、上传会话、后台任务和管理台文件列表不被打断。