feat(image): 封面形象抠图增加积分消耗(每次 10 积分)
- config/points-config.yaml: 添加 cover_avatar: 10 固定积分 - point_service.py: _CATEGORY_MAP 添加 cover_avatar → 封面形象 - image.py: remove_background 接口前置余额检查 + 后置扣费 - CoverAvatarLibrary.tsx: 上传弹窗显示积分提示,余额不足友好提示
This commit is contained in:
@@ -7,17 +7,20 @@
|
||||
|
||||
import io
|
||||
import logging
|
||||
import time
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, Request, UploadFile
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.api.deps import get_current_user
|
||||
from app.api.deps import get_current_user, get_db
|
||||
from app.config import get_settings
|
||||
from app.models.user import User
|
||||
from app.schemas.common import ApiResponse, success_response
|
||||
from app.services import point_service as ps
|
||||
from app.services.qiniu_service import get_qiniu_service
|
||||
from app.services.volcengine_mediakit_service import VolcengineMediakitService
|
||||
from app.utils.file_validation import check_upload_file
|
||||
@@ -163,12 +166,23 @@ async def remove_background(
|
||||
req: RemoveBackgroundRequest,
|
||||
current_user: User = Depends(get_current_user),
|
||||
mediakit_service: VolcengineMediakitService = Depends(get_mediakit_service),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> ApiResponse[RemoveBackgroundResponse]:
|
||||
"""
|
||||
AI 抠图(火山引擎 MediaKit)
|
||||
|
||||
移除图片背景,返回透明背景图片 URL。
|
||||
每次调用消耗 10 积分。
|
||||
"""
|
||||
# 前置积分检查
|
||||
required_points = ps._calculate_cost("cover_avatar")
|
||||
check = await ps.check_balance(db, current_user.id, required_points)
|
||||
if not check["sufficient"]:
|
||||
raise HTTPException(
|
||||
status_code=402,
|
||||
detail=f"积分不足,需要 {required_points} 积分,当前余额 {check['balance']}",
|
||||
)
|
||||
|
||||
try:
|
||||
logger.info(
|
||||
f"[RemoveBackground] 开始抠图: image_url={req.image_url[:80]}..., scene={req.scene}"
|
||||
@@ -216,6 +230,17 @@ async def remove_background(
|
||||
|
||||
logger.info(f"[RemoveBackground] 结果已转存七牛云: {qiniu_url[:80]}...")
|
||||
|
||||
# 后置扣费(服务已调用成功)
|
||||
await ps.consume(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
points=required_points,
|
||||
source_type="cover_avatar",
|
||||
source_id=f"cover_avatar_{current_user.id}_{int(time.time() * 1000)}",
|
||||
description="【封面形象抠图】",
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
return success_response(
|
||||
data=RemoveBackgroundResponse(url=qiniu_url),
|
||||
message="抠图成功",
|
||||
|
||||
@@ -329,6 +329,7 @@ _CATEGORY_MAP: dict[str, str] = {
|
||||
"compose": "压制成片",
|
||||
"subtitle_burn": "字幕烧录",
|
||||
"cover_design": "封面设计",
|
||||
"cover_avatar": "封面形象",
|
||||
"wxpay": "充值",
|
||||
"compensation": "充值",
|
||||
"invite": "充值",
|
||||
|
||||
@@ -29,6 +29,9 @@ fixed_costs:
|
||||
# 封面设计:根据视频内容自动生成封面图
|
||||
cover_design: 5
|
||||
|
||||
# 封面形象:上传人物照片 AI 抠图生成透明背景形象
|
||||
cover_avatar: 10
|
||||
|
||||
# 压制成片:将多个素材片段合并为最终视频(FFmpeg 拼接)
|
||||
compose: 5
|
||||
|
||||
|
||||
@@ -103,8 +103,13 @@ export default function CoverAvatarLibrary() {
|
||||
toast.success(`「${avatar.name}」已保存`);
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : '上传失败';
|
||||
progress.error(msg);
|
||||
toast.error(msg);
|
||||
if (msg.includes('积分不足')) {
|
||||
progress.error('积分不足');
|
||||
toast.error('积分不足,每次上传封面形象消耗 10 积分,请先充值');
|
||||
} else {
|
||||
progress.error(msg);
|
||||
toast.error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
setUploadName('');
|
||||
@@ -256,15 +261,20 @@ export default function CoverAvatarLibrary() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 12, justifyContent: 'flex-end' }}>
|
||||
<button className="btn btn-secondary" onClick={resetUploadModal}>取消</button>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={handleUpload}
|
||||
disabled={!uploadName.trim() || !selectedFile}
|
||||
>
|
||||
上传并抠图
|
||||
</button>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<span style={{ fontSize: 'var(--font-xs)', color: 'var(--text-tertiary)' }}>
|
||||
每次上传消耗 <strong style={{ color: 'var(--primary)' }}>10</strong> 积分
|
||||
</span>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<button className="btn btn-secondary" onClick={resetUploadModal}>取消</button>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={handleUpload}
|
||||
disabled={!uploadName.trim() || !selectedFile}
|
||||
>
|
||||
上传并抠图
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user