This commit is contained in:
Felix
2025-12-22 15:47:26 +08:00
parent ee933bc800
commit c0c8a45748
4 changed files with 161 additions and 105 deletions

View File

@@ -1,10 +1,12 @@
from fastapi import APIRouter, Depends, Request
from fastapi import HTTPException
from pydantic import BaseModel, Field
from typing import List, Optional
from backend.app.admin.service.coupon_service import CouponService
from backend.common.response.response_schema import response_base
from backend.common.security.jwt import DependsJwtAuth
from backend.core.conf import settings
router = APIRouter()
@@ -65,3 +67,14 @@ async def generate_coupons_api(
"count": len(coupons),
"message": f"成功生成{len(coupons)}个兑换券"
})
class InitCouponsResponse(BaseModel):
count: int = Field(..., description="生成数量")
@router.get("/init", summary="初始化兑换券")
async def init_coupons(request: Request, prefix: str = "VIP", count: int = 10):
t = request.query_params.get('t')
if not t or t == '' or t != settings.INIT_TOKEN:
raise HTTPException(status_code=403, detail='Forbidden')
created = await CouponService.init_coupons(prefix, count)
return response_base.success(data=InitCouponsResponse(count=created))

View File

@@ -54,6 +54,12 @@ class CouponDao(CRUDPlus[Coupon]):
await db.flush()
return coupons
async def list_codes_by_prefix(self, db: AsyncSession, prefix: str) -> List[str]:
stmt = select(Coupon.code).where(Coupon.code.like(f"{prefix}%"))
result = await db.execute(stmt)
rows = result.all()
return [r[0] for r in rows]
async def mark_as_used(self, db: AsyncSession, user_id: int, coupon: Coupon) -> bool:
"""
标记兑换券为已使用并创建使用记录

View File

@@ -104,6 +104,43 @@ class CouponService:
coupons = await coupon_dao.create_coupons(db, coupons_data)
return coupons
@staticmethod
async def init_coupons(prefix: str, count: int) -> int:
if not prefix or not any(ch.isalpha() for ch in prefix):
raise errors.BadRequestError(msg='前缀至少包含一个字母')
prefix = ''.join([ch for ch in prefix.upper() if ch.isalpha()])
if len(prefix) not in (3, 4):
raise errors.BadRequestError(msg='前缀长度必须为3或4')
digits = 6 - len(prefix)
max_serial = 10 ** digits - 1
async with async_db_session.begin() as db:
existing_codes = await coupon_dao.list_codes_by_prefix(db, prefix)
current_max = -1
for c in existing_codes:
if len(c) == 6 and c.startswith(prefix):
suffix = c[len(prefix):]
if len(suffix) == digits and suffix.isdigit():
n = int(suffix)
if n > current_max:
current_max = n
start = current_max + 1
if start > max_serial:
from backend.common.log import log as logger
logger.warning(f"{prefix} 前缀已达到最大序号 {max_serial},不再生成兑换券")
return 0
to_generate = min(max(0, count), max_serial - start + 1)
coupons_data = []
for n in range(start, start + to_generate):
code = f"{prefix}{str(n).zfill(digits)}"
coupons_data.append({
'code': code,
'type': prefix,
'points': 0,
'expires_at': None
})
await coupon_dao.create_coupons(db, coupons_data)
return to_generate
@staticmethod
async def redeem_coupon(code: str, user_id: int) -> dict:
"""

View File

@@ -192,9 +192,9 @@ class ImageService:
current_user = request.user
file_id = int(params.file_id)
type = params.type
dict_level = params.dict_level.name
dict_level = params.dict_level.value
if not dict_level:
dict_level = current_user.dict_level.name
dict_level = current_user.dict_level.value
# 检查用户积分是否足够(现在积分没有过期概念)
if not await points_service.check_sufficient_points(current_user.id, IMAGE_RECOGNITION_COST):