# RFC 133: 文件夹枚举字段重构为 SMALLINT **状态**: 实施中 **创建时间**: 2026-01-23 **作者**: System **关联文档**: [folder-service.md](../../requirements/backend/04-services/project/folder-service.md) --- ## 背景 当前 `folder_members` 表的 `role` 字段使用 PostgreSQL ENUM 类型存储成员角色。根据项目架构决策和性能考虑,需要将其重构为 SMALLINT 类型,与 `folders.folder_category` 保持一致。 同时,为未来的文件夹导出功能预留 `export_status` 枚举设计。 ## 问题 ### 当前实现 ```python # models/folder.py class MemberRole(str, Enum): OWNER = "owner" EDITOR = "editor" VIEWER = "viewer" # 数据库列定义 role: MemberRole = Field( sa_column=Column( SQLEnum(MemberRole, name="member_role"), nullable=False, default=MemberRole.VIEWER ) ) ``` ### 存在的问题 1. **性能问题**: PostgreSQL ENUM 类型在大量查询时性能不如整数类型 2. **扩展性差**: 添加新角色需要 ALTER TYPE,可能导致锁表 3. **不一致**: 项目中 `folder_category` 已使用 SMALLINT,应保持统一 4. **迁移复杂**: ENUM 类型的迁移比整数类型更复杂 ## 解决方案 ### 设计原则 1. **数据库层**: 使用 SMALLINT 存储枚举值(1-255) 2. **代码层**: 使用 Python IntEnum 提供类型安全 3. **API 层**: 保持字符串格式,确保向后兼容 4. **转换层**: Schema 层负责字符串 ↔ 整数转换 ### 枚举值映射 #### MemberRole(成员角色) | 值 | 名称 | 字符串 | 说明 | |----|------|--------|------| | 1 | OWNER | "owner" | 所有者 | | 2 | EDITOR | "editor" | 编辑者 | | 3 | VIEWER | "viewer" | 查看者 | #### ExportStatus(导出状态) | 值 | 名称 | 字符串 | 说明 | |----|------|--------|------| | 1 | PENDING | "pending" | 等待处理 | | 2 | PROCESSING | "processing" | 处理中 | | 3 | COMPLETED | "completed" | 已完成 | | 4 | FAILED | "failed" | 失败 | | 5 | CANCELLED | "cancelled" | 已取消 | ### 实现细节 #### 1. Model 层(数据库) ```python from enum import IntEnum class MemberRole(IntEnum): """成员角色枚举""" OWNER = 1 EDITOR = 2 VIEWER = 3 @classmethod def from_string(cls, value: str) -> "MemberRole": """从字符串转换""" mapping = {"owner": cls.OWNER, "editor": cls.EDITOR, "viewer": cls.VIEWER} return mapping.get(value.lower(), cls.VIEWER) def to_string(self) -> str: """转换为字符串""" mapping = {self.OWNER: "owner", self.EDITOR: "editor", self.VIEWER: "viewer"} return mapping[self] # 数据库列定义 role: int = Field( sa_column=Column(SmallInteger, nullable=False, default=MemberRole.VIEWER) ) ``` #### 2. Schema 层(API) ```python from pydantic import field_validator, field_serializer class MemberRoleEnum(str, Enum): """API 层角色枚举(字符串)""" OWNER = "owner" EDITOR = "editor" VIEWER = "viewer" class FolderMemberCreate(BaseModel): role: MemberRoleEnum = Field(default=MemberRoleEnum.VIEWER) @field_validator('role', mode='before') @classmethod def convert_role_to_int(cls, v): """API 输入:字符串 → 整数""" if isinstance(v, str): return MemberRole.from_string(v) return v class FolderMemberResponse(BaseModel): role: str @field_serializer('role') def serialize_role(self, value: int) -> str: """API 输出:整数 → 字符串""" return MemberRole(value).to_string() ``` #### 3. Service 层 ```python # 简化角色映射 role_enum = MemberRole.from_string(role_string) # 或直接使用整数值 has_permission = await self.repository.check_user_permission( user_id, folder_id, MemberRole.VIEWER ) ``` ### 数据库迁移 由于数据库可以清除重建,迁移脚本将直接创建新表结构: ```sql -- 006_folder_enum_to_smallint.py -- 删除旧的 ENUM 类型(如果存在) DROP TYPE IF EXISTS member_role CASCADE; -- folder_members 表使用 SMALLINT CREATE TABLE folder_members ( id TEXT PRIMARY KEY DEFAULT gen_uuid_v7(), folder_id TEXT NOT NULL REFERENCES folders(id) ON DELETE CASCADE, user_id TEXT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, role SMALLINT NOT NULL DEFAULT 3 CHECK (role IN (1, 2, 3)), -- 1: owner, 2: editor, 3: viewer ... ); -- folder_export_jobs 表使用 SMALLINT CREATE TABLE folder_export_jobs ( id TEXT PRIMARY KEY DEFAULT gen_uuid_v7(), status SMALLINT NOT NULL DEFAULT 1 CHECK (status IN (1, 2, 3, 4, 5)), -- 1: pending, 2: processing, 3: completed, 4: failed, 5: cancelled ... ); ``` ## 优势 1. **性能提升**: SMALLINT 查询和索引性能优于 ENUM 2. **易于扩展**: 添加新值无需 ALTER TYPE,只需更新 CHECK 约束 3. **一致性**: 与 `folder_category` 等字段保持统一 4. **向后兼容**: API 层仍使用字符串,客户端无感知 5. **类型安全**: Python IntEnum 提供编译时类型检查 ## 风险与缓解 | 风险 | 影响 | 缓解措施 | |------|------|----------| | 数据迁移失败 | 高 | 数据库可清除重建,无历史数据 | | API 兼容性问题 | 中 | Schema 层保持字符串格式 | | 代码逻辑错误 | 中 | 完整的单元测试覆盖 | ## 实施步骤 1. ✅ 创建 RFC 文档 2. ⏳ 更新 Model 层(IntEnum + SMALLINT) 3. ⏳ 更新 Schema 层(转换逻辑) 4. ⏳ 更新 Service 层(简化映射) 5. ⏳ 创建数据库迁移脚本 6. ⏳ 代码验证(getDiagnostics) 7. ⏳ 测试验证 ## 相关文档 - [folder-service.md](../../requirements/backend/04-services/project/folder-service.md) - [ADR 005: Variant to Tag System Refactor](../../architecture/adrs/005-variant-to-tag-system-refactor.md) - [RFC 130: Enum to SMALLINT Refactor](./130-enum-to-smallint-refactor.md) ## 变更记录 - 2026-01-23: 初始版本,定义重构方案