fix(backend): P0 前端#1911 删除模板选择UI后自动创建默认模板兜底
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 49s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m18s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m54s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m14s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 4m42s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 5m21s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m46s
AI Code Review / AI Code Review (pull_request) Successful in 6m55s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 38h20m34s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 38h22m53s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 38h20m7s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 38h20m27s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 38h20m32s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 38h20m37s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 38h22m20s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 38h22m21s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 38h22m21s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 38h22m24s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 38h20m2s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 38h20m2s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 38h20m42s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 38h20m47s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 49s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m18s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m54s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m14s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 4m42s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 5m21s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m46s
AI Code Review / AI Code Review (pull_request) Successful in 6m55s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 38h20m34s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 38h22m53s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 38h20m7s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 38h20m27s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 38h20m32s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 38h20m37s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 38h22m20s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 38h22m21s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 38h22m21s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 38h22m24s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 38h20m2s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 38h20m2s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 38h20m42s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 38h20m47s
- list_templates(valid_only=true):用户无任何有效模板时自动创建默认 voice_over 模板(含1个通用片段),避免前端selectedTemplate永远为空导致卡死 - POST /generation/variant-plans:放宽 schema 校验(template_id/source_edit_plan_id 均可不传),路由层自动为用户查找/创建默认模板 - 新增2个单测:schema允许空template_id、空参时自动创建默认模板并继续选片 - Refs #1899
This commit is contained in:
@@ -30,6 +30,74 @@ logger = logging.getLogger(__name__)
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _get_or_create_default_template_id(db: Session, user_id: str) -> str | None:
|
||||
"""为用户查找一个有效模板;若不存在则自动创建默认配音模板。
|
||||
|
||||
前端 #1911 删除了模板选择 UI,当调用方未传 template_id/source_edit_plan_id
|
||||
时(如剪辑页首次进入直接选片),后端兜底查找/创建默认模板,避免 400。
|
||||
|
||||
Returns:
|
||||
template_id(字符串);失败时返回 None。
|
||||
"""
|
||||
from packages.adapters.sqlalchemy_impl.models import TemplateClipConfigModel, TemplateModel
|
||||
from packages.adapters.sqlalchemy_impl.template_repository import SQLAlchemyTemplateRepository
|
||||
from packages.application.template.commands import CreateTemplateCommand, SegmentCommand
|
||||
from packages.application.template.use_cases import CreateTemplateUseCase
|
||||
import uuid
|
||||
|
||||
# 1. 先查已有有效模板(is_active=True 且存在片段配置)
|
||||
existing = (
|
||||
db.query(TemplateModel)
|
||||
.filter(
|
||||
TemplateModel.user_id == user_id,
|
||||
TemplateModel.is_active.is_(True),
|
||||
)
|
||||
.order_by(TemplateModel.created_at.asc())
|
||||
.first()
|
||||
)
|
||||
if existing is not None:
|
||||
# 验证该模板是否有片段配置;若没有继续尝试创建默认
|
||||
has_seg = (
|
||||
db.query(TemplateClipConfigModel.id).filter(TemplateClipConfigModel.template_id == existing.id).first()
|
||||
)
|
||||
if has_seg:
|
||||
return existing.id
|
||||
|
||||
# 2. 无有效模板 → 自动创建默认配音模板
|
||||
try:
|
||||
repo = SQLAlchemyTemplateRepository(db)
|
||||
cmd = CreateTemplateCommand(
|
||||
user_id=user_id,
|
||||
name="默认配音模板",
|
||||
mode="voice_over",
|
||||
category="default",
|
||||
tags=[],
|
||||
title_config={},
|
||||
subtitle_config={},
|
||||
bgm_config={},
|
||||
estimated_duration=0.0,
|
||||
segments=[
|
||||
SegmentCommand(
|
||||
segment_order=0,
|
||||
duration_min=1.0,
|
||||
duration_max=30.0,
|
||||
material_type=None,
|
||||
),
|
||||
],
|
||||
)
|
||||
use_case = CreateTemplateUseCase(repo)
|
||||
tpl = use_case.execute(cmd)
|
||||
logger.info(
|
||||
"[variant-plans] 自动创建默认模板: user=%s tpl=%s",
|
||||
user_id,
|
||||
tpl.id,
|
||||
)
|
||||
return tpl.id
|
||||
except Exception:
|
||||
logger.exception("[variant-plans] 自动创建默认模板失败: user=%s", user_id)
|
||||
return None
|
||||
|
||||
|
||||
class VariantPlanRequest(BaseModel):
|
||||
"""轻量选片请求体(与前端 variantPlans.ts 契约一致)。"""
|
||||
|
||||
@@ -43,8 +111,8 @@ class VariantPlanRequest(BaseModel):
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _validate(self) -> "VariantPlanRequest":
|
||||
if not self.template_id.strip() and not self.source_edit_plan_id.strip():
|
||||
raise ValueError("template_id 与 source_edit_plan_id 至少需要提供一个")
|
||||
# 不再强制要求 template_id / source_edit_plan_id:
|
||||
# 后端在路由内会自动查找/创建默认模板兜底(#1911 后前端不再显式选模板)。
|
||||
try:
|
||||
resolve_variant_voice_ids(
|
||||
count=self.count,
|
||||
@@ -94,8 +162,15 @@ def create_variant_plans(
|
||||
from app.services.generation_common import resolve_latest_plan_by_template
|
||||
|
||||
source_plan_id = request.source_edit_plan_id.strip()
|
||||
if not source_plan_id and request.template_id.strip():
|
||||
source_plan_id = resolve_latest_plan_by_template(db, template_id=request.template_id, user_id=user_id) or ""
|
||||
template_id = request.template_id.strip()
|
||||
|
||||
# P0 兜底:前端 #1911 已删除模板选择 UI,调用方可能不传 template_id;
|
||||
# 此时自动为该用户查找/创建默认模板。
|
||||
if not source_plan_id and not template_id:
|
||||
template_id = _get_or_create_default_template_id(db, user_id) or ""
|
||||
|
||||
if not source_plan_id and template_id:
|
||||
source_plan_id = resolve_latest_plan_by_template(db, template_id=template_id, user_id=user_id) or ""
|
||||
|
||||
if not source_plan_id:
|
||||
raise HTTPException(
|
||||
|
||||
@@ -95,6 +95,49 @@ def _to_response(template, usage_count: int = 0) -> TemplateResponse:
|
||||
)
|
||||
|
||||
|
||||
def _ensure_default_template(
|
||||
user_id: str,
|
||||
template_repository: SQLAlchemyTemplateRepository,
|
||||
):
|
||||
"""当用户无任何有效模板时,自动创建一个默认 voice_over 模式模板。
|
||||
|
||||
前端 #1911 删除了模板选择 UI(6步→5步),改为后台自动选第一个有效模板。
|
||||
为避免新用户/无模板用户在剪辑页卡死在"独立选片中...",当 valid_only 查询
|
||||
结果为空时自动创建一条默认配音模板(含 1 个 order=0 的通用片段配置),
|
||||
让 from-assets 能正常分配片段。
|
||||
|
||||
Returns:
|
||||
创建的默认 Template 实体;创建失败返回 None。
|
||||
"""
|
||||
try:
|
||||
cmd = CreateTemplateCommand(
|
||||
user_id=user_id,
|
||||
name="默认配音模板",
|
||||
mode="voice_over",
|
||||
category="default",
|
||||
tags=[],
|
||||
title_config={},
|
||||
subtitle_config={},
|
||||
bgm_config={},
|
||||
estimated_duration=0.0,
|
||||
segments=[
|
||||
SegmentCommand(
|
||||
segment_order=0,
|
||||
duration_min=1.0,
|
||||
duration_max=30.0,
|
||||
material_type=None,
|
||||
),
|
||||
],
|
||||
)
|
||||
use_case = CreateTemplateUseCase(template_repository)
|
||||
tpl = use_case.execute(cmd)
|
||||
logger.info("[template] 自动创建默认模板: user=%s tpl=%s", user_id, tpl.id)
|
||||
return tpl
|
||||
except Exception:
|
||||
logger.exception("自动创建默认模板失败: user_id=%s", user_id)
|
||||
return None
|
||||
|
||||
|
||||
# ── Template CRUD ──
|
||||
|
||||
|
||||
@@ -127,6 +170,14 @@ def list_templates(
|
||||
count_use_case = CountTemplatesUseCase(template_repository)
|
||||
total = count_use_case.execute(user_id, filter=tpl_filter)
|
||||
|
||||
# P0 兜底:剪辑页(valid_only=true)首次访问且用户无任何有效模板时,
|
||||
# 自动创建一条默认配音模板,避免前端 selectedTemplate 永远为空导致卡死。
|
||||
if valid_only and total == 0 and skip == 0:
|
||||
default_tpl = _ensure_default_template(user_id, template_repository)
|
||||
if default_tpl is not None:
|
||||
templates = [default_tpl]
|
||||
total = 1
|
||||
|
||||
# 批量查询使用次数
|
||||
items = []
|
||||
for t in templates:
|
||||
|
||||
Reference in New Issue
Block a user