# Folder Service 完整实现 **日期**:2026-02-02 **类型**:功能实现 **影响范围**:后端 - 文件夹服务 --- ## 变更概述 基于 `docs/requirements/backend/04-services/project/folder-service.md` 文档,完成文件夹服务的完整实现,包括 Model 层修复、Repository 层补充、数据库迁移文件创建。 --- ## 详细变更 ### 1. Model 层修复 **文件**:`server/app/models/folder.py` #### 1.1 为所有模型类添加 `__repr__` 方法 - ✅ `Folder` 类:添加 `__repr__` 方法 - ✅ `FolderMember` 类:添加 `__repr__` 方法 - ✅ `FolderShare` 类:添加 `__repr__` 方法 - ✅ `FolderExportJob` 类:添加 `__repr__` 方法 **示例**: ```python def __repr__(self) -> str: """字符串表示""" return f"" ``` #### 1.2 修复 FolderMember 时间戳字段 **变更**: - ❌ 删除:`joined_at` 字段(冗余) - ✅ 添加:`updated_at` 字段(标准时间戳) **修改前**: ```python joined_at: datetime = Field(...) created_at: datetime = Field(...) ``` **修改后**: ```python created_at: datetime = Field(...) updated_at: datetime = Field(...) ``` --- ### 2. Repository 层补充 #### 2.1 创建 FolderShareRepository **文件**:`server/app/repositories/folder_share_repository.py`(新建) **功能**: - ✅ 用户分享管理(创建、查询、撤销) - ✅ 链接分享管理(创建、查询、撤销) - ✅ 访问次数统计 - ✅ 分享记录查询 **核心方法**: ```python async def create_user_share(folder_id, shared_with_user_id, role, created_by) async def create_link_share(folder_id, token, access_level, created_by, ...) async def revoke_share(share_id) async def get_folder_user_shares(folder_id) async def get_folder_link_shares(folder_id) ``` #### 2.2 创建 FolderExportRepository **文件**:`server/app/repositories/folder_export_repository.py`(新建) **功能**: - ✅ 导出任务创建 - ✅ 任务状态更新 - ✅ 下载链接设置 - ✅ 任务取消 - ✅ 过期任务清理 **核心方法**: ```python async def create(folder_id, user_id, format, ...) async def update_status(job_id, status, progress, error_message) async def set_download_url(job_id, file_url, file_size, expires_at) async def cancel_job(job_id) async def cleanup_expired_jobs() ``` --- ### 3. 数据库迁移文件 **文件**:`server/alembic/versions/20260202_152029_folder_service_implementation.py`(新建) #### 3.1 创建表结构 **folders 表**: - ✅ 所有字段(id, name, description, parent_folder_id, owner_id, path, level, folder_category, sort_order, color, icon, cover_image_id, is_shared, created_at, updated_at, deleted_at) - ✅ 所有索引(包括部分索引、全文搜索索引) - ✅ 唯一性约束(部分唯一索引处理 NULL 值) - ✅ 表和字段注释 **folder_members 表**: - ✅ 所有字段(id, folder_id, user_id, role, inherited, invited_by, created_at, updated_at) - ✅ 所有索引 - ✅ 唯一约束(folder_id + user_id) - ✅ 表和字段注释 **folder_shares 表**: - ✅ 所有字段(id, folder_id, share_type, shared_with_user_id, role, share_token, access_level, password_hash, expires_at, access_count, created_by, created_at, revoked_at) - ✅ 所有索引 - ✅ 表和字段注释 **folder_export_jobs 表**: - ✅ 所有字段(id, folder_id, user_id, format, include_subfolders, include_resources, export_format, status, progress, file_url, file_size, estimated_size, error_message, created_at, started_at, completed_at, expires_at) - ✅ 所有索引 - ✅ 表和字段注释 #### 3.2 创建触发器 **触发器函数**: - ✅ `update_updated_at_column()`:自动更新 updated_at 字段 - ✅ `update_folder_path()`:自动计算文件夹路径和层级 - ✅ `inherit_folder_category()`:子文件夹自动继承父文件夹分类 **触发器**: - ✅ `update_folders_updated_at`:folders 表更新时触发 - ✅ `update_folder_path_trigger`:folders 表插入/更新时触发 - ✅ `trigger_inherit_folder_category`:folders 表插入时触发 - ✅ `update_folder_members_updated_at`:folder_members 表更新时触发 #### 3.3 扩展 projects 表 - ✅ 添加 `folder_id` 字段(逻辑外键) - ✅ 创建索引 `idx_projects_folder_id` - ✅ 添加字段注释 --- ### 4. Schema 层修复 **文件**:`server/app/schemas/folder.py` **变更**: - ✅ `FolderMemberResponse`:将 `joined_at` 改为 `updated_at` --- ### 5. API 层修复 **文件**:`server/app/api/v1/folders.py` **变更**: - ✅ 添加成员接口:返回 `updatedAt` 而非 `joinedAt` - ✅ 更新成员角色接口:返回 `updatedAt` 而非 `joinedAt` - ✅ 获取成员列表接口:返回 `updatedAt` 而非 `joinedAt` --- ## 技术栈合规性 ### ✅ 符合项 1. **数据库设计**: - ✅ 使用 TIMESTAMPTZ 记录事件时间 - ✅ 枚举值使用 SMALLINT 存储 - ✅ 无物理外键约束,引用完整性由应用层保证 - ✅ 使用触发器自动计算和继承字段 - ✅ 使用部分唯一索引处理 NULL 值 2. **Model 层**: - ✅ 所有模型类实现 `__repr__` 方法 - ✅ 标准时间戳字段(created_at, updated_at) - ✅ 使用 `primaryjoin` 明确关联条件 3. **Repository 层**: - ✅ 完整的 CRUD 操作 - ✅ 日志记录(使用 logger) - ✅ 异常处理 4. **迁移文件**: - ✅ 包含完整的表结构、索引、约束 - ✅ 包含触发器函数和触发器 - ✅ 包含表和字段注释 - ✅ 提供 downgrade 方法 --- ## 执行迁移 ### 1. 检查迁移状态 ```bash docker exec jointo-server-app alembic current ``` ### 2. 执行迁移 ```bash docker exec jointo-server-app alembic upgrade head ``` ### 3. 验证表结构 ```bash docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\d folders" docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\d folder_members" docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\d folder_shares" docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\d folder_export_jobs" ``` ### 4. 验证触发器 ```bash docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\df update_folder_path" docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\df inherit_folder_category" ``` --- ## 测试建议 ### 1. Model 层测试 ```python # 测试 __repr__ 方法 folder = Folder(id=uuid4(), name="测试文件夹", level=0) assert repr(folder) == "" ``` ### 2. Repository 层测试 ```python # 测试分享功能 share_repo = FolderShareRepository(session) share = await share_repo.create_user_share( folder_id=folder_id, shared_with_user_id=user_id, role=MemberRole.VIEWER, created_by=owner_id ) assert share.share_type == 'user' ``` ### 3. 触发器测试 ```sql -- 测试路径自动计算 INSERT INTO folders (id, name, owner_id, folder_category) VALUES (gen_random_uuid(), '根文件夹', 'user-id', 1); -- 验证 path = '/根文件夹', level = 0 -- 测试分类自动继承 INSERT INTO folders (id, name, parent_folder_id, owner_id) VALUES (gen_random_uuid(), '子文件夹', 'parent-id', 'user-id'); -- 验证 folder_category 自动继承父文件夹 ``` --- ## 影响范围 ### 新增功能 - ✅ 文件夹分享功能(用户分享、链接分享) - ✅ 文件夹导出功能(异步任务) - ✅ 文件夹路径自动计算 - ✅ 文件夹分类自动继承 ### 数据库变更 - ✅ 新增 4 张表 - ✅ 新增 3 个触发器函数 - ✅ 新增 4 个触发器 - ✅ projects 表新增 folder_id 字段 ### API 变更 - ✅ 成员响应字段变更(joined_at → updated_at) --- ## 后续工作 1. **测试覆盖**: - 编写 Repository 层单元测试 - 编写 Service 层单元测试 - 编写 API 层集成测试 2. **功能完善**: - 实现导出任务的 Celery Worker - 实现分享链接的访问验证 - 实现文件夹权限继承逻辑 3. **性能优化**: - 添加 Redis 缓存(文件夹树、权限) - 优化递归查询(使用 CTE) --- ## 相关文档 - 需求文档:`docs/requirements/backend/04-services/project/folder-service.md` - 技术栈规范:`.claude/skills/jointo-tech-stack/references/database.md` - 数据库规范:`.claude/skills/jointo-tech-stack/references/backend.md` --- ## 作者 AI Assistant ## 审核状态 待审核