Files
Zhang Jing Xuan 0841b1a103 智教助手平台:完整初始化
- 前端:Vue3 + TS + Element Plus,24 个页面路由(课件/组题/教案/动画/思维导图/作文批改/命题/课堂/资源/社区等)
- 后端:FastAPI + SQLAlchemy + SQLite,17 个路由模块,AI 服务层含降级模板
- AI:glm-5.x 推理模型已禁用思维链,确保输出真实内容
- 修复:ai_service 两处请求体注入 enable_thinking/thinking disabled
- 测试账号:13900999999 / test1234
2026-07-29 16:29:10 +08:00

211 lines
8.0 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import or_
from sqlalchemy.orm import Session
from typing import Optional
from database import get_db
from models.community import Post, Comment, PostFavorite
from models.user import User
from schemas.community import PostCreate, PostUpdate, PostOut, CommentCreate, CommentOut
from services.auth import get_current_user, get_optional_current_user
from routers.notification import push_notification
router = APIRouter(prefix="/api/community", tags=["教师社区"])
def _sync_post_favorite_count(post: Post, db: Session) -> None:
post.likes = db.query(PostFavorite).filter(PostFavorite.post_id == post.id).count()
def _serialize_post(post: Post, current_user: User | None, db: Session) -> Post:
is_favorited = False
if current_user:
is_favorited = db.query(PostFavorite).filter(
PostFavorite.user_id == current_user.id,
PostFavorite.post_id == post.id,
).first() is not None
setattr(post, "is_favorited", is_favorited)
setattr(post, "author", post.author)
return post
@router.post("/posts", response_model=PostOut, status_code=201)
def create_post(data: PostCreate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
post = Post(user_id=current_user.id, **data.model_dump())
db.add(post)
db.commit()
db.refresh(post)
return _serialize_post(post, current_user, db)
@router.get("/posts", response_model=list[PostOut])
def list_posts(
post_type: Optional[str] = None,
keyword: Optional[str] = None,
scope: Optional[str] = None,
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
current_user: User | None = Depends(get_optional_current_user),
db: Session = Depends(get_db),
):
query = db.query(Post)
if scope in {"mine", "favorite"} and not current_user:
raise HTTPException(status_code=401, detail="请先登录")
if scope == "mine":
query = query.filter(Post.user_id == current_user.id)
elif scope == "favorite":
query = query.join(PostFavorite, PostFavorite.post_id == Post.id).filter(
PostFavorite.user_id == current_user.id,
)
if post_type:
query = query.filter(Post.post_type == post_type)
if keyword:
query = query.filter(or_(
Post.title.contains(keyword),
Post.content.contains(keyword),
))
posts = query.order_by(Post.is_pinned.desc(), Post.created_at.desc()).offset(skip).limit(limit).all()
return [_serialize_post(post, current_user, db) for post in posts]
@router.get("/posts/ranking", response_model=list[PostOut])
def ranking_posts(
limit: int = Query(5, ge=1, le=20),
current_user: User | None = Depends(get_optional_current_user),
db: Session = Depends(get_db),
):
score = Post.views + Post.likes * 10 + Post.comments_count * 3
posts = (
db.query(Post)
.order_by(Post.is_pinned.desc(), score.desc(), Post.created_at.desc())
.limit(limit)
.all()
)
return [_serialize_post(post, current_user, db) for post in posts]
@router.get("/posts/{post_id}", response_model=PostOut)
def get_post(
post_id: int,
current_user: User | None = Depends(get_optional_current_user),
db: Session = Depends(get_db),
):
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="帖子不存在")
post.views = (post.views or 0) + 1
db.commit()
db.refresh(post)
return _serialize_post(post, current_user, db)
@router.put("/posts/{post_id}", response_model=PostOut)
def update_post(
post_id: int,
data: PostUpdate,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
post = db.query(Post).filter(Post.id == post_id, Post.user_id == current_user.id).first()
if not post:
raise HTTPException(status_code=404, detail="帖子不存在")
update_data = data.model_dump(exclude_unset=True)
for key, value in update_data.items():
setattr(post, key, value)
db.commit()
db.refresh(post)
return _serialize_post(post, current_user, db)
@router.post("/posts/{post_id}/like", response_model=dict)
def like_post(
post_id: int,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="帖子不存在")
favorite = db.query(PostFavorite).filter(
PostFavorite.user_id == current_user.id,
PostFavorite.post_id == post_id,
).first()
if not favorite:
db.add(PostFavorite(user_id=current_user.id, post_id=post_id))
db.flush()
push_notification(db, user_id=post.user_id, actor_id=current_user.id, ntype="like", title=f"{current_user.name} 收藏了你的帖子", content=post.title[:60], link=f"/community/{post_id}")
_sync_post_favorite_count(post, db)
db.commit()
return {"success": True, "likes": post.likes, "is_favorited": True}
@router.delete("/posts/{post_id}/like", response_model=dict)
def unlike_post(
post_id: int,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="帖子不存在")
favorite = db.query(PostFavorite).filter(
PostFavorite.user_id == current_user.id,
PostFavorite.post_id == post_id,
).first()
if favorite:
db.delete(favorite)
db.flush()
_sync_post_favorite_count(post, db)
db.commit()
return {"success": True, "likes": post.likes, "is_favorited": False}
@router.get("/posts/{post_id}/comments", response_model=list[CommentOut])
def list_comments(post_id: int, skip: int = Query(0, ge=0), limit: int = Query(50, ge=1, le=200), db: Session = Depends(get_db)):
return db.query(Comment).filter(Comment.post_id == post_id).order_by(Comment.created_at).offset(skip).limit(limit).all()
@router.post("/posts/{post_id}/comments", response_model=CommentOut, status_code=201)
def create_comment(
post_id: int, data: CommentCreate,
current_user: User = Depends(get_current_user), db: Session = Depends(get_db),
):
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="帖子不存在")
comment = Comment(post_id=post_id, user_id=current_user.id, **data.model_dump())
db.add(comment)
post.comments_count = (post.comments_count or 0) + 1
push_notification(db, user_id=post.user_id, actor_id=current_user.id, ntype="comment", title=f"{current_user.name} 评论了你的帖子", content=post.title[:60], link=f"/community/{post_id}")
db.commit()
db.refresh(comment)
return comment
@router.delete("/posts/{post_id}/comments/{comment_id}", status_code=204)
def delete_comment(
post_id: int,
comment_id: int,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="帖子不存在")
comment = db.query(Comment).filter(Comment.id == comment_id, Comment.post_id == post_id).first()
if not comment:
raise HTTPException(status_code=404, detail="评论不存在")
if comment.user_id != current_user.id and post.user_id != current_user.id:
raise HTTPException(status_code=403, detail="无权删除该评论")
db.delete(comment)
post.comments_count = max((post.comments_count or 0) - 1, 0)
db.commit()
@router.delete("/posts/{post_id}", status_code=204)
def delete_post(post_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
post = db.query(Post).filter(Post.id == post_id, Post.user_id == current_user.id).first()
if not post:
raise HTTPException(status_code=404, detail="帖子不存在")
db.delete(post)
db.commit()