from __future__ import annotations from pathlib import Path from fastapi import APIRouter, Depends, HTTPException, Query from ..auth import verify_admin_auth from ..config import ( ACCOUNTS_PATH, ADMIN_TASKS_PATH, CLASSNOTES_PATH, COURSE_SUMMARIES_ROOT, OPERATION_LOGS_PATH, write_lock, ) from ..data import ( append_operation_log, approve_admin_task, delete_course_summary, list_admin_tasks, list_operation_logs, migrate_operation_log_labels, query_course_summaries, reject_admin_task, rollback_operation_log, update_course_summary_time, ) router = APIRouter() def rollback_backup_paths() -> list[Path]: return [ CLASSNOTES_PATH, ACCOUNTS_PATH, ADMIN_TASKS_PATH, COURSE_SUMMARIES_ROOT, OPERATION_LOGS_PATH, ] @router.get("/api/admin/tasks") def admin_tasks( status_filter: str = Query("", alias="status"), task_type: str = Query("", alias="type"), _user: str = Depends(verify_admin_auth), ): try: return list_admin_tasks(ADMIN_TASKS_PATH, status_filter=status_filter, task_type=task_type) except ValueError as exc: raise HTTPException(status_code=500, detail=str(exc)) from exc @router.get("/api/admin/operation-logs") def admin_operation_logs( limit: int = Query(100, ge=1, le=500), operation: str = Query(""), status_filter: str = Query("", alias="status"), student: str = Query(""), _user: str = Depends(verify_admin_auth), ): migrate_operation_log_labels(OPERATION_LOGS_PATH) return list_operation_logs( OPERATION_LOGS_PATH, limit=limit, operation=operation, status_filter=status_filter, student=student, backup_paths=rollback_backup_paths(), ) @router.post("/api/admin/operation-logs/{log_id}/rollback") def admin_rollback_operation_log(log_id: str, _user: str = Depends(verify_admin_auth)): try: with write_lock: result = rollback_operation_log(OPERATION_LOGS_PATH, log_id, rollback_backup_paths()) append_operation_log( OPERATION_LOGS_PATH, "撤回操作", "已撤回", target_log_id=log_id, target_backup_id=str(result.get("target_backup_id") or ""), backup_id=str(result.get("backup_id") or ""), restored_files=result.get("restored_files") or [], ) except ValueError as exc: raise HTTPException(status_code=409, detail=str(exc)) from exc return {"ok": True, **result} @router.get("/api/admin/course-summaries") def admin_course_summaries( q: str = Query(""), student: str = Query(""), teacher: str = Query(""), subject: str = Query(""), date_from: str = Query(""), date_to: str = Query(""), missing_time: bool = Query(False), limit: int = Query(200, ge=1, le=1000), _user: str = Depends(verify_admin_auth), ): try: return query_course_summaries( COURSE_SUMMARIES_ROOT, q=q, student=student, teacher=teacher, subject=subject, date_from=date_from, date_to=date_to, missing_time=missing_time, limit=limit, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc @router.post("/api/admin/tasks/{task_id}/approve") def admin_approve_task(task_id: int, _user: str = Depends(verify_admin_auth)): try: with write_lock: result = approve_admin_task(ADMIN_TASKS_PATH, CLASSNOTES_PATH, ACCOUNTS_PATH, task_id) task = result.get("task", {}) append_operation_log( OPERATION_LOGS_PATH, "审核批准", "已批准", task_id=task_id, task_type=str(task.get("type") or ""), student=str(task.get("student") or task.get("corrected", {}).get("student") or ""), source_id=str(task.get("source_id") or ""), backup_id=str(result.get("backup_id") or task.get("backup_id") or ""), ) except ValueError as exc: raise HTTPException(status_code=409, detail=str(exc)) from exc return {"ok": True, **result} @router.post("/api/admin/tasks/{task_id}/reject") def admin_reject_task(task_id: int, _user: str = Depends(verify_admin_auth)): try: with write_lock: task = reject_admin_task(ADMIN_TASKS_PATH, task_id) append_operation_log( OPERATION_LOGS_PATH, "审核驳回", "已驳回", task_id=task_id, task_type=str(task.get("type") or ""), student=str(task.get("student") or task.get("corrected", {}).get("student") or ""), source_id=str(task.get("source_id") or ""), ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return {"ok": True, "task": task} @router.post("/api/admin/course-summaries/{summary_id}/time") def admin_update_course_summary_time(summary_id: str, payload: dict, _user: str = Depends(verify_admin_auth)): try: with write_lock: result = update_course_summary_time(COURSE_SUMMARIES_ROOT, summary_id, str(payload.get("time_range") or "")) append_operation_log( OPERATION_LOGS_PATH, "课程小结补齐时间", "已更新", summary_id=summary_id, time_range=str(payload.get("time_range") or ""), backup_id=str(result.get("backup_id") or ""), ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return {"ok": True, **result} @router.delete("/api/admin/course-summaries/{summary_id}") def admin_delete_course_summary(summary_id: str, _user: str = Depends(verify_admin_auth)): try: with write_lock: result = delete_course_summary(COURSE_SUMMARIES_ROOT, summary_id) append_operation_log( OPERATION_LOGS_PATH, "课程小结删除", "已删除", summary_id=summary_id, backup_id=str(result.get("backup_id") or ""), ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return {"ok": True, **result}