Initial commit: MyBlog full stack blog

This commit is contained in:
2026-08-22 22:28:41 +08:00
commit c61193f2af
64 changed files with 7290 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
"""
点赞路由。
接口(统一响应格式 {success, data, message}):
- POST /api/like/add 点赞(好友或博主,同一用户不能重复点赞)
- GET /api/like/list 点赞列表(可见性与文章一致)
"""
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Request, status
from sqlalchemy.orm import Session
from ..auth import get_current_user
from ..database import get_db
from ..models import (
ROLE_BLOGGER,
ROLE_FRIEND,
VISIBILITY_FRIEND,
Article,
Like,
User,
)
from ..schemas import LikeCreate, UnifiedResponse
from ..security import action_limiter, get_client_ip
from .deps import can_read_friend_article, get_optional_user
router = APIRouter(prefix="/api/like", tags=["like"])
@router.post("/add", response_model=UnifiedResponse)
def add_like(
payload: LikeCreate,
request: Request,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
) -> UnifiedResponse:
"""点赞:仅好友或博主可点赞,同一用户不能重复点赞。"""
# 写操作限流(IP 维度):防止好友账号刷点赞
if action_limiter.is_blocked(get_client_ip(request)):
raise HTTPException(status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail="操作过于频繁,请稍后再试")
if current_user.role not in (ROLE_FRIEND, ROLE_BLOGGER):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="只有好友或博主可以点赞")
article = db.query(Article).filter(Article.id == payload.article_id).first()
if article is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="文章不存在")
exists = (
db.query(Like)
.filter(Like.article_id == payload.article_id, Like.user_id == current_user.id)
.first()
)
if exists is not None:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="不能重复点赞")
like = Like(article_id=payload.article_id, user_id=current_user.id)
db.add(like)
db.commit()
db.refresh(like)
action_limiter.hit(get_client_ip(request))
data = {"id": like.id, "article_id": like.article_id, "user_id": like.user_id}
return UnifiedResponse(success=True, data=data, message="点赞成功")
@router.get("/list", response_model=UnifiedResponse)
def list_likes(
article_id: int,
current_user: Optional[User] = Depends(get_optional_user),
db: Session = Depends(get_db),
) -> UnifiedResponse:
"""点赞列表:公开文章所有人可见,好友文章仅好友/博主可见。"""
article = db.query(Article).filter(Article.id == article_id).first()
if article is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="文章不存在")
if article.visibility == VISIBILITY_FRIEND and not can_read_friend_article(current_user):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无权查看该文章点赞")
likes = db.query(Like).filter(Like.article_id == article_id).all()
# 不返回点赞用户名单(避免泄露用户名与账号关联,防枚举),
# 仅返回点赞数量与当前登录用户是否已点赞
data = {
"count": len(likes),
"includes_me": current_user is not None and any(l.user_id == current_user.id for l in likes),
}
return UnifiedResponse(success=True, data=data, message="获取成功")