feat: add user role system and public joke submission API

- Add AdminUser role field (admin/submitter)
- User management in admin panel
- Public joke submission API (no auth required)
- Document submission methods for external workers

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
bwstudio
2026-06-07 12:48:35 +08:00
co-authored by Claude Opus 4.8
parent ceed63fcb0
commit aecf1f1f15
10 changed files with 681 additions and 21 deletions
+79 -1
View File
@@ -16,6 +16,8 @@ from app.schemas.category import JokeCrowdCreate, JokeCrowdResponse, JokeTypeCre
from app.schemas.joke import JokeCreate, JokeResponse, JokeUpdate, PaginatedJokeResponse
from app.schemas.link import LinkCreate, LinkResponse
from app.schemas.feedback import FeedbackResponse
from app.schemas.user import UserCreate, UserResponse
from app.routers.auth import hash_password
router = APIRouter(prefix="/admin", tags=["管理后台"])
@@ -78,6 +80,8 @@ def joke_to_response(joke: Joke, db: Session = None) -> JokeResponse:
title=joke.title,
content=joke.content,
polished_content=joke.polished_content,
ai_score=joke.ai_score,
ai_level=joke.ai_level,
type_ids=ids,
crowd_ids=crowd_ids,
status=joke.status,
@@ -96,15 +100,24 @@ def admin_list_jokes(
page: int = 1,
page_size: int = 20,
status: str = None,
keyword: str = None,
ai_level: str = None,
db: Session = Depends(get_db),
current_user: AdminUser = Depends(get_current_admin_user),
):
"""获取所有笑话(支持状态筛选)"""
"""获取所有笑话(支持状态筛选、关键词搜索"""
# 限制 page_size 防止 DoS
page_size = max(1, min(page_size, 100))
query = db.query(Joke)
if status:
query = query.filter(Joke.status == status)
if keyword:
keyword_filter = f"%{keyword}%"
query = query.filter(
(Joke.title.like(keyword_filter)) | (Joke.content.like(keyword_filter))
)
if ai_level:
query = query.filter(Joke.ai_level == ai_level)
total = query.count()
offset = (page - 1) * page_size
jokes = query.order_by(Joke.created_at.desc()).offset(offset).limit(page_size).all()
@@ -159,6 +172,7 @@ def admin_update_joke(
db_joke = db.query(Joke).filter(Joke.id == joke_id).first()
if not db_joke:
raise HTTPException(status_code=404, detail="笑话不存在")
update_data = joke.model_dump(exclude_unset=True)
for key, value in update_data.items():
if key in ("type_ids", "crowd_ids") and value is not None:
@@ -420,4 +434,68 @@ def admin_delete_feedback(
raise HTTPException(status_code=404, detail="反馈不存在")
db.delete(db_feedback)
db.commit()
return {"message": "删除成功"}
# ============================================================
# 用户管理
# ============================================================
@router.get("/users", response_model=list[UserResponse])
def admin_list_users(
db: Session = Depends(get_db),
current_user: AdminUser = Depends(get_current_admin_user),
):
"""获取所有用户"""
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="仅管理员可操作")
return db.query(AdminUser).order_by(AdminUser.created_at.desc()).all()
@router.post("/users", response_model=UserResponse)
def admin_create_user(
user: UserCreate,
db: Session = Depends(get_db),
current_user: AdminUser = Depends(get_current_admin_user),
):
"""创建用户"""
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="仅管理员可操作")
# 检查用户名是否已存在
existing = db.query(AdminUser).filter(AdminUser.username == user.username).first()
if existing:
raise HTTPException(status_code=400, detail="用户名已存在")
db_user = AdminUser(
username=user.username,
password_hash=hash_password(user.password),
role=user.role,
)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
@router.delete("/users/{user_id}")
def admin_delete_user(
user_id: int,
db: Session = Depends(get_db),
current_user: AdminUser = Depends(get_current_admin_user),
):
"""删除用户"""
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="仅管理员可操作")
db_user = db.query(AdminUser).filter(AdminUser.id == user_id).first()
if not db_user:
raise HTTPException(status_code=404, detail="用户不存在")
# 不允许删除自己
if db_user.id == current_user.id:
raise HTTPException(status_code=400, detail="不能删除自己")
db.delete(db_user)
db.commit()
return {"message": "删除成功"}