from datetime import date, datetime from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import get_db from app.models.announcement import Announcement from app.models.schemas import AnnouncementListResponse, AnnouncementResponse router = APIRouter() def _parse_date(value: str | None) -> date | None: if not value: return None return datetime.strptime(value, "%Y-%m-%d").date() @router.get("/announcements", response_model=AnnouncementListResponse) async def list_announcements( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), source_code: str | None = None, keyword: str | None = None, start_date: str | None = None, end_date: str | None = None, crawl_mode: str | None = None, db: AsyncSession = Depends(get_db), ): conditions = [] if source_code: conditions.append(Announcement.source_code == source_code) if crawl_mode: conditions.append(Announcement.crawl_mode == crawl_mode) start = _parse_date(start_date) end = _parse_date(end_date) if start: conditions.append(Announcement.publish_date >= start) if end: conditions.append(Announcement.publish_date <= end) if keyword: conditions.append( Announcement.title.ilike(f"%{keyword}%") ) base_query = select(Announcement) if conditions: base_query = base_query.where(*conditions) count_query = select(func.count()).select_from(base_query.subquery()) total_result = await db.execute(count_query) total = total_result.scalar() or 0 items_query = base_query.order_by(Announcement.publish_date.desc()) \ .offset((page - 1) * page_size).limit(page_size) items_result = await db.execute(items_query) items = items_result.scalars().all() return AnnouncementListResponse( total=total, page=page, page_size=page_size, items=[AnnouncementResponse.model_validate(item) for item in items], ) @router.get("/announcements/today", response_model=AnnouncementListResponse) async def get_today_announcements(db: AsyncSession = Depends(get_db)): today = datetime.now().date() result = await db.execute( select(Announcement).where( func.date(Announcement.publish_date) == today ).order_by(Announcement.publish_date.desc()) ) items = result.scalars().all() return AnnouncementListResponse( total=len(items), page=1, page_size=len(items), items=[AnnouncementResponse.model_validate(item) for item in items], ) @router.get("/announcements/stats") async def get_stats(db: AsyncSession = Depends(get_db)): total = await db.execute(select(func.count()).select_from(Announcement)) today_count = await db.execute( select(func.count()).where( func.date(Announcement.publish_date) == func.current_date() ).select_from(Announcement) ) new_count = await db.execute( select(func.count()).where(Announcement.is_new == True) # noqa: E712 .select_from(Announcement) ) unsent = await db.execute( select(func.count()).where( Announcement.is_sent == False, Announcement.is_new == True # noqa: E712 ).select_from(Announcement) ) return { "total": total.scalar() or 0, "today": today_count.scalar() or 0, "new": new_count.scalar() or 0, "unsent": unsent.scalar() or 0, } @router.get("/announcements/{announcement_id}", response_model=AnnouncementResponse) async def get_announcement(announcement_id: int, db: AsyncSession = Depends(get_db)): result = await db.execute( select(Announcement).where(Announcement.id == announcement_id) ) item = result.scalar_one_or_none() if item is None: raise HTTPException(status_code=404, detail="公告不存在") return AnnouncementResponse.model_validate(item)