from datetime import date, datetime from typing import Dict, Any, List, Optional, Tuple from data_access import StudentDataAccess class IDCardValidator: """身份证验证工具类""" @staticmethod def validate_id_card(id_card: str) -> Tuple[bool, str]: """验证身份证号有效性""" # 基本格式验证 if len(id_card) != 18: return False, "身份证号必须为18位" # 前17位必须是数字,最后一位可以是数字或X if not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1].upper() == 'X')): return False, "身份证号格式不正确,前17位必须是数字,最后一位可以是数字或X" # 验证出生日期 birth_date = id_card[6:14] if not IDCardValidator.validate_birth_date(birth_date): return False, "身份证号中的出生日期无效" # 验证校验码 if not IDCardValidator.validate_check_code(id_card): return False, "身份证号校验码不正确" return True, "身份证号有效" @staticmethod def validate_birth_date(birth_date: str) -> bool: """验证出生日期是否有效""" try: # 尝试解析日期 year = int(birth_date[0:4]) month = int(birth_date[4:6]) day = int(birth_date[6:8]) # 检查日期范围 if not (1900 <= year <= date.today().year): return False if not (1 <= month <= 12): return False # 检查每月天数 max_day = IDCardValidator.get_days_in_month(year, month) if not (1 <= day <= max_day): return False return True except: return False @staticmethod def get_days_in_month(year: int, month: int) -> int: """获取某年某月的天数""" if month == 2: return 29 if IDCardValidator.is_leap_year(year) else 28 elif month in [4, 6, 9, 11]: return 30 else: return 31 @staticmethod def is_leap_year(year: int) -> bool: """判断是否为闰年""" return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0) @staticmethod def validate_check_code(id_card: str) -> bool: """验证身份证校验码""" # 权重因子 weight = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] # 校验码对应表 check_code_map = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'] # 计算加权和 total = sum(int(id_card[i]) * weight[i] for i in range(17)) # 计算校验码 calculated_code = check_code_map[total % 11] # 比较校验码 return id_card[-1].upper() == calculated_code @staticmethod def get_gender(id_card: str) -> str: """根据身份证号获取性别""" if len(id_card) < 17: return "未知" gender_num = int(id_card[16]) return "男" if gender_num % 2 == 1 else "女" @staticmethod def get_birth_date(id_card: str) -> Optional[date]: """从身份证号获取出生日期""" if len(id_card) < 14: return None try: birth_str = id_card[6:14] return datetime.strptime(birth_str, "%Y%m%d").date() except: return None @staticmethod def calculate_age(id_card: str) -> Optional[int]: """根据身份证号计算年龄""" birth_date = IDCardValidator.get_birth_date(id_card) if not birth_date: return None today = date.today() age = today.year - birth_date.year # 如果生日还没到,年龄减1 if (today.month, today.day) < (birth_date.month, birth_date.day): age -= 1 return age class StudentService: def __init__(self, data_access: StudentDataAccess): self.data_access = data_access self.subject_name = { 'math': '数学', 'python': 'Python程序设计', 'english': '英语', 'physics': '物理', 'sum': '总分' } def validate_id_card(self, id_card: str) -> Tuple[bool, str]: """验证身份证号格式""" return IDCardValidator.validate_id_card(id_card) def is_id_card_exists(self, id_card: str, exclude_id: str = None) -> bool: """检查身份证号是否已存在""" students = self.data_access.get_all_students() for sid, student in students.items(): if exclude_id and sid == exclude_id: continue if student['basic_info'].get('id_card', '').upper() == id_card.upper(): return True return False def add_student(self, student_info: Dict[str, Any]) -> bool: """添加新学生""" student_id = student_info['basic_info']['id'] # 验证学号 if not student_id.isdigit() or len(student_id) != 8: raise ValueError("学号必须为8位数字!") # 验证身份证号 id_card = student_info['basic_info'].get('id_card', '') if not id_card: raise ValueError("身份证号不能为空!") is_valid, msg = self.validate_id_card(id_card) if not is_valid: raise ValueError(f"身份证号无效: {msg}") if self.is_id_card_exists(id_card): raise ValueError("该身份证号已存在!") # 自动填充性别和出生日期 student_info['basic_info']['gender'] = IDCardValidator.get_gender(id_card) birth_date = IDCardValidator.get_birth_date(id_card) if birth_date: student_info['basic_info']['birth_date'] = birth_date.strftime("%Y-%m-%d") student_info['basic_info']['age'] = IDCardValidator.calculate_age(id_card) # 计算总分 scores = student_info['score'] scores['sum'] = sum([scores['math'], scores['python'], scores['english'], scores['physics']]) return self.data_access.add_student(student_id, student_info) def update_student(self, student_id: str, student_info: Dict[str, Any]) -> bool: """更新学生信息""" # 验证身份证号 id_card = student_info['basic_info'].get('id_card', '') if id_card: # 如果提供了身份证号才验证 is_valid, msg = self.validate_id_card(id_card) if not is_valid: raise ValueError(f"身份证号无效: {msg}") if self.is_id_card_exists(id_card, exclude_id=student_id): raise ValueError("该身份证号已被其他学生使用!") # 自动更新性别和出生日期 student_info['basic_info']['gender'] = IDCardValidator.get_gender(id_card) birth_date = IDCardValidator.get_birth_date(id_card) if birth_date: student_info['basic_info']['birth_date'] = birth_date.strftime("%Y-%m-%d") student_info['basic_info']['age'] = IDCardValidator.calculate_age(id_card) # 计算总分 scores = student_info['score'] scores['sum'] = sum([scores['math'], scores['python'], scores['english'], scores['physics']]) return self.data_access.update_student(student_id, student_info) def delete_student(self, student_id: str) -> bool: """删除学生""" return self.data_access.delete_student(student_id) def get_student(self, student_id: str) -> Dict[str, Any]: """获取单个学生信息""" return self.data_access.get_student(student_id) def get_all_students(self) -> Dict[str, Any]: """获取所有学生信息""" return self.data_access.get_all_students() def search_student_by_id(self, student_id: str) -> Dict[str, Any]: """按学号查询学生信息""" return self.data_access.get_student(student_id) def get_statistics(self) -> Dict[str, float]: """获取统计信息""" students = self.data_access.get_all_students().values() if not students: return {} total = len(students) return { 'total': total, 'math_avg': sum(s['score']['math'] for s in students) / total, 'python_avg': sum(s['score']['python'] for s in students) / total, 'english_avg': sum(s['score']['english'] for s in students) / total, 'physics_avg': sum(s['score']['physics'] for s in students) / total, 'total_avg': sum(s['score']['sum'] for s in students) / total } def get_sorted_students(self) -> List[Dict[str, Any]]: """按总分排序获取学生""" students = list(self.data_access.get_all_students().values()) return sorted(students, key=lambda x: x['score']['sum'], reverse=True) def load_data(self): pass