积分冻结释放

This commit is contained in:
2026-07-24 09:18:05 +08:00
parent 920d884e92
commit 68e902b4a4
38 changed files with 4743 additions and 391 deletions
@@ -15,12 +15,15 @@ from app.schemas.module_generation_v2 import (
)
from app.services.generation.pipeline.enqueue_service import enqueue_generation_create
from app.services.hot_opening_replicate_service import project_to_detail_out
from app.services.llm_billing import LlmBillingContext, log_celery_dispatch_compensated
from app.services.module_async_recovery_service import OBJECT_MODULE_STEP, has_live_object_lock
from app.services.module_generation_v2.config import HOT_OPENING_V2
from app.services.module_generation_v2.dispatch_service import (
dispatch_video_prompt_v2,
ensure_v2_celery_enabled,
)
from app.services.module_generation_v2.flow_service import (
build_v2_video_prompt_billing_context,
create_hot_opening_project_v2,
delete_project_v2,
generate_video_from_prompt_v2,
@@ -35,6 +38,19 @@ from app.services.upload_resource import cleanup_upload_resource_files_after_com
router = APIRouter(prefix="/hot-opening-replications", tags=["hot-opening-replications-v2"])
def _dispatch_context(*, user_id: str, project_id: str, step_id: str, step_version: int) -> LlmBillingContext:
context = build_v2_video_prompt_billing_context(
user_id=user_id,
project_id=project_id,
step_id=step_id,
step_version=step_version,
module=HOT_OPENING_V2.module,
display_name=HOT_OPENING_V2.display_name,
)
context.celery_task_id = f"module-v2-video-prompt:{step_id}"
return context
async def _detail(db: AsyncSession, current_user: User, project_id: str) -> HotOpeningTaskDetailOut:
project = await get_v2_project_for_user(
db,
@@ -50,14 +66,19 @@ async def _dispatch_or_mark_failed(
*,
project_id: str,
step_id: str,
billing_context: LlmBillingContext,
) -> None:
dispatch = await dispatch_video_prompt_v2(
config=HOT_OPENING_V2,
project_id=project_id,
step_id=step_id,
billing_context=billing_context,
)
if dispatch.recoverable:
return
if await has_live_object_lock(object_type=OBJECT_MODULE_STEP, object_id=step_id):
# apply_async 可能已送达但客户端收到异常;worker 已领取时不能释放冻结。
return
error_message = "视频提词任务的 Redis 注册和 Celery 投递均失败,请重新执行步骤2"
await mark_video_prompt_dispatch_failed_v2(
db,
@@ -66,6 +87,7 @@ async def _dispatch_or_mark_failed(
step_id=step_id,
error_message=error_message,
)
log_celery_dispatch_compensated(billing_context, error=error_message)
raise HTTPException(status_code=503, detail=error_message)
@@ -81,6 +103,12 @@ async def create_task_v2(
project_id = str(result.project.id)
step_id = str(result.prompt_step.id)
created_new = bool(result.created_new)
billing_context = _dispatch_context(
user_id=str(result.project.user_id),
project_id=project_id,
step_id=step_id,
step_version=int(result.prompt_step.version or 1),
)
await db.commit()
except IntegrityError as exc:
await db.rollback()
@@ -91,6 +119,12 @@ async def create_task_v2(
project_id = str(result.project.id)
step_id = str(result.prompt_step.id)
created_new = bool(result.created_new)
billing_context = _dispatch_context(
user_id=str(result.project.user_id),
project_id=project_id,
step_id=step_id,
step_version=int(result.prompt_step.version or 1),
)
await db.commit()
except HTTPException:
await db.rollback()
@@ -100,7 +134,9 @@ async def create_task_v2(
raise HTTPException(status_code=500, detail="创建爆款复刻 V2 项目失败") from exc
if created_new:
await _dispatch_or_mark_failed(db, project_id=project_id, step_id=step_id)
await _dispatch_or_mark_failed(
db, project_id=project_id, step_id=step_id, billing_context=billing_context
)
return await _detail(db, current_user, project_id)
@@ -136,11 +172,22 @@ async def retry_video_prompt_v2(
)
project_id_value = str(project.id)
step_id_value = str(new_step.id)
billing_context = _dispatch_context(
user_id=str(project.user_id),
project_id=project_id_value,
step_id=step_id_value,
step_version=int(new_step.version or 1),
)
await db.commit()
except HTTPException:
await db.rollback()
raise
await _dispatch_or_mark_failed(db, project_id=project_id_value, step_id=step_id_value)
await _dispatch_or_mark_failed(
db,
project_id=project_id_value,
step_id=step_id_value,
billing_context=billing_context,
)
return HotOpeningActionOut(
message="视频提词已重新提交",
project_id=project_id_value,