from __future__ import annotations from datetime import datetime, timezone from sqlalchemy.orm import Session from config import get_settings from models import User, WikiLlmUsage class WikiLlmQuotaExceeded(Exception): def __init__(self, *, daily_limit: int, used_today: int): self.daily_limit = daily_limit self.used_today = used_today super().__init__( f"今日 AI 整理额度已用完({used_today}/{daily_limit} 次),请充值后继续使用" ) class WikiLlmQuotaService: def daily_limit(self) -> int: return max(1, int(get_settings().ai_wiki_daily_llm_limit)) def usage_date(self, now: datetime | None = None) -> str: current = now or datetime.now(timezone.utc) return current.strftime("%Y-%m-%d") def get_usage_row(self, db: Session, user: User, usage_date: str | None = None) -> WikiLlmUsage: day = usage_date or self.usage_date() row = ( db.query(WikiLlmUsage) .filter(WikiLlmUsage.user_id == user.id, WikiLlmUsage.usage_date == day) .first() ) if not row: row = WikiLlmUsage( user_id=user.id, usage_date=day, call_count=0, ) db.add(row) db.flush() return row def used_today(self, db: Session, user: User) -> int: return int(self.get_usage_row(db, user).call_count) def remaining_today(self, db: Session, user: User) -> int: return max(0, self.daily_limit() - self.used_today(db, user)) def quota_dict(self, db: Session, user: User) -> dict: limit = self.daily_limit() used = self.used_today(db, user) remaining = max(0, limit - used) return { "daily_limit": limit, "used_today": used, "remaining_today": remaining, "quota_exceeded": remaining <= 0, "recharge_message": ( "今日 AI 整理额度已用完,请充值后继续使用" if remaining <= 0 else None ), } def consume(self, db: Session, user: User, *, count: int = 1) -> dict: if count <= 0: return self.quota_dict(db, user) row = self.get_usage_row(db, user) limit = self.daily_limit() if row.call_count + count > limit: raise WikiLlmQuotaExceeded(daily_limit=limit, used_today=row.call_count) row.call_count += count db.flush() return self.quota_dict(db, user) def require_available(self, db: Session, user: User) -> dict: quota = self.quota_dict(db, user) if quota["quota_exceeded"]: raise WikiLlmQuotaExceeded( daily_limit=quota["daily_limit"], used_today=quota["used_today"], ) return quota wiki_llm_quota_service = WikiLlmQuotaService()