feat(card): 优化卡片新增、修复搜卡改版、卡不存在抛异常 其他卡片支持自定义卡片名 修复psa页面改版 修复ccg卡不存在抛异常
This commit is contained in:
@@ -29,7 +29,7 @@ logger = logging.getLogger(__name__)
|
||||
card_router = APIRouter(prefix='/card', tags=['卡册模块'])
|
||||
|
||||
|
||||
@card_router.get('/search', summary='搜索卡片', response_model=base_schema.BaseResponse[schema.Card])
|
||||
@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(...),
|
||||
@@ -190,9 +190,9 @@ async def created_card(
|
||||
db_session: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
card_crud = CRUD(db_session, Card)
|
||||
if payload.source.value != 'other':
|
||||
# 卡片库查询
|
||||
card: Card = await card_crud.get(filters={'cert_number': payload.cert_number})
|
||||
if payload.source.value != 'other':
|
||||
if card is None:
|
||||
# 卡片缓存库转正至卡片库, 重新查询保障数据准确性
|
||||
cache_card_crud = CRUD(db_session, SearchCardCache)
|
||||
@@ -232,9 +232,10 @@ async def created_card(
|
||||
)
|
||||
|
||||
else:
|
||||
if card is None:
|
||||
card: Card = await card_crud.create(
|
||||
data={
|
||||
**payload.dict(exclude_none=True),
|
||||
**payload.dict(exclude_none=True, exclude={'reward'}),
|
||||
'create_user_id': login_user.user_id,
|
||||
'source': payload.source.value,
|
||||
},
|
||||
@@ -250,8 +251,6 @@ async def created_card(
|
||||
data=record,
|
||||
filters=record
|
||||
)
|
||||
# if result is False:
|
||||
# raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='卡片已存入')
|
||||
|
||||
return {'data': card}
|
||||
|
||||
|
||||
@@ -38,9 +38,18 @@ class Card(BaseModel):
|
||||
card_spare_picture: Optional[dict] = CardPicture()
|
||||
source: SupportPlatformType
|
||||
|
||||
@field_validator("card_year", mode="before")
|
||||
@classmethod
|
||||
def convert_to_str(cls, v):
|
||||
if isinstance(v, int):
|
||||
v = str(v)
|
||||
return v
|
||||
|
||||
|
||||
class CardIn(Card):
|
||||
category: CardCategoryType
|
||||
source: PlatformSourceType
|
||||
diy_source: Optional[str] = None
|
||||
reward: Optional[bool] = False
|
||||
|
||||
|
||||
@@ -49,14 +58,20 @@ class UpdateCard(BaseModel):
|
||||
display_price: condecimal(max_digits=10, decimal_places=2)
|
||||
|
||||
|
||||
class SearchCardOut(Card):
|
||||
pass
|
||||
|
||||
|
||||
class CardVisitOut(Card, UpdateCard):
|
||||
source: PlatformSourceType
|
||||
|
||||
|
||||
class CardOut(CardVisitOut):
|
||||
id: Optional[int] = None
|
||||
card_year: str | None
|
||||
purchase_price: Optional[condecimal(max_digits=10, decimal_places=2)] = None
|
||||
display_price: Optional[condecimal(max_digits=10, decimal_places=2)] = None
|
||||
diy_source: Optional[str] = None
|
||||
|
||||
|
||||
class DeleteCardIn(BaseModel):
|
||||
|
||||
@@ -85,13 +85,13 @@ class SiteApi:
|
||||
@async_retry(exceptions=(SearchCardRetry,))
|
||||
async def psa(self, cert_number: str | int):
|
||||
"""PSA"""
|
||||
url = f'https://www.psacard.com/cert/{cert_number}'
|
||||
url = f'https://www.psacard.com/cert/{cert_number}/psa'
|
||||
headers = {
|
||||
# 'X-Requested-With': 'XMLHttpRequest',
|
||||
'Origin': 'https://www.psacard.com',
|
||||
'Referer': f'https://www.psacard.com/cert/{cert_number}',
|
||||
}
|
||||
r = await self.get(url, headers=headers, use_abroad_proxy=True)
|
||||
r = await self.get(url, headers=headers, impersonate="chrome131", use_abroad_proxy=True)
|
||||
chck_string = 'Just a moment...'
|
||||
if chck_string in r.text:
|
||||
raise SearchCardRetry(chck_string)
|
||||
@@ -191,16 +191,16 @@ class CardScraper:
|
||||
"""PSA"""
|
||||
r = await self.api.psa(cert_number)
|
||||
html = Selector(r.text)
|
||||
if html.xpath('//div[contains(@class, "alert-danger")]/p[contains(string(), "not found")]').get() is not None:
|
||||
if html.xpath('//div[contains(@class, "wrapper")]//h1/text()').get() is not None:
|
||||
return
|
||||
|
||||
info_elements = html.xpath('//main[@id="mainContent"]/div/table[contains(@class, "text-medium")]//tr')
|
||||
info_elements = html.xpath('//div[@hidden and @id]/div[contains(@class, "py-3")]')
|
||||
info = dict()
|
||||
for info_element in info_elements:
|
||||
name = info_element.xpath('./th[1]').xpath('string()').get()
|
||||
name = info_element.xpath('./dt[1]').xpath('string()').get() or info_element.xpath('./dt/template/@id').get() or ''
|
||||
name = re.sub(r'\(|\)', '', name)
|
||||
name = name.lower().replace(' ', '_').replace('/', '_')
|
||||
value = info_element.xpath('./td[1]').xpath('string()').get()
|
||||
value = info_element.xpath('./dd[1]').xpath('string()').get()
|
||||
info[name.strip()] = value.strip()
|
||||
picture_elements = info_elements.xpath('.//img')
|
||||
info['pictures'] = [
|
||||
@@ -212,35 +212,29 @@ class CardScraper:
|
||||
|
||||
item = dict(cert_number=None)
|
||||
# 评级卡号
|
||||
item['cert_number'] = info['certification_number']
|
||||
item['cert_number'] = info['cert_number']
|
||||
# 年份
|
||||
item['card_year'] = info.get('year')
|
||||
# 卡片品牌
|
||||
item['card_brand'] = info.get('brand')
|
||||
item['card_brand'] = info.get('brand_title')
|
||||
# 卡片评分
|
||||
card_grade = info.get('card_grade') or info.get('grade') or ''
|
||||
card_grade = info.get('item_grade') or ''
|
||||
item['card_score'] = regx_match(r'(\d+)', card_grade, first=True)
|
||||
# 签字评分
|
||||
auto_score = info.get('autograph_grade') or ''
|
||||
item['auto_score'] = regx_match(r'(\d+)', auto_score, first=True)
|
||||
# 卡片名称
|
||||
item['card_name'] = info.get('player')
|
||||
item['card_name'] = info.get('subject') or info.get('p:16')
|
||||
# 卡片编号
|
||||
item['card_number'] = info.get('card_number')
|
||||
# 卡片类型
|
||||
item['card_type'] = info.get('sport')
|
||||
images_id = html.xpath(
|
||||
'//script[contains(text(), "getPSACertImages")]/text()'
|
||||
).re_first(r'getPSACertImages\(\'(\d+)\'\);')
|
||||
|
||||
if images_id:
|
||||
r = await self.api.psa_images(cert_number, images_id)
|
||||
result = r.json()
|
||||
item['card_type'] = info.get('category')
|
||||
# 卡片正、反图片(远程)
|
||||
item['card_spare_picture'] = {
|
||||
'front_picture': jsonpath(result, '$.PSACertImages[0].OriginalImageUrl', first=True),
|
||||
'reverse_picture': jsonpath(result, '$.PSACertImages[1].OriginalImageUrl', first=True)
|
||||
'front_picture': html.xpath('//div[contains(@class, "flex")]//span/noscript/img[@alt="left"]/@src').get(),
|
||||
'reverse_picture': html.xpath('//div[contains(@class, "flex")]//span/noscript/img[@alt="right"]/@src').get()
|
||||
}
|
||||
|
||||
# 来源
|
||||
item['source'] = 'psa'
|
||||
return {
|
||||
@@ -368,7 +362,7 @@ class CardScraper:
|
||||
"""重庆藏卡"""
|
||||
r = await self.api.ccg(cert_number)
|
||||
result = r.json()
|
||||
if result.get('code') != '200':
|
||||
if result.get('code') != '200' or not result['data']:
|
||||
return
|
||||
|
||||
item = dict(cert_number=None)
|
||||
|
||||
Reference in New Issue
Block a user