feat(points): 积分消耗系统全链路集成
后端: - 简化积分服务: 删除 freeze/settle/refund, 保留 consume/recharge/expire - 计费配置化: config/points-config.yaml 驱动 fixed/duration/free 三种模式 - TTS 时长探测: app/utils/audio_utils.py (httpx + mutagen 纯 Python) - Python 层扣费: script(5)/polish(1)/title(1)/voice_clone(200)/tts(按秒)/video(按秒) - 字幕 free_services: caption/auto_align 不扣费 - 新增 POST /points/consume 端点(402余额预检) - 新增 check_balance + /points/cost 返回 sufficient/balance/required - 新增 expire_batches 定时回收, 接入 scheduler main(每5分钟) - 删除废弃 tts_handler.py - Alembic 迁移: 删除 frozen/total_refunded 字段 - 同步 requirements.lock 添加 mutagen 前端: - Rust/IPC 层扣费: compose(5)/subtitle_burn(2)/cover_design(2) - 字幕打轴改异步: 走 scheduler subtitle handler - 对口型传 duration: VideoGeneration 传 actualDuration - 创建 pointStore: 全局余额 + fetchBalance + 充值弹窗控制 - 402 欠费弹 RechargeModal: VideoGeneration/SubtitleBurning/CoverDesign - 修复 VoiceDubbing.tsx 类型错误 (alignResult never) - 同步 PointBalance 类型(删除 frozen/available/totalRefunded) Refs: 积分消耗集成收尾
This commit is contained in:
@@ -10,8 +10,11 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.api.deps import get_current_user
|
||||
from app.db.session import get_db
|
||||
from app.ai.model_router import get_model_router
|
||||
from app.ai.prompts import list_categories, load_prompt, render_template
|
||||
from app.schemas.common import ApiResponse, success_response
|
||||
@@ -25,6 +28,8 @@ from app.schemas.script import (
|
||||
TestModelResponse,
|
||||
)
|
||||
from app.services.script_service import get_script_service
|
||||
from app.services import point_service as ps
|
||||
from app.models.user import User
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -45,7 +50,11 @@ async def get_categories():
|
||||
|
||||
|
||||
@router.post("/polish", response_model=ApiResponse[str])
|
||||
async def polish_content(request: PolishRequest):
|
||||
async def polish_content(
|
||||
request: PolishRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
AI 润色文案/画面描述
|
||||
|
||||
@@ -65,6 +74,17 @@ async def polish_content(request: PolishRequest):
|
||||
shot_type=request.shot_type or "segment",
|
||||
)
|
||||
|
||||
# 扣费
|
||||
points = ps._calculate_cost("polish")
|
||||
await ps.consume(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
points=points,
|
||||
source_type="polish",
|
||||
source_id=f"polish_{current_user.id}_{asyncio.get_event_loop().time()}",
|
||||
description=f"润色 {request.polish_type}",
|
||||
)
|
||||
|
||||
return success_response(
|
||||
data=polished,
|
||||
message=f"{type_name}润色完成",
|
||||
@@ -118,7 +138,11 @@ async def test_model(request: TestModelRequest):
|
||||
|
||||
|
||||
@router.post("/generate-title", response_model=ApiResponse[GenerateTitleResponse])
|
||||
async def generate_title(request: GenerateTitleRequest):
|
||||
async def generate_title(
|
||||
request: GenerateTitleRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
根据脚本内容智能生成标题
|
||||
|
||||
@@ -178,6 +202,17 @@ async def generate_title(request: GenerateTitleRequest):
|
||||
if len(title) > request.max_length:
|
||||
title = title[:request.max_length]
|
||||
|
||||
# 扣费
|
||||
points = ps._calculate_cost("title")
|
||||
await ps.consume(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
points=points,
|
||||
source_type="title",
|
||||
source_id=f"title_{current_user.id}_{asyncio.get_event_loop().time()}",
|
||||
description=f"生成{request.title_type}标题",
|
||||
)
|
||||
|
||||
return success_response(
|
||||
data=GenerateTitleResponse(title=title),
|
||||
message="标题生成成功",
|
||||
|
||||
Reference in New Issue
Block a user