497 lines
17 KiB
Python
497 lines
17 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
|