Compare commits

...

No commits in common. 'master' and '大' have entirely different histories.
master ...

@ -1,3 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml

@ -1,12 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredIdentifiers">
<list>
<option value="面向对象学生系统.dal.idal.*" />
</list>
</option>
</inspection_tool>
</profile>
</component>

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.11" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
</project>

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/学生系统AA版本.iml" filepath="$PROJECT_DIR$/.idea/学生系统AA版本.iml" />
</modules>
</component>
</project>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.11" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -1,80 +0,0 @@
from typing import List, Optional
from models.student import Student
from dal.interfaces import IStudentDAL
from bll.validators import validate_student
class StudentService:
def __init__(self, dal: IStudentDAL):
self.dal = dal
def add_student(self, student: Student) -> bool:
"""添加学生"""
if not validate_student(student):
return False
# 检查学号和身份证号是否已存在
if self.dal.get_by_id(student.id_card) or self.dal.get_by_stu_id(student.stu_id):
return False
return self.dal.add_student(student)
def delete_student(self, id_card: str) -> bool:
"""删除学生"""
return self.dal.delete_student(id_card)
def update_student(self, student: Student) -> bool:
"""更新学生信息"""
if not validate_student(student):
return False
# 检查学生是否存在
existing = self.dal.get_by_id(student.id_card)
if not existing:
return False
return self.dal.update_student(student)
def get_student_by_id(self, id_card: str) -> Optional[Student]:
"""根据身份证号查询学生"""
return self.dal.get_by_id(id_card)
def get_student_by_stu_id(self, stu_id: str) -> Optional[Student]:
"""根据学号查询学生"""
return self.dal.get_by_stu_id(stu_id)
def get_all_students(self) -> List[Student]:
"""获取所有学生"""
return self.dal.get_all()
def search_by_name(self, name: str) -> List[Student]:
"""按姓名模糊查询"""
return self.dal.get_by_name(name)
def search_by_class(self, class_name: str) -> List[Student]:
"""按班级模糊查询"""
return self.dal.get_by_class(class_name)
def search_by_major(self, major: str) -> List[Student]:
"""按专业模糊查询"""
return self.dal.get_by_major(major)
def count_total_students(self) -> int:
"""统计学生总数"""
return self.dal.count_students()
def count_students_by_major(self) -> dict:
"""按专业统计学生人数"""
return self.dal.count_by_major()
def calculate_average_height(self, group_by: str = None) -> dict:
"""计算平均身高"""
return self.dal.calculate_avg_height(group_by)
def calculate_average_weight(self, group_by: str = None) -> dict:
"""计算平均体重"""
return self.dal.calculate_avg_weight(group_by)
def clear_all_data(self) -> bool:
"""清空所有数据"""
return self.dal.clear_all()

@ -1,59 +0,0 @@
import re
from datetime import date
from typing import Optional
from models.student import Student
def validate_id_card(id_card: str) -> bool:
"""验证身份证号有效性"""
if len(id_card) != 18:
return False
# 校验位计算
factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
checksum_map = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
total = 0
for i in range(17):
total += int(id_card[i]) * factors[i]
return checksum_map[total % 11] == id_card[-1].upper()
def validate_stu_id(stu_id: str) -> bool:
"""验证学号有效性"""
# 假设学号格式为: 年份(4位)+学院代码(2位)+专业代码(2位)+序号(3位)
return len(stu_id) == 11 and stu_id.isdigit()
def validate_name(name: str) -> bool:
"""验证姓名有效性"""
return 2 <= len(name) <= 20 and re.match(r'^[\u4e00-\u9fa5a-zA-Z·]+$', name)
def validate_height(height: Optional[int]) -> bool:
"""验证身高有效性"""
return height is None or (50 <= height <= 250)
def validate_weight(weight: Optional[float]) -> bool:
"""验证体重有效性"""
return weight is None or (5 <= weight <= 300)
def validate_enrollment_date(enrollment_date: Optional[date], birthday: date) -> bool:
"""验证入学日期有效性"""
return enrollment_date is None or enrollment_date > birthday
def validate_student(student: Student) -> bool:
"""验证学生对象所有字段"""
return all([
validate_id_card(student.id_card),
validate_stu_id(student.stu_id),
validate_name(student.name),
validate_height(student.height),
validate_weight(student.weight),
validate_enrollment_date(student.enrollment_date, student.birthday)
])

@ -1,129 +0,0 @@
import csv
from typing import List, Optional
from pathlib import Path
from models.student import Student
from dal.interfaces import IStudentDAL
class CSVStudentDAL(IStudentDAL):
def clear_all(self) -> bool:
pass
def calculate_avg_weight(self, group_by: str = None) -> dict:
pass
def calculate_avg_height(self, group_by: str = None) -> dict:
pass
def count_by_major(self) -> dict:
pass
def count_students(self) -> int:
pass
def get_by_major(self, major: str) -> List[Student]:
pass
def get_by_class(self, class_name: str) -> List[Student]:
pass
def get_by_name(self, name: str) -> List[Student]:
pass
def __init__(self, file_path: str = "students.csv"):
self.file_path = Path(file_path)
self._ensure_file_exists()
def _ensure_file_exists(self):
"""确保CSV文件存在"""
if not self.file_path.exists():
with open(self.file_path, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow([
'name', 'id_card', 'stu_id', 'gender',
'height', 'weight', 'enrollment_date',
'class_name', 'major', 'birthday', 'age'
])
def add_student(self, student: Student) -> bool:
"""添加学生到CSV文件"""
students = self.get_all()
# 检查是否已存在
if any(s.id_card == student.id_card or s.stu_id == student.stu_id for s in students):
return False
students.append(student)
return self._save_all(students)
def delete_student(self, id_card: str) -> bool:
"""从CSV文件删除学生"""
students = self.get_all()
original_count = len(students)
students = [s for s in students if s.id_card != id_card]
if len(students) < original_count:
return self._save_all(students)
return False
def update_student(self, student: Student) -> bool:
"""更新CSV文件中的学生信息"""
students = self.get_all()
updated = False
for i, s in enumerate(students):
if s.id_card == student.id_card:
students[i] = student
updated = True
break
if updated:
return self._save_all(students)
return False
def get_by_id(self, id_card: str) -> Optional[Student]:
"""根据身份证号获取学生"""
students = self.get_all()
for s in students:
if s.id_card == id_card:
return s
return None
def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
"""根据学号获取学生"""
students = self.get_all()
for s in students:
if s.stu_id == stu_id:
return s
return None
def get_all(self) -> List[Student]:
"""获取所有学生"""
students = []
with open(self.file_path, 'r', newline='', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
students.append(Student.from_dict(row))
return students
# 其他方法实现类似,限于篇幅省略...
def _save_all(self, students: List[Student]) -> bool:
"""保存所有学生到CSV文件"""
try:
with open(self.file_path, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=[
'name', 'id_card', 'stu_id', 'gender',
'height', 'weight', 'enrollment_date',
'class_name', 'major', 'birthday', 'age'
])
writer.writeheader()
for student in students:
writer.writerow(student.to_dict())
return True
except Exception as e:
print(f"保存数据时出错: {e}")
return False

@ -1,175 +0,0 @@
import json
from pathlib import Path
from typing import List, Optional, Dict, Any
from dal.interfaces import IStudentDAL
from models.student import Student
class JSONStudentDAL(IStudentDAL):
def __init__(self, file_path: str = "students.json"):
self.file_path = Path(file_path)
self._ensure_file_exists()
def _ensure_file_exists(self):
"""确保JSON文件存在"""
if not self.file_path.exists():
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump([], f, ensure_ascii=False, indent=2)
def _load_data(self) -> List[Dict[str, Any]]:
"""从JSON文件加载所有数据"""
try:
with open(self.file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, FileNotFoundError):
return []
def _save_data(self, data: List[Dict[str, Any]]) -> bool:
"""保存数据到JSON文件"""
try:
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return True
except Exception as e:
print(f"保存数据时出错: {e}")
return False
def add_student(self, student: Student) -> bool:
"""添加学生到JSON文件"""
students_data = self._load_data()
# 检查是否已存在
if any(s['id_card'] == student.id_card or s['stu_id'] == student.stu_id
for s in students_data):
return False
students_data.append(student.to_dict())
return self._save_data(students_data)
def delete_student(self, id_card: str) -> bool:
"""从JSON文件删除学生"""
students_data = self._load_data()
original_count = len(students_data)
students_data = [s for s in students_data if s['id_card'] != id_card]
if len(students_data) < original_count:
return self._save_data(students_data)
return False
def update_student(self, student: Student) -> bool:
"""更新JSON文件中的学生信息"""
students_data = self._load_data()
updated = False
for i, s in enumerate(students_data):
if s['id_card'] == student.id_card:
students_data[i] = student.to_dict()
updated = True
break
if updated:
return self._save_data(students_data)
return False
def get_by_id(self, id_card: str) -> Optional[Student]:
"""根据身份证号获取学生"""
students_data = self._load_data()
for s in students_data:
if s['id_card'] == id_card:
return Student.from_dict(s)
return None
def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
"""根据学号获取学生"""
students_data = self._load_data()
for s in students_data:
if s['stu_id'] == stu_id:
return Student.from_dict(s)
return None
def get_all(self) -> List[Student]:
"""获取所有学生"""
students_data = self._load_data()
return [Student.from_dict(s) for s in students_data]
def get_by_name(self, name: str) -> List[Student]:
"""根据姓名查询学生信息(模糊查询)"""
students_data = self._load_data()
return [Student.from_dict(s) for s in students_data
if name.lower() in s['name'].lower()]
def get_by_class(self, class_name: str) -> List[Student]:
"""根据班级查询学生信息(模糊查询)"""
students_data = self._load_data()
return [Student.from_dict(s) for s in students_data
if s['class_name'] and class_name.lower() in s['class_name'].lower()]
def get_by_major(self, major: str) -> List[Student]:
"""根据专业查询学生信息(模糊查询)"""
students_data = self._load_data()
return [Student.from_dict(s) for s in students_data
if s['major'] and major.lower() in s['major'].lower()]
def count_students(self) -> int:
"""统计学生总数"""
students_data = self._load_data()
return len(students_data)
def count_by_major(self) -> Dict[str, int]:
"""按专业统计学生人数"""
students_data = self._load_data()
result = {}
for s in students_data:
major = s.get('major', '未指定')
result[major] = result.get(major, 0) + 1
return result
def calculate_avg_height(self, group_by: str = None) -> Dict[str, float]:
"""计算平均身高"""
students_data = self._load_data()
if not group_by:
heights = [s['height'] for s in students_data if s.get('height') is not None]
return {'all': sum(heights) / len(heights)} if heights else {}
# 按指定字段分组计算
groups = {}
for s in students_data:
if s.get('height') is None:
continue
key = s.get(group_by, '未指定')
if key not in groups:
groups[key] = []
groups[key].append(s['height'])
return {k: sum(v) / len(v) for k, v in groups.items()}
def calculate_avg_weight(self, group_by: str = None) -> Dict[str, float]:
"""计算平均体重"""
students_data = self._load_data()
if not group_by:
weights = [s['weight'] for s in students_data if s.get('weight') is not None]
return {'all': sum(weights) / len(weights)} if weights else {}
# 按指定字段分组计算
groups = {}
for s in students_data:
if s.get('weight') is None:
continue
key = s.get(group_by, '未指定')
if key not in groups:
groups[key] = []
groups[key].append(s['weight'])
return {k: sum(v) / len(v) for k, v in groups.items()}
def clear_all(self) -> bool:
"""清空所有学生数据"""
return self._save_data([])

@ -1,31 +0,0 @@
from dal.csv_repo import CSVStudentDAL
from dal.json_repo import JSONStudentDAL
from bll.student_service import StudentService
from ui.console_ui import ConsoleUI
def main():
# 选择数据存储方式
print("请选择数据存储方式:")
print("1. CSV文件")
print("2. JSON文件")
choice = input("请输入选择(1-2): ").strip()
if choice == "1":
dal = CSVStudentDAL("students.csv")
elif choice == "2":
dal = JSONStudentDAL("students.json")
else:
print("无效选择默认使用CSV存储")
dal = CSVStudentDAL("students.csv")
# 创建服务层和UI层
service = StudentService(dal)
ui = ConsoleUI(service)
# 运行系统
ui.run()
if __name__ == "__main__":
main()

@ -1,66 +0,0 @@
from datetime import date
from typing import Optional, Union
from utils.id_card import extract_birthday_from_id_card, calculate_age
class Student:
def __init__(self,
name: str,
id_card: str,
stu_id: str,
gender: Optional[bool] = None,
height: Optional[int] = None,
weight: Optional[float] = None,
enrollment_date: Optional[Union[date, str]] = None,
class_name: Optional[str] = None,
major: Optional[str] = None):
self.name = name
self.id_card = id_card
self.stu_id = stu_id
self.gender = gender # True: 男, False: 女
self.height = height
self.weight = weight
self.class_name = class_name
self.major = major
# 处理日期类型
if isinstance(enrollment_date, str):
self.enrollment_date = date.fromisoformat(enrollment_date)
else:
self.enrollment_date = enrollment_date
# 从身份证生成字段
self.birthday = extract_birthday_from_id_card(id_card)
self.age = calculate_age(self.birthday)
def to_dict(self):
"""将学生对象转换为字典"""
return {
'name': self.name,
'id_card': self.id_card,
'stu_id': self.stu_id,
'gender': self.gender,
'height': self.height,
'weight': self.weight,
'enrollment_date': self.enrollment_date.isoformat() if self.enrollment_date else None,
'class_name': self.class_name,
'major': self.major,
'birthday': self.birthday.isoformat(),
'age': self.age
}
@classmethod
def from_dict(cls, data: dict):
"""从字典创建学生对象"""
return cls(
name=data['name'],
id_card=data['id_card'],
stu_id=data['stu_id'],
gender=data.get('gender'),
height=data.get('height'),
weight=data.get('weight'),
enrollment_date=data.get('enrollment_date'),
class_name=data.get('class_name'),
major=data.get('major')
)

@ -1 +0,0 @@
name,id_card,stu_id,gender,height,weight,enrollment_date,class_name,major,birthday,age
1 name id_card stu_id gender height weight enrollment_date class_name major birthday age

@ -1,403 +0,0 @@
import sys
from datetime import date
from typing import Optional
from models.student import Student
from bll.student_service import StudentService
def _show_main_menu():
"""显示主菜单"""
print("\n" + "=" * 30)
print("学生信息管理系统".center(24))
print("=" * 30)
print("1. 添加学生信息")
print("2. 删除学生信息")
print("3. 更新学生信息")
print("4. 查询学生信息")
print("5. 统计分析")
print("6. 数据导入导出")
print("7. 清空所有学生信息")
print("0. 退出系统")
print("=" * 30)
def _data_import_export():
"""数据导入导出功能"""
while True:
print("\n" + "=" * 30)
print("数据导入导出".center(24))
print("=" * 30)
print("1. 导出数据到CSV")
print("2. 从CSV导入数据")
print("3. 导出数据到JSON")
print("4. 从JSON导入数据")
print("5. 返回上一级")
print("=" * 30)
choice = input("请选择操作: ").strip()
if choice in ("1", "2", "3", "4"):
print("\n此功能暂未实现")
input("\n按任意键继续...")
elif choice == "5":
break
else:
print("无效的选择,请重新输入!")
input("按任意键继续...")
def _input_student_info(update_mode=False) -> Optional[Student]:
"""输入学生信息"""
try:
print("\n请输入学生信息:")
name = input("姓名: ").strip()
id_card = input("身份证号: ").strip() if not update_mode else None
stu_id = input("学号: ").strip() if not update_mode else None
gender_input = input("性别(男/女, 可选): ").strip().lower()
gender = None
if gender_input == '':
gender = True
elif gender_input == '':
gender = False
height_input = input("身高(cm, 可选): ").strip()
height = int(height_input) if height_input else None
weight_input = input("体重(kg, 可选): ").strip()
weight = float(weight_input) if weight_input else None
enrollment_date_input = input("入学日期(YYYY-MM-DD, 可选): ").strip()
enrollment_date = date.fromisoformat(enrollment_date_input) if enrollment_date_input else None
class_name = input("班级名称(可选): ").strip() or None
major = input("专业(可选): ").strip() or None
if update_mode:
return Student(
name=name or None,
id_card=None,
stu_id=None,
gender=gender,
height=height,
weight=weight,
enrollment_date=enrollment_date,
class_name=class_name,
major=major
)
else:
return Student(
name=name,
id_card=id_card,
stu_id=stu_id,
gender=gender,
height=height,
weight=weight,
enrollment_date=enrollment_date,
class_name=class_name,
major=major
)
except ValueError as e:
print(f"\n输入格式错误: {e}")
return None
def _display_students_list(students: list[Student]):
"""显示学生列表"""
if not students:
print("\n没有找到符合条件的学生")
return
print("\n" + "=" * 90)
print(f"{'学号':<12}{'姓名':<10}{'性别':<6}{'年龄':<6}{'班级':<15}{'专业':<20}{'入学日期':<12}")
print("=" * 90)
for student in students:
gender = '' if student.gender else '' if student.gender is not None else '未知'
print(
f"{student.stu_id:<12}"
f"{student.name:<10}"
f"{gender:<6}"
f"{student.age:<6}"
f"{student.class_name or '未指定':<15}"
f"{student.major or '未指定':<20}"
f"{student.enrollment_date.isoformat() if student.enrollment_date else '未指定':<12}"
)
print("=" * 90)
print(f"共找到 {len(students)} 名学生")
def _display_student_details(student: Student):
"""显示学生详细信息"""
print("\n" + "=" * 40)
print("学生详细信息".center(36))
print("=" * 40)
print(f"学号: {student.stu_id}")
print(f"姓名: {student.name}")
print(f"性别: {'' if student.gender else '' if student.gender is not None else '未指定'}")
print(f"年龄: {student.age}")
print(f"出生日期: {student.birthday.isoformat()}")
print(f"身份证号: {student.id_card}")
print(f"身高: {student.height}cm" if student.height else "身高: 未指定")
print(f"体重: {student.weight}kg" if student.weight else "体重: 未指定")
print(f"班级: {student.class_name or '未指定'}")
print(f"专业: {student.major or '未指定'}")
print(f"入学日期: {student.enrollment_date.isoformat() if student.enrollment_date else '未指定'}")
print("=" * 40)
class ConsoleUI:
def __init__(self, service: StudentService):
self.service = service
def run(self):
"""运行控制台界面"""
while True:
_show_main_menu()
choice = input("请选择操作: ").strip()
if choice == "1":
self._add_student()
elif choice == "2":
self._delete_student()
elif choice == "3":
self._update_student()
elif choice == "4":
self._search_student()
elif choice == "5":
self._statistics()
elif choice == "6":
_data_import_export()
elif choice == "7":
self._clear_all_data()
elif choice == "0":
print("感谢使用学生信息管理系统,再见!")
sys.exit(0)
else:
print("无效的选择,请重新输入!")
input("按任意键继续...")
def _add_student(self):
"""添加学生信息"""
print("\n" + "=" * 30)
print("添加学生信息".center(24))
print("=" * 30)
student = _input_student_info()
if student:
if self.service.add_student(student):
print("\n学生信息添加成功!")
else:
print("\n添加失败,可能是学号或身份证号已存在,或数据验证失败!")
input("\n按任意键返回主菜单...")
def _delete_student(self):
"""删除学生信息"""
while True:
print("\n" + "=" * 30)
print("删除学生信息".center(24))
print("=" * 30)
print("1. 按身份证号删除")
print("2. 按学号删除")
print("3. 返回上一级")
print("=" * 30)
choice = input("请选择操作: ").strip()
if choice == "1":
id_card = input("请输入身份证号: ").strip()
if self.service.delete_student(id_card):
print("\n删除成功!")
else:
print("\n删除失败,未找到该学生!")
input("\n按任意键继续...")
elif choice == "2":
stu_id = input("请输入学号: ").strip()
student = self.service.get_student_by_stu_id(stu_id)
if student and self.service.delete_student(student.id_card):
print("\n删除成功!")
else:
print("\n删除失败,未找到该学生!")
input("\n按任意键继续...")
elif choice == "3":
break
else:
print("无效的选择,请重新输入!")
input("按任意键继续...")
def _update_student(self):
"""更新学生信息"""
print("\n" + "=" * 30)
print("更新学生信息".center(24))
print("=" * 30)
stu_id = input("请输入要更新的学生学号: ").strip()
student = self.service.get_student_by_stu_id(stu_id)
if not student:
print("\n未找到该学生!")
input("\n按任意键返回主菜单...")
return
print("\n当前学生信息:")
_display_student_details(student)
print("\n请输入新的学生信息(留空保持不变):")
new_student = _input_student_info(update_mode=True)
# 更新字段
for attr, value in new_student.__dict__.items():
if value is not None:
setattr(student, attr, value)
if self.service.update_student(student):
print("\n学生信息更新成功!")
else:
print("\n更新失败,数据验证未通过!")
input("\n按任意键返回主菜单...")
def _search_student(self):
"""查询学生信息"""
while True:
print("\n" + "=" * 30)
print("查询学生信息".center(24))
print("=" * 30)
print("1. 查询所有学生")
print("2. 按身份证号查询")
print("3. 按学号查询")
print("4. 按姓名查询")
print("5. 按班级查询")
print("6. 按专业查询")
print("7. 返回上一级")
print("=" * 30)
choice = input("请选择操作: ").strip()
if choice == "1":
students = self.service.get_all_students()
_display_students_list(students)
input("\n按任意键继续...")
elif choice == "2":
id_card = input("请输入身份证号: ").strip()
student = self.service.get_student_by_id(id_card)
if student:
_display_student_details(student)
else:
print("\n未找到该学生!")
input("\n按任意键继续...")
elif choice == "3":
stu_id = input("请输入学号: ").strip()
student = self.service.get_student_by_stu_id(stu_id)
if student:
_display_student_details(student)
else:
print("\n未找到该学生!")
input("\n按任意键继续...")
elif choice == "4":
name = input("请输入姓名(支持模糊查询): ").strip()
students = self.service.search_by_name(name)
_display_students_list(students)
input("\n按任意键继续...")
elif choice == "5":
class_name = input("请输入班级名称(支持模糊查询): ").strip()
students = self.service.search_by_class(class_name)
_display_students_list(students)
input("\n按任意键继续...")
elif choice == "6":
major = input("请输入专业名称(支持模糊查询): ").strip()
students = self.service.search_by_major(major)
_display_students_list(students)
input("\n按任意键继续...")
elif choice == "7":
break
else:
print("无效的选择,请重新输入!")
input("按任意键继续...")
def _statistics(self):
"""统计分析功能"""
while True:
print("\n" + "=" * 30)
print("统计分析".center(24))
print("=" * 30)
print("1. 学生总数")
print("2. 按专业统计人数")
print("3. 平均身高")
print("4. 平均体重")
print("5. 返回上一级")
print("=" * 30)
choice = input("请选择操作: ").strip()
if choice == "1":
count = self.service.count_total_students()
print(f"\n学生总数: {count}")
input("\n按任意键继续...")
elif choice == "2":
stats = self.service.count_students_by_major()
print("\n各专业学生人数统计:")
for major, count in stats.items():
print(f"{major}: {count}")
input("\n按任意键继续...")
elif choice == "3":
print("\n1. 全体平均身高")
print("2. 按班级统计平均身高")
print("3. 按专业统计平均身高")
sub_choice = input("请选择统计方式: ").strip()
if sub_choice == "1":
avg = self.service.calculate_average_height()
print(f"\n全体平均身高: {avg.get('all', 0):.1f}cm")
elif sub_choice == "2":
avg = self.service.calculate_average_height("class_name")
print("\n各班级平均身高:")
for class_name, height in avg.items():
print(f"{class_name}: {height:.1f}cm")
elif sub_choice == "3":
avg = self.service.calculate_average_height("major")
print("\n各专业平均身高:")
for major, height in avg.items():
print(f"{major}: {height:.1f}cm")
else:
print("无效的选择!")
input("\n按任意键继续...")
elif choice == "4":
print("\n1. 全体平均体重")
print("2. 按班级统计平均体重")
print("3. 按专业统计平均体重")
sub_choice = input("请选择统计方式: ").strip()
if sub_choice == "1":
avg = self.service.calculate_average_weight()
print(f"\n全体平均体重: {avg.get('all', 0):.1f}kg")
elif sub_choice == "2":
avg = self.service.calculate_average_weight("class_name")
print("\n各班级平均体重:")
for class_name, weight in avg.items():
print(f"{class_name}: {weight:.1f}kg")
elif sub_choice == "3":
avg = self.service.calculate_average_weight("major")
print("\n各专业平均体重:")
for major, weight in avg.items():
print(f"{major}: {weight:.1f}kg")
else:
print("无效的选择!")
input("\n按任意键继续...")
elif choice == "5":
break
else:
print("无效的选择,请重新输入!")
input("按任意键继续...")
def _clear_all_data(self):
"""清空所有学生数据"""
confirm = input("\n警告: 这将删除所有学生数据!确定要继续吗?(y/n): ").strip().lower()
if confirm == 'y':
if self.service.clear_all_data():
print("\n所有学生数据已清空!")
else:
print("\n清空数据失败!")
else:
print("\n操作已取消")
input("\n按任意键返回主菜单...")

@ -1,201 +0,0 @@
import re
from datetime import date
from typing import Optional, Tuple
def validate_id_card(id_card: str) -> bool:
"""
Validate a Chinese ID card number (18 digits)
Args:
id_card: ID card number string
Returns:
bool: True if valid, False otherwise
"""
if not isinstance(id_card, str) or len(id_card) != 18:
return False
# Check basic format (first 17 digits, last digit can be digit or X)
if not re.match(r'^\d{17}[\dXx]$', id_card):
return False
# Calculate checksum
factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
checksum_map = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
total = 0
for i in range(17):
try:
total += int(id_card[i]) * factors[i]
except ValueError:
return False
calculated_checksum = checksum_map[total % 11]
if id_card[-1].upper() != calculated_checksum:
return False
# Validate birthdate
try:
birth_date = extract_birthday_from_id_card(id_card)
# Validate the date is reasonable
if birth_date.year < 1900 or birth_date > date.today():
return False
except ValueError:
return False
return True
def extract_birthday_from_id_card(id_card: str) -> date:
"""
Extract birthdate from ID card number
Args:
id_card: Valid 18-digit ID card number
Returns:
date: Birth date as date object
Raises:
ValueError: If ID card format is invalid
"""
if len(id_card) != 18:
raise ValueError("ID card number must be 18 digits")
birth_str = id_card[6:14] # YYYYMMDD format
try:
year = int(birth_str[0:4])
month = int(birth_str[4:6])
day = int(birth_str[6:8])
# Validate month and day
if month < 1 or month > 12 or day < 1 or day > 31:
raise ValueError("Invalid date components")
return date(year, month, day)
except (ValueError, IndexError) as e:
raise ValueError(f"Invalid birth date format in ID card: {birth_str}") from e
def calculate_age(birth_date: date) -> int:
"""
Calculate current age based on birthdate
Args:
birth_date: Date of birth
Returns:
int: Current age in years
"""
today = date.today()
age = today.year - birth_date.year
# Adjust if birthday hasn't occurred yet this year
if (today.month, today.day) < (birth_date.month, birth_date.day):
age -= 1
return age
def get_gender_from_id_card(id_card: str) -> Optional[bool]:
"""
Get gender from ID card number
Args:
id_card: ID card number string
Returns:
Optional[bool]:
True for male,
False for female,
None if cannot determine
"""
if len(id_card) not in (15, 18):
return None
# For 18-digit ID: gender is 17th digit (index 16)
# For 15-digit ID: gender is last digit
gender_digit_pos = 16 if len(id_card) == 18 else -1
try:
gender_digit = int(id_card[gender_digit_pos])
return gender_digit % 2 == 1 # Odd for male, even for female
except (ValueError, IndexError):
return None
def get_region_from_id_card(id_card: str) -> Tuple[str, str]:
"""
Get region information from ID card number
Args:
id_card: ID card number string
Returns:
Tuple[str, str]: (province, city) names
"""
if len(id_card) not in (15, 18):
return "未知", "未知"
# First 6 digits represent region code
region_code = id_card[:6]
# Simplified region mapping (in a real app, use a complete database)
region_map = {
'110000': ('北京市', '北京市'),
'110100': ('北京市', '北京市'),
'310000': ('上海市', '上海市'),
'310100': ('上海市', '上海市'),
'440000': ('广东省', ''),
'440300': ('广东省', '深圳市'),
# Add more regions as needed...
}
return region_map.get(region_code, ("未知", "未知"))
def generate_test_id_card() -> str:
"""
Generate a valid format ID card number for testing
Returns:
str: A syntactically valid but fake ID card number
"""
import random
# Region code (random Beijing district)
region = '110' + str(random.randint(100, 199))[:3]
# Birth date (18-60 years old)
birth_year = date.today().year - random.randint(18, 60)
birth_date = f"{birth_year}{random.randint(1, 12):02d}{random.randint(1, 28):02d}"
# Sequence number
seq_num = f"{random.randint(0, 999):03d}"
# First 17 digits
first_17 = region + birth_date + seq_num
# Calculate checksum
factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
checksum_map = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
total = sum(int(first_17[i]) * factors[i] for i in range(17))
checksum = checksum_map[total % 11]
return first_17 + checksum
# Example usage
if __name__ == "__main__":
test_id = "110105199003078888" # Example valid ID
print(f"Validate ID: {validate_id_card(test_id)}")
print(f"Birth date: {extract_birthday_from_id_card(test_id)}")
print(f"Age: {calculate_age(extract_birthday_from_id_card(test_id))}")
print(f"Gender: {'Male' if get_gender_from_id_card(test_id) else 'Female'}")
print(f"Region: {get_region_from_id_card(test_id)}")
print(f"Test ID: {generate_test_id_card()}")

@ -0,0 +1,11 @@
class StudentDal:
def __init__(self,file_path:str):
self.file_path=file_path
self._ensure_file_exists()
def _ensure_file_exists(self):
if not os.path.exists(self.file_path):
with open(self.file_path,mode='w',encoding='utf-8')as file:
json.dump([],file)

@ -0,0 +1,8 @@
def add_student(self,student:Student)-> bool:
students=self.load_students()
for existing_student in students:
if existing_student.sid==student.sid:
return False
students.append(student)
self.save_students(students)
return True

@ -0,0 +1,2 @@
def check_students_exists(self,sid:str)-> bool:
return self.find_studemts_by_sid(sid) is not None

@ -0,0 +1,6 @@
def clear_all_students(self)-> None:
self.save_students([])
def get_student_count(self)->int:
return len(self.load_students())
def check_student_exists(self,sid:str)-> bool:
return self.find_student_by_sid(sid) is not None

@ -0,0 +1,7 @@
def delete_student(self,sid:str)-> bool:
students= self.load_students()
initial_length=len(students)
students=[student for student in students if student.sid!= sid]
if len(students)<initial_length:
return True
return False

@ -0,0 +1,3 @@
def find_student_by_class_name(self,class_name:str)-> Lise[Student]:
students = self.load_students()
return[s for s in students if class_name.lower()in s.class_name.lower()]

@ -0,0 +1,3 @@
def find_students_by_name(self,name:str)-> List[Student]:
students =self.load_students()
return [student for student in students if name.lower() in student.name.lower()]

@ -0,0 +1,6 @@
def find_student_by_sid(self,sid:str)-> Optional[Student]:
students =self.load_students()
for student in students:
if student.sid==sid:
return student
return None

@ -0,0 +1,2 @@
def get_all_students(self)-> List[Student]:
return self.load_students()

@ -0,0 +1,2 @@
def get_student_count(self)-> int:
return len(self.load_students())

@ -8,12 +8,12 @@ class IStudentDAL(ABC):
@abstractmethod
def add_student(self, student: Student) -> bool:
"""添加学生信息"""
"""添加学生"""
pass
@abstractmethod
def delete_student(self, id_card: str) -> bool:
"""根据身份证号删除学生信息"""
"""删除学生"""
pass
@abstractmethod
@ -23,32 +23,32 @@ class IStudentDAL(ABC):
@abstractmethod
def get_by_id(self, id_card: str) -> Optional[Student]:
"""根据身份证号获取学生信息"""
"""根据身份证号获取学生"""
pass
@abstractmethod
def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
"""根据学号获取学生信息"""
"""根据学号获取学生"""
pass
@abstractmethod
def get_all(self) -> List[Student]:
"""获取所有学生信息"""
"""获取所有学生"""
pass
@abstractmethod
def get_by_name(self, name: str) -> List[Student]:
"""根据姓名查询学生信息(模糊查询)"""
"""按姓名查询(模糊)"""
pass
@abstractmethod
def get_by_class(self, class_name: str) -> List[Student]:
"""根据班级查询学生信息(模糊查询)"""
"""按班级查询(模糊)"""
pass
@abstractmethod
def get_by_major(self, major: str) -> List[Student]:
"""根据专业查询学生信息(模糊查询)"""
"""按专业查询(模糊)"""
pass
@abstractmethod
@ -58,17 +58,7 @@ class IStudentDAL(ABC):
@abstractmethod
def count_by_major(self) -> dict:
"""按专业统计学生人数"""
pass
@abstractmethod
def calculate_avg_height(self, group_by: str = None) -> dict:
"""计算平均身高"""
pass
@abstractmethod
def calculate_avg_weight(self, group_by: str = None) -> dict:
"""计算平均体重"""
"""按专业统计人数"""
pass
@abstractmethod

@ -0,0 +1,117 @@
import json
import os
from typing import List, Optional
from models.student import Student
from dal.idal import IStudentDAL
class JsonStudentDAL(IStudentDAL):
"""JSON存储实现"""
def __init__(self, file_path: str = 'data/students.json'):
self.file_path = file_path
self._ensure_file_exists()
def _ensure_file_exists(self):
os.makedirs(os.path.dirname(self.file_path), exist_ok=True)
if not os.path.exists(self.file_path):
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump([], f)
def _load_data(self) -> List[dict]:
"""加载JSON数据"""
with open(self.file_path, 'r', encoding='utf-8') as f:
return json.load(f)
def _save_data(self, data: List[dict]):
"""保存数据到JSON文件"""
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def add_student(self, student: Student) -> bool:
data = self._load_data()
# 检查学号和身份证号是否已存在
for item in data:
if item['id_card'] == student.id_card or item['stu_id'] == student.stu_id:
return False
data.append(student.to_dict())
self._save_data(data)
return True
def delete_student(self, id_card: str) -> bool:
data = self._load_data()
original_length = len(data)
data = [item for item in data if item['id_card'] != id_card]
if len(data) != original_length:
self._save_data(data)
return True
return False
def update_student(self, student: Student) -> bool:
data = self._load_data()
updated = False
for i, item in enumerate(data):
if item['id_card'] == student.id_card:
data[i] = student.to_dict()
updated = True
break
if updated:
self._save_data(data)
return updated
def get_by_id(self, id_card: str) -> Optional[Student]:
data = self._load_data()
for item in data:
if item['id_card'] == id_card:
return Student.from_dict(item)
return None
def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
data = self._load_data()
for item in data:
if item['stu_id'] == stu_id:
return Student.from_dict(item)
return None
def get_all(self) -> List[Student]:
data = self._load_data()
return [Student.from_dict(item) for item in data]
def get_by_name(self, name: str) -> List[Student]:
data = self._load_data()
return [Student.from_dict(item) for item in data if name.lower() in item['name'].lower()]
def get_by_class(self, class_name: str) -> List[Student]:
data = self._load_data()
return [Student.from_dict(item) for item in data
if item['class_name'] and class_name.lower() in item['class_name'].lower()]
def get_by_major(self, major: str) -> List[Student]:
data = self._load_data()
return [Student.from_dict(item) for item in data
if item['major'] and major.lower() in item['major'].lower()]
def count_students(self) -> int:
data = self._load_data()
return len(data)
def count_by_major(self) -> dict:
data = self._load_data()
majors = {}
for item in data:
major = item.get('major', '未指定')
majors[major] = majors.get(major, 0) + 1
return majors
def clear_all(self) -> bool:
self._save_data([])
return True

@ -0,0 +1,4 @@
def load_students(self)-> List[Student]:
with open(self.file_path,mode='r',encoding='utf-8')as file:
student_dicts=json.load(file)
return[Student.from_dict(student_dict) for student_dict in student_dicts]

@ -0,0 +1,4 @@
def save_students(self,students:List[Student])-> None:
student_dicts=[student.to_dict() for student in students]
with open(self.file_path,mode="w",encoding="utf-8") as file:
json.dump(student_dicts,file,ensure_ascii=False,indent=4)

@ -0,0 +1,24 @@
import json
from typing_extensions import Optional
from xlwings.pro.reports.filters import height
from 面向对象学生系统.model.test.student import Student
class StudentDAL:
def __init__(self,file_path:str):
self.file_path=file_path
self._ensure_file_exists()
def _ensure_file_exist(self):
if not os.path.exists(self.file_path):
with open(self,file_path,mode='w',encoding='utf-8') as file:
json.dump([],file)

@ -0,0 +1,8 @@
def update_student(self,sid:str,updated_student:Student)-> bool:
students =self.load_students()
for i, student in enumerate(students):
if student.sid== sid:
student[i]=upadted_student
self.save_students(students)
return True
return False

@ -0,0 +1,18 @@
def update_student_partial(self, sid: str, name: Optional[str] = None, height:Optional[float]=None, birth_date: Optional[date] = None, enrollment_date:Optional[date] = None, class_name:Optional[
str] = None)->bool:
students = self.load_students()
for i, student in enumerate(students):
if students.sid == sid:
if name is not None:
students[i].name = name
if height is not None:
students[i].height = height
if birth_date is not None:
students[i].birth_date = birth_date
if enrollemnt_date is not None:
students[i].enrollemnt_date = enrollemnt_date
if class_name is not None:
students[i].class_name = class_name
self.save_students(students)
return True
return False

@ -0,0 +1,2 @@
def clear_all_students(self) -> None:
self.dal.clear_all_students()

@ -0,0 +1,5 @@
def delete_student(self,sid:str)-> bool:
if not self.dal.check_student_exists(sid):
raise ValueError(f"学生ID{sid}不存在。")
return self.dal.delete_student(sid)

@ -0,0 +1,9 @@
def export_students(self,file_path:str)-> bool:
student =self.dal.get_all_students()
try:
with open(file_path,'w',encoding='utf-8')as f:
json.dump([student.to_dict() for student in students],f,ensure_ascii=False,indent=4)
return True
except Exception as e:
print(f"导出学生数据到JOSN 时出错:{e}")
return False

@ -0,0 +1,6 @@
def get_avg_height(self)-> float:
students =self.dal.get_all_students()
if not students:
return 0.0
total_height = sum(student.height for student in students)
return total_height/len(students)

@ -0,0 +1,2 @@
def get_student_by_count(self)-> int:
return self.dal.get_student_by_count()

@ -0,0 +1,3 @@
def get_student_by_height(self,min_height:int,max_height:int)-> List[Student]:
students=self.dal.get_all_students()
return [s for s in students if min_height <= s.height <= max_height]

@ -0,0 +1,8 @@
def get_student_by_id(self,sid:str)-> Optional[Student]:
return self.dal.find_student_by_sid(sid)
def get_student_by_name(self,name:str)-> List[Student]:
return self.dal.find_student_by_name(name)
def get_student_by_class(self,class_name:str)-> List[Student]:
return self.dal.find_student_by_class_name(name)
def get_all_students(self)-> List[Student]:
return self.dal.get_all_students()

@ -0,0 +1,9 @@
def get_students_by_age(self,min_age:int,max_age:int)->List[Student]:
today =date.today()
students = self.dal.get_all_students()
age_filtered_students=[]
for student in students:
age =today.year-student.birth_date.year-((today.month,today,day)<(student.birth_date.month,student.birth_date.day))
if min_age <=age<=max_age:
age_filtered_students.append(student)
return age_filtered_students

@ -0,0 +1,3 @@
def get_students_by_enrollment_year(self,year:int)-> List[Student]:
students =self.dal.get_all_students()
return [student for student in students if student.enrollment_date.year ==year]

@ -0,0 +1,15 @@
def import_students(self,file_path:str)-> bool:
try:
with open(file_path,'r',encoding='utf-8') as f:
student_dicts =json.load(f)
students=[Student.from_dict(student_dict) for student_dict in student_dicts]
for student in students:
if not student.is_valid:
print(f"导入学生数据时校验不通过学生ID{student.sid},错误信息:{student.get_errors()}")
continue
if not self.dal.check_student_exists(student.sid):
self.dal.add_student(student)
return True
except Exception as e:
print(f"从JSON导入学生数据时出错{e}")
return False

@ -0,0 +1,7 @@
class StudentBll:
def __init__(self,dal:StudentDal):
self.dal=dal
def delete_student(self,sid:str)-> bool:
if not self.dal.check_student_exists(sid):
raise ValueError(f"学生 ID {sid}不存在。")
return self.dal.delete_students(sid)

@ -0,0 +1,117 @@
from typing import List, Optional
from models.student import Student
from dal.idal import IStudentDAL
from utils.validator import validate_student_data
from utils.id_utils import is_valid_id_card
class StudentService:
"""学生信息业务逻辑层"""
def __init__(self, dal: IStudentDAL):
self.dal = dal
def add_student(self, student: Student) -> tuple[bool, str]:
"""添加学生"""
# 数据验证
is_valid, msg = validate_student_data(student)
if not is_valid:
return False, msg
# 检查学号是否已存在
if self.dal.get_by_stu_id(student.stu_id):
return False, "学号已存在"
# 检查身份证号是否已存在
if self.dal.get_by_id(student.id_card):
return False, "身份证号已存在"
# 添加学生
success = self.dal.add_student(student)
return success, "添加成功" if success else "添加失败"
def delete_student(self, id_card: str) -> tuple[bool, str]:
"""删除学生"""
if not is_valid_id_card(id_card):
return False, "无效的身份证号"
success = self.dal.delete_student(id_card)
return success, "删除成功" if success else "删除失败"
def update_student(self, student: Student) -> tuple[bool, str]:
"""更新学生信息"""
# 数据验证
is_valid, msg = validate_student_data(student)
if not is_valid:
return False, msg
# 检查学生是否存在
existing = self.dal.get_by_id(student.id_card)
if not existing:
return False, "学生不存在"
# 检查学号是否被其他学生使用
stu_with_same_id = self.dal.get_by_stu_id(student.stu_id)
if stu_with_same_id and stu_with_same_id.id_card != student.id_card:
return False, "学号已被其他学生使用"
success = self.dal.update_student(student)
return success, "更新成功" if success else "更新失败"
def get_student_by_id(self, id_card: str) -> Optional[Student]:
"""根据身份证号获取学生"""
if not is_valid_id_card(id_card):
return None
return self.dal.get_by_id(id_card)
def get_student_by_stu_id(self, stu_id: str) -> Optional[Student]:
"""根据学号获取学生"""
return self.dal.get_by_stu_id(stu_id)
def get_all_students(self) -> List[Student]:
"""获取所有学生"""
return self.dal.get_all()
def search_by_name(self, name: str) -> List[Student]:
"""按姓名查询(模糊)"""
return self.dal.get_by_name(name)
def search_by_class(self, class_name: str) -> List[Student]:
"""按班级查询(模糊)"""
return self.dal.get_by_class(class_name)
def search_by_major(self, major: str) -> List[Student]:
"""按专业查询(模糊)"""
return self.dal.get_by_major(major)
def count_total_students(self) -> int:
"""统计学生总数"""
return self.dal.count_students()
def count_by_major(self) -> dict:
"""按专业统计人数"""
return self.dal.count_by_major()
def calculate_avg_height(self, group_by: str = None) -> dict:
"""计算平均身高"""
students = self.dal.get_all()
if not group_by:
heights = [s.height for s in students if s.height is not None]
return {'all': sum(heights) / len(heights)} if heights else {'all': 0}
groups = {}
for student in students:
if student.height is None:
continue
key = getattr(student, group_by)
if key not in groups:
groups[key] = []
groups[key].append(student.height)
return {k: sum(v) / len(v) for k, v in groups.items()}
def clear_all_data(self) -> tuple[bool, str]:
"""清空所有学生数据"""
success = self.dal.clear_all()
return success, "数据已清空" if success else "清空失败"

@ -0,0 +1,6 @@
def update_student(self,sid:str,student:Student)-> bool:
if not self.dal.check_student_exists(sid):
raise ValueError(f"学生ID{sid}不存在")
if not student.is_valid:
raise ValueError(f"学生数据校验不通过,错误信息:{student.get_errors()}")
return self.dal.update_student(sid,student)

@ -0,0 +1,4 @@
def update_student_partial(self,sid:str,name:Optional[str]=None,height:Optional[int]=None,birth_date:Optional[date]=None,enrollment_date:Optional[date]=None,class_name:Optional[str]=None)->bool:
if not self.dal.check_student_exists(sid):
raise ValueError(f"学生ID{sid}不存在。")
return self.dal.update_student_partial(sid,name,height,birth_date,enrollment_date,class_name)

@ -0,0 +1,3 @@
class StudentTUI:
def __init__(self,bll:StudentBLL):
self.bll=bll

@ -0,0 +1,88 @@
import json
from typing_extensions import Optional
from xlwings.pro.reports.filters import height
from 面向对象学生系统.model.test.student import Student
class StudentDAL:
def __init__(self,file_path:str):
self.file_path=file_path
self._ensure_file_exists()
def _ensure_file_exist(self):
if not os.path.exists(self.file_path):
with open(self,file_path,mode='w',encoding='utf-8') as file:
json.dump([],file)
def save_students(self,students:List[Student])-> None:
student_dicts=[student.to_dict() for student in students]
with open(self.file_path,mode="w",encoding="utf-8") as file:
json.dump(student_dicts,file,ensure_ascii=False,indent=4)
def load_students(self)-> List[Student]:
with open(self.file_path,mode='r',encoding='utf-8')as file:
student_dicts=json.load(file)
return[Student.from_dict(student_dict) for student_dict in student_dicts]
def add_student(self,student:Student)-> bool:
students=self.load_students()
for existing_student in students:
if existing_student.sid==student.sid:
return False
students.append(student)
self.save_students(students)
return True
def find_student_by_sid(self,sid:str)-> Optional[Student]:
students =self.load_students()
for student in students:
if student.sid==sid:
return student
return None
def find_students_by_name(self,name:str)-> List[Student]:
students =self.load_students()
return [student for student in students if name.lower() in student.name.lower()]
def find_student_by_class_name(slef,class_nmae:str)-> Lise[Student]:
students = self.load_students()
return[s for s in students if class_name.lower()in s.class_name.lower()]
def get_all_students(self)-> List[Student]:
return self.load_students()
def update_student(self,sid:str,upadted_student:Student)-> bool:
students =self.load_students()
for i, student in enumerate(students):
if student.sid== sid:
student[i]=upadted_student
self.save_students(students)
return True
return False
def update_student_partial(self,sid:str,name:Optional[str]=None,height=Optional[float]=None,birth_date:Optional[date]=None,enrollment_date:Optional[date]=None,class_name:Optional[str]=None)->bool:
students= self.load_students()
for i,student in enumerate(students):
if students.sid==sid:
if name is not None:
students[i].name=name
if height is not None:
students[i].height=height
if birth_date is not None:
students[i].birth_date=birth_date
if enrollemnt_date is not None:
students[i].enrollemnt_date=enrollemnt_date
if class_name is not None:
students[i].class_name=class_name
self.save_students(students)
return True
return False
def delete_student(self,sid:str)-> bool:
students= self.load_students()
initial_length=len(students)
students=[student for student in students if student.sid!= sid]
if len(students)<initial_length:
return True
return False
def clear_all_students(self)-> None:
self.save_students([])
def get_student_count(self)->int:
return len(self.load_students())
def check_student_exists(self,sid:str)-> bool:
return self.find_student_by_sid(sid) is not None

@ -0,0 +1,37 @@
def display_menu(self):
print("\n===== 学生信息管理系统 ====")
print("1.添加学生信息")
print("2.删除学生信息")
print("3.更新学生信息")
print("4.查询学生信息")
print("5.统计分析")
print("6.数据导入导出")
print("7.清空所有学生信息")
print("8.退出系统")
print("==========================")
def display_query_menu(self):
print("\n===== 查询学生信息 ====")
print("1.查询所有学生")
print("2.按学生ID查询")
print("3.按姓名查询")
print("4.按班级查询")
print("5.返回上一级")
print("==========================")
def display_states_menu(self):
print("\n===== 统计分析 ====")
print("1.学生总数")
print("2.平均身高")
print("3.按身高范围统计")
print("4.按入学年份统计")
print("5.按年龄范围统计")
print("6.返回上一级")
print("==========================")
def display_import_export_menu(self):
print("\n===== 数据导入导出 ====")
print("1.导出数据到JSON")
print("2.从JSON导入数据")
print("3.返回上一级")
print("==========================")

@ -0,0 +1,7 @@
class StudentBll:
def __init__(self,dal:StudentDal):
self.dal=dal
def delete_student(self,sid:str)-> bool:
if not self.dal.check_student_exists(sid):
raise ValueError(f"学生 ID {sid}不存在。")
return self.dal.delete_students(sid)

@ -0,0 +1,45 @@
class Student:
def __init__(self,sid:str,name:str,height:int,birth_date:date|str,enrollment_date:date| str,class_name:str):
self.sid=sid
self.name=name.strip()
self.height=height
self.birth_date =birth_date if isinstance(birth_date,date) else date.fromisoformat(birth_date)
self.enrollment_date=enrollment_date if isinstance(enrollment_date,date)else date.fromisoformat(enrollment_date)
self.class_name=class_name
self._validation_errors=[]
self._validation_height()
self._validation_date()
self._validation_name()
def _validate_height(self)-> None:
if not (50<=self.height<=250):
self._validation_errors.append(f"身高{self.height}cm超出合理范围50-250厘米")
def _validate_name(self)-> None:
if not(2<=len(self.name)<=20):
self._validation_errors.append(f"姓名包含不可打印字符")
def _validate_date(self)-> None:
today =date.today()
if self.birth_date>today:
self._validation_errors.append("出生日期不能在未来 ")
if self.enrollment_date >today:
self._validation_errors.append(" 入学日期不能在未来 ")
if self.birth_date>self.enrollment_date:
self._validation_errors.append("入学日期不能早于出生日期")
@property
def is_valid(self)->bool:
return len(self._validtion_errors)==0
def get_errors(self)-> list[str]:
return self.validtion_errors.copy()
def __eq__(self,other)->bool:
if not isinstance(other,Student):
return NotImplemented
return(self.sid==other.sid and self.name==other.name and self.height== other.height and self.birth_date==other.birth_date and self.enrollment_date==other.enrollment_date and self.class_name==other.class_name)
def to_dict(self)-> dict:
return{'sid':self.sid,'name':self.name,'height':self.height,'birth_date':self.birth_date.isoformat(),'enrollment_date':self.enrollment_date_isoformat().'class_name':self.class_name}
@classmethod
def from_dict(cls,date:dict)->'Student':
birth_date =bir_d if isinstance(bir_d:=data['birth_date'],date)else date.fromisoformat(bir_d)
enrollment_date=enr_d if isinstance(enr_d:=data['enrollment_date'],data)else date.fromisoformat(enr_d)
return cls(sid=data['sid'],name=data['name'].strip(),height=data['height'],birth_date=birth_date,enrollment_date=enrollment_date,class_name=data['class_name'])
def __repr__(self)->str:
return(f"{self.__class__.__name__}("f"sid='{self.sid}',"f"name='{self.name}',"f"height={self.height},"f"birth_date=date({self.birth_date.year},{self.birth_date.month},{self.birth_date.day}),"
f")

@ -0,0 +1,354 @@
import re
from datetime import date, datetime
from typing import Dict, Optional, Union, List
class Student:
"""
学生信息类用于封装学生基本信息及相关操作功能
"""
def __init__(
self,
name: str,
id_card: str,
stu_id: str,
gender: Optional[bool] = None,
height: Optional[int] = None,
weight: Optional[float] = None,
enrollment_date: Union[date, str, None] = None,
class_name: Optional[str] = None,
major: Optional[str] = None
):
"""
初始化学生对象
:param name: 学生姓名2-20个字符
:param id_card: 身份证号18位含正确校验码
:param stu_id: 学号非空
:param gender: 性别True()/False()/None(未知)
:param height: 身高(cm)50-250之间
:param weight: 体重(kg)5-300之间保留1位小数
:param enrollment_date: 入学日期date对象或YYYY-MM-DD格式字符串
:param class_name: 班级名称
:param major: 专业名称
"""
self.name = name
self.id_card = id_card
self.stu_id = stu_id
self.gender = gender
self.height = height
self.weight = weight
self.class_name = class_name
self.major = major
# 处理入学日期
if isinstance(enrollment_date, str):
try:
self.enrollment_date = date.fromisoformat(enrollment_date)
except ValueError:
self.enrollment_date = None
else:
self.enrollment_date = enrollment_date
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, value: str):
self._name = value
@property
def id_card(self) -> str:
return self._id_card
@id_card.setter
def id_card(self, value: str):
self._id_card = value
@property
def stu_id(self) -> str:
return self._stu_id
@stu_id.setter
def stu_id(self, value: str):
self._stu_id = value
@property
def gender(self) -> Optional[bool]:
return self._gender
@gender.setter
def gender(self, value: Optional[bool]):
self._gender = value
@property
def height(self) -> Optional[int]:
return self._height
@height.setter
def height(self, value: Optional[int]):
self._height = value
@property
def weight(self) -> Optional[float]:
return self._weight
@weight.setter
def weight(self, value: Optional[float]):
self._weight = value
@property
def enrollment_date(self) -> Optional[date]:
return self._enrollment_date
@enrollment_date.setter
def enrollment_date(self, value: Optional[Union[date, str]]):
if isinstance(value, str):
try:
self._enrollment_date = date.fromisoformat(value)
except ValueError:
self._enrollment_date = None
else:
self._enrollment_date = value
@property
def class_name(self) -> Optional[str]:
return self._class_name
@class_name.setter
def class_name(self, value: Optional[str]):
self._class_name = value
@property
def major(self) -> Optional[str]:
return self._major
@major.setter
def major(self, value: Optional[str]):
self._major = value
# 只读属性
@property
def birthday(self) -> Optional[date]:
"""从身份证号中提取出生日期"""
if not self.id_card or len(self.id_card) != 18:
return None
birth_date_str = self.id_card[6:14]
try:
return date(
year=int(birth_date_str[0:4]),
month=int(birth_date_str[4:6]),
day=int(birth_date_str[6:8])
)
except ValueError:
return None
@property
def age(self) -> Optional[int]:
"""计算年龄"""
if not self.birthday:
return None
today = date.today()
age = today.year - self.birthday.year
# 如果生日还没到年龄减1
if (today.month, today.day) < (self.birthday.month, self.birthday.day):
age -= 1
return age
@property
def errors(self) -> Dict[str, str]:
"""返回所有校验错误信息"""
errors = {}
# 校验姓名
if not isinstance(self.name, str) or len(self.name) < 2 or len(self.name) > 20:
errors['name'] = "姓名必须为2-20个字符的字符串"
# 校验学号
if not isinstance(self.stu_id, str) or not self.stu_id:
errors['stu_id'] = "学号不能为空"
# 校验身份证号
id_card_errors = self.__validate_id_card()
if id_card_errors:
errors['id_card'] = id_card_errors
# 校验身高
if self.height is not None:
if not isinstance(self.height, int) or self.height < 50 or self.height > 250:
errors['height'] = "身高必须在50-250厘米之间"
# 校验体重
if self.weight is not None:
if not isinstance(self.weight, (int, float)) or self.weight < 5 or self.weight > 300:
errors['weight'] = "体重必须在5-300千克之间"
elif isinstance(self.weight, float) and len(str(self.weight).split('.')[1]) > 1:
errors['weight'] = "体重最多保留一位小数"
# 校验入学日期
if self.enrollment_date is not None:
if not isinstance(self.enrollment_date, date):
errors['enrollment_date'] = "入学日期格式无效"
elif self.enrollment_date > date.today():
errors['enrollment_date'] = "入学日期不能晚于当前日期"
return errors
@property
def is_valid(self) -> bool:
"""检查学生信息是否全部有效"""
return not bool(self.errors)
def __validate_id_card(self) -> Optional[str]:
"""验证身份证号有效性"""
if not isinstance(self.id_card, str):
return "身份证号必须为字符串"
id_card = self.id_card.upper()
# 长度校验
if len(id_card) != 18:
return "身份证号长度必须为18位"
# 前17位必须为数字
if not id_card[:17].isdigit():
return "身份证号前17位必须为数字"
# 最后一位可以是数字或X
last_char = id_card[17]
if not (last_char.isdigit() or last_char == 'X'):
return "身份证号最后一位必须是数字或X"
# 校验码验证
check_code = self.__get_check_code(id_card[:17])
if check_code != last_char:
return f"身份证号校验位错误,应为{check_code}"
# 出生日期验证
birth_date_str = id_card[6:14]
if not self.__validate_date(birth_date_str):
return "身份证号中的出生日期无效"
return None
@staticmethod
def __get_check_code(first_17: str) -> str:
"""计算身份证校验码"""
if len(first_17) != 17 or not first_17.isdigit():
raise ValueError("输入必须是17位数字")
# 加权因子
weights = [2 ** (17 - i) % 11 for i in range(17)]
# 计算加权和
total = sum(int(digit) * weight for digit, weight in zip(first_17, weights))
# 计算校验码
check_map = {0: '1', 1: '0', 2: 'X', 3: '9', 4: '8',
5: '7', 6: '6', 7: '5', 8: '4', 9: '3', 10: '2'}
return check_map[total % 11]
@staticmethod
def __validate_date(date_str: str) -> bool:
"""验证日期字符串(YYYYMMDD)是否有效"""
if len(date_str) != 8 or not date_str.isdigit():
return False
year = int(date_str[0:4])
month = int(date_str[4:6])
day = int(date_str[6:8])
# 简单验证年份范围
if year < 1900 or year > date.today().year:
return False
# 验证月份
if month < 1 or month > 12:
return False
# 验证日期
try:
date(year=year, month=month, day=day)
return True
except ValueError:
return False
def to_dict(self) -> Dict:
"""将学生对象转换为字典"""
data = {
'name': self.name,
'id_card': self.id_card,
'stu_id': self.stu_id,
'gender': self.gender,
'height': self.height,
'weight': self.weight,
'class_name': self.class_name,
'major': self.major
}
# 处理入学日期
if self.enrollment_date:
data['enrollment_date'] = self.enrollment_date.isoformat()
else:
data['enrollment_date'] = None
return data
@classmethod
def from_dict(cls, data: Dict) -> 'Student':
"""从字典创建学生对象"""
if not isinstance(data, dict):
raise TypeError("输入数据必须是字典类型")
# 处理入学日期
enrollment_date = data.get('enrollment_date')
if isinstance(enrollment_date, str):
try:
enrollment_date = date.fromisoformat(enrollment_date)
except ValueError:
enrollment_date = None
return cls(
name=data.get('name'),
id_card=data.get('id_card'),
stu_id=data.get('stu_id'),
gender=data.get('gender'),
height=data.get('height'),
weight=data.get('weight'),
enrollment_date=enrollment_date,
class_name=data.get('class_name'),
major=data.get('major')
)
def __repr__(self) -> str:
"""返回对象的字符串表示"""
attrs = []
for key, value in vars(self).items():
# 去掉下划线前缀
clean_key = key.lstrip('_')
if value is None:
attrs.append(f"{clean_key}=None")
elif isinstance(value, str):
attrs.append(f"{clean_key}='{value}'")
elif isinstance(value, date):
attrs.append(f"{clean_key}=date.fromisoformat('{value.isoformat()}')")
else:
attrs.append(f"{clean_key}={value}")
return f"Student({', '.join(attrs)})"
@classmethod
def get_properties(cls) -> List[str]:
"""获取使用@property装饰的属性列表"""
properties = []
for name, value in vars(cls).items():
if isinstance(value, property):
properties.append(name)
return properties

@ -0,0 +1,49 @@
from datetime import date
class Student:
def __init__(self,sid:str,name:str,height:int,birth_date:date|str,enrollment_date:date| str,class_name:str):
self.sid=sid
self.name=name.strip()
self.height=height
self.birth_date =birth_date if isinstance(birth_date,date) else date.fromisoformat(birth_date)
self.enrollment_date=enrollment_date if isinstance(enrollment_date,date)else date.fromisoformat(enrollment_date)
self.class_name=class_name
self._validation_errors=[]
self._validation_height()
self._validation_date()
self._validation_name()
def _validate_height(self)-> None:
if not(50<=self.height<=250):
self._validation_errors.append(f"身高{self.height}cm超出合理范围50-250厘米")
def _validate_name(self)-> None:
if not(2<=len(self.name)<=20):
self._validation_errors.append("姓名长度需在2-20个字符之间")
if not self.name.isprintable():
self._validation_errors.append("姓名包含不可打印字符")
def _validate_date(self)-> None:
today=date.today()
if self.birth_date>today:
self._validation_errors.append("出生日期不能在未来")
if self.enrollment_date>today:
self._validation_errors.append("入学日期不能在未来")
if self.birth_date>self.enrollment_date:
self._validation_errors.append("入学日期不能早于出生日期")
@property
def is_valid(self)-> bool:
return len(self._validation_errors)==0
def get_errors(self)-> list[str]:
return self.validation_error.copy()
def __eq__(self,other)->bool:
if not isinstance(other,Student):
return NotImplemented
return(self.sid==other.sid and self.name==other.name and self.height== other.height and self.birth_date==other.birth_date and self.enrollment_date==other.enrollment_date and self.class_name==other.class_na)
def to_dict(self)-> dict:
return{'sid':self.sid,'name':self.name,'height':self.height,'birth_date':self.birth_date.isoformat(),'enrollment_date':self.enrollment_date_isoformat().'class_name':self.class_name}
@classmethod
def from_dict(cls,date:dict)->'Student':
birth_date =bir_d if isinstance(bir_d:=data['birth_date'],date)else date.fromisoformat(bir_d)
enrollment_date=enr_d if isinstance(enr_d:=data['enrollment_date'],data)else date.fromisoformat(enr_d)
return cla(sid=data['sid'],name=data['name'].strip(),height=data['height'],birth_date=birth_date,enrollment_date=enrollment_date,class_name=data['class_name'])
def __repr__(self)->str:
return(f"{self.__class__.__name__}("f"sid='{self.sid}',"f"name='{self.name}',"f"height={self.height},"f"birth_date=date({self.birth_date.year},{self.birth_date.month},{self.birth_date.day}),"
f")

@ -0,0 +1,67 @@
from datetime import date
from typing import Optional, Union
from dataclasses import dataclass
@dataclass
class Student:
name: str
id_card: str
stu_id: str
gender: Optional[bool] = None # True:男, False:女
height: Optional[int] = None # cm
weight: Optional[float] = None # kg
enrollment_date: Optional[Union[date, str]] = None
class_name: Optional[str] = None
major: Optional[str] = None
@property
def age(self) -> int:
"""从身份证号计算年龄(周岁)"""
from utils.id_utils import calculate_age
return calculate_age(self.id_card)
@property
def birthday(self) -> date:
"""从身份证号提取出生日期"""
from utils.id_utils import extract_birthday
return extract_birthday(self.id_card)
def to_dict(self) -> dict:
"""将学生对象转换为字典"""
return {
'name': self.name,
'id_card': self.id_card,
'stu_id': self.stu_id,
'gender': self.gender,
'height': self.height,
'weight': self.weight,
'enrollment_date': self.enrollment_date.isoformat()
if isinstance(self.enrollment_date, date) else self.enrollment_date,
'class_name': self.class_name,
'major': self.major
}
@classmethod
def from_dict(cls, data: dict) -> 'Student':
"""从字典创建学生对象"""
from datetime import datetime
enrollment_date = data.get('enrollment_date')
if enrollment_date and isinstance(enrollment_date, str):
try:
enrollment_date = datetime.strptime(enrollment_date, '%Y-%m-%d').date()
except ValueError:
pass
return cls(
name=data['name'],
id_card=data['id_card'],
stu_id=data['stu_id'],
gender=data.get('gender'),
height=data.get('height'),
weight=data.get('weight'),
enrollment_date=enrollment_date,
class_name=data.get('class_name'),
major=data.get('major')
)

@ -0,0 +1,197 @@
import sys
from datetime import date
from typing import Optional
from models.student import Student
from bll.student_service import StudentService
class ConsoleUI:
"""控制台用户界面"""
def __init__(self, service: StudentService):
self.service = service
def run(self):
"""运行主界面"""
while True:
self._display_main_menu()
choice = input("请选择操作: ").strip()
if choice == '1':
self._add_student()
elif choice == '2':
self._delete_student()
elif choice == '3':
self._update_student()
elif choice == '4':
self._query_students()
elif choice == '5':
self._statistics()
elif choice == '6':
self._import_export()
elif choice == '7':
self._clear_all_data()
elif choice == '0':
print("感谢使用学生信息管理系统,再见!")
sys.exit(0)
else:
print("无效选择,请重新输入!")
def _display_main_menu(self):
"""显示主菜单"""
print("\n" + "=" * 30)
print(" 学生信息管理系统 ")
print("=" * 30)
print("1. 添加学生信息")
print("2. 删除学生信息")
print("3. 更新学生信息")
print("4. 查询学生信息")
print("5. 统计分析")
print("6. 数据导入导出")
print("7. 清空所有学生信息")
print("0. 退出系统")
print("=" * 30)
def _add_student(self):
"""添加学生"""
print("\n" + "=" * 30)
print(" 添加学生信息 ")
print("=" * 30)
student = self._input_student_info()
if student is None:
return
success, msg = self.service.add_student(student)
print(msg)
def _delete_student(self):
"""删除学生"""
print("\n" + "=" * 30)
print(" 删除学生信息 ")
print("=" * 30)
id_card = input("请输入要删除学生的身份证号: ").strip()
success, msg = self.service.delete_student(id_card)
print(msg)
def _update_student(self):
"""更新学生信息"""
print("\n" + "=" * 30)
print(" 更新学生信息 ")
print("=" * 30)
id_card = input("请输入要更新学生的身份证号: ").strip()
existing = self.service.get_student_by_id(id_card)
if not existing:
print("学生不存在")
return
print("\n当前学生信息:")
self._display_student_details(existing)
print("\n请输入新的学生信息(留空保持原值):")
student = self._input_student_info(existing)
if student is None:
return
success, msg = self.service.update_student(student)
print(msg)
def _query_students(self):
"""查询学生信息"""
while True:
print("\n" + "=" * 30)
print(" 查询学生信息 ")
print("=" * 30)
print("1. 查询所有学生")
print("2. 按身份证号查询")
print("3. 按学号查询")
print("4. 按姓名查询")
print("5. 按班级查询")
print("6. 按专业查询")
print("7. 返回上一级")
print("=" * 30)
choice = input("请选择操作: ").strip()
if choice == '1':
self._list_all_students()
elif choice == '2':
self._query_by_id_card()
elif choice == '3':
self._query_by_stu_id()
elif choice == '4':
self._query_by_name()
elif choice == '5':
self._query_by_class()
elif choice == '6':
self._query_by_major()
elif choice == '7':
return
else:
print("无效选择,请重新输入!")
def _statistics(self):
"""统计分析"""
while True:
print("\n" + "=" * 30)
print(" 统计分析 ")
print("=" * 30)
print("1. 学生总数")
print("2. 各专业人数统计")
print("3. 平均身高")
print("4. 平均体重")
print("5. 返回上一级")
print("=" * 30)
choice = input("请选择操作: ").strip()
if choice == '1':
count = self.service.count_total_students()
print(f"\n学生总数: {count}")
elif choice == '2':
stats = self.service.count_by_major()
print("\n各专业人数统计:")
for major, count in stats.items():
print(f"{major}: {count}")
elif choice == '3':
avg_height = self.service.calculate_avg_height()
print(f"\n全体学生平均身高: {avg_height['all']:.1f}cm")
elif choice == '4':
# 类似实现平均体重
pass
elif choice == '5':
return
else:
print("无效选择,请重新输入!")
def _input_student_info(self, existing: Student = None) -> Optional[Student]:
"""输入学生信息"""
try:
name = self._get_input("姓名", existing.name if existing else None, required=True)
id_card = self._get_input("身份证号", existing.id_card if existing else None, required=True)
stu_id = self._get_input("学号", existing.stu_id if existing else None, required=True)
gender = self._get_gender_input(existing.gender if existing else None)
height = self._get_int_input("身高(cm)", existing.height if existing else None, 50, 250)
weight = self._get_float_input("体重(kg)", existing.weight if existing else None, 5, 300)
enrollment_date = self._get_date_input("入学日期", existing.enrollment_date if existing else None)
class_name = self._get_input("班级", existing.class_name if existing else None)
major = self._get_input("专业", existing.major if existing else None)
return Student(
name=name,
id_card=id_card,
stu_id=stu_id,
gender=gender,
height=height,
weight=weight,
enrollment_date=enrollment_date,
class_name=class_name,
major=major
)
except ValueError as e:
print(f"输入错误: {e}")
return None
# 其他辅助方法...

@ -0,0 +1,117 @@
import json
import os
from typing import List, Optional
from models import Student
from dal.idal import IStudentDAL
class JsonStudentDAL(IStudentDAL):
"""JSON存储实现"""
def __init__(self, file_path: str = 'data/students.json'):
self.file_path = file_path
self._ensure_file_exists()
def _ensure_file_exists(self):
"""确保JSON文件存在"""
os.makedirs(os.path.dirname(self.file_path), exist_ok=True)
if not os.path.exists(self.file_path):
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump([], f)
def _load_data(self) -> List[dict]:
"""加载JSON数据"""
with open(self.file_path, 'r', encoding='utf-8') as f:
return json.load(f)
def _save_data(self, data: List[dict]):
"""保存数据到JSON文件"""
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def add_student(self, student: Student) -> bool:
data = self._load_data()
# 检查学号和身份证号是否已存在
for item in data:
if item['id_card'] == student.id_card or item['stu_id'] == student.stu_id:
return False
data.append(student.to_dict())
self._save_data(data)
return True
def delete_student(self, id_card: str) -> bool:
data = self._load_data()
original_length = len(data)
data = [item for item in data if item['id_card'] != id_card]
if len(data) != original_length:
self._save_data(data)
return True
return False
def update_student(self, student: Student) -> bool:
data = self._load_data()
updated = False
for i, item in enumerate(data):
if item['id_card'] == student.id_card:
data[i] = student.to_dict()
updated = True
break
if updated:
self._save_data(data)
return updated
def get_by_id(self, id_card: str) -> Optional[Student]:
data = self._load_data()
for item in data:
if item['id_card'] == id_card:
return Student.from_dict(item)
return None
def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
data = self._load_data()
for item in data:
if item['stu_id'] == stu_id:
return Student.from_dict(item)
return None
def get_all(self) -> List[Student]:
data = self._load_data()
return [Student.from_dict(item) for item in data]
def get_by_name(self, name: str) -> List[Student]:
data = self._load_data()
return [Student.from_dict(item) for item in data if name.lower() in item['name'].lower()]
def get_by_class(self, class_name: str) -> List[Student]:
data = self._load_data()
return [Student.from_dict(item) for item in data
if item['class_name'] and class_name.lower() in item['class_name'].lower()]
def get_by_major(self, major: str) -> List[Student]:
data = self._load_data()
return [Student.from_dict(item) for item in data
if item['major'] and major.lower() in item['major'].lower()]
def count_students(self) -> int:
data = self._load_data()
return len(data)
def count_by_major(self) -> dict:
data = self._load_data()
majors = {}
for item in data:
major = item.get('major', '未指定')
majors[major] = majors.get(major, 0) + 1
return majors
def clear_all(self) -> bool:
self._save_data([])
return True
Loading…
Cancel
Save