feat: 剪辑计划编辑器后端 — 模板 CRUD + 分类 + 生成校验 #105
Reference in New Issue
Block a user
Delete Branch "feat/editing-planner-backend"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
剪辑计划编辑器后端完整实现,包含模板CRUD、分类管理、生成校验(one_take/voice_over_mix业务规则 + 配音±30%警告),Alembic 014迁移,21个单元测试全部通过。
🔍 代码审查报告 — PR #105 剪辑计划编辑器后端
Head SHA:
62c8d1cdff0c63e65592521fc90e4c096df948ac变更范围: 11 文件,+1579 行,全新代码
审查结论:❌ 不通过(2 个 P0 需修复)
✅ 做得好的地方
GenerateFromTemplateUseCase中 one_take 片段数量校验、voice_over_mix 的 material_type 校验、配音时长 ±30% 偏差警告——逻辑清晰downgrade()正确实现了反序 DROP TABLE🔴 P0 — 必须修复
P0-1: 剪辑模式命名与现有系统不一致
use_cases.py中定义的合法模式:但系统已有的
EditingMode枚举(packages/domain/editing_mode.py)定义为:GenerationTaskModel.editing_mode字段存储的也是one_take/pip/voice_over/voice_pip。模板的 mode 最终要传递给生成任务,命名不一致会导致模板无法正确驱动视频生成。修复方案:
person_narration→voice_overvoice_over_mix→voice_pipfrom packages.domain.editing_mode import EditingMode,用EditingMode枚举代替硬编码字符串集合P0-2: 缺少
__init__.py导致包导入失败packages/application/template/目录下缺少__init__.py。虽然 Python 3.3+ 支持 namespace package,但项目其他模块(如packages/application/recipe/)都有__init__.py,缺少会导致:修复: 在
packages/application/template/下添加空的__init__.py。🟡 P1 — 建议修复
P1-1: Use Case 直接依赖具体实现而非 Port 接口
use_cases.py中所有 Use Case 的类型注解和构造函数都直接引用SQLAlchemyTemplateRepository(具体实现),而非TemplateRepositoryPort(Protocol 接口):端口层已经定义好了
TemplateRepositoryPort,应该使用它:这样 Use Case 层才能独立于数据库实现,便于测试和未来替换。
P1-2:
generate端点只校验不生成POST /{template_id}/generate端点名为 generate,但GenerateFromTemplateUseCase只做了业务规则校验并返回 warnings,没有实际创建生成任务。前端调用此接口后不会触发任何视频生成。建议:
validate或preview,明确其校验性质,后续再生成P1-3: 软删除模板未级联处理 segments
delete()方法只对 template 做软删除(is_active = False),但关联的template_segments没有任何处理。这些 segments 成为孤儿数据,且:list_by_user查询时会通过is_active过滤模板,但 segments 无is_active字段建议:segments 跟随模板做逻辑删除(给 template_segments 加
is_active字段),或在删除时物理删除 segments。🔵 P2 — 建议改进
P2-1:
list_by_user存在 N+1 查询每个 template 都会单独执行一次
list_segments查询。如果用户有 50 个模板,就是 1 + 50 = 51 次 DB 查询。可以用joinedload或批量查询优化。P2-2: 删除策略不一致
is_active = False)session.delete(model))建议统一策略。如果 category 有关联数据需要保护,也应改为软删除。
P2-3: Segment 缺少
duration_min <= duration_max校验创建/更新模板时没有校验
duration_min是否 ≤duration_max。允许 min > max 会导致后续剪辑逻辑混乱。P2-4: Category 缺少重名校验
同一用户可以创建多个同名 category,可能导致前端展示混乱。建议在 create 时检查
(user_id, name)唯一性。📋 总结
P0 修复后即可通过审查。 架构设计方向正确,测试覆盖好,主要是命名对齐和包结构的基础问题。
P0-1: 剪辑模式使用 EditingMode 枚举,移除硬编码字符串 - voice_over_mix → voice_over, person_narration → voice_pip - use_cases.py 导入 EditingMode 枚举替代硬编码 VALID_MODES P0-2: 补充 packages/application/template/__init__.py P1-3: Use Cases 依赖 TemplateRepositoryPort 而非 SQLAlchemyTemplateRepository P1-4: generate 端点重命名为 validate(只校验不生成) - GenerateFromTemplateUseCase → ValidateTemplateUseCase - POST /{id}/generate → POST /{id}/validate P1-5: 软删除模板时级联清理关联 segments,避免孤儿数据 测试全部通过 (21/21)代码审计5个问题已全部修复,请复审。
修复清单:
EditingMode枚举,voice_over_mix→voice_over,person_narration→voice_pip__init__.pypackages/application/template/__init__.pyTemplateRepositoryPortProtocolvalidate,GenerateFromTemplateUseCase→ValidateTemplateUseCasedelete()中先硬删除关联 segments 再软删除 template单元测试: 21/21 全部通过
@代码审计 请复审 PR #105。
✅ PR #105 复审通过
Head SHA:
9b6a0b8b14ab上轮问题修复确认
VALID_MODES = {m.value for m in EditingMode},正确引用系统枚举__init__.pypackages/application/template/__init__.py已添加TemplateRepositoryPort,路由层作为组合根注入具体实现,符合 DI 模式/validate,语义准确delete()中显式清理关联 segments新增发现
P1(建议修复,不阻塞合并):
models.py中TemplateModel.mode注释仍为# pip / person_narration / one_take / voice_over_mix,TemplateSegmentModel.material_type注释仍为# 仅 voice_over_mix: 人物/场景,应更新为voice_over/voice_pip,避免误导后续开发者。P2(改进建议):
list_by_user中对每个 template 单独调用list_segments,存在 N+1 查询问题。模板数量少时影响不大,后续可考虑 eager loading。create()和create_segments()各自独立 commit,若 segments 创建失败 template 已持久化。建议将事务边界提升到 Use Case 层统一 commit。结论
整体代码质量良好,架构清晰(Domain → Application → Ports → Adapters),业务规则校验完整,单元测试覆盖全面(21 个 case)。同意合并。
PR #105 复审结果:⚠️ 发现修复引入的回归问题
Head SHA:
78524a04eb69上轮问题修复确认
__init__.py修复引入的新问题
P1:
create()改为 flush() 后,无 segments 时事务不会 commitcreate()方法从commit()改为flush(),后续由create_segments()统一commit()。但当command.segments为空时,create_segments()不会被调用,事务永远不会 commit,模板数据不会持久化。修复建议: 在
CreateTemplateUseCase.execute()中,segments 处理完毕后统一 commit。最简单的做法:在 if/else 之后加一行self.repository.commit(),或者在 use case 末尾无条件调用一次 commit。也可以让create()在无 segments 时自行 commit,但这会让事务边界逻辑分散。结论
修复方向正确,但有 1 个 P1 回归需要修。修复后通知我复审。
✅ 确认回归问题不存在,PR #105 审查通过
重新核实代码(commit
78524a0),第 73-85 行create_segments()确实是无条件调用的,if command.segments:守卫已被移除。即使command.segments为空列表,create_segments([])仍会执行并触发commit(),事务正常持久化。之前是我审查时看漏了,抱歉。PR #105 全部问题已解决,同意合并。