refactor: 清理未使用IPC命令、修正point_service注释与扣费逻辑、修复camelToSnake正则、优化vidu import

- 删除8个未使用IPC命令,保留validate_media_path
- file.rs返回类型优化为ApiResponse<()>
- point_service.consume()注释与签名一致
- VideoGeneration改为拼接成功后扣费
- 添加漏扣费风险注释
- 删除过时测试文件
- 修复camelToSnake连续大写字母问题
- vidu.py import移至模块顶层

Refs: P1-1~P1-6 技术债务清理
This commit is contained in:
小鱼开发
2026-05-14 17:45:28 +08:00
parent 7f2d61742e
commit 7550559aa0
40 changed files with 275 additions and 1731 deletions
@@ -10,12 +10,10 @@ import logging
from typing import Any
from app.core.platform_config import get_platform_config_loader
from app.db.session import AsyncSessionLocal
from app.scheduler.handlers.base import AsyncHandler
from app.scheduler.models import StateChange
from app.scheduler.registry import TaskRegistry
from app.scheduler.slot_manager import SlotManager
from app.services import point_service as ps
from app.services.vidu_service import ViduService
logger = logging.getLogger(__name__)
@@ -52,44 +50,6 @@ class VideoHandler(AsyncHandler):
)
return self.service
async def _deduct_video_points(self, task: Any, params: dict[str, Any]) -> None:
"""视频生成后置扣费:按总规划时长一次性扣费,批次幂等。"""
batch_id = params.get("batch_id")
total_planned_duration = float(params.get("total_planned_duration", 0) or 0)
if not batch_id or total_planned_duration <= 0:
return
async with AsyncSessionLocal() as db:
from sqlalchemy import select
from app.models.point_transaction import PointTransaction
# 幂等:该批次是否已扣过
result = await db.execute(
select(PointTransaction).where(
PointTransaction.user_id == task.user_id,
PointTransaction.source_type == "video",
PointTransaction.source_id == batch_id,
)
)
if result.scalar_one_or_none():
return
try:
points = ps._calculate_cost("video", {"seconds": total_planned_duration})
await ps.consume(
db,
user_id=task.user_id,
points=points,
source_type="video",
source_id=batch_id,
description="【视频生成】",
duration=total_planned_duration,
)
await db.commit()
except Exception as e:
logger.error(f"[Video {task.task_id}] 扣费失败: {e}")
async def tick(
self, tasks: list[Any], registry: TaskRegistry, slots: SlotManager
) -> list[StateChange]:
@@ -104,7 +64,7 @@ class VideoHandler(AsyncHandler):
result_data = task.result or {}
if result_data.get("video_url") or result_data.get("state") == "success":
# callback 已到达,结果已写入 TaskRegistry
await self._deduct_video_points(task, params)
await registry.remove_running(task.task_id)
changes.append(
StateChange(
@@ -145,8 +105,6 @@ class VideoHandler(AsyncHandler):
video_url = creations[0].get("url") if creations else None
if vidu_state == "success" and video_url:
await self._deduct_video_points(task, params)
changes.append(
StateChange(
task_id=task.task_id,
@@ -229,31 +187,6 @@ class VideoHandler(AsyncHandler):
)
continue # ← 已提交,不再重复提交
# 积分预检:余额不足直接失败,不提交 Vidu 任务
planned_duration = float(params.get("planned_duration", 0) or 0)
if planned_duration > 0:
try:
async with AsyncSessionLocal() as db:
from sqlalchemy import select
from app.models.user_point import UserPoint
points = ps._calculate_cost("video", {"seconds": planned_duration})
result = await db.execute(select(UserPoint).where(UserPoint.user_id == task.user_id))
up = result.scalar_one_or_none()
if not up or up.balance < points:
message = f"积分不足,需要 {points} 积分,当前余额 {up.balance if up else 0}"
await registry.update(
task.task_id,
status="failed",
progress=0,
message=message,
)
changes.append(StateChange(task_id=task.task_id, field_path="status", value="failed"))
changes.append(StateChange(task_id=task.task_id, field_path="message", value=message))
continue
except Exception as e:
logger.error(f"[Video {task.task_id}] 积分预检失败: {e}")
# 提交阶段:占用 slot,提交成功后自动释放
async with slots.acquire_ctx(
SLOT_KEY, task.task_id, self.max_slots