116 lines
3.4 KiB
Python
116 lines
3.4 KiB
Python
|
|
"""通知 API — 列表、已读、删除"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import logging
|
||
|
|
from datetime import datetime
|
||
|
|
from typing import List, Optional
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||
|
|
from pydantic import BaseModel
|
||
|
|
from sqlalchemy.orm import Session
|
||
|
|
|
||
|
|
from app.api.auth import get_current_user
|
||
|
|
from app.core.database import get_db
|
||
|
|
from app.models.user import User
|
||
|
|
from app.services.notification_service import (
|
||
|
|
delete_notification,
|
||
|
|
get_unread_count,
|
||
|
|
get_user_notifications,
|
||
|
|
mark_all_as_read,
|
||
|
|
mark_as_read,
|
||
|
|
)
|
||
|
|
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
router = APIRouter(prefix="/api/v1/notifications", tags=["notifications"])
|
||
|
|
|
||
|
|
|
||
|
|
# ─── Pydantic Schemas ──────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
class NotificationResponse(BaseModel):
|
||
|
|
id: str
|
||
|
|
user_id: str
|
||
|
|
title: str
|
||
|
|
content: Optional[str] = None
|
||
|
|
category: str
|
||
|
|
ref_type: Optional[str] = None
|
||
|
|
ref_id: Optional[str] = None
|
||
|
|
is_read: bool
|
||
|
|
created_at: datetime
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
from_attributes = True
|
||
|
|
|
||
|
|
|
||
|
|
class UnreadCountResponse(BaseModel):
|
||
|
|
count: int
|
||
|
|
|
||
|
|
|
||
|
|
# ─── API Endpoints ─────────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("", response_model=List[NotificationResponse])
|
||
|
|
async def list_notifications(
|
||
|
|
unread_only: bool = Query(False, description="仅未读"),
|
||
|
|
category: Optional[str] = Query(None, description="按分类过滤"),
|
||
|
|
limit: int = Query(50, ge=1, le=200),
|
||
|
|
offset: int = Query(0, ge=0),
|
||
|
|
current_user: User = Depends(get_current_user),
|
||
|
|
db: Session = Depends(get_db),
|
||
|
|
):
|
||
|
|
"""获取当前用户的通知列表。"""
|
||
|
|
return get_user_notifications(
|
||
|
|
db,
|
||
|
|
user_id=current_user.id,
|
||
|
|
unread_only=unread_only,
|
||
|
|
category=category,
|
||
|
|
limit=limit,
|
||
|
|
offset=offset,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/unread-count", response_model=UnreadCountResponse)
|
||
|
|
async def unread_count(
|
||
|
|
current_user: User = Depends(get_current_user),
|
||
|
|
db: Session = Depends(get_db),
|
||
|
|
):
|
||
|
|
"""获取当前用户的未读通知数。"""
|
||
|
|
count = get_unread_count(db, current_user.id)
|
||
|
|
return {"count": count}
|
||
|
|
|
||
|
|
|
||
|
|
@router.put("/{notification_id}/read")
|
||
|
|
async def read_notification(
|
||
|
|
notification_id: str,
|
||
|
|
current_user: User = Depends(get_current_user),
|
||
|
|
db: Session = Depends(get_db),
|
||
|
|
):
|
||
|
|
"""将一条通知标记为已读。"""
|
||
|
|
result = mark_as_read(db, notification_id, current_user.id)
|
||
|
|
if not result:
|
||
|
|
raise HTTPException(status_code=404, detail="通知不存在")
|
||
|
|
return {"message": "已标记为已读"}
|
||
|
|
|
||
|
|
|
||
|
|
@router.put("/read-all")
|
||
|
|
async def read_all_notifications(
|
||
|
|
current_user: User = Depends(get_current_user),
|
||
|
|
db: Session = Depends(get_db),
|
||
|
|
):
|
||
|
|
"""将所有通知标记为已读。"""
|
||
|
|
count = mark_all_as_read(db, current_user.id)
|
||
|
|
return {"message": f"已将 {count} 条通知标记为已读"}
|
||
|
|
|
||
|
|
|
||
|
|
@router.delete("/{notification_id}")
|
||
|
|
async def remove_notification(
|
||
|
|
notification_id: str,
|
||
|
|
current_user: User = Depends(get_current_user),
|
||
|
|
db: Session = Depends(get_db),
|
||
|
|
):
|
||
|
|
"""删除一条通知。"""
|
||
|
|
ok = delete_notification(db, notification_id, current_user.id)
|
||
|
|
if not ok:
|
||
|
|
raise HTTPException(status_code=404, detail="通知不存在")
|
||
|
|
return {"message": "通知已删除"}
|