from typing import Dict, Any, Optional from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.models.translation_quota import TranslationQuota from datetime import datetime import logging logger = logging.getLogger(__name__) class TranslationQuotaService: def __init__(self, db: AsyncSession): self.db = db async def _get_or_create(self, version: str) -> TranslationQuota: result = await self.db.execute( select(TranslationQuota).where(TranslationQuota.version == version) ) quota = result.scalar_one_or_none() if not quota: now = datetime.utcnow() quota = TranslationQuota( version=version, monthly_limit=1000000, used_chars=0, current_month=now.strftime("%Y-%m"), enabled=True, description=f"阿里云翻译{version}版", ) self.db.add(quota) await self.db.flush() return quota async def check_quota(self, version: str) -> bool: quota = await self._get_or_create(version) now = datetime.utcnow() current = now.strftime("%Y-%m") if quota.current_month != current: quota.current_month = current quota.used_chars = 0 await self.db.flush() return quota.enabled and quota.used_chars < quota.monthly_limit async def consume(self, version: str, chars: int): quota = await self._get_or_create(version) if not quota.enabled: raise ValueError(f"Translation API [{version}] is disabled") now = datetime.utcnow() current = now.strftime("%Y-%m") if quota.current_month != current: quota.current_month = current quota.used_chars = 0 quota.used_chars += chars await self.db.flush() remaining = max(0, quota.monthly_limit - quota.used_chars) logger.info(f"Quota [{version}] consumed {chars} chars, remaining {remaining} this month") return remaining async def get_all_quotas(self) -> list: default_versions = ["ecommerce", "general"] for v in default_versions: await self._get_or_create(v) result = await self.db.execute(select(TranslationQuota).order_by(TranslationQuota.version)) quotas = result.scalars().all() rows = [] for q in quotas: now = datetime.utcnow() current = now.strftime("%Y-%m") if q.current_month != current: q.current_month = current q.used_chars = 0 await self.db.flush() rows.append({ "version": q.version, "monthly_limit": q.monthly_limit, "used_chars": q.used_chars, "current_month": q.current_month, "enabled": q.enabled, "description": q.description, }) return rows async def update_quota(self, version: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]: quota = await self._get_or_create(version) if "monthly_limit" in data: quota.monthly_limit = int(data["monthly_limit"]) if "enabled" in data: quota.enabled = bool(data["enabled"]) if "description" in data: quota.description = str(data["description"]) await self.db.flush() return { "version": quota.version, "monthly_limit": quota.monthly_limit, "used_chars": quota.used_chars, "current_month": quota.current_month, "enabled": quota.enabled, "description": quota.description, } async def reset_usage(self, version: str) -> Optional[Dict[str, Any]]: quota = await self._get_or_create(version) quota.used_chars = 0 quota.current_month = datetime.utcnow().strftime("%Y-%m") await self.db.flush() return { "version": quota.version, "monthly_limit": quota.monthly_limit, "used_chars": quota.used_chars, "current_month": quota.current_month, "enabled": quota.enabled, }