Files
X25020501_card_book/core/routers/card/routes.py
2025-02-18 14:26:47 +08:00

221 lines
6.9 KiB
Python

# -*- coding = utf-8 -*-
# @Time : 2025/2/13 下午4:51
# @File : routes.py
# @Software : PyCharm
# @Author : xingc
# @Desc :
import logging
from typing import Literal, List
import ujson
from fastapi import APIRouter, Depends, Query, HTTPException, status
import redis.asyncio as aioredis
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from . import schema
from .scraper import card_scraper
from config import settings
from core.base import schema as base_schema
from core.db.crud import CRUD
from core.db.engine import get_async_session, get_cache
from core.db.models import Card, SearchCardCache, UserCollection
logger = logging.getLogger(__name__)
card_router = APIRouter(prefix='/card', tags=['卡册模块'])
@card_router.get('/search', response_model=base_schema.BaseResponse[schema.CardOut])
async def get_card_search(
cert_number: str = Query(pattern='[a-zA-Z0-9]+'),
platform: base_schema.SupportPlatformType = Query(...),
db_session: AsyncSession = Depends(get_async_session),
redis_conn: aioredis.Redis = Depends(get_cache)
):
logger.info(f'Get card search request: {cert_number}')
logger.info(platform)
result = None
# 缓存
cache_keyname = f'card_book:card_search:{platform.value}:{cert_number}'
result_str = await redis_conn.get(cache_keyname)
if result_str is not None:
result = ujson.decode(result_str)
return {'data': result}
db_crud = CRUD(db_session, SearchCardCache)
# 缓存库
if result is None:
card = await db_crud.get(filters={'cert_number': cert_number, 'source': platform.value})
if card is not None:
result = card.data
# 采集
if result is None:
scraper_result = await card_scraper.choice(platform.value, cert_number)
if scraper_result is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail='相关卡片信息未找到'
)
result = scraper_result['data']
await db_crud.create(
data={
**scraper_result,
'source': result.get('source') or platform.value,
},
filters={'cert_number': cert_number, 'source': platform.value}
)
await redis_conn.set(cache_keyname, ujson.encode(result), ex=settings.REDIS_CACHE_EXPIRATION_SECONDS)
return {'data': result}
@card_router.get(
'/me/all',
response_model=base_schema.BaseResponse[
base_schema.Paginated[
List[schema.CardOut]
]
],
)
async def get_me_all_card(
page: int = Query(default=1, gt=0),
page_size: int = Query(default=15, gt=0, le=200),
category: Literal['all', 'anime', 'sports', 'other', 'displayed'] = Query(default='all'),
db_session: AsyncSession = Depends(get_async_session)
):
user_id = None
conditions = [
UserCollection.user_id == user_id,
]
if category in ['anime', 'sports', 'other']:
conditions.append(Card.category == category)
if category == 'displayed':
conditions.append(UserCollection.displayed is True)
total_stmt = (
select(func.count()).select_from(UserCollection).filter(
*conditions
)
)
total = await db_session.execute(total_stmt)
stmt = (
select(UserCollection, Card)
.join(Card, UserCollection.card_id == Card.id)
.where(*conditions)
.order_by(UserCollection.created_at.desc()).limit(page_size).offset((page - 1) * page_size)
)
records = await db_session.execute(stmt)
return {
'data': {
'list': [record.dict() for record in records.scalars().all()],
'total': total.scalar(),
'page': page,
'page_size': page_size,
},
}
@card_router.get(
'/visit/{user_id}/all',
response_model=base_schema.BaseResponse[
base_schema.Paginated[
List[schema.CardOut]
]
],
)
async def get_visit_user_all_card(
user_id: str,
page: int = Query(default=1, gt=0),
page_size: int = Query(default=15, gt=0, le=200),
# category: Literal['displayed'] = Query(default='displayed'),
db_session: AsyncSession = Depends(get_async_session)
):
conditions = [
UserCollection.user_id == user_id, UserCollection.displayed is True
]
total_stmt = (
select(func.count()).select_from(UserCollection).filter(*conditions)
)
total = await db_session.execute(total_stmt)
stmt = (
select(UserCollection, Card)
.join(Card, UserCollection.card_id == Card.id)
.where(*conditions)
.order_by(UserCollection.created_at.desc()).limit(page_size).offset((page - 1) * page_size)
)
records = await db_session.execute(stmt)
return {
'data': {
'list': [record.dict() for record in records.scalars().all()],
'total': total.scalar(),
'page': page,
'page_size': page_size,
},
}
@card_router.post('/create', response_model=base_schema.BaseResponse[schema.CardOut])
async def created_card(
payload: schema.Card,
db_session: AsyncSession = Depends(get_async_session)
):
db_crud = CRUD(db_session, Card)
card: Card = await db_crud.create(
data={
**payload.dict(exclude_none=True),
'create_user_id': None,
'source': payload.source.value,
},
filters={'cert_number': payload.cert_number, 'source': payload.source.value}
)
return {'data': card}
@card_router.post('/{card_id}/update/')
async def update_card_price_info(
card_id: int,
payload: schema.UpdateCard,
db_session: AsyncSession = Depends(get_async_session),
):
db_crud = CRUD(db_session, UserCollection)
result = await db_crud.update(
data=payload.dict(exclude_none=True),
filters={'card_id': card_id, 'user_id': None}
)
return {'message': 'success' if result is not None else 'fail'}
@card_router.post('/{card_id}/display/')
async def display_card_status(
card_id: int,
status: bool,
db_session: AsyncSession = Depends(get_async_session)
):
db_crud = CRUD(db_session, UserCollection)
result = await db_crud.update(
data={
'displayed': status
},
filters={'card_id': card_id, 'user_id': None}
)
return {'message': 'success' if result is not None else 'fail'}
@card_router.delete('/delete/', response_model=base_schema.BaseResponse)
async def delete_card(
payload: schema.DeleteCardIn,
db_session: AsyncSession = Depends(get_async_session)
):
db_crud = CRUD(db_session, Card)
state = await db_crud.delete(filters={'card_id_in': payload.card_ids})
if state is False:
db_crud = CRUD(db_session, UserCollection)
state = await db_crud.delete(filters={'card_id_in': payload.card_ids, 'user_id': None})
return {'message': 'success' if state else 'fail'}