jiachenlong/backend/app/routers/information.py

523 lines
18 KiB
Python

# 资讯API路由
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session, joinedload
from typing import List, Optional
from pydantic import BaseModel
from datetime import datetime, date
from app.core.database import get_db
from app.core.auth import get_current_user
from app.models.models import User, Information, Collection
router = APIRouter(prefix="/api/information", tags=["资讯"])
# Schema
class InformationCreate(BaseModel):
info_type: str # seek-寻配号, deal-成交数据, publish-发布
title: str
content: Optional[str] = None
collection_id: 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
deal_price: Optional[float] = None
deal_date: Optional[date] = None
class InformationUpdate(BaseModel):
title: Optional[str] = None
content: Optional[str] = None
status: 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
deal_price: Optional[float] = None
deal_date: Optional[date] = None
class InformationResponse(BaseModel):
id: str
user_id: str
info_type: str
title: str
content: Optional[str]
collection_id: 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]
deal_price: Optional[float]
deal_date: Optional[date]
status: str
view_count: int
contact_count: int
created_at: datetime
# 用户信息
user_name: Optional[str] = None
user_avatar: Optional[str] = None
# 关联藏品信息
collection_name: Optional[str] = None
collection_category: Optional[str] = None
collection_version: Optional[str] = None
collection_number: Optional[str] = None
class Config:
from_attributes = True
# 资讯列表
@router.get("/list", response_model=List[InformationResponse])
def get_information_list(
info_type: Optional[str] = Query(None, description="类型: seek/deal/publish"),
status: str = Query("active", description="状态: active/closed/expired"),
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
db: Session = Depends(get_db)
):
"""获取资讯列表"""
query = db.query(Information).options(
joinedload(Information.user),
joinedload(Information.collection)
).filter(Information.status == status)
if info_type:
query = query.filter(Information.info_type == info_type)
# 按创建时间倒序
query = query.order_by(Information.created_at.desc())
# 分页
offset = (page - 1) * page_size
items = query.offset(offset).limit(page_size).all()
# 转换结果
result = []
for item in items:
result.append(InformationResponse(
id=item.id,
user_id=item.user_id,
info_type=item.info_type,
title=item.title,
content=item.content,
collection_id=item.collection_id,
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,
deal_price=item.deal_price,
deal_date=item.deal_date,
status=item.status,
view_count=item.view_count,
contact_count=item.contact_count,
created_at=item.created_at,
user_name=item.user.f01_01_name if item.user else None,
user_avatar=item.user.avatar if item.user else None,
collection_name=item.collection.f01_01_name if item.collection else None,
collection_category=item.collection.f01_03_category if item.collection else None,
collection_version=item.collection.f02_11_version if item.collection else None,
collection_number=item.collection.f02_10_prefix_serial if item.collection else None,
))
return result
# 获取单条资讯
@router.get("/{info_id}", response_model=InformationResponse)
def get_information(
info_id: str,
db: Session = Depends(get_db)
):
"""获取资讯详情"""
item = db.query(Information).options(
joinedload(Information.user),
joinedload(Information.collection)
).filter(Information.id == info_id).first()
if not item:
raise HTTPException(status_code=404, detail="资讯不存在")
# 增加浏览次数
item.view_count += 1
db.commit()
return InformationResponse(
id=item.id,
user_id=item.user_id,
info_type=item.info_type,
title=item.title,
content=item.content,
collection_id=item.collection_id,
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,
deal_price=item.deal_price,
deal_date=item.deal_date,
status=item.status,
view_count=item.view_count,
contact_count=item.contact_count,
created_at=item.created_at,
user_name=item.user.f01_01_name if item.user else None,
user_avatar=item.user.avatar if item.user else None,
collection_name=item.collection.f01_01_name if item.collection else None,
collection_category=item.collection.f01_03_category if item.collection else None,
collection_version=item.collection.f02_11_version if item.collection else None,
collection_number=item.collection.f02_10_prefix_serial if item.collection else None,
)
# 发布资讯
@router.post("/", response_model=InformationResponse)
def create_information(
data: InformationCreate,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""发布资讯"""
info = Information(
user_id=current_user.f99_90_id,
info_type=data.info_type,
title=data.title,
content=data.content,
collection_id=data.collection_id,
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,
deal_price=data.deal_price,
deal_date=data.deal_date,
status="active"
)
db.add(info)
db.commit()
db.refresh(info)
return InformationResponse(
id=info.id,
user_id=info.user_id,
info_type=info.info_type,
title=info.title,
content=info.content,
collection_id=info.collection_id,
expect_category=info.expect_category,
expect_version=info.expect_version,
expect_packaging=info.expect_packaging,
expect_number=info.expect_number,
expect_price_min=info.expect_price_min,
expect_price_max=info.expect_price_max,
deal_price=info.deal_price,
deal_date=info.deal_date,
status=info.status,
view_count=info.view_count,
contact_count=info.contact_count,
created_at=info.created_at,
user_name=current_user.f01_01_name,
user_avatar=current_user.avatar,
collection_name=None,
collection_category=None,
collection_version=None,
collection_number=None,
)
# 更新资讯
@router.put("/{info_id}", response_model=InformationResponse)
def update_information(
info_id: str,
data: InformationUpdate,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""更新资讯"""
info = db.query(Information).filter(
Information.id == info_id,
Information.user_id == current_user.f99_90_id
).first()
if not info:
raise HTTPException(status_code=404, detail="资讯不存在或无权修改")
# 更新字段
if data.title is not None:
info.title = data.title
if data.content is not None:
info.content = data.content
if data.status is not None:
info.status = data.status
if data.expect_category is not None:
info.expect_category = data.expect_category
if data.expect_version is not None:
info.expect_version = data.expect_version
if data.expect_packaging is not None:
info.expect_packaging = data.expect_packaging
if data.expect_number is not None:
info.expect_number = data.expect_number
if data.expect_price_min is not None:
info.expect_price_min = data.expect_price_min
if data.expect_price_max is not None:
info.expect_price_max = data.expect_price_max
if data.deal_price is not None:
info.deal_price = data.deal_price
if data.deal_date is not None:
info.deal_date = data.deal_date
db.commit()
db.refresh(info)
return InformationResponse(
id=info.id,
user_id=info.user_id,
info_type=info.info_type,
title=info.title,
content=info.content,
collection_id=info.collection_id,
expect_category=info.expect_category,
expect_version=info.expect_version,
expect_packaging=info.expect_packaging,
expect_number=info.expect_number,
expect_price_min=info.expect_price_min,
expect_price_max=info.expect_price_max,
deal_price=info.deal_price,
deal_date=info.deal_date,
status=info.status,
view_count=info.view_count,
contact_count=info.contact_count,
created_at=info.created_at,
user_name=current_user.f01_01_name,
user_avatar=current_user.avatar,
collection_name=info.collection.f01_01_name if info.collection else None,
collection_category=info.collection.f01_03_category if info.collection else None,
collection_version=info.collection.f02_11_version if info.collection else None,
collection_number=info.collection.f02_10_prefix_serial if info.collection else None,
)
# 删除资讯
@router.delete("/{info_id}")
def delete_information(
info_id: str,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""删除资讯"""
info = db.query(Information).filter(
Information.id == info_id,
Information.user_id == current_user.f99_90_id
).first()
if not info:
raise HTTPException(status_code=404, detail="资讯不存在或无权删除")
db.delete(info)
db.commit()
return {"message": "删除成功"}
# 寻配号 - 自动匹配推荐藏品
@router.get("/seek/match")
def get_seek_match(
info_id: str,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取符合条件的我的藏品推荐"""
info = db.query(Information).filter(
Information.id == info_id,
Information.info_type == "seek"
).first()
if not info:
raise HTTPException(status_code=404, detail="寻配号信息不存在")
# 查询当前用户的藏品,匹配条件
query = db.query(Collection).filter(
Collection.f99_91_user_id == current_user.f99_90_id,
Collection.f01_04_status == "in_collection"
)
if info.expect_category:
query = query.filter(Collection.f01_03_category == info.expect_category)
if info.expect_version:
query = query.filter(Collection.f02_11_version == info.expect_version)
if info.expect_packaging:
query = query.filter(Collection.f02_12_packaging == info.expect_packaging)
if info.expect_number:
query = query.filter(Collection.f02_10_prefix_serial.contains(info.expect_number))
if info.expect_price_min:
query = query.filter(Collection.f05_40_cost_price >= info.expect_price_min)
if info.expect_price_max:
query = query.filter(Collection.f05_40_cost_price <= info.expect_price_max)
matched_collections = query.all()
return {
"info_id": info_id,
"matched_count": len(matched_collections),
"collections": [
{
"id": c.f99_90_id,
"name": c.f01_01_name,
"category": c.f01_03_category,
"version": c.f02_11_version,
"packaging": c.f02_12_packaging,
"number": c.f02_10_prefix_serial,
"cost_price": c.f05_40_cost_price,
}
for c in matched_collections
]
}
# 成交数据统计
@router.get("/deal/stats")
def get_deal_stats(
days: int = Query(7, ge=1, le=90, description="统计天数"),
db: Session = Depends(get_db)
):
"""获取成交数据统计"""
from sqlalchemy import func
from datetime import timedelta
start_date = datetime.now() - timedelta(days=days)
# 按版别统计
by_version = db.query(
Information.expect_version,
func.count(Information.id).label("count"),
func.avg(Information.deal_price).label("avg_price"),
func.max(Information.deal_price).label("max_price"),
func.min(Information.deal_price).label("min_price")
).filter(
Information.info_type == "deal",
Information.status == "active",
Information.created_at >= start_date
).group_by(Information.expect_version).all()
# 按包装统计
by_packaging = db.query(
Information.expect_packaging,
func.count(Information.id).label("count"),
func.avg(Information.deal_price).label("avg_price")
).filter(
Information.info_type == "deal",
Information.status == "active",
Information.created_at >= start_date
).group_by(Information.expect_packaging).all()
# 按号码分类统计
by_number = db.query(
Information.expect_number,
func.count(Information.id).label("count"),
func.avg(Information.deal_price).label("avg_price")
).filter(
Information.info_type == "deal",
Information.status == "active",
Information.expect_number.isnot(None),
Information.created_at >= start_date
).group_by(Information.expect_number).all()
return {
"days": days,
"by_version": [
{"version": r[0] or "未知", "count": r[1], "avg_price": float(r[2] or 0), "max_price": float(r[3] or 0), "min_price": float(r[4] or 0)}
for r in by_version if r[0]
],
"by_packaging": [
{"packaging": r[0] or "未知", "count": r[1], "avg_price": float(r[2] or 0)}
for r in by_packaging if r[0]
],
"by_number": [
{"number": r[0], "count": r[1], "avg_price": float(r[2] or 0)}
for r in by_number
]
}
# 获取我的发布列表
@router.get("/my/list", response_model=List[InformationResponse])
def get_my_information_list(
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取我的发布列表"""
items = db.query(Information).options(
joinedload(Information.collection)
).filter(
Information.user_id == current_user.f99_90_id
).order_by(Information.created_at.desc()).offset((page-1)*page_size).limit(page_size).all()
result = []
for item in items:
result.append(InformationResponse(
id=item.id,
user_id=item.user_id,
info_type=item.info_type,
title=item.title,
content=item.content,
collection_id=item.collection_id,
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,
deal_price=item.deal_price,
deal_date=item.deal_date,
status=item.status,
view_count=item.view_count,
contact_count=item.contact_count,
created_at=item.created_at,
user_name=current_user.f01_01_name,
user_avatar=current_user.avatar,
collection_name=item.collection.f01_01_name if item.collection else None,
collection_category=item.collection.f01_03_category if item.collection else None,
collection_version=item.collection.f02_11_version if item.collection else None,
collection_number=item.collection.f02_10_prefix_serial if item.collection else None,
))
return result
# ============ 获取当前用户发布的列表 ============
@router.get("/my")
def get_my_information(
page: int = Query(1, ge=1),
limit: int = Query(20, ge=1, le=100),
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取当前用户发布的信息列表"""
total = db.query(Information).filter(Information.author == current_user.f01_01_name).count()
infos = db.query(Information).filter(
Information.author == current_user.f01_01_name
).order_by(Information.created_at.desc()).offset((page-1)*limit).limit(limit).all()
return {
"total": total,
"list": [{
"id": i.id,
"title": i.title,
"content": i.content,
"info_type": i.info_type,
"author": i.author,
"created_at": i.created_at.isoformat() if i.created_at else None
} for i in infos]
}