Files

429 lines
14 KiB
Python
Raw Permalink Normal View History

from sqlalchemy import func
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from datetime import date
from model import Student, Classes, Score, Employment
from schema.student_schema import AgeQuery
class BusinessException(Exception):
"""业务异常类,DAO层捕获数据库异常后抛出,由API层处理"""
pass
class DuplicateStudentNo(BusinessException):
"""学号已存在。继承 BusinessException,API 层可以单独挑出来返回 409。"""
pass
#=============把一个学生对象变成字典形式================
def student_dict(student):
if student is None:
return {}
return {"s_id": student.sid,
"student_no": student.student_no,
"student_name":student.student_name,
"class_id":student.class_id,
"flag":student.flag,
"advisor_id":student.advisor_id,
"native_place":student.native_place,
"school":student.school,
"major":student.major,
"enrollment_time":student.enrollment_time,
"graduation_time":student.graduation_time,
"education":student.education,
"age":student.age,
"gender":student.gender,
"state":student.state}
def create_student(db: Session,
student_no: str,
student_name: str,
class_id: int = None,
flag: int = 1,
advisor_id: int = None,
native_place: str = None,
school: str = None,
major: str = None,
enrollment_time: date = None,
graduation_time: date = None,
education: str = None,
age: int = None,
gender: str = None,
state: str = None
):
"""
创建学生
"""
try:
# 学号是唯一约束,先查一下,给出比 IntegrityError 更好懂的提示
existing = db.query(Student).filter(Student.student_no == student_no).first()
if existing is not None:
raise DuplicateStudentNo(f"学号 {student_no} 已存在")
new_student = Student(
student_no=student_no,
student_name=student_name,
class_id=class_id,
flag=flag,
advisor_id=advisor_id,
native_place=native_place,
school=school,
major=major,
enrollment_time=enrollment_time,
graduation_time=graduation_time,
education=education,
age=age,
gender=gender,
state=state,
)
db.add(new_student)
db.commit()
db.refresh(new_student)
return new_student
except SQLAlchemyError as e:
db.rollback()
raise BusinessException(f"创建学生失败: {str(e)}")
#================查询学生==================
def search_student(db: Session, student_no: str,
student_name: str,
class_id: int = None,
page: int = 1,
size:int=5):
try:
query = db.query(Student).filter(Student.flag == 1)
if student_no is not None:
query = query.filter(Student.student_no == student_no)
if student_name:
query = query.filter(Student.student_name.like(f"%{student_name}%"))
if class_id is not None:
query = query.filter(Student.class_id == class_id)
total = query.count()
offset = (page - 1) * size
students = query.order_by(Student.sid.desc()).offset(offset).limit(size).all()
items = [student_dict(s) for s in students]
pages = (total + size - 1) // size if size > 0 else 0
return {
"items": items,
"total": total,
"page": page,
"page_size": size,
"pages": pages,
}
except SQLAlchemyError as e:
raise BusinessException(f"查询学生列表失败: {str(e)}")
#=================ID查询学生详情====================
def get_student(db: Session, sid: int):
"""
根据ID查询学生,自动过滤逻辑删除
"""
try:
student = db.query(Student).filter(
Student.sid == sid,
Student.flag == 1
).first()
return student
except SQLAlchemyError as e:
raise BusinessException(f"查询学生失败: {str(e)}")
#===============更新学生信息================
def update_student(db: Session, sid: int,
student_no: str = None,
student_name: str = None,
class_id: int = None,
flag: int = None,
advisor_id: int = None,
native_place: str = None,
school: str = None,
major: str = None,
enrollment_time: date = None,
graduation_time: date = None,
education: str = None,
age: int = None,
gender: str = None,
state: str = None):
"""
更新学生信息:只改传了值的字段,没传(None)的保持原样。
"""
try:
student = db.query(Student).filter(Student.sid == sid).first()
if student is None:
return None
# 判断的是"传进来的参数",是否是None
if student_no is not None:
student.student_no = student_no
if student_name is not None:
student.student_name = student_name
if class_id is not None:
student.class_id = class_id
if flag is not None:
student.flag = flag
if advisor_id is not None:
student.advisor_id = advisor_id
if native_place is not None:
student.native_place = native_place
if school is not None:
student.school = school
if major is not None:
student.major = major
if enrollment_time is not None:
student.enrollment_time = enrollment_time
if graduation_time is not None:
student.graduation_time = graduation_time
if education is not None:
student.education = education
if age is not None:
student.age = age
if gender is not None:
student.gender = gender
if state is not None:
student.state = state
db.commit()
db.refresh(student)
return student
except SQLAlchemyError as e:
db.rollback()
raise BusinessException(f"学生更新信息失败: {str(e)}")
#==============逻辑删除学生==================
def delete_student(db: Session, sid: int):
try:
student = db.query(Student).filter(Student.sid == sid).first()
if student is None or student.flag == 0:
return None
student.flag = 0
db.commit() # 进行任务提交
db.refresh(student)
return student
except SQLAlchemyError as e:
db.rollback()
raise BusinessException(f"学生删除失败: {str(e)}")
#==============性别查询学生==================
def get_class_gender(db: Session):
class_list = db.query(Classes).all()
res = []
for cls in class_list:
cid = cls.cid
# 当前班级有效学生总数
class_total = (
db.query(func.count(Student.sid))
.filter(Student.class_id == cid, Student.flag == 1)
.scalar()
)
# 当前班级男生数量
male_count = (
db.query(func.count(Student.sid))
.filter(Student.class_id == cid, Student.flag == 1, Student.gender == "男")
.scalar()
)
# 当前班级女生数量
female_count = (
db.query(func.count(Student.sid))
.filter(Student.class_id == cid, Student.flag == 1, Student.gender == "女")
.scalar()
)
res.append({
"class_name": cls.class_name,
"class_total": class_total,
"boys_count": male_count,
"girls_count": female_count
})
return res
#==============查询每次考试高于自定义分数线的学生==================
def get_higher_score(db:Session,score):
all_students = (db.query(Student)
.filter(Student.flag==1)
.all())
higherStu=[]
for student in all_students:
all_scores = []
for stu_score in student.scores:
if stu_score.score<score:
break
all_scores.append(stu_score.score)
else:
if len(all_scores) > 0:
higherStu.append({
'stu_id':student.sid,
'stu_name':student.student_name,
'stu_score':all_scores
})
return higherStu
#==============查询不及格次数大于指定次数的学生==================
def get_fail_more_than(db: Session, fail_times: int):
# 查询有效学生
all_students = (db.query(Student)
.filter(Student.flag == 1)
.all())
result = []
for student in all_students:
#不及格成绩列表
fail_score_list = []
for stu_score in student.scores:
if stu_score.score < 60:
fail_score_list.append(stu_score.score)
if len(fail_score_list) >= fail_times:
result.append({
'stu_id': student.sid,
'stu_name': student.student_name,
'class_name': student.classes.class_name,
'fail_scores': fail_score_list
})
return result
#==============查询所有学生就业时长==================
def work_time(db:Session):
all_times = []
#所有工作信息
worksInfo = db.query(Employment).filter(Employment.flag == 1).all()
#所有学生信息
all_students = (db.query(Student).filter(Student.flag==1).all())
for work in worksInfo:
if work.offer_time is not None and work.employment_start_time is not None and work.employment_start_time >= work.offer_time:
delta = work.employment_start_time - work.offer_time
days = delta.days # 整数天数
for i in all_students:
if i.sid==work.student_id:
all_times.append({'name':i.student_name,'id': work.student_id, 'work_time': days})
return all_times
#==============统计平均分,可以切换排序方式==================
def count_avg(db: Session,order_type: str = "desc"):
avg_score = func.avg(Score.score).label("avg_score")
query = (
db.query(
Score.score.label("score"),
Classes.cid.label("class_id"),
Classes.class_name.label("class_name"),
avg_score,
)
.join(Student, Score.student_id == Student.sid)
.join(Classes, Student.class_id == Classes.cid)
.group_by(Score.score, Classes.cid, Classes.class_name)
)
# 动态排序
if order_type == "asc":
query = query.order_by(avg_score.asc())
else:
query = query.order_by(avg_score.desc())
rows = query.all()
# Row 转 dict,方便上层使用
return [
{
"score": row.score,
"class_id": row.class_id,
"class_name": row.class_name,
"avg_score": float(row.avg_score) if row.avg_score is not None else 0.0,
}
for row in rows
]
#==============按年龄区间自定义比较==================
def get_students_by_age(db: Session, query:AgeQuery):
q = db.query(Student).filter(Student.flag==1)
op = query.operator
if op == "gt":
q = q.filter(Student.age > query.value)
elif op == "lt":
q = q.filter(Student.age < query.value)
elif op == "eq":
q = q.filter(Student.age == query.value)
elif op == "ge":
q = q.filter(Student.age >= query.value)
elif op == "le":
q = q.filter(Student.age <= query.value)
elif op == "between":
q = q.filter(Student.age.between(query.min_value, query.max_value))
return q.all()
#==============按薪资排名==================
def get_top_n_salary(db: Session, rank: int):
rows = (
db.query(
Student.student_name.label("student_name"),
Classes.class_name.label("class_name"),
Employment.offer_time.label("offer_time"),
Employment.company_name.label("company"),
Employment.salary.label("salary"),
)
.join(Student, Employment.student_id == Student.sid)
.join(Classes, Student.class_id == Classes.cid)
.order_by(Employment.salary.desc())
.limit(rank)
.all()
)
return [
{
"student_name": row.student_name,
"class_name": row.class_name,
"offer_time": row.offer_time,
"company": row.company,
"salary": float(row.salary) if row.salary is not None else 0.0,
}
for row in rows
]
#==============每个班的平均就业时长==================
def avg_work_time_by_class(db: Session):
rows = (
db.query(
Classes.cid.label("class_id"),
Classes.class_name.label("class_name"),
Employment.employment_start_time,
Employment.offer_time,
)
.join(Student, Employment.student_id == Student.sid)
.join(Classes, Student.class_id == Classes.cid)
.filter(Employment.employment_start_time.isnot(None))
.filter(Employment.offer_time.isnot(None))
.filter(Employment.flag == 1, Student.flag == 1)
.filter(Employment.employment_start_time >= Employment.offer_time)
.all()
)
# 普通字典:{class_id: {"class_name": ..., "days_list": [...]}}
class_data = {}
for row in rows:
delta = row.employment_start_time - row.offer_time
days = delta.total_seconds() / 86400
if row.class_id not in class_data:
class_data[row.class_id] = {
"class_name": row.class_name,
"days_list": [],
}
class_data[row.class_id]["days_list"].append(days)
# 组装结果
result = []
for class_id, data in class_data.items():
days_list = data["days_list"]
avg_days = round(sum(days_list) / len(days_list), 2)
result.append({
"class_id": class_id,
"class_name": data["class_name"],
"days": avg_days,
})
return result