Initial project commit
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.deps import get_current_active_user, get_db
|
||||
from app.models.comment import Comment
|
||||
from app.models.report import Report
|
||||
from app.models.spot import Spot
|
||||
from app.models.user import User
|
||||
from app.schemas.comment import CommentCreate, CommentOut, ReportCreate
|
||||
from app.schemas.common import PageResponse
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/spots/{spot_id}/comments", response_model=CommentOut, status_code=status.HTTP_201_CREATED)
|
||||
async def create_comment(
|
||||
spot_id: int,
|
||||
payload: CommentCreate,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
from app.services.content_safety import check_text
|
||||
safety = check_text(payload.content)
|
||||
if not safety["safe"]:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"评论包含敏感词:{'、'.join(safety['matched'][:3])}",
|
||||
)
|
||||
|
||||
result = await db.execute(select(Spot).where(Spot.id == spot_id))
|
||||
if not result.scalar_one_or_none():
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Spot not found")
|
||||
|
||||
if payload.parent_id is not None:
|
||||
parent_result = await db.execute(
|
||||
select(Comment).where(Comment.id == payload.parent_id, Comment.spot_id == spot_id)
|
||||
)
|
||||
if not parent_result.scalar_one_or_none():
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Parent comment not found")
|
||||
|
||||
comment = Comment(
|
||||
spot_id=spot_id,
|
||||
user_id=current_user.id,
|
||||
parent_id=payload.parent_id,
|
||||
content=payload.content,
|
||||
)
|
||||
db.add(comment)
|
||||
await db.commit()
|
||||
await db.refresh(comment)
|
||||
return comment
|
||||
|
||||
|
||||
@router.get("/spots/{spot_id}/comments", response_model=PageResponse[CommentOut])
|
||||
async def list_comments(
|
||||
spot_id: int,
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
base_filter = (
|
||||
Comment.spot_id == spot_id,
|
||||
Comment.parent_id.is_(None),
|
||||
Comment.audit_status == "approved",
|
||||
)
|
||||
|
||||
count_result = await db.execute(select(func.count(Comment.id)).where(*base_filter))
|
||||
total = count_result.scalar() or 0
|
||||
|
||||
offset = (page - 1) * page_size
|
||||
result = await db.execute(
|
||||
select(Comment)
|
||||
.where(*base_filter)
|
||||
.order_by(Comment.created_at.desc())
|
||||
.offset(offset)
|
||||
.limit(page_size)
|
||||
)
|
||||
top_comments = result.scalars().all()
|
||||
|
||||
if top_comments:
|
||||
top_ids = [c.id for c in top_comments]
|
||||
replies_result = await db.execute(
|
||||
select(Comment)
|
||||
.where(
|
||||
Comment.parent_id.in_(top_ids),
|
||||
Comment.audit_status == "approved",
|
||||
)
|
||||
.order_by(Comment.created_at.asc())
|
||||
)
|
||||
all_replies = replies_result.scalars().all()
|
||||
|
||||
replies_map: dict[int, list] = {}
|
||||
for r in all_replies:
|
||||
replies_map.setdefault(r.parent_id, []).append(r)
|
||||
else:
|
||||
replies_map = {}
|
||||
|
||||
items = []
|
||||
for c in top_comments:
|
||||
out = CommentOut.model_validate(c)
|
||||
out.replies = [CommentOut.model_validate(r) for r in replies_map.get(c.id, [])]
|
||||
items.append(out)
|
||||
|
||||
return PageResponse(total=total, items=items)
|
||||
|
||||
|
||||
@router.delete("/comments/{comment_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_comment(
|
||||
comment_id: int,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
result = await db.execute(select(Comment).where(Comment.id == comment_id))
|
||||
comment = result.scalar_one_or_none()
|
||||
if not comment:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Comment not found")
|
||||
|
||||
is_admin = current_user.role in ("admin", "moderator")
|
||||
if comment.user_id != current_user.id and not is_admin:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not allowed")
|
||||
|
||||
await db.delete(comment)
|
||||
await db.commit()
|
||||
|
||||
|
||||
@router.post("/comments/{comment_id}/report", status_code=status.HTTP_201_CREATED)
|
||||
async def report_comment(
|
||||
comment_id: int,
|
||||
payload: ReportCreate,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
result = await db.execute(select(Comment).where(Comment.id == comment_id))
|
||||
if not result.scalar_one_or_none():
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Comment not found")
|
||||
|
||||
report = Report(
|
||||
reporter_id=current_user.id,
|
||||
target_type="comment",
|
||||
target_id=comment_id,
|
||||
reason=payload.reason,
|
||||
)
|
||||
db.add(report)
|
||||
await db.commit()
|
||||
return {"code": 0, "message": "Report submitted"}
|
||||
Reference in New Issue
Block a user