# Changelog: 剧本管理服务枚举类型重构 **日期**: 2026-01-22 **类型**: 重构 **影响范围**: 剧本管理模块(screenplays, screenplay_characters, screenplay_scenes, screenplay_props) --- ## 概述 将剧本管理服务中所有 PostgreSQL ENUM 类型改为 SMALLINT + Python IntEnum 实现方式,与项目模块保持架构一致性。 ## 变更内容 ### 数据库层 #### 移除的 ENUM 类型 ```sql -- 移除以下 ENUM 类型定义 DROP TYPE IF EXISTS screenplay_type; DROP TYPE IF EXISTS screenplay_status; DROP TYPE IF EXISTS character_role_type; DROP TYPE IF EXISTS time_of_day_type; DROP TYPE IF EXISTS prop_importance_type; ``` #### 改为 SMALLINT 字段 | 表名 | 字段名 | 旧类型 | 新类型 | 默认值 | |------|--------|--------|--------|--------| | screenplays | type | screenplay_type | SMALLINT | 1 | | screenplays | status | screenplay_status | SMALLINT | 1 | | screenplay_characters | role_type | character_role_type | SMALLINT | 2 | | screenplay_scenes | time_of_day | time_of_day_type | SMALLINT | NULL | | screenplay_props | importance | prop_importance_type | SMALLINT | 2 | #### 添加列注释 ```sql COMMENT ON COLUMN screenplays.type IS '剧本类型:1=text(文本), 2=file(文件)'; COMMENT ON COLUMN screenplays.status IS '剧本状态:1=draft(草稿), 2=review(审核中), 3=approved(已批准), 4=archived(已归档)'; COMMENT ON COLUMN screenplay_characters.role_type IS '角色类型:1=main(主角), 2=supporting(配角), 3=extra(群演)'; COMMENT ON COLUMN screenplay_scenes.time_of_day IS '时间段:1=dawn(黎明), 2=morning(上午), 3=noon(中午), 4=afternoon(下午), 5=dusk(黄昏), 6=night(夜晚)'; COMMENT ON COLUMN screenplay_props.importance IS '重要性:1=key(关键道具), 2=normal(普通道具), 3=background(背景道具)'; ``` ### Python 模型层 #### 新增 IntEnum 类 ```python from enum import IntEnum class ScreenplayType(IntEnum): TEXT = 1 FILE = 2 @classmethod def from_string(cls, value: str) -> int: mapping = {'text': cls.TEXT, 'file': cls.FILE} return mapping.get(value.lower(), cls.TEXT) @classmethod def to_string(cls, value: int) -> str: mapping = {cls.TEXT: 'text', cls.FILE: 'file'} return mapping.get(value, 'text') class ScreenplayStatus(IntEnum): DRAFT = 1 REVIEW = 2 APPROVED = 3 ARCHIVED = 4 @classmethod def from_string(cls, value: str) -> int: mapping = { 'draft': cls.DRAFT, 'review': cls.REVIEW, 'approved': cls.APPROVED, 'archived': cls.ARCHIVED } return mapping.get(value.lower(), cls.DRAFT) @classmethod def to_string(cls, value: int) -> str: mapping = { cls.DRAFT: 'draft', cls.REVIEW: 'review', cls.APPROVED: 'approved', cls.ARCHIVED: 'archived' } return mapping.get(value, 'draft') class CharacterRoleType(IntEnum): MAIN = 1 SUPPORTING = 2 EXTRA = 3 @classmethod def from_string(cls, value: str) -> int: mapping = {'main': cls.MAIN, 'supporting': cls.SUPPORTING, 'extra': cls.EXTRA} return mapping.get(value.lower(), cls.SUPPORTING) @classmethod def to_string(cls, value: int) -> str: mapping = {cls.MAIN: 'main', cls.SUPPORTING: 'supporting', cls.EXTRA: 'extra'} return mapping.get(value, 'supporting') class TimeOfDay(IntEnum): DAWN = 1 MORNING = 2 NOON = 3 AFTERNOON = 4 DUSK = 5 NIGHT = 6 @classmethod def from_string(cls, value: str) -> int: mapping = { 'dawn': cls.DAWN, 'morning': cls.MORNING, 'noon': cls.NOON, 'afternoon': cls.AFTERNOON, 'dusk': cls.DUSK, 'night': cls.NIGHT } return mapping.get(value.lower(), cls.MORNING) @classmethod def to_string(cls, value: int) -> str: mapping = { cls.DAWN: 'dawn', cls.MORNING: 'morning', cls.NOON: 'noon', cls.AFTERNOON: 'afternoon', cls.DUSK: 'dusk', cls.NIGHT: 'night' } return mapping.get(value, 'morning') class PropImportance(IntEnum): KEY = 1 NORMAL = 2 BACKGROUND = 3 @classmethod def from_string(cls, value: str) -> int: mapping = {'key': cls.KEY, 'normal': cls.NORMAL, 'background': cls.BACKGROUND} return mapping.get(value.lower(), cls.NORMAL) @classmethod def to_string(cls, value: int) -> str: mapping = {cls.KEY: 'key', cls.NORMAL: 'normal', cls.BACKGROUND: 'background'} return mapping.get(value, 'normal') ``` #### 模型字段更新 ```python # 修改前 type = Column(Enum(ScreenplayType), nullable=False) status = Column(Enum(ScreenplayStatus), default=ScreenplayStatus.DRAFT) # 修改后 type = Column(SmallInteger, nullable=False) status = Column(SmallInteger, nullable=False, default=ScreenplayStatus.DRAFT) # 添加 property 方法 @property def type_str(self) -> str: return ScreenplayType.to_string(self.type) @property def status_str(self) -> str: return ScreenplayStatus.to_string(self.status) ``` ### API 层保持兼容 API 接口继续使用字符串,对外无影响: ```python # Schema 层 class ScreenplayCreate(BaseModel): type: str = Field(..., pattern="^(text|file)$") status: str = Field(default="draft", pattern="^(draft|review|approved|archived)$") # Response 层 class ScreenplayResponse(BaseModel): type: str # 返回 'text' 或 'file' status: str # 返回 'draft', 'review', 'approved', 'archived' @classmethod def from_orm(cls, obj): return cls( type=obj.type_str, # 使用 property 方法 status=obj.status_str, ... ) ``` ## 枚举值映射表 | 枚举类型 | 数值 | 字符串值 | 说明 | |---------|------|---------|------| | **screenplay_type** | 1 | text | 文本剧本 | | | 2 | file | 文件剧本 | | **screenplay_status** | 1 | draft | 草稿 | | | 2 | review | 审核中 | | | 3 | approved | 已批准 | | | 4 | archived | 已归档 | | **character_role_type** | 1 | main | 主角 | | | 2 | supporting | 配角 | | | 3 | extra | 群演 | | **time_of_day** | 1 | dawn | 黎明 | | | 2 | morning | 上午 | | | 3 | noon | 中午 | | | 4 | afternoon | 下午 | | | 5 | dusk | 黄昏 | | | 6 | night | 夜晚 | | **prop_importance** | 1 | key | 关键道具 | | | 2 | normal | 普通道具 | | | 3 | background | 背景道具 | ## 优势 ### 1. 架构一致性 - 与项目模块(ProjectType, ProjectStatus, FolderCategory)保持一致 - 统一的代码模式,降低维护成本 - 新开发者更容易理解 ### 2. 简化数据库 - 无需 ENUM 类型定义 - 字段类型统一为 SMALLINT - 迁移脚本更简洁 ### 3. 扩展灵活 - 添加新枚举值无需 ALTER TYPE - 可以动态扩展(如添加新的剧本类型、状态) - 便于版本管理 ### 4. 性能提升 - SMALLINT 比 TEXT 更高效(2 字节 vs 可变长度) - 索引性能更好 - 存储空间更小 ### 5. 跨数据库兼容 - SMALLINT 是标准 SQL 类型 - 便于迁移到其他数据库 - 减少数据库特定依赖 ### 6. 向后兼容 - API 接口保持不变 - 对外仍使用字符串 - 客户端无需修改 ## 影响评估 ### Breaking Changes - ❌ 无破坏性变更(API 接口保持不变) ### 数据库迁移 - ⚠️ 需要数据迁移脚本(如果已有数据) - ⚠️ 开发环境可以删除重建 - ⚠️ 生产环境需要编写迁移脚本 ### 代码变更 - ✅ 模型层:使用 IntEnum 和 SmallInteger - ✅ Service 层:添加枚举转换逻辑 - ✅ Schema 层:保持使用字符串,添加验证器 - ✅ Repository 层:查询时使用数字 ## 后续工作 - [ ] 创建数据库迁移脚本 - [ ] 实现 Python 模型层 - [ ] 更新 Service 层转换逻辑 - [ ] 更新 Schema 层验证器 - [ ] 编写单元测试 - [ ] 编写集成测试 - [ ] 更新 API 文档 ## 相关文档 - [RFC 130: 枚举类型重构](../rfcs/130-enum-to-smallint-refactor.md) - [RFC 132: 剧本元素类型枚举重构](../rfcs/132-screenplay-element-type-refactor.md) - [剧本管理服务文档](../../requirements/backend/04-services/project/screenplay-service.md) --- **变更完成时间**: 2026-01-22 **文档更新**: 已完成 **代码实现**: 待实施