351 lines
12 KiB
Python
351 lines
12 KiB
Python
# -*- coding = utf-8 -*-
|
|
# @Time : 2025/2/13 下午4:51
|
|
# @File : routes.py
|
|
# @Software : PyCharm
|
|
# @Author : xingc
|
|
# @Desc :
|
|
import logging
|
|
from typing import List, Literal
|
|
|
|
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 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, User
|
|
from core.routers.auth.schema import LoginUser
|
|
from core.routers.auth.services import get_current_user_form_http
|
|
from core.utils.helper import generate_keyname, check_picture_for_empty
|
|
|
|
from . import schema
|
|
from .actions import reward_points_after_uploading_pictures
|
|
from .scraper import card_scraper
|
|
from .services import check_user_card_created_permission
|
|
|
|
logger = logging.getLogger(__name__)
|
|
card_router = APIRouter(prefix='/card', tags=['卡册模块'])
|
|
|
|
|
|
@card_router.get('/search', summary='搜索卡片', response_model=base_schema.BaseResponse[schema.SearchCardOut])
|
|
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 = generate_keyname(f'card_search:{platform.value}:{cert_number}', use_today=True)
|
|
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': scraper_result['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',
|
|
summary='卡片列表(所有者)',
|
|
response_model=base_schema.BaseResponse[
|
|
base_schema.Paginated[
|
|
List[schema.CardOut]
|
|
]
|
|
],
|
|
)
|
|
async def get_me_all_card(
|
|
login_user: LoginUser = Depends(get_current_user_form_http),
|
|
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)
|
|
):
|
|
# 组装查询条件
|
|
conditions = [
|
|
UserCollection.user_id == login_user.user_id,
|
|
]
|
|
if category in ['anime', 'sports', 'other']:
|
|
conditions.append(Card.category == category)
|
|
|
|
if category == 'displayed':
|
|
conditions.append(UserCollection.displayed == True)
|
|
|
|
# 记录总数查询
|
|
total_stmt = (
|
|
select(func.count()).select_from(UserCollection).filter(
|
|
*conditions
|
|
)
|
|
)
|
|
total = await db_session.execute(total_stmt)
|
|
# 分页记录查询
|
|
stmt = (
|
|
select(Card, UserCollection)
|
|
.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': [
|
|
{
|
|
**card.dict(),
|
|
**user_collection.dict(exclude={'id'})
|
|
} for card, user_collection in records
|
|
],
|
|
'total': total.scalar(),
|
|
'page': page,
|
|
'page_size': page_size,
|
|
},
|
|
}
|
|
|
|
|
|
@card_router.get(
|
|
'/visit/{user_id}/all',
|
|
summary='卡片列表(访问者)',
|
|
response_model=base_schema.BaseResponse[
|
|
base_schema.Paginated[
|
|
List[schema.CardVisitOut]
|
|
]
|
|
],
|
|
)
|
|
async def get_visit_user_all_card(
|
|
user_id: int,
|
|
login_user: LoginUser = Depends(get_current_user_form_http),
|
|
page: int = Query(default=1, gt=0),
|
|
page_size: int = Query(default=15, gt=0, le=200),
|
|
db_session: AsyncSession = Depends(get_async_session)
|
|
):
|
|
# 组装查询条件
|
|
conditions = [
|
|
UserCollection.user_id == user_id, UserCollection.displayed == True
|
|
]
|
|
|
|
# 记录总数查询
|
|
total_stmt = (
|
|
select(func.count()).select_from(UserCollection).filter(*conditions)
|
|
)
|
|
total = await db_session.execute(total_stmt)
|
|
|
|
# 分页记录查询
|
|
stmt = (
|
|
select(Card, UserCollection)
|
|
.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': [
|
|
{
|
|
**card.dict(),
|
|
**user_collection.dict()
|
|
} for card, user_collection in records
|
|
],
|
|
'total': total.scalar(),
|
|
'page': page,
|
|
'page_size': page_size,
|
|
},
|
|
}
|
|
|
|
|
|
@card_router.post('/create', summary='新增卡片', response_model=base_schema.BaseResponse[schema.CardOut])
|
|
async def created_card(
|
|
payload: schema.CardIn,
|
|
login_user: LoginUser = Depends(get_current_user_form_http),
|
|
db_session: AsyncSession = Depends(get_async_session)
|
|
):
|
|
db_crud = CRUD(db_session, Card)
|
|
# 确认用户存卡权限
|
|
if await check_user_card_created_permission(
|
|
db_session, login_user=login_user
|
|
) is False:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f'非会员仅支持存入{settings.USER_MAX_CREATED_CARD_NUM}张卡片'
|
|
)
|
|
|
|
# 卡片库查询
|
|
card: Card = await db_crud.get(filters={'cert_number': payload.cert_number})
|
|
if payload.source.value != 'other':
|
|
if card is None:
|
|
# 卡片缓存库转正至卡片库, 重新查询保障数据准确性
|
|
cache_card_crud = CRUD(db_session, SearchCardCache)
|
|
cache_card = await cache_card_crud.get(
|
|
filters={'cert_number': payload.cert_number, 'source': payload.source.value}
|
|
)
|
|
card = await db_crud.create(
|
|
data={
|
|
**cache_card.data,
|
|
'category': payload.category,
|
|
},
|
|
filters={
|
|
'cert_number': payload.cert_number,
|
|
'source': payload.source.value
|
|
}
|
|
)
|
|
|
|
# 校验是否需要更新图片
|
|
card_picture_empty = check_picture_for_empty(card.card_picture)
|
|
if all([
|
|
check_picture_for_empty(payload.card_picture.dict()) is False, # 创建时用户有提交上传图片链接
|
|
check_picture_for_empty(card.card_spare_picture), # 采集原始来源无图片
|
|
check_picture_for_empty(card.card_picture, or_=True), # 无本地化图片
|
|
]):
|
|
# 补充有效值
|
|
card_picture = {} if card_picture_empty else card.card_picture.copy()
|
|
for key, value in payload.card_picture.dict(exclude_none=True).items():
|
|
if card_picture.get(key) is None:
|
|
card_picture[key] = value
|
|
|
|
await db_crud.update(
|
|
data={
|
|
'card_picture': card_picture,
|
|
'perfect_user_id': login_user.user_id,
|
|
},
|
|
filters={'id': card.id}
|
|
)
|
|
# 上传图片奖励
|
|
if payload.reward and settings.UPLOAD_REWARD_POINTS_GIFT_ID > 0:
|
|
await reward_points_after_uploading_pictures(
|
|
db_crud, gift_product_id=settings.UPLOAD_REWARD_POINTS_GIFT_ID, login_user=login_user
|
|
)
|
|
|
|
|
|
else:
|
|
if card is None:
|
|
card: Card = await db_crud.create(
|
|
data={
|
|
**payload.dict(exclude_none=True, exclude={'reward'}),
|
|
'create_user_id': login_user.user_id,
|
|
'source': payload.source.value,
|
|
},
|
|
filters={'cert_number': payload.cert_number, 'source': payload.source.value}
|
|
)
|
|
|
|
record = {
|
|
'user_id': login_user.user_id,
|
|
'card_id': card.id
|
|
}
|
|
await db_crud.create(
|
|
model=UserCollection,
|
|
data=record,
|
|
filters=record
|
|
)
|
|
|
|
return {'data': card}
|
|
|
|
|
|
@card_router.post(
|
|
'/{card_id}/update/',
|
|
summary='卡片更新(会员功能,入手价值、展示价值)',
|
|
response_model=base_schema.BaseResponse
|
|
)
|
|
async def update_card_price_info(
|
|
card_id: int,
|
|
payload: schema.UpdateCard,
|
|
login_user: LoginUser = Depends(get_current_user_form_http),
|
|
db_session: AsyncSession = Depends(get_async_session),
|
|
):
|
|
db_crud = CRUD(db_session, UserCollection)
|
|
user = await db_crud.get(model=User, filters={'user_id': login_user.user_id})
|
|
if user.is_vip() is False:
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='会员未开通或者已过期')
|
|
|
|
result = await db_crud.update(
|
|
data=payload.dict(exclude_none=True),
|
|
filters={'card_id': card_id, 'user_id': login_user.user_id}
|
|
)
|
|
return {'message': 'ok' if result is not None else 'fail'}
|
|
|
|
|
|
@card_router.post('/display/', summary='卡片展示/隐藏', response_model=base_schema.BaseResponse)
|
|
async def display_card_status(
|
|
action: Literal['displayed', 'undisplayed'],
|
|
payload: schema.DisplayCardIn,
|
|
login_user: LoginUser = Depends(get_current_user_form_http),
|
|
db_session: AsyncSession = Depends(get_async_session)
|
|
):
|
|
# 确认用户展示卡权限
|
|
if await check_user_card_created_permission(
|
|
db_session, login_user=login_user, other_conditions={'displayed': True}
|
|
) is False and action == 'displayed':
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f'非会员仅支持展示{settings.USER_MAX_CREATED_CARD_NUM}张卡片'
|
|
)
|
|
|
|
db_crud = CRUD(db_session, UserCollection)
|
|
result = await db_crud.updates(
|
|
data={
|
|
'displayed': (action == 'displayed'),
|
|
},
|
|
filters={'card_id__in': payload.card_ids, 'user_id': login_user.user_id}
|
|
)
|
|
return {'message': 'ok' if result is not None else 'fail'}
|
|
|
|
|
|
@card_router.delete('/delete/', summary='卡片删除(批量)', response_model=base_schema.BaseResponse)
|
|
async def delete_card(
|
|
payload: schema.DeleteCardIn,
|
|
login_user: LoginUser = Depends(get_current_user_form_http),
|
|
db_session: AsyncSession = Depends(get_async_session)
|
|
):
|
|
# 删除关联关系
|
|
db_crud = CRUD(db_session, UserCollection)
|
|
state = await db_crud.deletes(
|
|
filters={
|
|
'card_id__in': payload.card_ids,
|
|
'user_id': login_user.user_id
|
|
}
|
|
)
|
|
|
|
if state is True:
|
|
# 删除原始卡片(自定义)
|
|
db_crud = CRUD(db_session, Card)
|
|
await db_crud.deletes(
|
|
filters={
|
|
'id__in': payload.card_ids,
|
|
'create_user_id': login_user.user_id,
|
|
'source': 'OTHER'
|
|
}
|
|
)
|
|
|
|
return {'message': 'ok' if state else 'fail'}
|