352221f199
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 16s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m0s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 2m42s
CI/CD Pipeline / Integration Tests (push) Successful in 3m50s
CI/CD Pipeline / Build Staging API Image (push) Successful in 3m35s
CI/CD Pipeline / Validate - Style (push) Successful in 4m42s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m29s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 5m0s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 43s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m47s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m5s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m10s
CI/CD Pipeline / Validate - Security (push) Successful in 11m12s
CI/CD Pipeline / Unit Tests (push) Successful in 11m18s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Failing after 27h18m15s
CI/CD Pipeline / Build Production API Image (push) Failing after 27h18m15s
CI/CD Pipeline / Frontend Lint (push) Failing after 27h29m35s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 27h24m8s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 27h28m58s
CI/CD Pipeline / PR Build API Image (push) Failing after 27h29m0s
CI/CD Pipeline / Deploy Production (push) Failing after 27h17m36s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 27h17m38s
CI/CD Pipeline / Build Production Web Image (push) Failing after 27h17m38s
CI/CD Pipeline / Canary Release to Production (push) Failing after 27h17m36s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 27h24m7s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 27h24m8s
CI/CD Pipeline / PR Build Web Image (push) Failing after 27h28m58s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 27h29m0s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
92 lines
3.4 KiB
Python
92 lines
3.4 KiB
Python
"""默认模板兜底共享逻辑(P0 #1922).
|
||
|
||
提供 get_or_create_default_template_id(db, user_id) 共享函数,
|
||
供 templates.py 列表查询、clips_standalone.py 独立端点、dependencies.py
|
||
resolve_draft_plan_id 三处复用,避免三处各写一套兜底逻辑产生分叉。
|
||
|
||
根因:PR#1918 清理模板管理 API 时误删了 GET /templates 自动创建默认模板
|
||
兜底,前端 PR#1913 去掉空 tid 拦截后首次进入生成页拼出
|
||
/templates//editor/clips/from-assets(双斜杠)→ FastAPI 404,阻断新用户首次
|
||
生成。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
from typing import Optional
|
||
|
||
from sqlalchemy.orm import Session
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def get_or_create_default_template_id(db: Session, user_id: str) -> Optional[str]:
|
||
"""获取或自动创建默认配音模板的 id。
|
||
|
||
判定逻辑(不做异常降级,只有确实创建失败时才回滚重查):
|
||
1. 查用户名下 is_active=True 且有 TemplateClipConfig 的模板 → 返回其 id;
|
||
2. 无则调用 CreateTemplateUseCase 创建一条默认 voice_over 模板;
|
||
3. 创建异常时 rollback 再重查一次(防并发唯一键冲突),重查仍无返回 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
|
||
|
||
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
|
||
|
||
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)],
|
||
)
|
||
tpl = CreateTemplateUseCase(repo).execute(cmd)
|
||
db.commit()
|
||
logger.info("auto-created default voice_over template: id=%s user=%s", tpl.id, user_id)
|
||
return tpl.id
|
||
except Exception:
|
||
db.rollback()
|
||
# 重查:可能并发请求已建好
|
||
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
|
||
logger.exception("failed to auto-create default template user=%s", user_id)
|
||
return None
|