statistics.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. from datetime import date
  2. from fastapi import APIRouter, Depends, Query
  3. from sqlalchemy import func, select, case
  4. from sqlalchemy.ext.asyncio import AsyncSession
  5. from backend.app.core.dependencies import require_permissions, get_current_user
  6. from backend.app.db.session import get_db
  7. from backend.app.models import ScheduleItem, User
  8. router = APIRouter(prefix="/statistics", tags=["statistics"])
  9. def is_limited_scope(user: User) -> bool:
  10. return user.role and user.role.name not in {"管理员", "排班员"}
  11. @router.get("/schedule", dependencies=[Depends(require_permissions(["statistics.view"]))])
  12. async def get_schedule_stats(
  13. db: AsyncSession = Depends(get_db),
  14. current_user: User = Depends(get_current_user),
  15. dept_id: str | None = Query(default=None),
  16. start: date | None = Query(default=None),
  17. end: date | None = Query(default=None),
  18. ):
  19. conditions = []
  20. if is_limited_scope(current_user):
  21. if not current_user.dept_id:
  22. return {"byDept": {}, "byStaff": {}, "byShift": {}, "total": 0}
  23. conditions.append(ScheduleItem.dept_id == current_user.dept_id)
  24. elif dept_id:
  25. conditions.append(ScheduleItem.dept_id == dept_id)
  26. conditions.append(ScheduleItem.tag != "stopClinic")
  27. if start:
  28. conditions.append(ScheduleItem.date >= start)
  29. if end:
  30. conditions.append(ScheduleItem.date <= end)
  31. total_result = await db.execute(
  32. select(func.count()).select_from(select(ScheduleItem).where(*conditions).subquery())
  33. )
  34. total = total_result.scalar_one() or 0
  35. by_dept = await db.execute(
  36. select(ScheduleItem.dept_id, func.count())
  37. .where(*conditions)
  38. .group_by(ScheduleItem.dept_id)
  39. )
  40. by_shift = await db.execute(
  41. select(ScheduleItem.shift_id, func.count())
  42. .where(*conditions)
  43. .group_by(ScheduleItem.shift_id)
  44. )
  45. by_staff = await db.execute(
  46. select(
  47. ScheduleItem.staff_id,
  48. func.count(case((ScheduleItem.tag == "substitute", 1))).label("substitute"),
  49. func.count(case((ScheduleItem.tag != "substitute", 1))).label("normal"),
  50. )
  51. .where(*conditions)
  52. .group_by(ScheduleItem.staff_id)
  53. )
  54. return {
  55. "byDept": {str(row[0]): row[1] for row in by_dept.fetchall()},
  56. "byShift": {str(row[0]): row[1] for row in by_shift.fetchall()},
  57. "byStaff": {str(row[0]): {"normal": row[2], "substitute": row[1]} for row in by_staff.fetchall()},
  58. "total": total
  59. }