Compare commits

...

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

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

@ -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,32 @@
from src.dal.csv_dal import CSVStudentDAL
from src.dal.json_dal import JSONStudentDAL
from src.dal.sqlite_dal import SQLiteStudentDAL
from src.bll.student_service import StudentService
from src.ui.console_ui import ConsoleUI
def main():
# 选择数据存储方式
print("请选择数据存储方式:")
print("1. CSV文件")
print("2. JSON文件")
print("3. SQLite数据库")
choice = input("请选择(1-3): ").strip()
if choice == "1":
dal = CSVStudentDAL()
elif choice == "2":
dal = JSONStudentDAL()
elif choice == "3":
dal = SQLiteStudentDAL()
else:
print("无效选择默认使用CSV文件存储")
dal = CSVStudentDAL()
service = StudentService(dal)
ui = ConsoleUI(service)
ui.run()
if __name__ == "__main__":
main()

@ -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…
Cancel
Save