feat: credit-based billing system
- New DB models: credit_packages, subscription_plans, user_credits, credit_consumptions, credit_purchases - CreditService: balance, deduct, add_credits, grant_free_trial, history - User API: /api/v1/credits/* (balance/history/packages/purchase/subscribe) - Admin API: /api/v1/admin/credit-* (CRUD packages/plans, user credits, consumptions) - PaymentService.create_credit_order + handle_callback for credit purchases - Credit deduction on: discovery, translate, marketing, ai_chat, followup - Free trial 30 credits on registration - Documentation: docs/CREDIT_SYSTEM.md
This commit is contained in:
@@ -6,6 +6,7 @@ from app.database import get_db
|
||||
from app.services.translation import TranslationService
|
||||
from app.services.tts import tts_service
|
||||
from app.services.preference import UserPreferenceService
|
||||
from app.services.credit import CreditService
|
||||
from app.core.security import decode_token
|
||||
from app.api.v1.deps import get_current_user_id
|
||||
|
||||
@@ -35,6 +36,7 @@ class ExtractRequest(BaseModel):
|
||||
async def translate_text(
|
||||
data: TranslateRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
service = TranslationService()
|
||||
result = await service.translate(
|
||||
@@ -44,6 +46,13 @@ async def translate_text(
|
||||
context=data.context,
|
||||
user_id=user_id,
|
||||
)
|
||||
|
||||
credit_svc = CreditService(db)
|
||||
char_count = len(data.text)
|
||||
await credit_svc.deduct(
|
||||
user_id, "translate",
|
||||
metadata={"chars": char_count, "target_lang": data.target_lang},
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@@ -54,6 +63,15 @@ async def generate_reply(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
pref_service = UserPreferenceService(db)
|
||||
|
||||
credit_svc = CreditService(db)
|
||||
ok, balance = await credit_svc.deduct(user_id, "reply_suggest")
|
||||
if not ok:
|
||||
raise HTTPException(
|
||||
status_code=402,
|
||||
detail=f"次数不足 (剩余 {balance:.1f}, 需要 2)"
|
||||
)
|
||||
|
||||
pref_context = await pref_service.get_preference_context(user_id, "reply")
|
||||
|
||||
service = TranslationService()
|
||||
@@ -71,7 +89,16 @@ async def generate_reply(
|
||||
async def extract_info(
|
||||
data: ExtractRequest,
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
credit_svc = CreditService(db)
|
||||
ok, balance = await credit_svc.deduct(user_id, "info_extract")
|
||||
if not ok:
|
||||
raise HTTPException(
|
||||
status_code=402,
|
||||
detail=f"次数不足 (剩余 {balance:.1f}, 需要 1)"
|
||||
)
|
||||
|
||||
service = TranslationService()
|
||||
result = await service.extract_info(data.text, data.extract_type)
|
||||
return {"extracted": result, "type": data.extract_type}
|
||||
|
||||
Reference in New Issue
Block a user