jiachenlong/backend/app/routers/seek.py

302 lines
9.7 KiB
Python

from fastapi import APIRouter, Depends, Query, HTTPException
from sqlalchemy.orm import Session
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from app.core.database import get_db
from app.core.auth import get_current_user
from app.models.seek_info import SeekInfo
from app.models.models import User, Collection, Information
from sqlalchemy import text
router = APIRouter(prefix="/api/seek", tags=["寻配号"])
# ============ Schema ============
class SeekInfoCreate(BaseModel):
title: str
content: Optional[str] = None
expect_category: Optional[str] = None
expect_version: Optional[str] = None
expect_packaging: Optional[str] = None
expect_number: Optional[str] = None
expect_price_min: Optional[float] = None
expect_price_max: Optional[float] = None
class SeekInfoUpdate(BaseModel):
title: Optional[str] = None
content: Optional[str] = None
expect_category: Optional[str] = None
expect_version: Optional[str] = None
expect_packaging: Optional[str] = None
expect_number: Optional[str] = None
expect_price_min: Optional[float] = None
expect_price_max: Optional[float] = None
status: Optional[str] = None
class SeekInfoResponse(BaseModel):
id: str
user_id: str
title: str
content: Optional[str]
expect_category: Optional[str]
expect_version: Optional[str]
expect_packaging: Optional[str]
expect_number: Optional[str]
expect_price_min: Optional[float]
expect_price_max: Optional[float]
status: str
is_matched: Optional[str]
matched_user_id: Optional[str]
matched_contact: Optional[str]
view_count: int
contact_count: int
created_at: Optional[datetime]
updated_at: Optional[datetime]
user_name: Optional[str] = None # 发布者用户名
matched_count: Optional[int] = 0 # 自有匹配数量
network_matched_count: Optional[int] = 0 # 网络匹配数量
class Config:
from_attributes = True
# ============ API ============
@router.get("/list", response_model=list[SeekInfoResponse])
def get_seek_list(
status: str = Query("active"),
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=1000),
user_only: bool = Query(False),
current_user: Optional = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取寻配号列表"""
query = db.query(SeekInfo).filter(SeekInfo.status == status)
# 我的寻配号:只查看自己的
if user_only and current_user:
query = query.filter(SeekInfo.user_id == current_user.f99_90_id)
# 排序
query = query.order_by(SeekInfo.created_at.desc())
# 分页
offset = (page - 1) * page_size
items = query.offset(offset).limit(page_size).all()
# 添加用户名和匹配数量
from app.routers.information import match_collections_count, match_collections_count_from_coolbot
result = []
for item in items:
user = db.query(User).filter(User.f99_90_id == item.user_id).first()
user_name = user.f01_01_name if user else '匿名用户'
# 计算匹配数量
matched_count = 0
network_matched_count = 0
if item.expect_number and len(item.expect_number) == 10:
if current_user and current_user.f99_90_id:
matched_count = match_collections_count(db, current_user.f99_90_id, item.expect_number)
network_matched_count = match_collections_count_from_coolbot(item.expect_number)
# 构建响应
result.append(SeekInfoResponse(
id=item.id,
user_id=item.user_id,
title=item.title,
content=item.content,
expect_category=item.expect_category,
expect_version=item.expect_version,
expect_packaging=item.expect_packaging,
expect_number=item.expect_number,
expect_price_min=item.expect_price_min,
expect_price_max=item.expect_price_max,
status=item.status,
is_matched=item.is_matched,
matched_user_id=item.matched_user_id,
matched_contact=item.matched_contact,
view_count=item.view_count,
contact_count=item.contact_count,
created_at=item.created_at,
updated_at=item.updated_at,
user_name=user_name,
matched_count=matched_count,
network_matched_count=network_matched_count
))
return result
@router.get("/stats")
def get_seek_stats(
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取寻配号统计"""
total = db.query(SeekInfo).filter(SeekInfo.status == "active").count()
matched = db.query(SeekInfo).filter(SeekInfo.is_matched == "true").count()
return {
"total": total,
"matched": matched,
"unmatched": total - matched
}
@router.post("", response_model=SeekInfoResponse)
def create_seek(
data: SeekInfoCreate,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""创建寻配号"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
seek = SeekInfo(
user_id=current_user.f99_90_id,
title=data.title,
content=data.content,
expect_category=data.expect_category,
expect_version=data.expect_version,
expect_packaging=data.expect_packaging,
expect_number=data.expect_number,
expect_price_min=data.expect_price_min,
expect_price_max=data.expect_price_max,
status="active"
)
db.add(seek)
db.commit()
db.refresh(seek)
return seek
@router.get("/{seek_id}", response_model=SeekInfoResponse)
def get_seek(
seek_id: str,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取寻配号详情"""
seek = db.query(SeekInfo).filter(SeekInfo.id == seek_id).first()
if not seek:
raise HTTPException(status_code=404, detail="寻配号不存在")
# 增加浏览数
seek.view_count += 1
db.commit()
return seek
@router.put("/{seek_id}", response_model=SeekInfoResponse)
def update_seek(
seek_id: str,
data: SeekInfoUpdate,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""更新寻配号"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
seek = db.query(SeekInfo).filter(
SeekInfo.id == seek_id,
SeekInfo.user_id == current_user.f99_90_id
).first()
if not seek:
raise HTTPException(status_code=404, detail="寻配号不存在")
for key, value in data.model_dump(exclude_unset=True).items():
setattr(seek, key, value)
db.commit()
db.refresh(seek)
return seek
@router.delete("/{seek_id}")
def delete_seek(
seek_id: str,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""删除寻配号"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
seek = db.query(SeekInfo).filter(
SeekInfo.id == seek_id,
SeekInfo.user_id == current_user.f99_90_id
).first()
if not seek:
raise HTTPException(status_code=404, detail="寻配号不存在")
seek.status = "deleted"
db.commit()
return {"message": "删除成功"}
# 获取自有藏品匹配列表
@router.get("/my-match")
def get_seek_match(
info_id: str,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取符合条件的我的藏品推荐"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
info = db.query(SeekInfo).filter(SeekInfo.id == info_id).first()
if not info:
raise HTTPException(status_code=404, detail="寻配号信息不存在")
# 获取用户所有藏品
collections = db.query(Collection).filter(
Collection.f99_91_user_id == current_user.f99_90_id,
Collection.f01_04_status == "in_collection"
).all()
# 按号码特征模式匹配
from app.routers.information import match_pattern
matched = []
if info.expect_number and len(info.expect_number) == 10:
pattern = info.expect_number[2:]
for c in collections:
number = c.f02_10_prefix_serial or ''
if len(number) >= 10 and number.startswith('J0'):
col_pattern = number[2:10]
if match_pattern(col_pattern, pattern):
matched.append(c)
elif len(number) >= 8:
col_pattern = number[:8]
if match_pattern(col_pattern, pattern):
matched.append(c)
else:
matched = collections
return {
"matched_count": len(matched),
"collections": [
{"id": c.f99_90_id, "code": c.f01_02_code or '', "name": c.f01_01_name,
"number": c.f02_10_prefix_serial, "status": c.f01_04_status}
for c in matched[:20]
]
}
# 获取网络数据匹配列表
@router.get("/network-match/{info_id}")
def get_network_match(
info_id: str,
limit: int = Query(20, ge=1, le=100),
db: Session = Depends(get_db)
):
"""获取一尘数据库中匹配的藏品列表"""
from app.routers.information import match_collections_list_from_coolbot
info = db.query(SeekInfo).filter(SeekInfo.id == info_id).first()
if not info:
raise HTTPException(status_code=404, detail="寻配号信息不存在")
if not info.expect_number:
return {"matched_count": 0, "collections": []}
matched = match_collections_list_from_coolbot(info.expect_number, limit=limit)
return {"matched_count": len(matched), "collections": matched}