""" 公告管理接口 """ from datetime import datetime from app.utils.timezone import beijing_now from typing import Optional from fastapi import APIRouter, Depends, Query, Request from pydantic import BaseModel from sqlalchemy import or_ from sqlalchemy.orm import Session from app.api.deps import get_current_admin from app.database import get_db from app.models.announcement import Announcement router = APIRouter() class AnnouncementCreate(BaseModel): """创建公告请求""" title: str content: str priority: int = 0 is_active: bool = True publish_time: Optional[str] = None # ISO格式时间字符串 @router.get("") async def get_announcements( page: int = Query(1, ge=1), page_size: int = Query(10, ge=1, le=100), keyword: Optional[str] = Query(None, description="搜索关键词(标题、内容)"), db: Session = Depends(get_db), current_admin=Depends(get_current_admin), ): """获取公告列表(支持检索与分页),按发布时间倒序""" offset = (page - 1) * page_size query = db.query(Announcement).filter(Announcement.is_deleted == 0) # 关键字搜索:标题或内容模糊匹配 if keyword: like_pattern = f"%{keyword}%" query = query.filter( or_( Announcement.title.like(like_pattern), Announcement.content.like(like_pattern), ) ) # 按发布时间倒序,其次按ID倒序,确保最新的数据在前 # 这里不使用 nullslast,以避免部分数据库方言不支持导致的错误 query = query.order_by(Announcement.publish_time.desc(), Announcement.id.desc()) total = query.count() announcements = query.offset(offset).limit(page_size).all() return { "code": 200, "msg": "成功", "data": [ { "id": a.id, "title": a.title, "content": a.content, "priority": a.priority, "is_active": bool(a.is_active), "publish_time": a.publish_time.isoformat() if a.publish_time else None, "created_at": a.created_at.isoformat() if a.created_at else None, "updated_at": a.updated_at.isoformat() if a.updated_at else None, } for a in announcements ], "total": total, "page": page, "page_size": page_size, } @router.get("/{announcement_id}") async def get_announcement_detail( announcement_id: int, db: Session = Depends(get_db), current_admin = Depends(get_current_admin) ): """获取公告详情""" announcement = db.query(Announcement).filter(Announcement.id == announcement_id).first() if not announcement: return {"code": 404, "msg": "公告不存在"} return { "code": 200, "msg": "成功", "data": { "id": announcement.id, "title": announcement.title, "content": announcement.content, "priority": announcement.priority, "is_active": bool(announcement.is_active), "publish_time": announcement.publish_time.isoformat() if announcement.publish_time else None, "created_at": announcement.created_at.isoformat() if announcement.created_at else None, "updated_at": announcement.updated_at.isoformat() if announcement.updated_at else None } } @router.post("") async def create_announcement( req: AnnouncementCreate, request: Request, db: Session = Depends(get_db), current_admin = Depends(get_current_admin) ): """创建公告""" publish_time = beijing_now() if req.publish_time: try: publish_time = datetime.fromisoformat(req.publish_time.replace('Z', '+00:00')) except: pass announcement = Announcement( title=req.title, content=req.content, priority=req.priority, is_active=1 if req.is_active else 0, publish_time=publish_time, created_at=beijing_now(), updated_at=beijing_now() ) db.add(announcement) # 先提交主记录,获取ID db.flush() # 创建历史记录 from app.utils.history import create_announcement_history, create_operation_log create_announcement_history(db, announcement, "INSERT") # 记录操作日志 client_ip = request.client.host if request.client else None create_operation_log( db=db, admin_id=current_admin.id, module="公告管理", action="创建公告", description=f"创建公告:{req.title}", new_data={ "title": req.title, "content": req.content[:100] + "..." if len(req.content) > 100 else req.content, "priority": req.priority, "is_active": req.is_active }, ip_address=client_ip ) # 一次性提交所有更改 db.commit() return {"code": 200, "msg": "创建成功", "data": {"id": announcement.id}} @router.put("/{announcement_id}") async def update_announcement( announcement_id: int, req: AnnouncementCreate, request: Request, db: Session = Depends(get_db), current_admin = Depends(get_current_admin) ): """更新公告""" announcement = db.query(Announcement).filter(Announcement.id == announcement_id).first() if not announcement: return {"code": 404, "msg": "公告不存在"} # 记录更新前的状态 old_data = { "title": announcement.title, "content": announcement.content[:100] + "..." if len(announcement.content) > 100 else announcement.content, "priority": announcement.priority, "is_active": bool(announcement.is_active) } announcement.title = req.title announcement.content = req.content announcement.priority = req.priority announcement.is_active = 1 if req.is_active else 0 if req.publish_time: try: announcement.publish_time = datetime.fromisoformat(req.publish_time.replace('Z', '+00:00')) except: pass announcement.updated_at = beijing_now() # 创建历史记录 from app.utils.history import create_announcement_history, create_operation_log create_announcement_history(db, announcement, "UPDATE") # 记录操作日志 client_ip = request.client.host if request.client else None create_operation_log( db=db, admin_id=current_admin.id, module="公告管理", action="更新公告", description=f"更新公告:{announcement.title}", old_data=old_data, new_data={ "title": req.title, "content": req.content[:100] + "..." if len(req.content) > 100 else req.content, "priority": req.priority, "is_active": req.is_active }, ip_address=client_ip ) # 一次性提交所有更改 db.commit() return {"code": 200, "msg": "更新成功"} @router.put("/{announcement_id}/status") async def toggle_announcement_status( announcement_id: int, request: Request, db: Session = Depends(get_db), current_admin = Depends(get_current_admin) ): """切换公告状态""" announcement = db.query(Announcement).filter(Announcement.id == announcement_id).first() if not announcement: return {"code": 404, "msg": "公告不存在"} old_active = announcement.is_active announcement.is_active = 0 if announcement.is_active else 1 announcement.updated_at = beijing_now() # 创建历史记录 from app.utils.history import create_announcement_history, create_operation_log create_announcement_history(db, announcement, "UPDATE") # 记录操作日志 client_ip = request.client.host if request.client else None status_text = "启用" if announcement.is_active == 1 else "禁用" create_operation_log( db=db, admin_id=current_admin.id, module="公告管理", action="切换公告状态", description=f"{status_text}公告:{announcement.title}", old_data={"is_active": bool(old_active)}, new_data={"is_active": bool(announcement.is_active)}, ip_address=client_ip ) # 一次性提交所有更改 db.commit() return {"code": 200, "msg": "状态更新成功", "data": {"is_active": bool(announcement.is_active)}} @router.delete("/{announcement_id}") async def delete_announcement( announcement_id: int, request: Request, db: Session = Depends(get_db), current_admin = Depends(get_current_admin) ): """删除公告(软删除)""" announcement = db.query(Announcement).filter(Announcement.id == announcement_id, Announcement.is_deleted == 0).first() if not announcement: return {"code": 404, "msg": "公告不存在"} announcement.is_deleted = 1 announcement.updated_at = beijing_now() # 记录操作日志(删除公告没有历史记录,因为它本身就是历史记录) from app.utils.history import create_operation_log client_ip = request.client.host if request.client else None create_operation_log( db=db, admin_id=current_admin.id, module="公告管理", action="删除公告", description=f"删除公告:{announcement.title}", old_data={ "title": announcement.title, "content": announcement.content[:100] + "..." if len(announcement.content) > 100 else announcement.content, "priority": announcement.priority, "is_active": bool(announcement.is_active) }, ip_address=client_ip ) # 一次性提交所有更改 db.commit() return {"code": 200, "msg": "删除成功"}