jiachenlong/backend/app/routers/yichens.py

182 lines
5.5 KiB
Python

from fastapi import APIRouter, Depends, Query
from sqlalchemy import func, text
from sqlalchemy.orm import Session
from pydantic import BaseModel
from typing import Optional, List
from datetime import datetime, date
from app.core.coolbot_db import get_coolbot_db
router = APIRouter(prefix="/api/yichens", tags=["一尘看板"])
# ============ 数据模型 ============
class YichensPostStats(BaseModel):
total_posts: int
total_deals: int # 出售
total_wants: int # 求购
total_replies: int
total_views: int
avg_price: Optional[float]
class CategoryStat(BaseModel):
category: str
count: int
class PostItem(BaseModel):
post_id: str
title: str
category: Optional[str]
post_type: str
price: Optional[float]
author_username: str
post_time: str
reply_count: int
view_count: int
url: Optional[str]
class UserStat(BaseModel):
total_users: int
new_users_today: int
sellers: int
class UserItem(BaseModel):
user_id: str
username: str
avatar_url: Optional[str]
credit_level: Optional[str]
credit_score: Optional[int]
post_count: int
is_seller: bool
registration_date: Optional[str]
# ============ 统计接口 ============
@router.get("/stats/posts", response_model=YichensPostStats)
def get_post_stats(days: int = Query(30, ge=1, le=365), db: Session = Depends(get_coolbot_db)):
"""获取帖子统计"""
result = db.execute(text("""
SELECT
COUNT(*) as total_posts,
COUNT(*) FILTER (WHERE post_type = 'deal') as total_deals,
COUNT(*) FILTER (WHERE post_type = 'want') as total_wants,
COALESCE(SUM(reply_count), 0) as total_replies,
COALESCE(SUM(view_count), 0) as total_views,
AVG(price) as avg_price
FROM yichens_posts
WHERE post_time >= NOW() - INTERVAL '1 day' * :days
"""), {"days": days}).fetchone()
return YichensPostStats(
total_posts=result[0] or 0,
total_deals=result[1] or 0,
total_wants=result[2] or 0,
total_replies=result[3] or 0,
total_views=result[4] or 0,
avg_price=float(result[5]) if result[5] else None
)
@router.get("/stats/categories", response_model=List[CategoryStat])
def get_category_stats(days: int = Query(30, ge=1, le=365), db: Session = Depends(get_coolbot_db)):
"""按分类统计帖子数量"""
results = db.execute(text("""
SELECT category, COUNT(*) as count
FROM yichens_posts
WHERE post_time >= NOW() - INTERVAL '1 day' * :days
GROUP BY category
ORDER BY count DESC
"""), {"days": days}).fetchall()
return [CategoryStat(category=r[0] or "未知", count=r[1]) for r in results]
@router.get("/stats/users", response_model=UserStat)
def get_user_stats(db: Session = Depends(get_coolbot_db)):
"""获取用户统计"""
result = db.execute(text("""
SELECT
COUNT(*) as total_users,
COUNT(*) FILTER (WHERE created_at::date = CURRENT_DATE) as new_users_today,
COUNT(*) FILTER (WHERE is_seller = true) as sellers
FROM yichens_users
""")).fetchone()
return UserStat(
total_users=result[0] or 0,
new_users_today=result[1] or 0,
sellers=result[2] or 0
)
@router.get("/posts", response_model=List[PostItem])
def get_posts(
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
category: Optional[str] = None,
post_type: Optional[str] = None,
db: Session = Depends(get_coolbot_db)
):
"""获取帖子列表"""
query = """
SELECT post_id, title, category, post_type, price,
author_username, post_time, reply_count, view_count, url
FROM yichens_posts
WHERE 1=1
"""
params = {"limit": limit, "offset": offset}
if category:
query += " AND category = :category"
params["category"] = category
if post_type:
query += " AND post_type = :post_type"
params["post_type"] = post_type
query += " ORDER BY post_time DESC LIMIT :limit OFFSET :offset"
results = db.execute(text(query), params).fetchall()
return [PostItem(
post_id=r[0],
title=r[1] or "",
category=r[2],
post_type=r[3] or "",
price=float(r[4]) if r[4] else None,
author_username=r[5] or "",
post_time=str(r[6]) if r[6] else "",
reply_count=r[7] or 0,
view_count=r[8] or 0,
url=r[9]
) for r in results]
@router.get("/users", response_model=List[UserItem])
def get_users(
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
is_seller: Optional[bool] = None,
db: Session = Depends(get_coolbot_db)
):
"""获取用户列表"""
query = """
SELECT user_id, username, avatar_url, credit_level, credit_score,
post_count, is_seller, registration_date
FROM yichens_users
WHERE 1=1
"""
params = {"limit": limit, "offset": offset}
if is_seller is not None:
query += " AND is_seller = :is_seller"
params["is_seller"] = is_seller
query += " ORDER BY post_count DESC LIMIT :limit OFFSET :offset"
results = db.execute(text(query), params).fetchall()
return [UserItem(
user_id=r[0],
username=r[1] or "",
avatar_url=r[2],
credit_level=r[3],
credit_score=r[4],
post_count=r[5] or 0,
is_seller=r[6] or False,
registration_date=str(r[7]) if r[7] else None
) for r in results]