Files
stu_teacher/api/students.py
T

147 lines
6.1 KiB
Python
Raw Normal View History

2026-09-21 23:17:51 +08:00
# api/students.py
# 学生模块接口:增、查(多条件分页)、改、逻辑删除
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.deps import get_current_user, require_roles, get_teacher_class_ids
from dao.students_dao import StudentDAO
from database import get_db
from model.user import User
from scheme.students import StudentAdd, StudentUpdate, StudentResponse, StudentListResponse
router = APIRouter()
def _can_read_student(user: User, student) -> bool:
"""学生角色只能读取自己的信息"""
if user.role == "student":
return user.stu_id == student.stu_id
return True
def _can_manage_student(db: Session, user: User, student) -> bool:
"""教师角色只能管理自己所带班级的学生;管理员不受限"""
if user.role == "admin":
return True
if user.role == "teacher":
return student.class_id in get_teacher_class_ids(db, user.teacher_id)
return False
# ---------- 创建新学生 ----------
@router.post("/add", response_model=StudentResponse, summary="创建学生(学号可自动生成)")
async def add_student(
student: StudentAdd,
db: Session = Depends(get_db),
current_user: User = Depends(require_roles("admin", "teacher")),
):
# 外键校验:班级 / 顾问
if not StudentDAO.inspect_class_id_unq(db, student.class_id):
raise HTTPException(status_code=404, detail="班级ID不存在,或已被软删除")
if not StudentDAO.inspect_advisor_id_unq(db, student.advisor_id):
raise HTTPException(status_code=404, detail="顾问ID不存在,或已被软删除")
# 教师只能给自己班上的学生建档
if current_user.role == "teacher" and student.class_id not in get_teacher_class_ids(
db, current_user.teacher_id
):
raise HTTPException(status_code=403, detail="教师只能管理自己所带班级的学生")
# 主键校验(显式传入学号时)
if student.stu_id is not None:
existing = StudentDAO.inspect_student_id_unq(db, student.stu_id)
if existing:
raise HTTPException(status_code=409, detail="学号已存在(包括软删除记录)")
return StudentDAO.add_student(db, student)
# ---------- 查询学生列表(多条件 + 分页) ----------
@router.get("/total_query", response_model=StudentListResponse, summary="查询学生列表(多条件筛选+分页)")
async def get_students(
skip: int = Query(0, ge=0, description="跳过的记录数"),
limit: int = Query(100, ge=1, le=200, description="返回的最大记录数"),
stu_id: Optional[int] = Query(None, description="按学号精确查询"),
stu_name: Optional[str] = Query(None, description="按姓名模糊查询"),
class_id: Optional[int] = Query(None, description="按班级ID筛选"),
gender: Optional[str] = Query(None, description="按性别筛选:男/女"),
status: Optional[str] = Query(None, description="按状态筛选:在读/进入就业/已就业"),
education: Optional[str] = Query(None, description="按学历筛选"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
total, items = StudentDAO.get_all(
db,
skip=skip,
limit=limit,
stu_id=stu_id,
stu_name=stu_name,
class_id=class_id,
gender=gender,
status=status,
education=education,
)
# 学生角色只能看到自己
if current_user.role == "student":
items = [s for s in items if s.stu_id == current_user.stu_id]
total = len(items)
return StudentListResponse(
total=total,
items=[StudentResponse.model_validate(s) for s in items],
)
# ---------- 根据 ID 查询单个学生 ----------
@router.get("/single_query", response_model=StudentResponse, summary="根据学号查询学生")
async def get_student(
stu_id: int = Query(..., ge=1, description="学号"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
student = StudentDAO.get_active(db, stu_id)
if student is None:
raise HTTPException(status_code=404, detail="学生不存在,或已被软删除")
if not _can_read_student(current_user, student):
raise HTTPException(status_code=403, detail="学生角色只能查看自己的信息")
resp = StudentResponse.model_validate(student)
# 联表补充班级名与顾问名
if student.classes:
resp.class_name = student.classes.class_name
if student.advisor:
resp.advisor_name = student.advisor.advisor_name
return resp
# ---------- 更新学生信息 ----------
@router.put("/update", response_model=StudentResponse, summary="更新学生信息")
async def update_student(
student_data: StudentUpdate,
stu_id: int = Query(..., ge=1, description="要更新的学号"),
db: Session = Depends(get_db),
current_user: User = Depends(require_roles("admin", "teacher")),
):
student = StudentDAO.get_active(db, stu_id)
if student is None:
raise HTTPException(status_code=404, detail="学生不存在,或已被软删除")
if not _can_manage_student(db, current_user, student):
raise HTTPException(status_code=403, detail="教师只能管理自己所带班级的学生")
updated = StudentDAO.update(db, stu_id, student_data)
return StudentResponse.model_validate(updated)
# ---------- 逻辑删除学生 ----------
@router.delete("/delete", status_code=204, summary="逻辑删除学生")
async def delete_student(
stu_id: int = Query(..., ge=1, description="要删除的学号"),
db: Session = Depends(get_db),
current_user: User = Depends(require_roles("admin", "teacher")),
):
student = StudentDAO.get_active(db, stu_id)
if student is None:
raise HTTPException(status_code=404, detail="学生不存在,或已被软删除")
if not _can_manage_student(db, current_user, student):
raise HTTPException(status_code=403, detail="教师只能管理自己所带班级的学生")
success = StudentDAO.delete_light(db, stu_id)
if not success:
raise HTTPException(status_code=404, detail="学生不存在")
return None