From 71dbf01fc8d4736605efdb5ac7f206f846af486d Mon Sep 17 00:00:00 2001 From: GinHa <15201596918@163.com> Date: Wed, 3 Jun 2026 17:45:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E6=80=BB=E7=A7=AF=E5=88=86=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- video-gen-api/app/api/v1/admin.py | 4 +- video-gen-api/app/api/v1/generation.py | 46 +++++++++++++++++-- .../app/services/generation_refund_service.py | 13 ++++-- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/video-gen-api/app/api/v1/admin.py b/video-gen-api/app/api/v1/admin.py index 43bb0e5f..4439eadb 100644 --- a/video-gen-api/app/api/v1/admin.py +++ b/video-gen-api/app/api/v1/admin.py @@ -758,13 +758,13 @@ async def list_credit_ratios_grouped( ): result = await db.execute(select(CreditRatio)) ratios = result.scalars().all() - + grouped = {} for ratio in ratios: if ratio.gen_type not in grouped: grouped[ratio.gen_type] = [] grouped[ratio.gen_type].append(CreditRatioOut.model_validate(ratio)) - + return grouped diff --git a/video-gen-api/app/api/v1/generation.py b/video-gen-api/app/api/v1/generation.py index 0a05c617..f735fa08 100644 --- a/video-gen-api/app/api/v1/generation.py +++ b/video-gen-api/app/api/v1/generation.py @@ -125,6 +125,11 @@ async def list_records( description="每页返回的生成记录数量,范围 1~100", examples=[10], ), + record_ids: list[str] | None = Query( + None, + description="对应记录ID数组", + examples=[["0019e8c55ddd1429b86", "0019e8c54dfc1e13262"]], + ), current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): @@ -155,6 +160,9 @@ async def list_records( if status: conditions.append(GenerationRecord.status == status) + if record_ids: + conditions.append(GenerationRecord.id.in_(record_ids)) + total_result = await db.execute( select(func.count(GenerationRecord.id)) .join(Project, GenerationRecord.project_id == Project.id) @@ -291,10 +299,40 @@ async def optimize( db, token_usage["input_tokens"], token_usage["output_tokens"], ) - await deduct_credits( - db, current_user.id, text_credits, - f"提示词优化 - {project.name}", - ) + failed_record_id = record.id + failed_user_id = current_user.id + try: + await deduct_credits( + db, current_user.id, text_credits, + f"提示词优化 - {project.name}", + ) + except InsufficientCreditsError as e: + # /optimize 阶段只处理提示词优化扣费。 + # 提示词积分不足时,之前已落库的 optimizing 记录必须改为 failed,避免前端长期显示生成中。 + # 此阶段没有媒体生成扣费,不调用生成失败退款逻辑。 + await db.rollback() + result = await db.execute( + select(GenerationRecord) + .where( + GenerationRecord.id == failed_record_id, + GenerationRecord.user_id == failed_user_id, + GenerationRecord.deleted_at.is_(None), + ) + .with_for_update() + .limit(1) + ) + failed_record = result.scalar_one_or_none() + if failed_record: + failed_record.status = "failed" + failed_record.error_message = e.detail + failed_record.optimized_prompt = None + failed_record.text_credits_cost = 0 + failed_record.credits_cost = 0 + failed_record.text_tokens_used = token_usage.get("total_tokens", 0) + await db.flush() + # 这里必须主动提交,否则后续抛出 402 后 get_db 会 rollback,failed 状态会被回滚。 + await db.commit() + raise e record.optimized_prompt = optimized record.status = "prompt_optimized" diff --git a/video-gen-api/app/services/generation_refund_service.py b/video-gen-api/app/services/generation_refund_service.py index 0bec6f5b..d447a715 100644 --- a/video-gen-api/app/services/generation_refund_service.py +++ b/video-gen-api/app/services/generation_refund_service.py @@ -109,7 +109,7 @@ async def refund_unrefunded_media_charges( db, user_id=user_id, amount=amount, - description=f"{description_prefix}失败积分回退 attempt:{attempt_no}", + description=f"{description_prefix}失败积分回退", related_id=owner_id, biz_key=refund_biz_key, refund_for_biz_key=charge.biz_key, @@ -149,13 +149,17 @@ async def mark_generation_record_failed_and_refund_once( if error_message: record.error_message = error_message - await refund_unrefunded_media_charges( + refunded_amount = await refund_unrefunded_media_charges( db, user_id=record.user_id, owner_type=OWNER_GENERATION_RECORD, owner_id=record.id, description_prefix="生成记录", ) + if refunded_amount > 0: + # GenerationRecord.credits_cost 只代表视频/图片生成媒体积分。 + # 提示词优化积分在 text_credits_cost 中记录,优化成功后不参与生成失败退款。 + record.credits_cost = max(0.0, round(float(record.credits_cost or 0) - refunded_amount, 2)) await db.flush() return record @@ -194,12 +198,15 @@ async def mark_chat_generation_task_failed_and_refund_once( if error_message: task.error_message = error_message - await refund_unrefunded_media_charges( + refunded_amount = await refund_unrefunded_media_charges( db, user_id=task.user_id, owner_type=OWNER_CHAT_GENERATION_TASK, owner_id=task.id, description_prefix="Chat生成任务", ) + + if refunded_amount > 0: + task.credits_cost = max(0.0, round(float(task.credits_cost or 0) - refunded_amount, 2)) await db.flush() return task