7766ba1479
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 4s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 7s
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 29s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 30s
CI/CD Pipeline / Validate - Style (push) Has been cancelled
CI/CD Pipeline / Validate - Security (push) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API 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 / ACR Image Cleanup (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
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
157 lines
5.8 KiB
Python
Executable File
157 lines
5.8 KiB
Python
Executable File
"""SQLAlchemy implementation of TemplateClipConfigRepository."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import List, Optional
|
||
|
||
from sqlalchemy.orm import Session
|
||
|
||
from packages.adapters.sqlalchemy_impl.models import (
|
||
TemplateClipConfigModel,
|
||
TemplateModel,
|
||
)
|
||
from packages.domain.template_clip_config import (
|
||
ClipType,
|
||
TemplateClipConfig,
|
||
TransitionEffect,
|
||
)
|
||
|
||
|
||
class SQLAlchemyTemplateClipConfigRepository:
|
||
"""SQLAlchemy 模板片段配置仓储"""
|
||
|
||
def __init__(self, session: Session) -> None:
|
||
self.session = session
|
||
|
||
def list_by_template(
|
||
self,
|
||
template_id: str,
|
||
*,
|
||
clip_type: Optional[ClipType] = None,
|
||
skip: int = 0,
|
||
limit: int = 100,
|
||
) -> List[TemplateClipConfig]:
|
||
"""按模板列出片段配置,按 order 排序"""
|
||
query = self.session.query(TemplateClipConfigModel).filter(
|
||
TemplateClipConfigModel.template_id == template_id,
|
||
)
|
||
if clip_type:
|
||
query = query.filter(TemplateClipConfigModel.clip_type == clip_type)
|
||
query = query.order_by(TemplateClipConfigModel.order)
|
||
models = query.offset(skip).limit(limit).all()
|
||
return [self._model_to_entity(m) for m in models]
|
||
|
||
def template_owned_by(self, template_id: str, user_id: str) -> bool:
|
||
"""校验旧模板主表 ``templates`` 中模板归属当前用户且未删除(is_active=True).
|
||
|
||
片段配置主表 ``template_clip_configs`` 本身没有 user_id 列,
|
||
归属关系通过模板主表 ``templates.user_id`` 确定。
|
||
新表 ``edit_templates`` 为全局模板库(无 user_id 列),不走此校验。
|
||
"""
|
||
return (
|
||
self.session.query(TemplateModel.id)
|
||
.filter(
|
||
TemplateModel.id == template_id,
|
||
TemplateModel.user_id == user_id,
|
||
TemplateModel.is_active.is_(True),
|
||
)
|
||
.first()
|
||
is not None
|
||
)
|
||
|
||
def get(self, config_id: str) -> Optional[TemplateClipConfig]:
|
||
"""根据 ID 获取配置"""
|
||
model = self.session.query(TemplateClipConfigModel).filter(TemplateClipConfigModel.id == config_id).first()
|
||
if model is None:
|
||
return None
|
||
return self._model_to_entity(model)
|
||
|
||
def create(self, config: TemplateClipConfig) -> TemplateClipConfig:
|
||
"""创建配置"""
|
||
model = TemplateClipConfigModel(
|
||
id=config.id,
|
||
template_id=config.template_id,
|
||
clip_type=config.clip_type,
|
||
order=config.order,
|
||
min_duration=config.min_duration,
|
||
max_duration=config.max_duration,
|
||
text_template=config.text_template,
|
||
material_requirements=config.material_requirements,
|
||
transition_effect=config.transition_effect,
|
||
config=config.config,
|
||
)
|
||
self.session.add(model)
|
||
self.session.commit()
|
||
self.session.refresh(model)
|
||
return self._model_to_entity(model)
|
||
|
||
def update(self, config: TemplateClipConfig) -> TemplateClipConfig:
|
||
"""更新配置"""
|
||
model = self.session.query(TemplateClipConfigModel).filter(TemplateClipConfigModel.id == config.id).first()
|
||
if model is None:
|
||
raise ValueError(f"TemplateClipConfig {config.id} not found")
|
||
model.template_id = config.template_id
|
||
model.clip_type = config.clip_type
|
||
model.order = config.order
|
||
model.min_duration = config.min_duration
|
||
model.max_duration = config.max_duration
|
||
model.text_template = config.text_template
|
||
model.material_requirements = config.material_requirements
|
||
model.transition_effect = config.transition_effect
|
||
model.config = config.config
|
||
model.updated_at = config.updated_at
|
||
self.session.commit()
|
||
self.session.refresh(model)
|
||
return self._model_to_entity(model)
|
||
|
||
def delete(self, config_id: str) -> bool:
|
||
"""删除配置"""
|
||
model = self.session.query(TemplateClipConfigModel).filter(TemplateClipConfigModel.id == config_id).first()
|
||
if model is None:
|
||
return False
|
||
self.session.delete(model)
|
||
self.session.commit()
|
||
return True
|
||
|
||
def delete_by_template(self, template_id: str, *, commit: bool = True) -> int:
|
||
"""删除模板下所有片段配置,返回删除数量
|
||
|
||
Args:
|
||
template_id: 模板ID
|
||
commit: 是否提交事务,默认True。外层有事务控制时传False。
|
||
"""
|
||
count = (
|
||
self.session.query(TemplateClipConfigModel)
|
||
.filter(TemplateClipConfigModel.template_id == template_id)
|
||
.delete()
|
||
)
|
||
if commit:
|
||
self.session.commit()
|
||
return count
|
||
|
||
def count(self, *, template_id: Optional[str] = None) -> int:
|
||
"""统计配置数量"""
|
||
query = self.session.query(TemplateClipConfigModel)
|
||
if template_id:
|
||
query = query.filter(TemplateClipConfigModel.template_id == template_id)
|
||
return query.count()
|
||
|
||
@staticmethod
|
||
def _model_to_entity(model: TemplateClipConfigModel) -> TemplateClipConfig:
|
||
return TemplateClipConfig(
|
||
id=model.id,
|
||
template_id=model.template_id,
|
||
clip_type=ClipType(model.clip_type),
|
||
order=model.order,
|
||
min_duration=model.min_duration or 0.0,
|
||
max_duration=model.max_duration or 0.0,
|
||
text_template=model.text_template or "",
|
||
material_requirements=model.material_requirements or {},
|
||
transition_effect=(
|
||
TransitionEffect(model.transition_effect) if model.transition_effect else TransitionEffect.CUT
|
||
),
|
||
config=model.config or {},
|
||
created_at=model.created_at,
|
||
updated_at=model.updated_at,
|
||
)
|