From c392cd90e66ef1093a95d0e065d978e7f325b45a Mon Sep 17 00:00:00 2001 From: pzc Date: Sun, 13 Sep 2026 23:06:01 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=BE=E9=97=AE=E6=A8=A1=E5=9D=97=E5=90=88?= =?UTF-8?q?=E5=B9=B6=EF=BC=8C=E9=83=BD=E5=90=88=E5=AE=8C=E4=BA=86=E5=93=A6?= =?UTF-8?q?=EF=BC=8C=E5=BA=86=E7=A5=9D=E4=B8=80=E4=B8=8B=EF=BC=81=EF=BC=81?= =?UTF-8?q?=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/wl_advisor_api.py | 57 +++++++++++++++++++++++++++++ api/wl_student_api.py | 3 +- dao/wl_advisor_dao.py | 71 +++++++++++++++++++++++++++++++++++++ main.py | 3 +- scheme/wl_advisor_scheme.py | 30 ++++++++++++++++ scheme/wl_score_scheme.py | 11 ------ 6 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 api/wl_advisor_api.py create mode 100644 dao/wl_advisor_dao.py create mode 100644 scheme/wl_advisor_scheme.py diff --git a/api/wl_advisor_api.py b/api/wl_advisor_api.py new file mode 100644 index 0000000..d98734c --- /dev/null +++ b/api/wl_advisor_api.py @@ -0,0 +1,57 @@ +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.orm import Session + +from dao import wl_advisor_dao +from database import get_db +from scheme.wl_advisor_scheme import AdvisorCreate, AdvisorOut, AdvisorUpdate + +router = APIRouter(prefix="/advisor", tags=["顾问管理"]) + + +# 查询顾问列表(支持姓名/手机号/区域/性别模糊筛选 + 分页) +# 注意:本路由必须写在 /{adv_id} 前面,否则 "/advisor/" 会被当成 adv_id 去解析 +@router.get("/", response_model=list[AdvisorOut]) +def query_list( + adv_name: str = Query(None), + adv_phone: str = Query(None), + adv_region: str = Query(None), + adv_gender: str = Query(None), + skip: int = 0, + limit: int = 100, + db: Session = Depends(get_db), +): + return wl_advisor_dao.list_advisors( + db, adv_name, adv_phone, adv_region, adv_gender, skip, limit + ) + + +# 新增顾问 +@router.post("/", response_model=AdvisorOut, status_code=201) +def add_advisor(body: AdvisorCreate, db: Session = Depends(get_db)): + return wl_advisor_dao.create_advisor(db, body) + + +# 根据编号查询单个顾问 +@router.get("/{adv_id}", response_model=AdvisorOut) +def query_one(adv_id: int, db: Session = Depends(get_db)): + advisor = wl_advisor_dao.get_advisor_by_id(db, adv_id) + if not advisor: + raise HTTPException(status_code=404, detail="顾问不存在") + return advisor + + +# 修改顾问 +@router.put("/{adv_id}", response_model=AdvisorOut) +def edit_advisor(adv_id: int, body: AdvisorUpdate, db: Session = Depends(get_db)): + advisor = wl_advisor_dao.update_advisor(db, adv_id, body) + if not advisor: + raise HTTPException(status_code=404, detail="顾问不存在") + return advisor + + +# 逻辑删除顾问 +@router.delete("/{adv_id}") +def remove_advisor(adv_id: int, db: Session = Depends(get_db)): + if not wl_advisor_dao.delete_advisor(db, adv_id): + raise HTTPException(status_code=404, detail="顾问不存在") + return {"code": 200, "msg": "删除成功"} diff --git a/api/wl_student_api.py b/api/wl_student_api.py index 9a2f5cb..572f00d 100644 --- a/api/wl_student_api.py +++ b/api/wl_student_api.py @@ -12,9 +12,10 @@ def list_students( stu_no: str = Query(None), stu_name: str = Query(None), class_id: int = Query(None), + advisor_id: int = Query(None, description="按顾问筛选该顾问名下的学生"), db: Session = Depends(get_db), ): - return wl_student_dao.search_students(db, stu_no, stu_name, class_id) + return wl_student_dao.search_students(db, stu_no, stu_name, class_id, advisor_id) @router.post("", response_model=StudentOut) diff --git a/dao/wl_advisor_dao.py b/dao/wl_advisor_dao.py new file mode 100644 index 0000000..0032933 --- /dev/null +++ b/dao/wl_advisor_dao.py @@ -0,0 +1,71 @@ +from fastapi import HTTPException +from sqlalchemy.orm import Session + +from model.wl_advisor_model import Advisor +from model.wl_student_model import Student +from scheme.wl_advisor_scheme import AdvisorCreate, AdvisorUpdate + + +# ---- 增 ---- +def create_advisor(db: Session, data: AdvisorCreate) -> Advisor: + db_advisor = Advisor(**data.model_dump()) + db.add(db_advisor) + db.commit() + db.refresh(db_advisor) + return db_advisor + + +# ---- 查(多条件 + 分页)---- +def list_advisors(db: Session, adv_name=None, adv_phone=None, adv_region=None, + adv_gender=None, skip: int = 0, limit: int = 100): + q = db.query(Advisor).filter(Advisor.is_deleted == 0) + if adv_name: + q = q.filter(Advisor.adv_name.like(f"%{adv_name}%")) + if adv_phone: + q = q.filter(Advisor.adv_phone.like(f"%{adv_phone}%")) + if adv_region: + q = q.filter(Advisor.adv_region.like(f"%{adv_region}%")) + if adv_gender: + q = q.filter(Advisor.adv_gender == adv_gender) + # 后录进来的顾问排前面,方便前端看到最新数据 + return q.order_by(Advisor.adv_id.desc()).offset(skip).limit(limit).all() + + +# ---- 根据编号查单个顾问 ---- +def get_advisor_by_id(db: Session, adv_id: int) -> Advisor | None: + return db.query(Advisor).filter( + Advisor.adv_id == adv_id, + Advisor.is_deleted == 0, + ).first() + + +# ---- 改 ---- +def update_advisor(db: Session, adv_id: int, data: AdvisorUpdate) -> Advisor | None: + db_advisor = get_advisor_by_id(db, adv_id) + if not db_advisor: + return None + # exclude_unset:前端传了什么就改什么,没传的字段保持原值 + for k, v in data.model_dump(exclude_unset=True).items(): + setattr(db_advisor, k, v) + db.commit() + db.refresh(db_advisor) + return db_advisor + + +# ---- 逻辑删除 ---- +def delete_advisor(db: Session, adv_id: int) -> bool: + db_advisor = get_advisor_by_id(db, adv_id) + if not db_advisor: + return False + + # 名下还挂着学生就不给删,处理方式和班级模块 delete_class 保持一致 + student = db.query(Student).filter( + Student.advisor_id == adv_id, + Student.is_deleted == 0, + ).first() + if student: + raise HTTPException(status_code=400, detail="该顾问名下还有学生,不能删除!") + + db_advisor.is_deleted = 1 + db.commit() + return True diff --git a/main.py b/main.py index 94a271b..09fca56 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,7 @@ #项目初始化入口 from fastapi import FastAPI # 周学灵、熊浩钦、圣国伟、刘盼、彭少、赵康宁的路由 -from api import wl_student_api,wl_class_api,wl_score_api,wl_teacher_api,statistics,wl_emp_api +from api import wl_student_api,wl_class_api,wl_score_api,wl_teacher_api,statistics,wl_emp_api,wl_advisor_api from database import Base, engine # wl_emp_model 必须显式导入:否则 wl_emp 表只能靠 statistics 的传递导入进 metadata, # 一旦统计路由被拿掉,create_all 会漏建 wl_emp,Student.emp 关系随即报错 @@ -17,6 +17,7 @@ app.include_router(wl_score_api.router) app.include_router(wl_teacher_api.router) app.include_router(statistics.router) app.include_router(wl_emp_api.router) +app.include_router(wl_advisor_api.router) if __name__ == "__main__": diff --git a/scheme/wl_advisor_scheme.py b/scheme/wl_advisor_scheme.py new file mode 100644 index 0000000..1f0e020 --- /dev/null +++ b/scheme/wl_advisor_scheme.py @@ -0,0 +1,30 @@ +from pydantic import BaseModel, Field, ConfigDict +from typing import Optional + +# ---------- 请求模型 ---------- +# 顾问模块:入参格式,字段长度跟 model/wl_advisor_model.py 里的列宽保持一致 +class AdvisorCreate(BaseModel): + adv_name: str = Field(..., max_length=50, description="顾问姓名") + adv_gender: Optional[str] = Field(None, max_length=8, description="性别") + adv_phone: Optional[str] = Field(None, max_length=20, description="手机号") + adv_email: Optional[str] = Field(None, max_length=64, description="邮箱") + adv_region: Optional[str] = Field(None, max_length=64, description="负责区域/招生渠道") + +class AdvisorUpdate(BaseModel): + adv_name: Optional[str] = Field(None, max_length=50) + adv_gender: Optional[str] = Field(None, max_length=8) + adv_phone: Optional[str] = Field(None, max_length=20) + adv_email: Optional[str] = Field(None, max_length=64) + adv_region: Optional[str] = Field(None, max_length=64) + +# ---------- 响应模型 ---------- +# is_deleted 是逻辑删除标记,不对外暴露 +class AdvisorOut(BaseModel): + adv_id: int + adv_name: str + adv_gender: Optional[str] = None + adv_phone: Optional[str] = None + adv_email: Optional[str] = None + adv_region: Optional[str] = None + + model_config = ConfigDict(from_attributes=True) diff --git a/scheme/wl_score_scheme.py b/scheme/wl_score_scheme.py index 5dc7146..feafd0a 100644 --- a/scheme/wl_score_scheme.py +++ b/scheme/wl_score_scheme.py @@ -51,14 +51,3 @@ class TeacherResp(TeacherCreate): t_id: int class Config: from_attributes = True - -# ----------------顾问---------------- -class AdvisorCreate(BaseModel): - t_name: str - gender: Optional[str] = None - phone: Optional[str] = None - -class AdvisorResp(AdvisorCreate): - advisor_id: int - class Config: - from_attributes = True \ No newline at end of file