feat: 当月热门笑话 + 统计面板,删除首页清除筛选按钮

- 后端新增 GET /api/jokes/hot-monthly(当月浏览量 top 10)
- 后端新增 GET /api/jokes/stats(公开统计:总数/审核/待审/浏览等)
- 右侧栏热门改为当月热门笑话
- 右侧栏新增网站统计区(笑话总数/已审核/待审核/总浏览)
- 首页删除清除筛选按钮
This commit is contained in:
bwstudio
2026-06-12 05:48:32 +08:00
parent 93b1d725e3
commit 076812f1aa
5 changed files with 139 additions and 34 deletions
+61 -20
View File
@@ -1,5 +1,6 @@
import json
import random
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import func, text
@@ -161,26 +162,6 @@ def list_jokes(
)
@router.get("/{joke_id}", response_model=JokeResponse)
def get_joke(joke_id: int, db: Session = Depends(get_db)):
"""获取单条笑话详情"""
# 只返回已审核通过的笑话
joke = db.query(Joke).filter(
Joke.id == joke_id,
Joke.status == "approved"
).first()
if not joke:
raise HTTPException(status_code=404, detail="笑话不存在")
# 使用原子更新避免并发竞态
db.query(Joke).filter(Joke.id == joke_id).update({Joke.view_count: Joke.view_count + 1})
db.commit()
# 重新查询获取更新后的数据
db.refresh(joke)
# 批量加载类型和人群名称
type_map, crowd_map = _batch_load_categories(db, [joke])
return joke_to_response(joke, type_map, crowd_map)
@router.get("/random", response_model=JokeResponse)
def get_random_joke(db: Session = Depends(get_db)):
"""随机获取一条已审核通过的笑话"""
@@ -208,6 +189,66 @@ def get_random_joke(db: Session = Depends(get_db)):
return joke_to_response(joke, type_map, crowd_map)
@router.get("/hot-monthly")
def hot_monthly_jokes(db: Session = Depends(get_db)):
"""当月热门笑话:本月浏览量最高的前 10 条"""
now = datetime.now()
start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
jokes = (
db.query(Joke)
.filter(
Joke.status == "approved",
Joke.created_at >= start_of_month,
)
.order_by(Joke.view_count.desc())
.limit(10)
.all()
)
type_map, crowd_map = _batch_load_categories(db, jokes)
return [joke_to_response(j, type_map, crowd_map) for j in jokes]
@router.get("/stats")
def public_stats(db: Session = Depends(get_db)):
"""公开统计数据:笑话总数、审核/待审/拒绝数量、总浏览/赞/踩"""
total = db.query(Joke).count()
approved = db.query(Joke).filter(Joke.status == "approved").count()
pending = db.query(Joke).filter(Joke.status == "pending").count()
rejected = db.query(Joke).filter(Joke.status == "rejected").count()
total_views = db.query(Joke).with_entities(func.sum(Joke.view_count)).scalar() or 0
total_likes = db.query(Joke).with_entities(func.sum(Joke.like_count)).scalar() or 0
total_dislikes = db.query(Joke).with_entities(func.sum(Joke.dislike_count)).scalar() or 0
return {
"total_jokes": total,
"approved_jokes": approved,
"pending_jokes": pending,
"rejected_jokes": rejected,
"total_views": total_views,
"total_likes": total_likes,
"total_dislikes": total_dislikes,
}
@router.get("/{joke_id}", response_model=JokeResponse)
def get_joke(joke_id: int, db: Session = Depends(get_db)):
"""获取单条笑话详情"""
# 只返回已审核通过的笑话
joke = db.query(Joke).filter(
Joke.id == joke_id,
Joke.status == "approved"
).first()
if not joke:
raise HTTPException(status_code=404, detail="笑话不存在")
# 使用原子更新避免并发竞态
db.query(Joke).filter(Joke.id == joke_id).update({Joke.view_count: Joke.view_count + 1})
db.commit()
# 重新查询获取更新后的数据
db.refresh(joke)
# 批量加载类型和人群名称
type_map, crowd_map = _batch_load_categories(db, [joke])
return joke_to_response(joke, type_map, crowd_map)
def _vote_joke(joke_id: int, field: str, db: Session):
"""通用投票逻辑:点赞或点踩(仅允许已审核通过的笑话)"""
joke = db.query(Joke).filter(