style: 后端代码black格式化
Deploy / Deploy Staging (push) Failing after 138h4m33s
CI/CD Pipeline / Frontend Lint (push) Failing after 138h4m39s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 138h4m39s
Deploy / Staging E2E Tests (push) Failing after 1796h40m0s
Deploy / Production Browser E2E (push) Failing after 1796h40m1s
Deploy / Build Production Runtime Images (push) Failing after 1796h40m6s
Deploy / Deploy Production (push) Failing after 1797h11m28s

This commit is contained in:
灵应
2026-07-03 18:49:54 +08:00
parent bcb1663de4
commit 8935196fcd
104 changed files with 2490 additions and 1519 deletions
+2 -2
View File
@@ -237,8 +237,6 @@ async def reset_password(
return MessageResponse(message="密码重置成功")
@router.post("/logout")
async def logout(
credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme),
@@ -246,6 +244,7 @@ async def logout(
):
"""登出 - 将当前 token 加入黑名单"""
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
if credentials:
try:
payload = jwt.decode(credentials.credentials, settings.JWT_SECRET_KEY, algorithms=["HS256"])
@@ -255,6 +254,7 @@ async def logout(
logger.warning(f"Operation failed in apps/api/app/api/routes/auth.py: {e}", exc_info=True)
return MessageResponse(message="已登出")
@router.get("/me", response_model=CurrentUserResponse)
async def get_current_user_info(
authenticated_user: AuthenticatedUser = Depends(get_current_user),
+3 -9
View File
@@ -161,10 +161,7 @@ def list_plans(
except ValueError:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=(
f"无效的状态值: {status_filter},"
f"可选值: draft, editing, rendering, completed, failed"
),
detail=(f"无效的状态值: {status_filter}," f"可选值: draft, editing, rendering, completed, failed"),
)
skip = (page - 1) * page_size
@@ -261,10 +258,7 @@ def update_plan(
except ValueError:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=(
f"无效的状态值: {body.status},"
f"可选值: draft, editing, rendering, completed, failed"
),
detail=(f"无效的状态值: {body.status}," f"可选值: draft, editing, rendering, completed, failed"),
)
svc.transition_status(plan_id, target_status)
except ValueError as exc:
@@ -419,7 +413,7 @@ class TimelineSceneResponse(BaseModel):
"""时间线场景"""
scene: str = Field(..., description="场景描述")
time: str = Field(..., description="时间范围,如 \"0:00 - 0:05\"")
time: str = Field(..., description='时间范围,如 "0:00 - 0:05"')
duration: float = Field(..., ge=0, description="时长(秒)")
color: str = Field(..., description="展示颜色")
clip_id: str = Field(default="", description="关联的片段 ID")
+1 -2
View File
@@ -97,8 +97,7 @@ def create_job(
except ValueError:
raise HTTPException(
status_code=400,
detail=f"不支持的任务类型: {request.job_type},"
f"可选值: {[t.value for t in JobType]}",
detail=f"不支持的任务类型: {request.job_type}," f"可选值: {[t.value for t in JobType]}",
)
use_case = CreateJobUseCase(job_repo)
+11 -10
View File
@@ -205,8 +205,6 @@ async def cancel_subscription(
)
@router.post("/payment-callback")
async def payment_callback(
user_id: str,
@@ -234,14 +232,16 @@ async def payment_callback(
# 创建账单记录
record_id = uuid.uuid4().hex
record = repo.create({
"id": record_id,
"user_id": user_id,
"plan_name": _get_plan_name(plan),
"amount": amount,
"billing_cycle": billing_cycle,
"status": "pending",
})
record = repo.create(
{
"id": record_id,
"user_id": user_id,
"plan_name": _get_plan_name(plan),
"amount": amount,
"billing_cycle": billing_cycle,
"status": "pending",
}
)
# 在事务中标记支付成功并更新订阅
repo.mark_paid(record_id, payment_method, payment_id)
@@ -258,6 +258,7 @@ async def payment_callback(
finally:
session.close()
@router.post("/toggle-auto-renew", response_model=SimpleResponse)
async def toggle_auto_renew(
request: ToggleAutoRenewRequest,
+5 -7
View File
@@ -88,7 +88,8 @@ def synthesize(
# 提交 CosyVoice 合成任务
workflow = TTSWorkflowService(
repository=repository, cosyvoice_service=cosyvoice_service,
repository=repository,
cosyvoice_service=cosyvoice_service,
)
job = workflow.start_synthesis(job.id)
@@ -98,12 +99,11 @@ def synthesize(
if task_id:
try:
from worker_app.tasks import process_tts_synthesis
process_tts_synthesis.delay(job.id)
except Exception as e:
# Celery 调度失败,标记 job 为 failed
workflow.process_synthesis_failure(
job.id, f"Celery 任务调度失败: {e}"
)
workflow.process_synthesis_failure(job.id, f"Celery 任务调度失败: {e}")
return TTSSynthesizeResponse(
job_id=job.id,
@@ -124,9 +124,7 @@ def list_tts_jobs(
user_id = authenticated_user.user.id
use_case = ListTTSJobsUseCase(repository)
skip = (page - 1) * page_size
items, total = use_case.execute(
user_id, status=status_filter, skip=skip, limit=page_size
)
items, total = use_case.execute(user_id, status=status_filter, skip=skip, limit=page_size)
return ListTTSJobResponse(
items=[_to_response(j) for j in items],
total=total,
+15 -45
View File
@@ -59,14 +59,10 @@ def _to_response(profile) -> VoiceCloneProfileResponse:
def _get_workflow_service(
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(
get_voice_clone_profile_repository
),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
cosyvoice_service: CosyVoiceService = Depends(get_cosyvoice_service),
) -> VoiceCloneWorkflowService:
return VoiceCloneWorkflowService(
repository=repository, cosyvoice_service=cosyvoice_service
)
return VoiceCloneWorkflowService(repository=repository, cosyvoice_service=cosyvoice_service)
@router.post(
@@ -109,13 +105,9 @@ def create_voice_clone(
logger.error(f"Failed to dispatch Celery task: {e}")
# P2-3: Celery 调度失败时标记 profile 为 failed,避免永久卡在 processing
try:
workflow.process_clone_failure(
profile.id, f"Celery 任务调度失败: {e}"
)
workflow.process_clone_failure(profile.id, f"Celery 任务调度失败: {e}")
except Exception as inner_e:
logger.error(
f"Failed to mark profile as failed after dispatch error: {inner_e}"
)
logger.error(f"Failed to mark profile as failed after dispatch error: {inner_e}")
return _to_response(profile)
@@ -126,16 +118,12 @@ def list_voice_clones(
skip: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=200),
authenticated_user: AuthenticatedUser = Depends(get_current_user),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(
get_voice_clone_profile_repository
),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
) -> ListVoiceCloneResponse:
"""获取用户的音色克隆列表。"""
user_id = authenticated_user.user.id
use_case = ListVoiceClonesUseCase(repository)
items, total = use_case.execute(
user_id, status=status_filter, skip=skip, limit=limit
)
items, total = use_case.execute(user_id, status=status_filter, skip=skip, limit=limit)
return ListVoiceCloneResponse(
items=[_to_response(p) for p in items],
total=total,
@@ -146,9 +134,7 @@ def list_voice_clones(
def get_voice_clone(
clone_id: str,
authenticated_user: AuthenticatedUser = Depends(get_current_user),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(
get_voice_clone_profile_repository
),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
) -> VoiceCloneProfileResponse:
"""获取音色克隆详情。"""
user_id = authenticated_user.user.id
@@ -156,9 +142,7 @@ def get_voice_clone(
try:
profile = use_case.execute(clone_id, user_id)
except VoiceCloneNotFoundError:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found"
)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found")
return _to_response(profile)
@@ -166,9 +150,7 @@ def get_voice_clone(
def get_voice_clone_status(
clone_id: str,
authenticated_user: AuthenticatedUser = Depends(get_current_user),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(
get_voice_clone_profile_repository
),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
) -> VoiceCloneStatusResponse:
"""查询音色克隆状态(用于前端轮询)。"""
user_id = authenticated_user.user.id
@@ -176,9 +158,7 @@ def get_voice_clone_status(
try:
profile = use_case.execute(clone_id, user_id)
except VoiceCloneNotFoundError:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found"
)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found")
return VoiceCloneStatusResponse(
id=profile.id,
status=profile.status,
@@ -195,18 +175,14 @@ def get_voice_clone_status(
def delete_voice_clone(
clone_id: str,
authenticated_user: AuthenticatedUser = Depends(get_current_user),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(
get_voice_clone_profile_repository
),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
) -> Response:
"""删除音色克隆档案。"""
user_id = authenticated_user.user.id
use_case = DeleteVoiceCloneUseCase(repository)
deleted = use_case.execute(clone_id, user_id)
if not deleted:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found"
)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found")
return Response(status_code=204)
@@ -224,9 +200,7 @@ def retry_voice_clone(
try:
profile = workflow.retry_clone(clone_id, user_id)
except VoiceCloneNotFoundError:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found"
)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found")
except VoiceCloneNotRetryableError:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
@@ -245,12 +219,8 @@ def retry_voice_clone(
logger.error(f"Failed to dispatch Celery task: {e}")
# P2-3: Celery 调度失败时标记 profile 为 failed,避免永久卡在 processing
try:
workflow.process_clone_failure(
profile.id, f"Celery 任务调度失败: {e}"
)
workflow.process_clone_failure(profile.id, f"Celery 任务调度失败: {e}")
except Exception as inner_e:
logger.error(
f"Failed to mark profile as failed after dispatch error: {inner_e}"
)
logger.error(f"Failed to mark profile as failed after dispatch error: {inner_e}")
return _to_response(profile)