diff --git a/README.md b/README.md index 2eb6a2d..b3ec6ae 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,4 @@ test33 gesd +jhj \ No newline at end of file diff --git a/api/class_api.py b/api/class_api.py index e69de29..d5af66f 100644 --- a/api/class_api.py +++ b/api/class_api.py @@ -0,0 +1,25 @@ +from fastapi import APIRouter,Depends,HTTPException +from schemas.class_schema import * +from core.database import get_db +from dao.class_dao import * + +classes_router = APIRouter() + +@classes_router.post('/',response_model=ClassResponse) +def post_classes(classes:ClassCreate,db=Depends(get_db)): + c = classes.model_dump() + r = post_classes_dao( c , db ) + if r : + return ClassResponse( total=1,data=c ) + else : + raise HTTPException(status_code=500,detail='添加失败,服务器繁忙,请稍后添加!') + + +@classes_router.put('/{id}',response_model=ClassResponse) +def put_classes(id:int,classes:ClassUpdate,db=Depends(get_db)): + updates = classes.model_dump( exclude_unset=True ) + r = update_classes_dao( id , db , updates ) + if r : + return ClassResponse( total=1,data=updates ) + else : + raise HTTPException(status_code=500, detail='更新失败,该班级已不存在或服务器繁忙,请稍后更新!') \ No newline at end of file diff --git a/api/consultant.py b/api/consultant.py index e69de29..2e9a897 100644 --- a/api/consultant.py +++ b/api/consultant.py @@ -0,0 +1,65 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from core.database import get_db +from schemas.consultant import ConsultantCreate, ConsultantUpdate, ConsultantResponse +from dao import consultant as dao_consultant + +router = APIRouter() + + +@router.get( + path="/consultants", + tags=['顾问管理模块'] +) +def read_consultants( + db: Session = Depends(get_db), + page:int=1, + page_size:int=20, + name:str|None=None, + phone:str|None=None +): + return dao_consultant.get_consultant_list(db, page, page_size, name, phone) + + + +@router.post("/consultants", response_model=ConsultantResponse,tags=['顾问管理模块']) +def create_consultant( + obj_in: ConsultantCreate, + db: Session = Depends(get_db) +): + return dao_consultant.create_consultant(db, obj_in) + + +@router.get("/consultants/{id}", response_model=ConsultantResponse,tags=['顾问管理模块']) +def read_consultant(id: int, db: Session = Depends(get_db)): + res = dao_consultant.get_consultant_by_id(db, cid=id) + if res is None: + raise HTTPException(status_code=404, detail="顾问不存在或已删除") + return res + + +@router.put( + path="/consultants/{id}", + response_model=ConsultantResponse, + tags=['顾问管理模块'] +) +def update_consultant( + id: int, + obj_in: ConsultantUpdate, + db: Session = Depends(get_db) +): + # model_dump 排除为None的字段,只更新前端传过来的字段 + update_dict = obj_in.model_dump(exclude_none=True) + res = dao_consultant.update_consultant(db, cid=id, update_data=update_dict) + if res is None: + raise HTTPException(status_code=404, detail="顾问不存在或已删除") + return res + + + +@router.delete("/consultants/{id}", response_model=ConsultantResponse,tags=['顾问管理模块']) +def delete_consultant(id: int, db: Session = Depends(get_db)): + res = dao_consultant.delete_consultant(db, cid=id) + if res is None: + raise HTTPException(status_code=404, detail="顾问不存在或已删除") + return res diff --git a/api/statistics.py b/api/statistics.py index e69de29..082f7f4 100644 --- a/api/statistics.py +++ b/api/statistics.py @@ -0,0 +1,28 @@ +from fastapi import APIRouter,HTTPException,Depends +from DoubaoTeam.core.database import get_db +from DoubaoTeam.models.student import Student +from DoubaoTeam.models.score import Score +from DoubaoTeam.dao.statistics import * +from DoubaoTeam.schemas.common import SuccessResponse +from sqlalchemy.orm import Session + + +statistics_router=APIRouter() + + +@statistics_router.get("/scores/age-over-30",response_model=SuccessResponse) +def api_age_over_thirty(db:Session=Depends(get_db)): + try: + age_over_thirty_list=age_over_thirty(Student,db) + return SuccessResponse(status_code=200,msg=age_over_thirty_list) + except NoExist as e: + raise HTTPException(status_code=404,detail=str(e)) +# 这边功能函数返回的是一个字典 +@statistics_router.get("/classes/gender-count",response_model=SuccessResponse) +def api_gender_count(db:Session=Depends(get_db)): + try: + result = gender_count(Student,db) + return SuccessResponse(status_code=200,msg=result) + except NoExist as e: + raise HTTPException(status_code=404,detail=str(e)) + diff --git a/api/student.py b/api/student.py index e69de29..9299659 100644 --- a/api/student.py +++ b/api/student.py @@ -0,0 +1,14 @@ +from fastapi import APIRouter,Depends,HTTPException +from sqlalchemy.orm import Session +from dao.student import * +from schemas.student import StudentCreate + +student_router = APIRouter() + +@student_router.post("/student") +def create_student(request:StudentCreate,db:Session,create = Depends(create_students)): + d = request.model_dump() + r = create_students(d,db) + if not r: + raise HTTPException(status_code=500,detail='服务器繁忙,请稍后添加!') + return {'code':200,'detail':"添加学生成功"} \ No newline at end of file diff --git a/core/database.py b/core/database.py index ae95c99..cb3bab3 100644 --- a/core/database.py +++ b/core/database.py @@ -17,7 +17,7 @@ IP:localhost from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker,declarative_base -from .config import ( +from core.config import ( database_connect_user, database_connect_password, database_connect_ip, diff --git a/dao/class_dao.py b/dao/class_dao.py index e69de29..e1f93a0 100644 --- a/dao/class_dao.py +++ b/dao/class_dao.py @@ -0,0 +1,29 @@ +from models.class_model import Classes + + +def post_classes_dao( c , db ): + try : + cc = Classes( **c ) + db.add( cc ) + except : + db.rollback() + return False + else : + db.commit() + return True + +def update_classes_dao( class_id , db , updates ): + try : + db.query( Classes ).filter( Classes.id==class_id , Classes.is_deleted==0 ) .update( updates ) + except : + db.rollback() + return False + else : + db.commit() + return True + +# def get_classes_dao( class_id , class_name , db ): +# try : +# r = db.query( Classes ).filter( Classes.id==class_id , Classes.is_deleted==0 ) .all() +# except : +# db.rollback() \ No newline at end of file diff --git a/dao/consultant.py b/dao/consultant.py index e69de29..2306990 100644 --- a/dao/consultant.py +++ b/dao/consultant.py @@ -0,0 +1,82 @@ +from models.consultant import Consultant +from schemas.consultant import ConsultantCreate, ConsultantUpdate +from sqlalchemy.orm import Session +def create_consultant(db:Session, obj_in: ConsultantCreate): + try: + db_obj = Consultant(**obj_in.model_dump()) + db.add(db_obj) + except: + db.rollback() + return False + else: + db.commit() + return db_obj +# 查询全部可分页(过滤已删除) +def get_consultant_list( + db: Session, + page: int = 1, + page_size: int = 20, + name: str | None = None, + phone: str | None = None +): + page_size = max(1, min(page_size, 100)) + skip = (page - 1) * page_size + + query = db.query(Consultant).filter(Consultant.is_deleted == 0) + # 有传参数才追加过滤条件 + if name: + query = query.filter(Consultant.consultant_name.like(f"%{name}%")) + if phone: + query = query.filter(Consultant.phone.like(f"%{phone}%")) + + total = query.count() + items = query.offset(skip).limit(page_size).all() + + return { + "items": items, + "total": total, + "page": page, + "page_size": page_size + } + + + +# 根据id查单条 +def get_consultant_by_id(db:Session, cid: int): + return db.query(Consultant).filter(Consultant.id == cid, Consultant.is_deleted == 0 ).first() + + +# 修改 +def update_consultant(db: Session, cid: int, update_data: dict): + try: + db_obj = db.query(Consultant).filter( + Consultant.id == cid, + Consultant.is_deleted == 0 + ).first() + if not db_obj: + return None + # 循环赋值 + for k, v in update_data.items(): + setattr(db_obj, k, v) + db.commit() + db.refresh(db_obj) # 刷新从数据库拿最新数据(updated_at) + return db_obj + except Exception: + db.rollback() + return None + + + +# 逻辑删除(只改is_deleted=1) +def delete_consultant(db:Session, cid: int): + try: + db_obj = get_consultant_by_id(db, cid) + if not db_obj: + return None + db_obj.is_deleted = 1 + except: + db.rollback() + return None + else: + db.commit() + return db_obj diff --git a/dao/statistics.py b/dao/statistics.py index e69de29..6b7c9e8 100644 --- a/dao/statistics.py +++ b/dao/statistics.py @@ -0,0 +1,30 @@ +from DoubaoTeam.schemas.statistics import NoExist +from sqlalchemy import func + +#`GET /statistics/students/age-over-30` +#查询所有超过 30 岁的学员信息 + +def age_over_thirty(Student,db): + student_list=db.query(Student).filter(Student.age>=30).all() + if not student_list : + raise NoExist('无目标年龄大于30岁') + return student_list + +def gender_count(Student,db): + result=db.query(Student.class_id,Student.gender,func.count(Student.id)).group_by(Student.class_id,Student.gender).all() + if not result : + raise NoExist('班里招点人吧,要倒闭了') + dict1 = {} + for i,j,k in result: + if i not in dict1 : + dict1[i]={'男':0,'女':0} + dict[i][j]+=k + return dict1 +#查询每次考试成绩都在 80 分以上的学生编号、姓名和成绩 +def score_over_eighty(Score,Student,db): + student_list=db.query(Student.id, Student.student_name)\ + .join(Student,Score.student_id==Student.id).group_by(Student.student_no,Student.student_name)\ + .having(func.min(Score.score)>80).all() + for i in student_list: + student_list2 = db.query(Student.student_no, Student.student_name,Score.score).join(Student,Score.student_id==Student.id).filter(i.id==Student.id).all() + diff --git a/dao/student.py b/dao/student.py index e69de29..9014fe5 100644 --- a/dao/student.py +++ b/dao/student.py @@ -0,0 +1,16 @@ +from schemas.student import * +from core.database import get_db +from models.student import * +from utils.code_generator import generate_no + + +def create_students(d,db): + stu = Student(**d) + try: + db.add(stu) + generate_no("student_no", "STU", stu, db) + db.commit() + except:return False + else: + return True + diff --git a/database.py b/database.py index e69de29..ecff48f 100644 --- a/database.py +++ b/database.py @@ -0,0 +1,13 @@ +from models import * +from core.database import * +from sqlalchemy import create_engine + + +db_url = "mysql+pymysql://root:123456@127.0.0.1:3306/student_manager?charset=utf8mb4" +engine = create_engine(db_url + ,pool_size=50 + ,echo=True) + +Base.metadata.create_all(engine) +db = SessionLocal() +db.commit() \ No newline at end of file diff --git a/main.py b/main.py index fe32962..e136053 100644 --- a/main.py +++ b/main.py @@ -1,20 +1,26 @@ from fastapi import FastAPI from api.example import example_router from middleware import log_middleware +from api.statistics import statistics_router import uvicorn +from api.student import student_router app = FastAPI() app.middleware("http")(log_middleware) #-------------朱婷婷-------------- +from api.class_api import classes_router +app.include_router(classes_router,tags=['班级接口'],prefix="/classes") #-------------李玉杰-------------- - +app.include_router(student_router) #-------------张义--------------- #-------------薄鑫--------------- - +from api import consultant +app.include_router(consultant.router) #-------------张昕浩--------------- app.include_router(example_router) +app.include_router(statistics_router, prefix="/statistics") #-------------曾凯--------------- diff --git a/models/__init__.py b/models/__init__.py index 8b13789..10d5f19 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1 +1,7 @@ - +from .class_model import Classes +from .class_teacher import ClassTeachers +from .consultant import Consultant +from .employment import Employment +from .score import Score +from .student import Student +from .teacher import Teacher diff --git a/models/class_model.py b/models/class_model.py index efd97c7..35c1c9f 100644 --- a/models/class_model.py +++ b/models/class_model.py @@ -1,10 +1,9 @@ -from sqlalchemy import * -from datetime import datetime,time,date - -Base = declarative_base() # 执行函数,返回一个基类 +from sqlalchemy import * # 导入sqlalchemy模块下所有的类,以便下面创建班级表模型和声明表的字段 +from datetime import datetime # 导入datetime模块下的datetime类 +from core.database import Base # 从core软件包下的database模块导入Base class Classes( Base ): # 在python里的名字 - __tablename__ = 'classes' # 在数据库中表的名字 + __tablename__ = 'class_info_detail' # 在数据库中表的名字 #————创建班级"主键"的字段名———— id = Column( Integer # 声明字段的数据类型是"整数" , primary_key = True # 声明是"主键" diff --git a/models/class_teacher.py b/models/class_teacher.py index db97dfd..37d1e35 100644 --- a/models/class_teacher.py +++ b/models/class_teacher.py @@ -6,24 +6,25 @@ from sqlalchemy.dialects.mssql import TINYINT from core.database import Base from datetime import datetime + #班级老师关系表 class_teachers_info_detail 中间表 ORM 模型完整定义 #字段包括id,class_id,teacher_id,role,created_at,updated_at,is_deleted class ClassTeachers(Base): __tablename__ = "class_teachers_info_detail" id = Column( - BigInteger + Integer ,primary_key=True ,autoincrement=True ,comment='关系') class_id = Column( - BigInteger - ,ForeignKey('class_id') + Integer + ,ForeignKey('class_info_detail.id') ,nullable=False ,comment='关联 class_id' ) teacher_id = Column( - BigInteger - ,ForeignKey('teacher_id') + Integer + ,ForeignKey("teacher_info_detail.id") ,nullable=False ,comment='关联 teacher_id' ) diff --git a/models/consultant.py b/models/consultant.py index e69de29..5f6f4ed 100644 --- a/models/consultant.py +++ b/models/consultant.py @@ -0,0 +1,17 @@ + +from sqlalchemy import * +from datetime import datetime +from core.database import Base +class Consultant(Base): + __tablename__ = "consultant_info_detail" + + id = Column(Integer, primary_key=True, autoincrement=True, comment="主键") + consultant_no = Column(String(50), unique=True, nullable=False, comment="顾问编号") + consultant_name = Column(String(50), nullable=False, comment="顾问姓名") + phone = Column(String(11), nullable=True, comment="联系电话") + email = Column(String(100), nullable=True, comment="邮箱") + remark = Column(String(255), nullable=True, comment="备注") + + created_at = Column(DateTime, default=datetime.now, nullable=False, comment="创建时间") + updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False, comment="更新时间") + is_deleted = Column(Integer, default=0, nullable=False, comment="逻辑删除:0正常,1删除") diff --git a/models/employment.py b/models/employment.py index e69de29..76a1f7c 100644 --- a/models/employment.py +++ b/models/employment.py @@ -0,0 +1,40 @@ +from sqlalchemy import * +from sqlalchemy.orm import declarative_base,sessionmaker +from datetime import datetime +from core.database import Base, engine + +# 为了符合三范式,就业表只保存 student_id,不保存学生姓名和班级名称。查询返回时通过关联 students 和 classes 带出学生姓名、班级名称。 + +class Employment(Base): + __tablename__ = "employment_info_detail" # 实际数据库的表名字 + id = Column(Integer, primary_key=True # 主键 + , autoincrement=True # 声明是自增主键 + ,comment="就业信息主键id") + student_id = Column(Integer, ForeignKey("student_info_detail.id") # 外键 -> students.id + , nullable=False # 不允许为null + ,comment="关联学生id") + employment_status = Column(String(50) + , default="not_started" + ,comment="就业状态:not_started/job_hunting/offered/employed") + employment_open_date = Column(Date + ,comment="就业开放时间") + offer_date = Column(Date + ,comment="offer下发时间") + company_name = Column(String(100) + , comment="就业公司名称") + salary = Column(DECIMAL(10, 2) # 高精度浮点数薪资,最多 10 位数字,小数占 2 位 + , comment="就业薪资") + remark = Column(String(255) + ,default=None + , comment="备注信息") + created_at = Column(DateTime + , default=datetime.now + , comment="创建时间") + updated_at = Column(DateTime + , default=datetime.now + , onupdate=datetime.now + ,comment="更新时间") + is_deleted = Column(SmallInteger + ,default=0 + ,comment="逻辑删除:0正常,1删除") + diff --git a/models/score.py b/models/score.py index e69de29..90e9682 100644 --- a/models/score.py +++ b/models/score.py @@ -0,0 +1,46 @@ +from sqlalchemy import * +from core.database import Base +from datetime import datetime,date +class Score(Base): + __tablename__ = "score_info_detail" + id = Column(Integer + , primary_key=True + ,autoincrement=True + ,comment="成绩表序号,自增主键" + ) + student_id = Column(Integer + ,ForeignKey("student_info_detail.id") + ,nullable=False + ,comment="学生学号" + ) + score = Column(DECIMAL(4,1) + ,CheckConstraint("score between 0 and 100") + ) + exam_seq= Column(Integer + , nullable=False + ,comment="考试序次") + __table_args__ = ( + UniqueConstraint("student_id" + , "exam_seq" + , name="uk_student_exam_seq"), + )#学生id和学生姓名应为联合唯一,即一个学生一次考试只能有一个分数 + exam_date=Column(Date + ,default=date.today + ,comment="考试日期" + ) + created_at=Column(DateTime + ,default=datetime.now + ,comment="创建时间" + ) + updated_at=Column(DateTime + ,default=datetime.now + ,onupdate=datetime.now + ,comment="更新时间" + ) + remark=Column(String(225) + ,comment="备注说明" + ) + is_deleted=Column(Integer + ,default=0 + ,comment="逻辑删除标记:未删除0,已删除1" + ) \ No newline at end of file diff --git a/models/student.py b/models/student.py index e69de29..ab805e4 100644 --- a/models/student.py +++ b/models/student.py @@ -0,0 +1,45 @@ +from core.database import Base +from sqlalchemy import * +from datetime import datetime + +class Student(Base): + __tablename__ = 'student_info_detail' + id = Column(Integer + , primary_key=True + , autoincrement=True + , comment = '学生编号,自增主键' + ) + student_no = Column(VARCHAR(50) + , unique = True) + student_name = Column(VARCHAR(50)) + class_id = Column(Integer + ,ForeignKey('class_info_detail.id') + )#外键约束 班级表的主键id,存在这个班级,学生表才可以输入 + consultant_id = Column(Integer + , ForeignKey('consultant_info_detail.id') + ,nullable = True + )#外键约束 顾问表的主键id,存在这个顾问,才可以写入,允许为空 + native_place = Column(VARCHAR(100) + ,nullable = True) + graduation_school = Column(VARCHAR(100) + ,nullable = True) + major = Column(VARCHAR(100) + ,nullable = True) + enrollment_date = Column(Date + ,nullable = False)#入学时间必填 + graduation_date = Column(Date, + nullable = True)#毕业时间可以为空 + education = Column(VARCHAR(50) + ,nullable = True)#学历可以为空 + age = Column(Integer + ,nullable = False) + gender = Column(VARCHAR(10) + ,nullable = False) + create_at = Column(DATETIME + ,default = datetime.now) + update_at = Column(DATETIME + ,default = datetime.now + ,onupdate = datetime.now) + is_deleted = Column(Integer + ,default = 0)#逻辑删除,默认为0,为1则表示已删除 + diff --git a/models/teacher.py b/models/teacher.py index bcfe6a0..9c8c6bf 100644 --- a/models/teacher.py +++ b/models/teacher.py @@ -1,16 +1,14 @@ -from sqlalchemy import (Column, Integer, String, Text, - BigInteger,ForeignKey,VARCHAR,DateTime +from sqlalchemy import (Column, Integer,VARCHAR,DateTime ) from sqlalchemy.dialects.mssql import TINYINT from core.database import Base from datetime import datetime - # teachers 老师表 ORM 模型 #字段包括id,teacher_no,teacher_name,phone,email,remark,created_at,updated_at,is_deleted class Teacher(Base): - __tablename__ = 'teachers_info_detail' + __tablename__ = 'teacher_info_detail' id = Column( Integer , primary_key=True @@ -62,4 +60,3 @@ class Teacher(Base): ) - diff --git a/schemas/class_schema.py b/schemas/class_schema.py index e69de29..05029b6 100644 --- a/schemas/class_schema.py +++ b/schemas/class_schema.py @@ -0,0 +1,24 @@ +from pydantic import BaseModel # 导入pydantic模块下的BaseModel类 +from datetime import datetime,date # 导入datetime模块下的datetime,date类 + + +# ————定义"创建班级"的"请求体"模型———— +class ClassCreate( BaseModel ): + class_no: str + class_name: str + +# ————定义"更新班级"的"请求体"模型———— +class ClassUpdate( BaseModel ): + class_no: str|None = None + class_name: str|None = None + start_date : date|None = None + end_date : date|None = None + created_at : datetime|None = None + update_at : datetime|None = None + +# ————定义"班级"的"响应体"模型———— +class ClassResponse( BaseModel ): + code:int = 200 + detail:str = '执行成功' + total:int = 0 + data:str|dict|list|tuple diff --git a/schemas/consultant.py b/schemas/consultant.py index e69de29..dc29e62 100644 --- a/schemas/consultant.py +++ b/schemas/consultant.py @@ -0,0 +1,33 @@ +from pydantic import BaseModel +from datetime import datetime +from typing import Optional + + +# 新增顾问 请求体 +class ConsultantCreate(BaseModel): + consultant_no: str + consultant_name: str + phone: Optional[str] = None + email: Optional[str] = None + remark: Optional[str] = None + + +# 修改顾问 请求体 +class ConsultantUpdate(BaseModel): + consultant_name: Optional[str] = None + phone: Optional[str] = None + email: Optional[str] = None + remark: Optional[str] = None + + +# 返回模型 +class ConsultantResponse(BaseModel): + id: int + consultant_no: str + consultant_name: str + phone: Optional[str] + email: Optional[str] + remark: Optional[str] + created_at: datetime + updated_at: datetime + diff --git a/schemas/statistics.py b/schemas/statistics.py index e69de29..cdd636f 100644 --- a/schemas/statistics.py +++ b/schemas/statistics.py @@ -0,0 +1,4 @@ +class NoExist(Exception): + def __init__(self,value): + self.value = value + pass \ No newline at end of file diff --git a/schemas/student.py b/schemas/student.py index e69de29..d4a044a 100644 --- a/schemas/student.py +++ b/schemas/student.py @@ -0,0 +1,18 @@ +from pydantic import BaseModel +from datetime import date + +class StudentCreate(BaseModel): + student_no: str|None = None + student_name: str + class_id: int + consultant_id: int | None = None + native_place: str | None = None + graduate_school: str | None = None + major: str | None = None + enrollment_date: date + graduation_date: date | None = None + education: str | None = None + age: int + gender: str + +