代码提交
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
"""老师管理接口(需求 2.5)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
|
||||
from app.core.deps import DbSession, ReadAccount, WriteAccount
|
||||
from app.core.exceptions import BusinessError, ConflictError, NotFoundError
|
||||
from app.core.response import ok
|
||||
from app.core.response import Resp
|
||||
from app.core.utils import parse_date
|
||||
from app.dao.clazz_dao import ClazzDao
|
||||
from app.dao.teacher_dao import TeacherDao
|
||||
from app.model import Teacher
|
||||
from app.schema.common import PageResult
|
||||
from app.schema.teacher_schema import TeacherCreate, TeacherOut, TeacherUpdate
|
||||
|
||||
router = APIRouter(prefix="/teachers", tags=["2.5 老师管理"])
|
||||
|
||||
|
||||
@router.get("", response_model=Resp[PageResult[TeacherOut]], summary="老师列表(含带班信息)")
|
||||
def list_teachers(
|
||||
db: DbSession,
|
||||
_: ReadAccount,
|
||||
page: Annotated[int, Query(ge=1)] = 1,
|
||||
page_size: Annotated[int, Query(ge=1, le=200)] = 10,
|
||||
keyword: Annotated[str | None, Query(description="姓名/工号/电话")] = None,
|
||||
subject: Annotated[str | None, Query(description="授课方向")] = None,
|
||||
title: Annotated[str | None, Query(description="职称")] = None,
|
||||
gender: Annotated[int | None, Query(ge=1, le=2)] = None,
|
||||
class_id: Annotated[int | None, Query(description="带某个班的老师")] = None,
|
||||
order_by: Annotated[str, Query()] = "id",
|
||||
order: Annotated[str, Query(pattern="^(asc|desc)$")] = "desc",
|
||||
):
|
||||
stmt = TeacherDao.build_stmt(
|
||||
keyword=keyword, subject=subject, title=title, gender=gender,
|
||||
class_id=class_id, order_by=order_by, order=order,
|
||||
)
|
||||
items, total, page, pages = TeacherDao.paginate(db, stmt, page, page_size)
|
||||
return ok({
|
||||
"total": total, "page": page, "page_size": page_size, "pages": pages,
|
||||
"items": [TeacherOut.model_validate(t) for t in items],
|
||||
})
|
||||
|
||||
|
||||
@router.post("", response_model=Resp[TeacherOut], summary="新增老师")
|
||||
def create_teacher(db: DbSession, _: WriteAccount, payload: TeacherCreate):
|
||||
teacher_no = (payload.teacher_no or "").strip()
|
||||
if not teacher_no:
|
||||
from datetime import date
|
||||
|
||||
year = (parse_date(payload.hire_date, "入职时间") or date.today()).year
|
||||
teacher_no = TeacherDao.next_teacher_no(db, year)
|
||||
if TeacherDao.get_by_no(db, teacher_no, with_deleted=True):
|
||||
raise ConflictError(f"工号 {teacher_no} 已存在")
|
||||
|
||||
teacher = Teacher(
|
||||
teacher_no=teacher_no,
|
||||
name=payload.name,
|
||||
gender=payload.gender,
|
||||
phone=payload.phone,
|
||||
email=payload.email,
|
||||
title=payload.title,
|
||||
subject=payload.subject,
|
||||
hire_date=parse_date(payload.hire_date, "入职时间"),
|
||||
remark=payload.remark,
|
||||
)
|
||||
if payload.class_ids:
|
||||
teacher.classes = _resolve_classes(db, payload.class_ids)
|
||||
|
||||
db.add(teacher)
|
||||
db.commit()
|
||||
db.refresh(teacher)
|
||||
return ok(TeacherOut.model_validate(teacher), msg=f"老师 {teacher.name} 已加入,工号 {teacher.teacher_no}")
|
||||
|
||||
|
||||
@router.get("/{teacher_id}", response_model=Resp[TeacherOut], summary="老师详情")
|
||||
def get_teacher(db: DbSession, _: ReadAccount, teacher_id: int):
|
||||
return ok(TeacherOut.model_validate(TeacherDao.get_or_404(db, teacher_id, "老师")))
|
||||
|
||||
|
||||
@router.put("/{teacher_id}", response_model=Resp[TeacherOut], summary="更新老师(含带班关系)")
|
||||
def update_teacher(db: DbSession, _: WriteAccount, teacher_id: int, payload: TeacherUpdate):
|
||||
teacher = TeacherDao.get_or_404(db, teacher_id, "老师")
|
||||
data = payload.model_dump(exclude_unset=True)
|
||||
if "hire_date" in data:
|
||||
teacher.hire_date = parse_date(data.pop("hire_date"), "入职时间")
|
||||
class_ids = data.pop("class_ids", None)
|
||||
|
||||
for key, value in data.items():
|
||||
if value is not None and hasattr(teacher, key):
|
||||
setattr(teacher, key, value)
|
||||
|
||||
if class_ids is not None:
|
||||
teacher.classes = _resolve_classes(db, class_ids)
|
||||
|
||||
db.commit()
|
||||
db.refresh(teacher)
|
||||
return ok(TeacherOut.model_validate(teacher), msg="更新成功")
|
||||
|
||||
|
||||
@router.delete("/{teacher_id}", summary="逻辑删除老师(仍带班时拒绝)")
|
||||
def delete_teacher(db: DbSession, _: WriteAccount, teacher_id: int):
|
||||
teacher = TeacherDao.get_or_404(db, teacher_id, "老师")
|
||||
if teacher.class_names:
|
||||
raise BusinessError(
|
||||
f"老师「{teacher.name}」还在带 {len(teacher.class_names)} 个班:"
|
||||
f"{'、'.join(teacher.class_names)}。请先解除带班关系"
|
||||
)
|
||||
teacher.soft_delete()
|
||||
db.commit()
|
||||
return ok(msg=f"已删除老师 {teacher.name}")
|
||||
|
||||
|
||||
def _resolve_classes(db, class_ids: list[int]) -> list:
|
||||
all_classes = {c.id: c for c in ClazzDao.all(db, ClazzDao.build_stmt())}
|
||||
missing = [cid for cid in class_ids if cid not in all_classes]
|
||||
if missing:
|
||||
raise NotFoundError(f"班级不存在:{missing}")
|
||||
return [all_classes[cid] for cid in class_ids]
|
||||
Reference in New Issue
Block a user