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

21 KiB
Raw Blame History

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 主模型,不推翻个人网盘语义,而是在其上增加 SpaceSpaceMemberFilePermissionEntryAuditLog 这四层扩展。个人网盘先通过“每个用户自动拥有一个 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
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
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
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
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
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
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
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

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” 中看到结果。
  • 审计日志能覆盖空间、权限、分享、删除/恢复等关键动作。
  • 旧公开分享、快传、上传会话、后台任务和管理台文件列表不被打断。