Compare commits
No commits in common. 'main' and 'master' have entirely different histories.
@ -0,0 +1,87 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Optional
|
||||
from src.models.student import Student
|
||||
|
||||
|
||||
class IStudentDAL(ABC):
|
||||
"""学生信息数据访问层接口"""
|
||||
|
||||
@abstractmethod
|
||||
def add_student(self, student: Student) -> bool:
|
||||
"""添加学生信息"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def delete_student(self, id_card: str) -> bool:
|
||||
"""根据身份证号删除学生信息"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def update_student(self, student: Student) -> bool:
|
||||
"""更新学生信息"""
|
||||
pass
|
||||
|
||||
@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
|
||||
def count_students(self) -> int:
|
||||
"""统计学生总数"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def count_by_major(self, major: str) -> int:
|
||||
"""统计各专业学生人数"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_avg_height(self, filter_by: Optional[str] = None, filter_value: Optional[str] = None) -> float:
|
||||
"""计算平均身高"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_avg_weight(self, filter_by: Optional[str] = None, filter_value: Optional[str] = None) -> float:
|
||||
"""计算平均体重"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def clear_all(self) -> bool:
|
||||
"""清空所有学生信息"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def import_from_file(self, file_path: str) -> bool:
|
||||
"""从文件导入数据"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def export_to_file(self, file_path: str) -> bool:
|
||||
"""导出数据到文件"""
|
||||
pass
|
||||
@ -0,0 +1,70 @@
|
||||
import sys
|
||||
from datetime import date
|
||||
from typing import Optional
|
||||
from src.models.student import Student
|
||||
from src.bll.student_service import StudentService
|
||||
|
||||
|
||||
class ConsoleUI:
|
||||
def __init__(self, service: StudentService):
|
||||
self.service = service
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
self._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._query_students()
|
||||
elif choice == "5":
|
||||
self._show_statistics()
|
||||
elif choice == "6":
|
||||
self._data_import_export()
|
||||
elif choice == "7":
|
||||
self._clear_all_data()
|
||||
elif choice == "0":
|
||||
print("感谢使用学生信息管理系统,再见!")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("无效选择,请重新输入!")
|
||||
|
||||
def _show_main_menu(self):
|
||||
print("\n" + "=" * 30)
|
||||
print("学生信息管理系统".center(26))
|
||||
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("添加学生信息".center(26))
|
||||
print("=" * 30)
|
||||
|
||||
student_data = {}
|
||||
student_data["name"] = input("姓名: ").strip()
|
||||
student_data["id_card"] = input("身份证号: ").strip()
|
||||
student_data["stu_id"] = input("学号: ").strip()
|
||||
|
||||
# 其他可选字段输入...
|
||||
|
||||
try:
|
||||
student = Student(**student_data)
|
||||
success, msg = self.service.add_student(student)
|
||||
print(msg)
|
||||
except Exception as e:
|
||||
print(f"添加失败: {str(e)}")
|
||||
|
||||
# 其他UI方法...
|
||||
@ -0,0 +1,60 @@
|
||||
import csv
|
||||
from typing import List, Optional
|
||||
from pathlib import Path
|
||||
from datetime import date
|
||||
from src.dal.abstract_dal import IStudentDAL
|
||||
from src.models.student import Student
|
||||
|
||||
|
||||
class CSVStudentDAL(IStudentDAL):
|
||||
def __init__(self, file_path: str = "data/students.csv"):
|
||||
self.file_path = Path(file_path)
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
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"
|
||||
])
|
||||
|
||||
def _read_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:
|
||||
try:
|
||||
student = Student(
|
||||
name=row["name"],
|
||||
id_card=row["id_card"],
|
||||
stu_id=row["stu_id"],
|
||||
gender=bool(int(row["gender"])) if row["gender"] else None,
|
||||
height=int(row["height"]) if row["height"] else None,
|
||||
weight=float(row["weight"]) if row["weight"] else None,
|
||||
enrollment_date=row["enrollment_date"],
|
||||
class_name=row["class_name"],
|
||||
major=row["major"]
|
||||
)
|
||||
students.append(student)
|
||||
except (ValueError, KeyError) as e:
|
||||
continue
|
||||
return students
|
||||
|
||||
def _write_all(self, students: List[Student]) -> bool:
|
||||
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"
|
||||
])
|
||||
writer.writeheader()
|
||||
for student in students:
|
||||
writer.writerow(student.to_dict())
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
# 实现所有抽象方法...
|
||||
# 完整实现会根据上述_reader_all和_write_all方法完成各个接口
|
||||
@ -0,0 +1,42 @@
|
||||
from datetime import date
|
||||
from typing import Optional, Union
|
||||
from dataclasses import dataclass
|
||||
from src.utils.id_card_utils import parse_id_card
|
||||
|
||||
|
||||
@dataclass
|
||||
class Student:
|
||||
name: str
|
||||
id_card: str
|
||||
stu_id: str
|
||||
gender: Optional[bool] = None # True for male, False for female
|
||||
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
|
||||
|
||||
def __post_init__(self):
|
||||
# 从身份证号解析生日和年龄
|
||||
self.birthday = parse_id_card.get_birthday(self.id_card)
|
||||
self.age = parse_id_card.get_age(self.id_card)
|
||||
|
||||
# 确保入学日期是date对象
|
||||
if isinstance(self.enrollment_date, str):
|
||||
try:
|
||||
self.enrollment_date = date.fromisoformat(self.enrollment_date)
|
||||
except ValueError:
|
||||
raise ValueError("Invalid enrollment date format. Use YYYY-MM-DD")
|
||||
|
||||
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
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
from typing import List, Optional
|
||||
from src.dal.abstract_dal import IStudentDAL
|
||||
from src.models.student import Student
|
||||
from src.bll.validators import validate_student
|
||||
|
||||
|
||||
class StudentService:
|
||||
def __init__(self, dal: IStudentDAL):
|
||||
self.dal = dal
|
||||
|
||||
def add_student(self, student: Student) -> tuple[bool, str]:
|
||||
"""添加学生信息,返回是否成功和错误信息"""
|
||||
is_valid, msg = validate_student(student)
|
||||
if not is_valid:
|
||||
return False, msg
|
||||
|
||||
if self.dal.get_by_id(student.id_card):
|
||||
return False, "身份证号已存在"
|
||||
|
||||
if self.dal.get_by_stu_id(student.stu_id):
|
||||
return False, "学号已存在"
|
||||
|
||||
if self.dal.add_student(student):
|
||||
return True, "添加成功"
|
||||
return False, "添加失败"
|
||||
|
||||
def delete_student(self, id_card: str) -> tuple[bool, str]:
|
||||
"""删除学生信息"""
|
||||
if not self.dal.get_by_id(id_card):
|
||||
return False, "学生不存在"
|
||||
|
||||
if self.dal.delete_student(id_card):
|
||||
return True, "删除成功"
|
||||
return False, "删除失败"
|
||||
|
||||
def update_student(self, student: Student) -> tuple[bool, str]:
|
||||
"""更新学生信息"""
|
||||
is_valid, msg = validate_student(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, "学号已被其他学生使用"
|
||||
|
||||
if self.dal.update_student(student):
|
||||
return True, "更新成功"
|
||||
return False, "更新失败"
|
||||
|
||||
# 其他业务方法...
|
||||
Loading…
Reference in new issue