from __future__ import annotations import json from pathlib import Path from fastapi import HTTPException, Request from pydantic import ValidationError from .config import ACCOUNTS_PATH, CLASSNOTES_PATH, TEACHERS_PATH from .data import Account, Payment, Teacher, read_accounts, read_classnotes, read_teachers from .schemas import AccountPayload, RegisterLinesPayload, TeacherPayload async def read_register_payload(request: Request) -> RegisterLinesPayload: body = await request.body() if not body.strip(): return RegisterLinesPayload() content_type = request.headers.get("content-type", "").lower() if "application/json" in content_type: try: data = json.loads(body) if isinstance(data, str): return RegisterLinesPayload(line=data) if isinstance(data, list): return RegisterLinesPayload(lines=data) if isinstance(data, dict): return RegisterLinesPayload(**data) except (json.JSONDecodeError, ValidationError) as exc: raise ValueError("登记 JSON 格式错误") from exc raise ValueError("登记 JSON 必须是字符串、字符串数组,或包含 line/lines 的对象") try: text = body.decode("utf-8") except UnicodeDecodeError as exc: raise ValueError("登记内容必须使用 UTF-8 编码") from exc return RegisterLinesPayload(lines=text.splitlines()) def load_records(): if not CLASSNOTES_PATH.exists(): raise HTTPException(status_code=503, detail=f"课程记录文件不存在: {CLASSNOTES_PATH}") return read_classnotes(CLASSNOTES_PATH) def load_accounts(): if not ACCOUNTS_PATH.exists(): raise HTTPException(status_code=503, detail=f"课时账户文件不存在: {ACCOUNTS_PATH}") return read_accounts(ACCOUNTS_PATH) def load_teachers(): if not TEACHERS_PATH.exists(): return [] return read_teachers(TEACHERS_PATH) def file_meta(path: Path) -> dict: if not path.exists(): return {"exists": False, "path": str(path)} stat = path.stat() return { "exists": True, "path": str(path), "size": stat.st_size, "mtime": stat.st_mtime, } def payload_to_account(payload: AccountPayload, student_id: str | None = None) -> Account: return Account( student_id=student_id if student_id is not None else payload.student_id, student=payload.student, payments=[Payment(date=item.date, hours=item.hours) for item in payload.payments], remaining=payload.remaining, account_status=payload.account_status, note=payload.note, ) def payload_to_teacher(payload: TeacherPayload, teacher_id: str | None = None) -> Teacher: return Teacher( teacher_id=teacher_id if teacher_id is not None else payload.teacher_id, name=payload.name, alias=payload.alias, subjects=payload.subjects, status=payload.status, note=payload.note, )