jiachenlong/backend/app/routers/yichens.py

378 lines
13 KiB
Python
Raw Normal View History

2026-04-06 21:17:15 +08:00
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]
2026-04-06 23:22:57 +08:00
content: Optional[str]
2026-04-06 21:17:15 +08:00
class UserStat(BaseModel):
total_users: int
new_users_today: int
sellers: int
class UserItem(BaseModel):
user_id: str
username: str
avatar_url: Optional[str]
2026-04-06 23:22:57 +08:00
content: Optional[str]
2026-04-06 21:17:15 +08:00
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")
2026-04-06 21:17:15 +08:00
def get_posts(
2026-04-06 23:22:57 +08:00
limit: int = Query(20, ge=1, le=500),
2026-04-06 21:17:15 +08:00
offset: int = Query(0, ge=0),
category: Optional[str] = None,
post_type: Optional[str] = None,
2026-04-08 22:40:46 +08:00
keyword: Optional[str] = None,
2026-04-06 21:17:15 +08:00
db: Session = Depends(get_coolbot_db)
):
"""获取帖子列表 - 支持全局搜索,返回总数和分页信息"""
# 构建WHERE条件
where_clauses = ["1=1"]
2026-04-06 21:17:15 +08:00
params = {"limit": limit, "offset": offset}
if category:
where_clauses.append("category = :category")
2026-04-06 21:17:15 +08:00
params["category"] = category
if post_type:
where_clauses.append("post_type = :post_type")
2026-04-06 21:17:15 +08:00
params["post_type"] = post_type
# 全局搜索
2026-04-08 22:40:46 +08:00
if keyword:
where_clauses.append("(title ILIKE :keyword OR content ILIKE :keyword OR category ILIKE :keyword OR author_username ILIKE :keyword)")
2026-04-08 22:40:46 +08:00
params["keyword"] = f"%{keyword}%"
where_sql = " AND ".join(where_clauses)
# 查询总数
count_query = f"SELECT COUNT(*) FROM yichens_posts WHERE {where_sql}"
total_count = db.execute(text(count_query), {k: v for k, v in params.items() if k not in ['limit', 'offset']}).scalar() or 0
2026-04-06 21:17:15 +08:00
# 查询数据
data_query = f"""
SELECT post_id, title, content, category, post_type, price,
author_username, post_time, reply_count, view_count, url
FROM yichens_posts
WHERE {where_sql}
ORDER BY post_time DESC LIMIT :limit OFFSET :offset
"""
results = db.execute(text(data_query), params).fetchall()
2026-04-06 21:17:15 +08:00
posts = [PostItem(
2026-04-06 21:17:15 +08:00
post_id=r[0],
title=r[1] or "",
2026-04-06 23:22:57 +08:00
content=r[2] or "",
category=r[3],
post_type=r[4] or "",
price=float(r[5]) if r[5] else None,
author_username=r[6] or "",
post_time=str(r[7]) if r[7] else "",
reply_count=r[8] or 0,
view_count=r[9] or 0,
url=r[10]
2026-04-06 21:17:15 +08:00
) for r in results]
return {
"posts": posts,
"total": total_count,
"page": offset // limit + 1,
"page_size": limit
}
2026-04-06 21:17:15 +08:00
@router.get("/users", response_model=List[UserItem])
def get_users(
2026-04-06 23:22:57 +08:00
limit: int = Query(20, ge=1, le=500),
2026-04-06 21:17:15 +08:00
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]
2026-04-06 23:22:57 +08:00
@router.get("/stats/today")
async def get_today_stats(db: Session = Depends(get_coolbot_db)):
"""获取今日新增帖子统计"""
query = """
SELECT
COUNT(*) as total,
SUM(CASE WHEN post_type = 'deal' THEN 1 ELSE 0 END) as deals,
SUM(CASE WHEN post_type = 'want' THEN 1 ELSE 0 END) as wants,
SUM(CASE WHEN post_type = 'normal' THEN 1 ELSE 0 END) as others,
SUM(CASE WHEN category LIKE '%%' THEN 1 ELSE 0 END) as dragons,
SUM(CASE WHEN category LIKE '%%' THEN 1 ELSE 0 END) as horses,
SUM(CASE WHEN category LIKE '%%' THEN 1 ELSE 0 END) as snakes,
SUM(CASE WHEN title LIKE '%天马%' OR content LIKE '%天马%' THEN 1 ELSE 0 END) as tianma
2026-04-06 23:22:57 +08:00
FROM yichens_posts
WHERE post_time >= CURRENT_DATE
"""
result = db.execute(text(query)).fetchone()
return {
"total": result[0] or 0,
"deals": result[1] or 0,
"wants": result[2] or 0,
"others": result[3] or 0,
"dragons": result[4] or 0,
"horses": result[5] or 0,
"snakes": result[6] or 0,
"tianma": result[7] or 0
2026-04-06 23:22:57 +08:00
}
@router.get("/stats/hour")
async def get_hour_stats(db: Session = Depends(get_coolbot_db)):
"""获取近一个小时新增帖子统计"""
query = """
SELECT
COUNT(*) as total,
SUM(CASE WHEN post_type = 'deal' THEN 1 ELSE 0 END) as deals,
SUM(CASE WHEN post_type = 'want' THEN 1 ELSE 0 END) as wants,
SUM(CASE WHEN post_type = 'normal' THEN 1 ELSE 0 END) as others,
SUM(CASE WHEN category LIKE '%%' THEN 1 ELSE 0 END) as dragons,
SUM(CASE WHEN category LIKE '%%' THEN 1 ELSE 0 END) as horses,
SUM(CASE WHEN category LIKE '%%' THEN 1 ELSE 0 END) as snakes
FROM yichens_posts
WHERE post_time >= NOW() - INTERVAL '1 hour'
"""
result = db.execute(text(query)).fetchone()
return {
"total": result[0] or 0,
"deals": result[1] or 0,
"wants": result[2] or 0,
"others": result[3] or 0,
"dragons": result[3] or 0,
"horses": result[4] or 0,
"snakes": result[5] or 0
}
@router.get("/stats/today-category")
async def get_today_category_stats(db: Session = Depends(get_coolbot_db)):
"""获取今日帖子分类统计"""
query = """
SELECT category, COUNT(*) as count
FROM yichens_posts
WHERE post_time >= CURRENT_DATE
GROUP BY category
ORDER BY count DESC
"""
results = db.execute(text(query)).fetchall()
return [{"category": r[0] or "未分类", "count": r[1]} for r in results]
@router.get("/stats/dragons-today")
def get_dragons_stats_today(
db: Session = Depends(get_coolbot_db)
):
"""获取今日龙钞详细统计数据(按号码分类)- 就高不就低"""
from sqlalchemy import text
# 1. 带4包含"带4"、"带四"、"通货"
2026-04-08 15:27:52 +08:00
dai4 = db.execute(text("""
SELECT
COUNT(*) as total,
2026-04-08 15:27:52 +08:00
SUM(CASE WHEN post_type = 'deal' THEN 1 ELSE 0 END) as deals,
SUM(CASE WHEN post_type = 'want' THEN 1 ELSE 0 END) as wants
FROM yichens_posts
WHERE post_time >= CURRENT_DATE
AND category LIKE '%%'
AND (
content LIKE '%带4%' OR title LIKE '%带4%'
OR content LIKE '%带四%' OR title LIKE '%带四%'
OR content LIKE '%通货%' OR title LIKE '%通货%'
)
2026-04-08 15:27:52 +08:00
""")).fetchone()
# 2. 无4包含"无4"、"无四",排除"无47"、"无247"、"无四七"、"永恒"
2026-04-08 15:27:52 +08:00
wu4 = db.execute(text("""
SELECT
COUNT(*) as total,
SUM(CASE WHEN post_type = 'deal' THEN 1 ELSE 0 END) as deals,
SUM(CASE WHEN post_type = 'want' THEN 1 ELSE 0 END) as wants
FROM yichens_posts
WHERE post_time >= CURRENT_DATE
AND category LIKE '%%'
AND (
content LIKE '%无4%' OR title LIKE '%无4%'
OR content LIKE '%无四%' OR title LIKE '%无四%'
)
AND content NOT LIKE '%无47%' AND title NOT LIKE '%无47%'
AND content NOT LIKE '%无247%' AND title NOT LIKE '%无247%'
AND content NOT LIKE '%永恒%' AND title NOT LIKE '%永恒%'
AND content NOT LIKE '%无四七%' AND title NOT LIKE '%无四七%'
2026-04-08 15:27:52 +08:00
""")).fetchone()
# 3. 无47包含"无47"、"永恒"、"无四七",排除"无247"
2026-04-08 15:27:52 +08:00
wu47 = db.execute(text("""
SELECT
COUNT(*) as total,
SUM(CASE WHEN post_type = 'deal' THEN 1 ELSE 0 END) as deals,
SUM(CASE WHEN post_type = 'want' THEN 1 ELSE 0 END) as wants
FROM yichens_posts
WHERE post_time >= CURRENT_DATE
AND category LIKE '%%'
AND (
content LIKE '%无47%' OR title LIKE '%无47%'
OR content LIKE '%永恒%' OR title LIKE '%永恒%'
OR content LIKE '%无四七%' OR title LIKE '%无四七%'
)
AND content NOT LIKE '%无247%' AND title NOT LIKE '%无247%'
2026-04-08 15:27:52 +08:00
""")).fetchone()
# 4. 无247包含"无247"、"天马"、"金山",排除"无347"
2026-04-08 15:27:52 +08:00
wu247 = db.execute(text("""
SELECT
2026-04-08 15:27:52 +08:00
COUNT(*) as total,
SUM(CASE WHEN post_type = 'deal' THEN 1 ELSE 0 END) as deals,
SUM(CASE WHEN post_type = 'want' THEN 1 ELSE 0 END) as wants
FROM yichens_posts
WHERE post_time >= CURRENT_DATE
AND category LIKE '%%'
AND (
content LIKE '%无247%' OR title LIKE '%无247%'
OR content LIKE '%天马%' OR title LIKE '%天马%'
OR content LIKE '%金山%' OR title LIKE '%金山%'
)
AND content NOT LIKE '%无347%' AND title NOT LIKE '%无347%'
2026-04-08 15:27:52 +08:00
""")).fetchone()
# 5. 无347包含"无347"、"钻石"、"金马"、"魅力"、"朦胧"
2026-04-08 15:27:52 +08:00
wu347 = db.execute(text("""
SELECT
2026-04-08 15:27:52 +08:00
COUNT(*) as total,
SUM(CASE WHEN post_type = 'deal' THEN 1 ELSE 0 END) as deals,
SUM(CASE WHEN post_type = 'want' THEN 1 ELSE 0 END) as wants
FROM yichens_posts
WHERE post_time >= CURRENT_DATE
AND category LIKE '%%'
AND (
content LIKE '%无347%' OR title LIKE '%无347%'
OR content LIKE '%钻石%' OR title LIKE '%钻石%'
OR content LIKE '%金马%' OR title LIKE '%金马%'
OR content LIKE '%魅力%' OR title LIKE '%魅力%'
OR content LIKE '%朦胧%' OR title LIKE '%朦胧%'
)
2026-04-08 15:27:52 +08:00
""")).fetchone()
return {
2026-04-08 15:27:52 +08:00
"dai4": {"total": dai4[0] or 0, "deals": dai4[1] or 0, "wants": dai4[2] or 0},
"wu4": {"total": wu4[0] or 0, "deals": wu4[1] or 0, "wants": wu4[2] or 0},
"wu47": {"total": wu47[0] or 0, "deals": wu47[1] or 0, "wants": wu47[2] or 0},
"wu247": {"total": wu247[0] or 0, "deals": wu247[1] or 0, "wants": wu247[2] or 0},
"wu347": {"total": wu347[0] or 0, "deals": wu347[1] or 0, "wants": wu347[2] or 0}
}
2026-04-08 15:27:52 +08:00