feat: add Leave API endpoints + frontend API module

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-12 13:05:49 +08:00
parent 2633513ce3
commit b6105295bb
3 changed files with 148 additions and 1 deletions
+127
View File
@@ -0,0 +1,127 @@
from uuid import UUID
from fastapi import APIRouter, Depends, Query, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.middleware.auth import get_current_user, RoleChecker
from app.schemas.leave import LeaveCreate, LeaveUpdate
from app.services import leaves as leave_service
router = APIRouter(prefix="/leaves", tags=["Leaves"])
@router.get("")
async def list_leaves(
manager_id: str | None = Query(None),
status: str | None = Query(None),
page: int = Query(1, ge=1),
page_size: int = Query(25, ge=1, le=100),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(get_current_user),
):
"""List leave records. Managers see only their own."""
return await leave_service.get_leaves(
db=db,
user_id=current_user["user_id"],
role=current_user["role"],
manager_id=manager_id,
status=status,
page=page,
page_size=page_size,
)
@router.post("")
async def create_leave(
data: LeaveCreate,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(get_current_user),
):
"""Create a leave record. Director can create for anyone; manager only for self."""
try:
leave = await leave_service.create_leave(
db=db,
data=data.model_dump(),
submitted_by=UUID(current_user["user_id"]),
role=current_user["role"],
)
return {
"id": str(leave.id),
"manager_id": str(leave.manager_id),
"leave_type": leave.leave_type,
"start_date": str(leave.start_date),
"end_date": str(leave.end_date),
"reason": leave.reason,
"submitted_by": str(leave.submitted_by),
}
except PermissionError as e:
raise HTTPException(status_code=403, detail=str(e))
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.put("/{leave_id}")
async def update_leave(
leave_id: UUID,
data: LeaveUpdate,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(get_current_user),
):
"""Update a leave record."""
try:
leave = await leave_service.update_leave(
db=db,
leave_id=leave_id,
data=data.model_dump(exclude_none=True),
user_id=UUID(current_user["user_id"]),
role=current_user["role"],
)
return {
"id": str(leave.id),
"manager_id": str(leave.manager_id),
"leave_type": leave.leave_type,
"start_date": str(leave.start_date),
"end_date": str(leave.end_date),
"reason": leave.reason,
}
except PermissionError as e:
raise HTTPException(status_code=403, detail=str(e))
except ValueError as e:
raise HTTPException(status_code=404 if "不存在" in str(e) else 400, detail=str(e))
@router.delete("/{leave_id}")
async def delete_leave(
leave_id: UUID,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(get_current_user),
):
"""Delete a leave record."""
try:
ok = await leave_service.delete_leave(
db=db,
leave_id=leave_id,
user_id=UUID(current_user["user_id"]),
role=current_user["role"],
)
if not ok:
raise HTTPException(status_code=404, detail="请假记录不存在")
return {"status": "deleted"}
except PermissionError as e:
raise HTTPException(status_code=403, detail=str(e))
@router.get("/overview")
async def leave_overview(
reference_date: str | None = Query(None),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(get_current_user),
):
"""Get weekly leave overview for dashboard card."""
from datetime import date
ref = date.fromisoformat(reference_date) if reference_date else None
return await leave_service.get_leave_overview(
db=db,
user_id=current_user["user_id"],
role=current_user["role"],
reference_date=ref,
)
+2 -1
View File
@@ -6,7 +6,7 @@ from app.config import settings
from app.database import engine, Base, async_session
from app.api import router as api_router
from app.api import auth, users, customers, visits, work_plans, mini_business, key_visits
from app.api import dashboard, upload, export, import_data, wecom, daily_notes, ai_summary, system_config
from app.api import dashboard, upload, export, import_data, wecom, daily_notes, ai_summary, system_config, leaves
from app.models.system_config import SystemConfig
from app.services.scheduler_manager import start_scheduler, shutdown_scheduler
@@ -119,6 +119,7 @@ app.include_router(work_plans.router, prefix="/api")
app.include_router(mini_business.router, prefix="/api")
app.include_router(key_visits.router, prefix="/api")
app.include_router(dashboard.router, prefix="/api")
app.include_router(leaves.router, prefix="/api")
app.include_router(upload.router, prefix="/api")
app.include_router(export.router, prefix="/api")
app.include_router(import_data.router, prefix="/api")