feat: 任务3.05 音色克隆API — CRUD + 状态管理 #165
Reference in New Issue
Block a user
Delete Branch "feature/task-305-voice-clone-api"
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?
任务 3.05:音色克隆 API
内容
延期说明
CosyVoice API 实际调用延期到 Task 3.07,新建克隆停留在 pending 状态。
- Add VoiceCloneProfile CRUD endpoints: POST /api/v1/voice-clones (create clone task, status=pending) GET /api/v1/voice-clones (list with pagination + status filter) GET /api/v1/voice-clones/{id} (get details) GET /api/v1/voice-clones/{id}/status (polling endpoint) DELETE /api/v1/voice-clones/{id} (soft delete) POST /api/v1/voice-clones/{id}/retry (retry failed clone) - Add VoiceCloneProfile SQLAlchemy model and repository adapter - Add Alembic migration 019 for voice_clone_profiles table - Add use cases: Create, List, Get, GetStatus, Delete, Retry - Add Pydantic schemas for request/response validation - Add 19 unit tests (all passing) - CosyVoice API integration deferred to Task 3.07 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>PR #165 审查报告 — 任务 3.05 音色克隆 API
PR: #165
分支:
feature/task-305-voice-clone-api→developCommit:
c55dafdb变更: 9 文件,+930 / -0
结论: ✅ 有条件通过(0 P0 / 0 P1 / 2 P2 / 4 P3)
mergeable: ✅ True
一、变更文件清单
apps/api/app/api/routes/voice_clones.pyapps/api/app/schemas/voice_clone.pypackages/adapters/sqlalchemy_impl/voice_clone_profile_repository.pypackages/adapters/sqlalchemy_impl/models.pypackages/application/voice_clone/__init__.pypackages/application/voice_clone/use_cases.pyalembic/versions/019_add_voice_clone_profiles_table.pyapps/api/app/api/router.pytests/unit/test_voice_clone_api.py二、审查要点验证
✅ 1. Repository 正确实现端口接口
VoiceCloneProfileRepository(Protocol)定义 7 个方法:create / get / update / delete / list_by_user / count_by_user / find_by_voice_idSQLAlchemyVoiceCloneProfileRepository全部实现,方法签名完全匹配Protocol实现结构化子类型,符合 Hexagonal 架构✅ 2. 状态机转换符合领域模型
pending → processing → ready / failed,failed → pending(重试),ready/disabled为终态_VALID_TRANSITIONS字典严格定义合法转换transition_to()方法校验非法转换并抛出ValueErrormark_processing()/mark_ready(voice_id)/mark_failed(error_message)/mark_disabled()/prepare_retry()✅ 3. 重试逻辑正确
is_retryable属性:status == FAILED and retry_count < max_retriesprepare_retry():retry_count += 1→transition_to(PENDING)→ 清除error_message和voice_idis_retryable,不可重试抛出VoiceCloneNotRetryableError✅ 4. API 认证/鉴权正确
get_current_user获取认证用户profile.user_id != user_id→ 抛出VoiceCloneNotFoundError(404)✅ 5. 分页实现正确
skip(offset)+limit分页,默认 skip=0, limit=50, 最大 200total(通过count_by_user)list_by_user支持 status 过滤 + 按created_at desc排序✅ 6. 错误处理完善
VoiceCloneNotFoundError→ GET/{id} / GET/{id}/status / DELETE / POST/{id}/retryVoiceCloneNotRetryableError→ POST/{id}/retry✅ 7. Alembic 迁移正确
revision = "019",down_revision = "018"链路正确user_id和status有索引metadata列使用JSON类型created_at/updated_at使用server_default=sa.func.now()✅ 8. 单元测试覆盖充分
✅ 9. Hexagonal 架构分层合理
三、发现的问题
P2-1:分页参数风格不一致(skip/limit vs page/page_size)
voice_clones.py第 96-97 行skip+limit分页page+page_size,风格不一致page(从 1 开始)+page_size,在 Use Case 层转换为skip = (page - 1) * page_sizeP2-2:DELETE 缺少关联引用检查
use_cases.pyDeleteVoiceCloneUseCaseready状态的音色正被视频生成任务引用,删除后可能导致引用断裂is_in_use检查或在voice_id维度做引用计数P3-1:
metadata_命名风格voice_clone.pySchema +models.pyORMmetadata_,通过alias="metadata"映射到 JSON 字段metadatametadata_后缀命名略不直观;项目中其他 Model 使用extra_meta = Column("metadata", ...)风格extra_meta命名P3-2:VoiceCloneStatus 枚举不包含 "deleted"
voice_clone_profile.py+voice_clone_profile_repository.pystatus = "deleted",不在枚举中get()和list_by_user()已过滤status != "deleted",实际运行中不会触发问题。但设计上存在类型不一致_model_to_entity中增加断言P3-3:source_audio_url 缺少格式校验
voice_clone.pyCreateVoiceCloneRequestsource_audio_url默认为空字符串,无 URL 格式校验P3-4:缺少硬删除/数据清理机制
四、亮点
get_current_user+ Use Case 层user_id校验VoiceCloneStatusResponse轻量版用于轮询,VoiceCloneProfileResponse完整版用于详情五、总结
结论: ✅ 有条件通过 — 代码质量高,架构合理,核心功能完整。P2 问题不阻塞合并,建议后续迭代修复。
可合并: ✅ mergeable=True,修复 P2 后合并或合并后修复均可。