You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xsxx/business_logic.py

240 lines
9.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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