|
|
|
@ -5,14 +5,28 @@ from ..util.validator import Validator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class StudentBLL:
|
|
|
|
|
"""学生信息管理系统的业务逻辑层"""
|
|
|
|
|
"""学生信息管理系统的业务逻辑层,处理学生信息的业务逻辑和数据验证"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, dal: IStudentDAL):
|
|
|
|
|
"""
|
|
|
|
|
初始化业务逻辑层
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
dal: 数据访问层对象,实现IStudentDAL接口
|
|
|
|
|
"""
|
|
|
|
|
self.dal = dal
|
|
|
|
|
|
|
|
|
|
def add_student(self, student: Student) -> (bool, str):
|
|
|
|
|
"""添加学生,包含数据验证"""
|
|
|
|
|
# 验证学生数据
|
|
|
|
|
"""
|
|
|
|
|
添加学生信息,包含完整的数据验证流程
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
student: 要添加的学生对象
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
元组(操作是否成功, 操作结果信息)
|
|
|
|
|
"""
|
|
|
|
|
# 验证学生基本信息格式
|
|
|
|
|
if not Validator.validate_id_card(student.id_card):
|
|
|
|
|
return False, "身份证号码格式不正确"
|
|
|
|
|
|
|
|
|
@ -34,27 +48,35 @@ class StudentBLL:
|
|
|
|
|
if student.phone and not Validator.validate_phone(student.phone):
|
|
|
|
|
return False, "手机号码格式不正确"
|
|
|
|
|
|
|
|
|
|
# 验证入学日期
|
|
|
|
|
# 验证入学日期与出生日期的逻辑关系
|
|
|
|
|
if student.enrollment_date and student.birthday:
|
|
|
|
|
if not Validator.validate_enrollment_date(student.enrollment_date, student.birthday):
|
|
|
|
|
return False, "入学日期不能早于出生日期"
|
|
|
|
|
|
|
|
|
|
# 检查学号和身份证号是否已存在
|
|
|
|
|
# 检查数据唯一性
|
|
|
|
|
if self.dal.get_by_id(student.id_card):
|
|
|
|
|
return False, "该身份证号已存在"
|
|
|
|
|
|
|
|
|
|
if self.dal.get_by_stu_id(student.stu_id):
|
|
|
|
|
return False, "该学号已存在"
|
|
|
|
|
|
|
|
|
|
# 添加学生
|
|
|
|
|
# 执行数据添加操作
|
|
|
|
|
if self.dal.add(student):
|
|
|
|
|
return True, "学生添加成功"
|
|
|
|
|
else:
|
|
|
|
|
return False, "学生添加失败"
|
|
|
|
|
|
|
|
|
|
def update_student(self, student: Student) -> (bool, str):
|
|
|
|
|
"""更新学生信息,包含数据验证"""
|
|
|
|
|
# 验证学生数据
|
|
|
|
|
"""
|
|
|
|
|
更新学生信息,包含与添加操作相同的数据验证流程
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
student: 包含更新信息的学生对象,通过id_card匹配记录
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
元组(操作是否成功, 操作结果信息)
|
|
|
|
|
"""
|
|
|
|
|
# 重复添加操作中的数据验证逻辑
|
|
|
|
|
if not Validator.validate_id_card(student.id_card):
|
|
|
|
|
return False, "身份证号码格式不正确"
|
|
|
|
|
|
|
|
|
@ -76,55 +98,125 @@ class StudentBLL:
|
|
|
|
|
if student.phone and not Validator.validate_phone(student.phone):
|
|
|
|
|
return False, "手机号码格式不正确"
|
|
|
|
|
|
|
|
|
|
# 验证入学日期
|
|
|
|
|
if student.enrollment_date and student.birthday:
|
|
|
|
|
if not Validator.validate_enrollment_date(student.enrollment_date, student.birthday):
|
|
|
|
|
return False, "入学日期不能早于出生日期"
|
|
|
|
|
|
|
|
|
|
# 更新学生
|
|
|
|
|
# 执行数据更新操作
|
|
|
|
|
if self.dal.update(student):
|
|
|
|
|
return True, "学生信息更新成功"
|
|
|
|
|
else:
|
|
|
|
|
return False, "学生信息更新失败,未找到该学生"
|
|
|
|
|
|
|
|
|
|
def delete_student_by_id(self, id_card: str) -> bool:
|
|
|
|
|
"""根据身份证号删除学生"""
|
|
|
|
|
"""
|
|
|
|
|
根据身份证号删除学生记录
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
id_card: 要删除学生的身份证号
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
操作是否成功
|
|
|
|
|
"""
|
|
|
|
|
return self.dal.delete_by_id(id_card)
|
|
|
|
|
|
|
|
|
|
def delete_student_by_stu_id(self, stu_id: str) -> bool:
|
|
|
|
|
"""根据学号删除学生"""
|
|
|
|
|
"""
|
|
|
|
|
根据学号删除学生记录
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
stu_id: 要删除学生的学号
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
操作是否成功
|
|
|
|
|
"""
|
|
|
|
|
return self.dal.delete_by_stu_id(stu_id)
|
|
|
|
|
|
|
|
|
|
def get_student_by_id(self, id_card: str) -> Optional[Student]:
|
|
|
|
|
"""根据身份证号获取学生"""
|
|
|
|
|
"""
|
|
|
|
|
根据身份证号查询学生信息
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
id_card: 要查询学生的身份证号
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
匹配的学生对象,未找到返回None
|
|
|
|
|
"""
|
|
|
|
|
return self.dal.get_by_id(id_card)
|
|
|
|
|
|
|
|
|
|
def get_student_by_stu_id(self, stu_id: str) -> Optional[Student]:
|
|
|
|
|
"""根据学号获取学生"""
|
|
|
|
|
"""
|
|
|
|
|
根据学号查询学生信息
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
stu_id: 要查询学生的学号
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
匹配的学生对象,未找到返回None
|
|
|
|
|
"""
|
|
|
|
|
return self.dal.get_by_stu_id(stu_id)
|
|
|
|
|
|
|
|
|
|
def get_all_students(self) -> List[Student]:
|
|
|
|
|
"""获取所有学生"""
|
|
|
|
|
"""
|
|
|
|
|
获取系统中所有学生记录
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
包含所有学生对象的列表
|
|
|
|
|
"""
|
|
|
|
|
return self.dal.get_all()
|
|
|
|
|
|
|
|
|
|
def search_students_by_name(self, name: str) -> List[Student]:
|
|
|
|
|
"""根据姓名搜索学生(模糊查询)"""
|
|
|
|
|
"""
|
|
|
|
|
根据姓名模糊查询学生信息
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
name: 要查询的姓名关键词
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
包含匹配学生对象的列表
|
|
|
|
|
"""
|
|
|
|
|
return self.dal.search_by_name(name)
|
|
|
|
|
|
|
|
|
|
def search_students_by_class(self, class_name: str) -> List[Student]:
|
|
|
|
|
"""根据班级搜索学生(模糊查询)"""
|
|
|
|
|
"""
|
|
|
|
|
根据班级模糊查询学生信息
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
class_name: 要查询的班级关键词
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
包含匹配学生对象的列表
|
|
|
|
|
"""
|
|
|
|
|
return self.dal.search_by_class(class_name)
|
|
|
|
|
|
|
|
|
|
def search_students_by_major(self, major: str) -> List[Student]:
|
|
|
|
|
"""根据专业搜索学生(模糊查询)"""
|
|
|
|
|
"""
|
|
|
|
|
根据专业模糊查询学生信息
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
major: 要查询的专业关键词
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
包含匹配学生对象的列表
|
|
|
|
|
"""
|
|
|
|
|
return self.dal.search_by_major(major)
|
|
|
|
|
|
|
|
|
|
def get_total_students(self) -> int:
|
|
|
|
|
"""获取学生总数"""
|
|
|
|
|
"""
|
|
|
|
|
获取系统中学生总数
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
学生总人数
|
|
|
|
|
"""
|
|
|
|
|
return len(self.get_all_students())
|
|
|
|
|
|
|
|
|
|
def get_students_by_major(self) -> dict:
|
|
|
|
|
"""统计各专业学生人数"""
|
|
|
|
|
"""
|
|
|
|
|
统计各专业学生分布情况
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
字典,键为专业名称,值为该专业学生人数
|
|
|
|
|
"""
|
|
|
|
|
major_count = {}
|
|
|
|
|
for student in self.get_all_students():
|
|
|
|
|
if student.major:
|
|
|
|
@ -132,45 +224,68 @@ class StudentBLL:
|
|
|
|
|
return major_count
|
|
|
|
|
|
|
|
|
|
def calculate_average_height(self, group_by: str = None, group_value: str = None) -> float:
|
|
|
|
|
"""计算平均身高
|
|
|
|
|
group_by: 分组依据,可选 'class' 或 'major'
|
|
|
|
|
group_value: 分组值,如班级名称或专业名称
|
|
|
|
|
"""
|
|
|
|
|
计算学生平均身高,支持按班级或专业分组计算
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
group_by: 分组依据,可选'class'或'major',不分组时为None
|
|
|
|
|
group_value: 分组值,如具体班级名称或专业名称
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
平均身高,单位厘米,无有效数据时返回0
|
|
|
|
|
"""
|
|
|
|
|
students = self.get_all_students()
|
|
|
|
|
|
|
|
|
|
# 根据分组条件筛选学生
|
|
|
|
|
# 按指定条件筛选学生群体
|
|
|
|
|
if group_by == 'class':
|
|
|
|
|
students = [s for s in students if s.class_name == group_value]
|
|
|
|
|
elif group_by == 'major':
|
|
|
|
|
students = [s for s in students if s.major == group_value]
|
|
|
|
|
|
|
|
|
|
# 计算平均身高
|
|
|
|
|
# 过滤有效身高数据并计算平均值
|
|
|
|
|
heights = [s.height for s in students if s.height is not None]
|
|
|
|
|
return sum(heights) / len(heights) if heights else 0
|
|
|
|
|
|
|
|
|
|
def calculate_average_weight(self, group_by: str = None, group_value: str = None) -> float:
|
|
|
|
|
"""计算平均体重
|
|
|
|
|
group_by: 分组依据,可选 'class' 或 'major'
|
|
|
|
|
group_value: 分组值,如班级名称或专业名称
|
|
|
|
|
"""
|
|
|
|
|
计算学生平均体重,支持按班级或专业分组计算
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
group_by: 分组依据,可选'class'或'major',不分组时为None
|
|
|
|
|
group_value: 分组值,如具体班级名称或专业名称
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
平均体重,单位千克,无有效数据时返回0
|
|
|
|
|
"""
|
|
|
|
|
students = self.get_all_students()
|
|
|
|
|
|
|
|
|
|
# 根据分组条件筛选学生
|
|
|
|
|
# 按指定条件筛选学生群体
|
|
|
|
|
if group_by == 'class':
|
|
|
|
|
students = [s for s in students if s.class_name == group_value]
|
|
|
|
|
elif group_by == 'major':
|
|
|
|
|
students = [s for s in students if s.major == group_value]
|
|
|
|
|
|
|
|
|
|
# 计算平均体重
|
|
|
|
|
# 过滤有效体重数据并计算平均值
|
|
|
|
|
weights = [s.weight for s in students if s.weight is not None]
|
|
|
|
|
return sum(weights) / len(weights) if weights else 0
|
|
|
|
|
|
|
|
|
|
def get_gender_ratio(self) -> dict:
|
|
|
|
|
"""统计性别比例"""
|
|
|
|
|
"""
|
|
|
|
|
统计学生性别比例
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
包含以下键的字典:
|
|
|
|
|
- total: 总人数
|
|
|
|
|
- male: 男生人数
|
|
|
|
|
- female: 女生人数
|
|
|
|
|
- male_ratio: 男生比例
|
|
|
|
|
- female_ratio: 女生比例
|
|
|
|
|
"""
|
|
|
|
|
total = 0
|
|
|
|
|
male_count = 0
|
|
|
|
|
female_count = 0
|
|
|
|
|
|
|
|
|
|
# 遍历统计性别分布
|
|
|
|
|
for student in self.get_all_students():
|
|
|
|
|
if student.gender is not None:
|
|
|
|
|
total += 1
|
|
|
|
@ -179,6 +294,7 @@ class StudentBLL:
|
|
|
|
|
else:
|
|
|
|
|
female_count += 1
|
|
|
|
|
|
|
|
|
|
# 计算比例并返回结果
|
|
|
|
|
return {
|
|
|
|
|
'total': total,
|
|
|
|
|
'male': male_count,
|
|
|
|
|