Compare commits

...

No commits in common. 'main' and '164a21006ef34eb4e8de1d01133b088a4991e8cb' have entirely different histories.

@ -1,2 +0,0 @@
# stu1

@ -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 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,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,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.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):
"""确保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