diff --git a/business_logic.py b/business_logic.py new file mode 100644 index 0000000..ccc64e6 --- /dev/null +++ b/business_logic.py @@ -0,0 +1,240 @@ +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 \ No newline at end of file