from typing import Optional from datetime import date from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from backend.app.core.dependencies import require_permissions, get_current_user from backend.app.db.session import get_db from backend.app.models import DutyItem, User from backend.app.schemas.pagination import PaginatedResponse from backend.app.schemas.duty import DutyCreate, DutyResponse, DutyUpdate router = APIRouter(prefix="/duty", tags=["duty"]) def is_limited_scope(user: User) -> bool: return user.role and user.role.name not in {"管理员", "排班员"} @router.get( "", response_model=list[DutyResponse] | PaginatedResponse[DutyResponse], dependencies=[Depends(require_permissions(["duty.view"]))], ) async def list_duty( db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user), start: Optional[str] = Query(default=None), end: Optional[str] = Query(default=None), page: Optional[int] = Query(default=None, ge=1), page_size: Optional[int] = Query(default=None, ge=1, le=200, alias="pageSize"), size: Optional[int] = Query(default=None, ge=1, le=200), ): query = select(DutyItem) if is_limited_scope(current_user) and current_user.dept_id: query = query.where(DutyItem.staff_id == current_user.id) if start: query = query.where(DutyItem.date >= date.fromisoformat(start)) if end: query = query.where(DutyItem.date <= date.fromisoformat(end)) if page is None and page_size is None and size is None: result = await db.execute(query.order_by(DutyItem.date)) return result.scalars().all() page_value = page or 1 size_value = page_size or size or 10 total = await db.scalar(select(func.count()).select_from(query.subquery())) result = await db.execute( query.order_by(DutyItem.date) .offset((page_value - 1) * size_value) .limit(size_value) ) return { "items": result.scalars().all(), "total": total or 0, "page": page_value, "pageSize": size_value, } @router.post("", response_model=DutyResponse, dependencies=[Depends(require_permissions(["duty.create"]))]) async def create_duty( payload: DutyCreate, db: AsyncSession = Depends(get_db), ): item = DutyItem( date=date.fromisoformat(payload.date), staff_id=payload.staff_id, duty_type=payload.duty_type, contact=payload.contact, note=payload.note ) db.add(item) await db.commit() await db.refresh(item) return item @router.put("/{item_id}", response_model=DutyResponse, dependencies=[Depends(require_permissions(["duty.edit"]))]) async def update_duty(item_id: UUID, payload: DutyUpdate, db: AsyncSession = Depends(get_db)): result = await db.execute(select(DutyItem).where(DutyItem.id == item_id)) item = result.scalar_one_or_none() if not item: raise HTTPException(status_code=404, detail="值班不存在") update_data = payload.model_dump(exclude_unset=True) if "date" in update_data and update_data["date"]: update_data["date"] = date.fromisoformat(update_data["date"]) for key, value in update_data.items(): setattr(item, key, value) await db.commit() await db.refresh(item) return item @router.delete("/{item_id}", dependencies=[Depends(require_permissions(["duty.delete"]))]) async def delete_duty(item_id: UUID, db: AsyncSession = Depends(get_db)): result = await db.execute(select(DutyItem).where(DutyItem.id == item_id)) item = result.scalar_one_or_none() if not item: raise HTTPException(status_code=404, detail="值班不存在") await db.delete(item) await db.commit() return {"success": True}