import os import json import csv from abc import ABC, abstractmethod from datetime import date from typing import List, Optional, Dict from model.student import Student class IStudentDAL(ABC): """学生信息数据访问层接口""" @abstractmethod def add_student(self, student: Student) -> bool: """添加学生""" pass @abstractmethod def delete_student_by_id_card(self, id_card: str) -> bool: """根据身份证号删除学生""" pass @abstractmethod def delete_student_by_stu_id(self, stu_id: str) -> bool: """根据学号删除学生""" pass @abstractmethod def update_student(self, id_card: str, updated_student: Student) -> bool: """更新学生信息""" pass @abstractmethod def find_student_by_id_card(self, id_card: str) -> Optional[Student]: """根据身份证号查找学生""" pass @abstractmethod def find_student_by_stu_id(self, stu_id: str) -> Optional[Student]: """根据学号查找学生""" pass @abstractmethod def find_students_by_name(self, name: str) -> List[Student]: """根据姓名查找学生(模糊匹配)""" pass @abstractmethod def find_students_by_class_name(self, class_name: str) -> List[Student]: """根据班级名称查找学生(模糊匹配)""" pass @abstractmethod def find_students_by_major(self, major: str) -> List[Student]: """根据专业查找学生(模糊匹配)""" pass @abstractmethod def get_all_students(self) -> List[Student]: """获取所有学生信息""" pass @abstractmethod def get_student_count(self) -> int: """获取学生总数""" pass @abstractmethod def get_students_by_height_range(self, min_height: int, max_height: int) -> List[Student]: """按身高范围查找学生""" pass @abstractmethod def get_students_by_enrollment_year(self, year: int) -> List[Student]: """按入学年份查找学生""" pass @abstractmethod def get_students_by_age_range(self, min_age: int, max_age: int) -> List[Student]: """按年龄范围查找学生""" pass @abstractmethod def get_students_by_major_group(self) -> Dict[str, List[Student]]: """按专业分组学生""" pass @abstractmethod def get_average_height(self) -> float: """计算平均身高""" pass @abstractmethod def get_average_weight(self) -> float: """计算平均体重""" pass @abstractmethod def get_gender_ratio(self) -> Dict[Optional[bool], float]: """计算性别比例""" pass @abstractmethod def clear_all_students(self) -> None: """清空所有学生信息""" pass @abstractmethod def check_student_exists_by_id_card(self, id_card: str) -> bool: """检查身份证号是否存在""" pass @abstractmethod def check_student_exists_by_stu_id(self, stu_id: str) -> bool: """检查学号是否存在""" pass class JsonStudentDAL(IStudentDAL): """JSON文件存储实现""" def __init__(self, file_path: str): self.file_path = file_path self.__ensure_file_exists() def __ensure_file_exists(self): """确保JSON文件存在""" if not os.path.exists(self.file_path): with open(self.file_path, 'w', encoding='utf-8') as file: # noinspection PyTypeChecker json.dump([], file) def __load_students(self) -> List[Student]: """从JSON文件加载学生列表""" try: with open(self.file_path, 'r', encoding='utf-8') as file: student_dicts = json.load(file) return [Student.from_dict(sd) for sd in student_dicts] except json.JSONDecodeError: return [] def __save_students(self, students: List[Student]) -> None: """将学生列表保存到JSON文件""" student_dicts = [s.to_dict() for s in students] with open(self.file_path, 'w', encoding='utf-8') as file: json.dump(student_dicts, file, ensure_ascii=False, indent=4) # 实现接口方法 def add_student(self, student: Student) -> bool: students = self.__load_students() if any(s.id_card == student.id_card for s in students): return False if any(s.stu_id == student.stu_id for s in students): return False students.append(student) self.__save_students(students) return True def delete_student_by_id_card(self, id_card: str) -> bool: students = self.__load_students() original_count = len(students) students = [s for s in students if s.id_card != id_card] if len(students) < original_count: self.__save_students(students) return True return False def delete_student_by_stu_id(self, stu_id: str) -> bool: students = self.__load_students() original_count = len(students) students = [s for s in students if s.stu_id != stu_id] if len(students) < original_count: self.__save_students(students) return True return False def update_student(self, id_card: str, updated_student: Student) -> bool: students = self.__load_students() for i, s in enumerate(students): if s.id_card == id_card: students[i] = updated_student self.__save_students(students) return True return False def find_student_by_id_card(self, id_card: str) -> Optional[Student]: students = self.__load_students() for s in students: if s.id_card == id_card: return s return None def find_student_by_stu_id(self, stu_id: str) -> Optional[Student]: students = self.__load_students() for s in students: if s.stu_id == stu_id: return s return None def find_students_by_name(self, name: str) -> List[Student]: students = self.__load_students() return [s for s in students if name.lower() in s.name.lower()] def find_students_by_class_name(self, class_name: str) -> List[Student]: students = self.__load_students() return [s for s in students if s.class_name and class_name.lower() in s.class_name.lower()] def find_students_by_major(self, major: str) -> List[Student]: students = self.__load_students() return [s for s in students if s.major and major.lower() in s.major.lower()] def get_all_students(self) -> List[Student]: return self.__load_students() def get_student_count(self) -> int: return len(self.__load_students()) def get_students_by_height_range(self, min_height: int, max_height: int) -> List[Student]: students = self.__load_students() return [s for s in students if s.height and min_height <= s.height <= max_height] def get_students_by_enrollment_year(self, year: int) -> List[Student]: students = self.__load_students() return [s for s in students if s.enrollment_date and s.enrollment_date.year == year] def get_students_by_age_range(self, min_age: int, max_age: int) -> List[Student]: students = self.__load_students() today = date.today() result = [] for s in students: if s.birth_date: age = today.year - s.birth_date.year if (today.month, today.day) < (s.birth_date.month, s.birth_date.day): age -= 1 if min_age <= age <= max_age: result.append(s) return result def get_students_by_major_group(self) -> Dict[str, List[Student]]: students = self.__load_students() groups = {} for s in students: if s.major: if s.major not in groups: groups[s.major] = [] groups[s.major].append(s) return groups def get_average_height(self) -> float: students = self.__load_students() heights = [s.height for s in students if s.height is not None] if not heights: return 0.0 return sum(heights) / len(heights) def get_average_weight(self) -> float: students = self.__load_students() weights = [s.weight for s in students if s.weight is not None] if not weights: return 0.0 return sum(weights) / len(weights) def get_gender_ratio(self) -> Dict[Optional[bool], float]: students = self.__load_students() genders = [s.gender for s in students if s.gender is not None] total = len(genders) if total == 0: return {True: 0.0, False: 0.0} male_count = sum(1 for g in genders if g) female_count = total - male_count return { True: male_count / total * 100, False: female_count / total * 100 } def clear_all_students(self) -> None: self.__save_students([]) def check_student_exists_by_id_card(self, id_card: str) -> bool: return self.find_student_by_id_card(id_card) is not None def check_student_exists_by_stu_id(self, stu_id: str) -> bool: return self.find_student_by_stu_id(stu_id) is not None class CsvStudentDAL(IStudentDAL): """CSV文件存储实现""" def __init__(self, file_path: str): self.file_path = file_path self.__ensure_file_exists() def __ensure_file_exists(self): """确保CSV文件存在""" if not os.path.exists(self.file_path): with open(self.file_path, 'w', encoding='utf-8', newline='') as file: writer = csv.writer(file) writer.writerow([ 'name', 'id_card', 'stu_id', 'gender', 'height', 'weight', 'enrollment_date', 'class_name', 'major', 'email', 'phone' ]) def __load_students(self) -> List[Student]: """从CSV文件加载学生列表""" students = [] try: with open(self.file_path, 'r', encoding='utf-8', newline='') as file: reader = csv.DictReader(file) for row in reader: # 转换数据类型 if row['gender']: row['gender'] = True if row['gender'] == 'True' else False if row['height']: row['height'] = int(row['height']) if row['weight']: row['weight'] = float(row['weight']) if row['enrollment_date']: row['enrollment_date'] = date.fromisoformat(row['enrollment_date']) students.append(Student.from_dict(row)) except (FileNotFoundError, csv.Error, ValueError): pass return students def __save_students(self, students: List[Student]) -> None: """将学生列表保存到CSV文件""" with open(self.file_path, 'w', encoding='utf-8', newline='') as file: fieldnames = [ 'name', 'id_card', 'stu_id', 'gender', 'height', 'weight', 'enrollment_date', 'class_name', 'major', 'email', 'phone' ] writer = csv.DictWriter(file, fieldnames=fieldnames) writer.writeheader() for student in students: writer.writerow(student.to_dict()) # 实现接口方法 def add_student(self, student: Student) -> bool: students = self.__load_students() if any(s.id_card == student.id_card for s in students): return False if any(s.stu_id == student.stu_id for s in students): return False students.append(student) self.__save_students(students) return True def delete_student_by_id_card(self, id_card: str) -> bool: students = self.__load_students() original_count = len(students) students = [s for s in students if s.id_card != id_card] if len(students) < original_count: self.__save_students(students) return True return False def delete_student_by_stu_id(self, stu_id: str) -> bool: students = self.__load_students() original_count = len(students) students = [s for s in students if s.stu_id != stu_id] if len(students) < original_count: self.__save_students(students) return True return False def update_student(self, id_card: str, updated_student: Student) -> bool: students = self.__load_students() for i, s in enumerate(students): if s.id_card == id_card: students[i] = updated_student self.__save_students(students) return True return False def find_student_by_id_card(self, id_card: str) -> Optional[Student]: students = self.__load_students() for s in students: if s.id_card == id_card: return s return None def find_student_by_stu_id(self, stu_id: str) -> Optional[Student]: students = self.__load_students() for s in students: if s.stu_id == stu_id: return s return None def find_students_by_name(self, name: str) -> List[Student]: students = self.__load_students() return [s for s in students if name.lower() in s.name.lower()] def find_students_by_class_name(self, class_name: str) -> List[Student]: students = self.__load_students() return [s for s in students if s.class_name and class_name.lower() in s.class_name.lower()] def find_students_by_major(self, major: str) -> List[Student]: students = self.__load_students() return [s for s in students if s.major and major.lower() in s.major.lower()] def get_all_students(self) -> List[Student]: return self.__load_students() def get_student_count(self) -> int: return len(self.__load_students()) def get_students_by_height_range(self, min_height: int, max_height: int) -> List[Student]: students = self.__load_students() return [s for s in students if s.height and min_height <= s.height <= max_height] def get_students_by_enrollment_year(self, year: int) -> List[Student]: students = self.__load_students() return [s for s in students if s.enrollment_date and s.enrollment_date.year == year] def get_students_by_age_range(self, min_age: int, max_age: int) -> List[Student]: students = self.__load_students() today = date.today() result = [] for s in students: if s.birth_date: age = today.year - s.birth_date.year if (today.month, today.day) < (s.birth_date.month, s.birth_date.day): age -= 1 if min_age <= age <= max_age: result.append(s) return result def get_students_by_major_group(self) -> Dict[str, List[Student]]: students = self.__load_students() groups = {} for s in students: if s.major: if s.major not in groups: groups[s.major] = [] groups[s.major].append(s) return groups def get_average_height(self) -> float: students = self.__load_students() heights = [s.height for s in students if s.height is not None] if not heights: return 0.0 return sum(heights) / len(heights) def get_average_weight(self) -> float: students = self.__load_students() weights = [s.weight for s in students if s.weight is not None] if not weights: return 0.0 return sum(weights) / len(weights) def get_gender_ratio(self) -> Dict[Optional[bool], float]: students = self.__load_students() genders = [s.gender for s in students if s.gender is not None] total = len(genders) if total == 0: return {True: 0.0, False: 0.0} male_count = sum(1 for g in genders if g) female_count = total - male_count return { True: male_count / total * 100, False: female_count / total * 100 } def clear_all_students(self) -> None: self.__save_students([]) def check_student_exists_by_id_card(self, id_card: str) -> bool: return self.find_student_by_id_card(id_card) is not None def check_student_exists_by_stu_id(self, stu_id: str) -> bool: return self.find_student_by_stu_id(stu_id) is not None