You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

8.4 KiB

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__ 方法

示例

def __repr__(self) -> str:
    """字符串表示"""
    return f"<Folder(id={self.id}, name='{self.name}', level={self.level})>"

1.2 修复 FolderMember 时间戳字段

变更

  • 删除:joined_at 字段(冗余)
  • 添加:updated_at 字段(标准时间戳)

修改前

joined_at: datetime = Field(...)
created_at: datetime = Field(...)

修改后

created_at: datetime = Field(...)
updated_at: datetime = Field(...)

2. Repository 层补充

2.1 创建 FolderShareRepository

文件server/app/repositories/folder_share_repository.py(新建)

功能

  • 用户分享管理(创建、查询、撤销)
  • 链接分享管理(创建、查询、撤销)
  • 访问次数统计
  • 分享记录查询

核心方法

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(新建)

功能

  • 导出任务创建
  • 任务状态更新
  • 下载链接设置
  • 任务取消
  • 过期任务清理

核心方法

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. 检查迁移状态

docker exec jointo-server-app alembic current

2. 执行迁移

docker exec jointo-server-app alembic upgrade head

3. 验证表结构

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. 验证触发器

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 层测试

# 测试 __repr__ 方法
folder = Folder(id=uuid4(), name="测试文件夹", level=0)
assert repr(folder) == "<Folder(id=..., name='测试文件夹', level=0)>"

2. Repository 层测试

# 测试分享功能
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. 触发器测试

-- 测试路径自动计算
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

审核状态

待审核