Files
test/app/api/advisor_api.py
T

96 lines
3.8 KiB
Python
Raw Normal View History

2026-09-21 19:03:31 +08:00
"""顾问管理接口(需求第 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
from app.core.response import Resp, ok
from app.dao.advisor_dao import AdvisorDao
from app.model import Advisor
from app.schema.advisor_schema import AdvisorCreate, AdvisorOut, AdvisorUpdate
from app.schema.common import PageResult
router = APIRouter(prefix="/advisors", tags=["2.8 顾问管理(扩展)"])
def _to_out(advisor, count: int | None = None) -> AdvisorOut:
out = AdvisorOut.model_validate(advisor)
if count is not None:
out.student_count = count
return out
@router.get("", response_model=Resp[PageResult[AdvisorOut]], summary="顾问列表")
def list_advisors(
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,
dept: Annotated[str | None, Query()] = None,
gender: Annotated[int | None, Query(ge=1, le=2)] = None,
order_by: Annotated[str, Query()] = "id",
order: Annotated[str, Query(pattern="^(asc|desc)$")] = "desc",
):
stmt = AdvisorDao.build_stmt(keyword=keyword, dept=dept, gender=gender, order_by=order_by, order=order)
items, total, page, pages = AdvisorDao.paginate(db, stmt, page, page_size)
counts = AdvisorDao.student_count_map(db)
return ok({
"total": total, "page": page, "page_size": page_size, "pages": pages,
"items": [_to_out(a, counts.get(a.id, 0)) for a in items],
})
@router.post("", response_model=Resp[AdvisorOut], summary="新增顾问")
def create_advisor(db: DbSession, _: WriteAccount, payload: AdvisorCreate):
advisor_no = (payload.advisor_no or "").strip() or AdvisorDao.next_advisor_no(db)
if AdvisorDao.first_by(db, advisor_no=advisor_no):
raise ConflictError(f"顾问编号 {advisor_no} 已存在")
advisor = Advisor(
advisor_no=advisor_no,
name=payload.name,
gender=payload.gender,
phone=payload.phone,
email=payload.email,
dept=payload.dept,
remark=payload.remark,
)
db.add(advisor)
db.commit()
db.refresh(advisor)
return ok(_to_out(advisor, 0), msg=f"顾问 {advisor.name} 已创建,编号 {advisor.advisor_no}")
@router.get("/{advisor_id}", response_model=Resp[AdvisorOut], summary="顾问详情")
def get_advisor(db: DbSession, _: ReadAccount, advisor_id: int):
advisor = AdvisorDao.get_or_404(db, advisor_id, "顾问")
counts = AdvisorDao.student_count_map(db)
return ok(_to_out(advisor, counts.get(advisor.id, 0)))
@router.put("/{advisor_id}", response_model=Resp[AdvisorOut], summary="更新顾问")
def update_advisor(db: DbSession, _: WriteAccount, advisor_id: int, payload: AdvisorUpdate):
advisor = AdvisorDao.get_or_404(db, advisor_id, "顾问")
for key, value in payload.model_dump(exclude_unset=True).items():
if value is not None:
setattr(advisor, key, value)
db.commit()
db.refresh(advisor)
counts = AdvisorDao.student_count_map(db)
return ok(_to_out(advisor, counts.get(advisor.id, 0)), msg="更新成功")
@router.delete("/{advisor_id}", summary="逻辑删除顾问(名下还有学生时拒绝)")
def delete_advisor(db: DbSession, _: WriteAccount, advisor_id: int):
advisor = AdvisorDao.get_or_404(db, advisor_id, "顾问")
count = AdvisorDao.student_count_map(db).get(advisor_id, 0)
if count:
raise BusinessError(f"顾问「{advisor.name}」名下还有 {count} 名学生,请先转交后再删除")
advisor.soft_delete()
db.commit()
return ok(msg=f"已删除顾问 {advisor.name}")