"""草稿管理路由. 端点: - GET / 获取草稿详情 - PUT / 更新草稿 - POST /publish 发布草稿到模板 - GET /versions 模板版本历史 - POST /rollback 回滚到指定版本 """ from __future__ import annotations from app.auth import AuthenticatedUser, get_current_user from app.services.edit_plan_service import EditPlanService from app.services.edit_template_service import EditTemplateService from fastapi import APIRouter, Depends, HTTPException, Query, status from .dependencies import get_draft_plan_id, get_editor_services from .schemas import ( EditorDraftResponse, EditorPublishResponse, EditorRollbackRequest, EditorRollbackResponse, EditorTemplateVersionItem, EditorUpdateRequest, EditorVersionListResponse, ) router = APIRouter(tags=["Template Editor"]) @router.get("", response_model=EditorDraftResponse) def get_editor_draft( template_id: str, plan_id: str = Depends(get_draft_plan_id), services: tuple[EditTemplateService, EditPlanService] = Depends(get_editor_services), _: AuthenticatedUser = Depends(get_current_user), ): """获取模板编辑器草稿详情 首次访问时自动创建草稿。 """ _, plan_svc = services plan = plan_svc.get_plan_or_raise(plan_id) clips = plan_svc.list_clips(plan_id) return EditorDraftResponse( plan_id=plan.id, template_id=plan.template_id, name=plan.name, status=plan.status.value if hasattr(plan.status, "value") else str(plan.status), config=plan.config or {}, total_duration=plan.total_duration, clip_count=len(clips), ) @router.put("", response_model=EditorDraftResponse) def update_editor_draft( template_id: str, req: EditorUpdateRequest, plan_id: str = Depends(get_draft_plan_id), services: tuple[EditTemplateService, EditPlanService] = Depends(get_editor_services), _: AuthenticatedUser = Depends(get_current_user), ): """更新模板编辑器草稿""" _, plan_svc = services plan = plan_svc.update_plan( plan_id, name=req.name, config=req.config, total_duration=req.total_duration, ) clips = plan_svc.list_clips(plan_id) return EditorDraftResponse( plan_id=plan.id, template_id=plan.template_id, name=plan.name, status=plan.status.value if hasattr(plan.status, "value") else str(plan.status), config=plan.config or {}, total_duration=plan.total_duration, clip_count=len(clips), ) @router.post("/publish", response_model=EditorPublishResponse, status_code=status.HTTP_200_OK) def publish_draft_to_template( template_id: str, plan_id: str = Depends(get_draft_plan_id), services: tuple[EditTemplateService, EditPlanService] = Depends(get_editor_services), _: AuthenticatedUser = Depends(get_current_user), ): """将草稿发布(同步)到正式模板 草稿的 config 和 clips 会同步覆盖到模板,事务保证一致性。 """ tpl_svc, plan_svc = services try: tpl = tpl_svc.publish_template_from_draft(template_id, plan_id) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc clips = plan_svc.list_clips(plan_id) return EditorPublishResponse( template_id=tpl.id, status="published", clip_count=len(clips), version=tpl.version, ) @router.get("/versions", response_model=EditorVersionListResponse) def list_template_versions( template_id: str, services: tuple[EditTemplateService, EditPlanService] = Depends(get_editor_services), _: AuthenticatedUser = Depends(get_current_user), limit: int = Query(default=50, ge=1, le=200), ): """查询模板发布版本历史""" tpl_svc, _ = services versions = tpl_svc.list_template_versions(template_id, limit=limit) items = [ EditorTemplateVersionItem( version=v.version, name=v.name, editing_mode=v.editing_mode, clip_count=len(v.clip_configs), change_note=v.change_note, published_by=v.published_by, created_at=( v.created_at.isoformat() if hasattr(v.created_at, "isoformat") else str(v.created_at) ), ) for v in versions ] return EditorVersionListResponse(versions=items, total=len(items)) @router.post("/rollback", response_model=EditorRollbackResponse, status_code=status.HTTP_200_OK) def rollback_template( template_id: str, request: EditorRollbackRequest, services: tuple[EditTemplateService, EditPlanService] = Depends(get_editor_services), _: AuthenticatedUser = Depends(get_current_user), ): """回滚模板到指定历史版本 回滚本身也是一次发布,版本号会 +1,可以再次回滚。 """ tpl_svc, _ = services try: tpl = tpl_svc.rollback_to_version(template_id, request.version) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc clip_configs = tpl_svc.list_clip_configs(template_id) return EditorRollbackResponse( template_id=tpl.id, status="rolled_back", rollback_to_version=request.version, new_version=tpl.version, clip_count=len(clip_configs), )