commit 1018878da40a0234fce20524a85de710336c7370
Author: 员卓豪 <2232041467@qq.com>
Date: Wed Jun 25 10:48:35 2025 +0800
提交项目
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..359bb53
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..e05e84c
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..a89ab40
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/script.py b/script.py
new file mode 100644
index 0000000..aa31e90
--- /dev/null
+++ b/script.py
@@ -0,0 +1,259 @@
+import csv
+import json
+import sqlite3
+from abc import ABC, abstractmethod
+from datetime import date
+from typing import List, Optional, Dict, Any, Union
+
+
+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
+ self.height = height
+ self.weight = weight
+ self.enrollment_date = date.fromisoformat(enrollment_date) if isinstance(enrollment_date,
+ str) else enrollment_date
+ self.class_name = class_name
+ self.major = major
+ self.age, self.birthday = self._calculate_age_and_birthday()
+ self._validation_errors = self._validate_all()
+
+ def _calculate_age_and_birthday(self) -> tuple[int, date]:
+ birth_year, birth_month, birth_day = map(int, (self.id_card[6:10], self.id_card[10:12], self.id_card[12:14]))
+ birthday = date(birth_year, birth_month, birth_day)
+ today = date.today()
+ age = today.year - birth_year - ((today.month, today.day) < (birth_month, birth_day))
+ return age, birthday
+
+ def _validate_all(self) -> List[str]:
+ errors = []
+ if len(self.id_card) != 18 or not self.id_card[:17].isdigit():
+ errors.append("身份证号必须为18位数字")
+ if not self.stu_id:
+ errors.append("学号不能为空")
+ if not (2 <= len(self.name) <= 20) or not self.name.isalpha():
+ errors.append("姓名需为2-20个字母")
+ if self.enrollment_date and self.enrollment_date < self.birthday:
+ errors.append("入学日期不能早于出生日期")
+ if self.height and not 50 <= self.height <= 250:
+ errors.append("身高需在50-250cm之间")
+ if self.weight and not 5 <= self.weight <= 300:
+ errors.append("体重需在5-300kg之间")
+ return errors
+
+ @property
+ def is_valid(self) -> bool:
+ return not self._validation_errors
+
+ def to_dict(self) -> Dict[str, Any]:
+ 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,
+ 'age': self.age, 'birthday': self.birthday.isoformat()
+ }
+
+ @classmethod
+ def from_dict(cls, data: Dict[str, Any]) -> 'Student':
+ return cls(**data)
+
+
+class IStudentDAL(ABC):
+ @abstractmethod
+ def get_by_id(self, id_card: str) -> Optional[Student]: ...
+
+ @abstractmethod
+ def get_by_stu_id(self, stu_id: str) -> Optional[Student]: ...
+
+ @abstractmethod
+ def get_all(self) -> List[Student]: ...
+
+ @abstractmethod
+ def add(self, student: Student) -> bool: ...
+
+ @abstractmethod
+ def delete(self, id_card: str = None, stu_id: str = None) -> bool: ...
+
+ @abstractmethod
+ def update(self, student: Student) -> bool: ...
+
+
+class BaseDAL(IStudentDAL):
+ def __init__(self, file_path: str):
+ self.file_path = file_path
+ self._init_storage()
+
+ def _init_storage(self): pass
+
+ def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
+ return next((s for s in self.get_all() if s.stu_id == stu_id), None)
+
+
+class CSVStudentDAL(BaseDAL):
+ def _init_storage(self):
+ with open(self.file_path, 'a') as f: pass # Ensure file exists
+
+ def get_all(self) -> List[Student]:
+ with open(self.file_path, 'r') as f:
+ return [Student.from_dict(row) for row in csv.DictReader(f)]
+
+ def add(self, student: Student) -> bool:
+ if self.get_by_id(student.id_card) or self.get_by_stu_id(student.stu_id):
+ return False
+ with open(self.file_path, 'a', newline='') as f:
+ writer = csv.DictWriter(f, fieldnames=student.to_dict().keys())
+ if f.tell() == 0: writer.writeheader()
+ writer.writerow(student.to_dict())
+ return True
+
+ def delete(self, id_card: str = None, stu_id: str = None) -> bool:
+ students = [s for s in self.get_all()
+ if not ((id_card and s.id_card == id_card) or (stu_id and s.stu_id == stu_id))]
+ with open(self.file_path, 'w', newline='') as f:
+ if students:
+ writer = csv.DictWriter(f, fieldnames=students[0].to_dict().keys())
+ writer.writeheader()
+ writer.writerows(s.to_dict() for s in students)
+ return len(students) < len(self.get_all())
+
+
+class JSONStudentDAL(BaseDAL):
+ def _init_storage(self):
+ with open(self.file_path, 'a') as f: pass
+
+ def get_all(self) -> List[Student]:
+ with open(self.file_path, 'r') as f:
+ return [Student.from_dict(row) for row in json.load(f)]
+
+ def add(self, student: Student) -> bool:
+ data = self.get_all()
+ if any(s.id_card == student.id_card or s.stu_id == student.stu_id for s in data):
+ return False
+ data.append(student)
+ self._save_all(data)
+ return True
+
+ def _save_all(self, data: List[Student]):
+ with open(self.file_path, 'w') as f:
+ json.dump([s.to_dict() for s in data], f, indent=2)
+
+
+class SQLiteStudentDAL(BaseDAL):
+ def _init_storage(self):
+ with sqlite3.connect(self.file_path) as conn:
+ conn.execute('''
+ CREATE TABLE IF NOT EXISTS students (
+ id_card TEXT PRIMARY KEY, stu_id TEXT UNIQUE, name TEXT,
+ gender BOOLEAN, height INTEGER, weight REAL,
+ enrollment_date TEXT, class_name TEXT, major TEXT,
+ age INTEGER, birthday TEXT)''')
+
+ def get_all(self) -> List[Student]:
+ with sqlite3.connect(self.file_path) as conn:
+ rows = conn.execute("SELECT * FROM students").fetchall()
+ return [Student.from_dict(dict(zip(('id_card', 'stu_id', 'name', 'gender', 'height',
+ 'weight', 'enrollment_date', 'class_name', 'major',
+ 'age', 'birthday'), row))) for row in rows]
+
+ def add(self, student: Student) -> bool:
+ try:
+ with sqlite3.connect(self.file_path) as conn:
+ conn.execute('''
+ INSERT INTO students VALUES (?,?,?,?,?,?,?,?,?,?,?)''',
+ (student.id_card, student.stu_id, student.name, student.gender,
+ student.height, student.weight,
+ student.enrollment_date.isoformat() if student.enrollment_date else None,
+ student.class_name, student.major, student.age, student.birthday.isoformat()))
+ return True
+ except sqlite3.IntegrityError:
+ return False
+
+
+class StudentService:
+ def __init__(self, dal: IStudentDAL):
+ self.dal = dal
+
+ def add_student(self, student: Student) -> bool:
+ if not student.is_valid:
+ raise ValueError(f"Invalid student: {', '.join(student._validation_errors)}")
+ return self.dal.add(student)
+
+ def get_all(self) -> List[Student]:
+ return self.dal.get_all()
+
+ def query(self, **kwargs) -> List[Student]:
+ return [s for s in self.get_all()
+ if all(getattr(s, k, None) == v for k, v in kwargs.items() if v)]
+
+ def get_stats(self, field: str) -> Dict[str, int]:
+ return {v: sum(1 for s in self.get_all() if getattr(s, field) == v)
+ for v in set(getattr(s, field) for s in self.get_all())}
+
+
+class StudentCLI:
+ def __init__(self, service: StudentService):
+ self.service = service
+
+ def run(self):
+ while True:
+ print("\n1. Add 2. List 3. Query 4. Stats 0. Exit")
+ choice = input("Choice: ")
+ if choice == "1":
+ self._add()
+ elif choice == "2":
+ self._list()
+ elif choice == "3":
+ self._query()
+ elif choice == "4":
+ self._stats()
+ elif choice == "0":
+ break
+
+ def _add(self):
+ student = Student(
+ name=input("Name: "),
+ id_card=input("ID Card: "),
+ stu_id=input("Student ID: "),
+ gender=input("Gender (True/False): ").lower() == 'true',
+ height=int(input("Height: ")) if input("Height (optional): ") else None,
+ weight=float(input("Weight: ")) if input("Weight (optional): ") else None
+ )
+ try:
+ if self.service.add_student(student):
+ print("Added successfully!")
+ except ValueError as e:
+ print(f"Error: {e}")
+
+ def _list(self):
+ for student in self.service.get_all():
+ print(student.to_dict())
+
+ def _query(self):
+ field = input("Field to query (name/class_name/major): ")
+ value = input(f"{field} value: ")
+ for student in self.service.query(**{field: value}):
+ print(student.to_dict())
+
+ def _stats(self):
+ field = input("Field for stats (class_name/major): ")
+ for k, v in self.service.get_stats(field).items():
+ print(f"{k}: {v}")
+
+
+if __name__ == "__main__":
+ storage = input("Storage (csv/json/sqlite): ").lower()
+ dal = {
+ 'csv': CSVStudentDAL('students.csv'),
+ 'json': JSONStudentDAL('students.json'),
+ 'sqlite': SQLiteStudentDAL('students.db')
+ }.get(storage, SQLiteStudentDAL('students.db'))
+
+ service = StudentService(dal)
+ StudentCLI(service).run()
\ No newline at end of file
diff --git a/students.csv b/students.csv
new file mode 100644
index 0000000..e69de29
diff --git a/students.json b/students.json
new file mode 100644
index 0000000..0d7e7bb
--- /dev/null
+++ b/students.json
@@ -0,0 +1,15 @@
+[
+ {
+ "name": "\u5458\u5353\u8c6a",
+ "id_card": "610502200403240018",
+ "stu_id": "110309230907009",
+ "gender": false,
+ "height": 176,
+ "weight": 62.0,
+ "enrollment_date": "2023-09-01",
+ "class_name": "4",
+ "major": "\u8bf7\u9ad8\u7ea7",
+ "age": 21,
+ "birthday": "2004-03-24"
+ }
+]
\ No newline at end of file
diff --git a/期末复习.py b/期末复习.py
new file mode 100644
index 0000000..1cb22cb
--- /dev/null
+++ b/期末复习.py
@@ -0,0 +1,836 @@
+import csv
+import json
+import os
+import sqlite3
+from abc import ABC, abstractmethod
+from datetime import date
+from typing import List, Optional, Union
+
+
+# 学生类定义
+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: str = None):
+ self.name = name
+ self.id_card = id_card
+ self.stu_id = stu_id
+ self.gender = gender
+ self.height = height
+ self.weight = weight
+ if isinstance(enrollment_date, str):
+ self.enrollment_date = date.fromisoformat(enrollment_date)
+ else:
+ self.enrollment_date = enrollment_date
+ self.class_name = class_name
+ self.major = major
+ self.age, self.birthday = self._calculate_age_and_birthday()
+ self._validation_errors = []
+ self._validate_all()
+
+ def _calculate_age_and_birthday(self):
+ birth_year = int(self.id_card[6:10])
+ birth_month = int(self.id_card[10:12])
+ birth_day = int(self.id_card[12:14])
+ birthday = date(birth_year, birth_month, birth_day)
+ today = date.today()
+ age = today.year - birth_year
+ if (today.month, today.day) < (birth_month, birth_day):
+ age -= 1
+ return age, birthday
+
+ def _validate_all(self):
+ self._validate_id_card()
+ self._validate_stu_id()
+ self._validate_name()
+ self._validate_enrollment_date()
+ self._validate_height()
+ self._validate_weight()
+
+ def _validate_id_card(self):
+ if len(self.id_card) != 18:
+ self._validation_errors.append("身份证号必须为18位")
+ # 简单的校验位验证,可根据国家标准完善
+ try:
+ int(self.id_card[:17])
+ except ValueError:
+ self._validation_errors.append("身份证号前17位必须为数字")
+
+ def _validate_stu_id(self):
+ if not self.stu_id:
+ self._validation_errors.append("学号不能为空")
+
+ def _validate_name(self):
+ if not (2 <= len(self.name) <= 20):
+ self._validation_errors.append("姓名长度需在2-20个字符之间")
+ if any(char.isdigit() or not char.isalpha() for char in self.name):
+ self._validation_errors.append("姓名不能包含数字和特殊符号")
+
+ def _validate_enrollment_date(self):
+ if self.enrollment_date and self.enrollment_date < self.birthday:
+ self._validation_errors.append("入学日期不能早于出生日期")
+
+ def _validate_height(self):
+ if self.height is not None and not (50 <= self.height <= 250):
+ self._validation_errors.append("身高需在50-250cm之间")
+
+ def _validate_weight(self):
+ if self.weight is not None and not (5 <= self.weight <= 300):
+ self._validation_errors.append("体重需在5-300kg之间")
+
+ @property
+ def is_valid(self):
+ return len(self._validation_errors) == 0
+
+ def get_errors(self):
+ return self._validation_errors.copy()
+
+ 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,
+ 'age': self.age,
+ 'birthday': self.birthday.isoformat()
+ }
+
+ @classmethod
+ def from_dict(cls, data):
+ return cls(
+ name=data['name'],
+ id_card=data['id_card'],
+ stu_id=data['stu_id'],
+ gender=data['gender'],
+ height=data['height'],
+ weight=data['weight'],
+ enrollment_date=data['enrollment_date'],
+ class_name=data['class_name'],
+ major=data['major']
+ )
+
+
+# 数据访问层接口
+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 add(self, student: Student) -> bool:
+ pass
+
+ @abstractmethod
+ def delete(self, id_card: str = None, stu_id: str = None) -> bool:
+ pass
+
+ @abstractmethod
+ def update(self, student: Student) -> bool:
+ pass
+
+
+# CSV数据访问层实现
+class CSVStudentDAL(IStudentDAL):
+ def __init__(self, file_path):
+ self.file_path = file_path
+ if not os.path.exists(file_path):
+ with open(file_path, 'w', newline='') as f:
+ writer = csv.DictWriter(f, fieldnames=Student.from_dict({}).to_dict().keys())
+ writer.writeheader()
+
+ def get_by_id(self, id_card: str) -> Optional[Student]:
+ with open(self.file_path, 'r', newline='') as f:
+ reader = csv.DictReader(f)
+ for row in reader:
+ if row['id_card'] == id_card:
+ return Student.from_dict(row)
+ return None
+
+ def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
+ with open(self.file_path, 'r', newline='') as f:
+ reader = csv.DictReader(f)
+ for row in reader:
+ if row['stu_id'] == stu_id:
+ return Student.from_dict(row)
+ return None
+
+ def get_all(self) -> List[Student]:
+ students = []
+ with open(self.file_path, 'r', newline='') as f:
+ reader = csv.DictReader(f)
+ for row in reader:
+ students.append(Student.from_dict(row))
+ return students
+
+ def add(self, student: Student) -> bool:
+ if self.get_by_id(student.id_card) or self.get_by_stu_id(student.stu_id):
+ return False
+ with open(self.file_path, 'a', newline='') as f:
+ writer = csv.DictWriter(f, fieldnames=student.to_dict().keys())
+ writer.writerow(student.to_dict())
+ return True
+
+ def delete(self, id_card: str = None, stu_id: str = None) -> bool:
+ students = self.get_all()
+ initial_length = len(students)
+ if id_card:
+ students = [s for s in students if s.id_card != id_card]
+ elif stu_id:
+ students = [s for s in students if s.stu_id != stu_id]
+ if len(students) < initial_length:
+ with open(self.file_path, 'w', newline='') as f:
+ writer = csv.DictWriter(f, fieldnames=students[0].to_dict().keys() if students else [])
+ writer.writeheader()
+ for s in students:
+ writer.writerow(s.to_dict())
+ return True
+ return False
+
+ def update(self, student: Student) -> bool:
+ students = self.get_all()
+ for i, s in enumerate(students):
+ if s.id_card == student.id_card or s.stu_id == student.stu_id:
+ students[i] = student
+ with open(self.file_path, 'w', newline='') as f:
+ writer = csv.DictWriter(f, fieldnames=students[0].to_dict().keys() if students else [])
+ writer.writeheader()
+ for s in students:
+ writer.writerow(s.to_dict())
+ return True
+ return False
+
+
+# JSON数据访问层实现
+class JSONStudentDAL(IStudentDAL):
+ def __init__(self, file_path):
+ self.file_path = file_path
+ if not os.path.exists(file_path):
+ with open(file_path, 'w') as f:
+ json.dump([], f)
+
+ def get_by_id(self, id_card: str) -> Optional[Student]:
+ with open(self.file_path, 'r') as f:
+ data = json.load(f)
+ for row in data:
+ if row['id_card'] == id_card:
+ return Student.from_dict(row)
+ return None
+
+ def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
+ with open(self.file_path, 'r') as f:
+ data = json.load(f)
+ for row in data:
+ if row['stu_id'] == stu_id:
+ return Student.from_dict(row)
+ return None
+
+ def get_all(self) -> List[Student]:
+ with open(self.file_path, 'r') as f:
+ data = json.load(f)
+ return [Student.from_dict(row) for row in data]
+
+ def add(self, student: Student) -> bool:
+ if self.get_by_id(student.id_card) or self.get_by_stu_id(student.stu_id):
+ return False
+ with open(self.file_path, 'r') as f:
+ data = json.load(f)
+ data.append(student.to_dict())
+ with open(self.file_path, 'w') as f:
+ json.dump(data, f, indent=4)
+ return True
+
+ def delete(self, id_card: str = None, stu_id: str = None) -> bool:
+ with open(self.file_path, 'r') as f:
+ data = json.load(f)
+ initial_length = len(data)
+ if id_card:
+ data = [s for s in data if s['id_card'] != id_card]
+ elif stu_id:
+ data = [s for s in data if s['stu_id'] != stu_id]
+ if len(data) < initial_length:
+ with open(self.file_path, 'w') as f:
+ json.dump(data, f, indent=4)
+ return True
+ return False
+
+ def update(self, student: Student) -> bool:
+ with open(self.file_path, 'r') as f:
+ data = json.load(f)
+ for i, s in enumerate(data):
+ if s['id_card'] == student.id_card or s['stu_id'] == student.stu_id:
+ data[i] = student.to_dict()
+ with open(self.file_path, 'w') as f:
+ json.dump(data, f, indent=4)
+ return True
+ return False
+
+
+# SQLite数据访问层实现
+class SQLiteStudentDAL(IStudentDAL):
+ def __init__(self, db_path):
+ self.db_path = db_path
+ self._create_table()
+
+ def _create_table(self):
+ conn = sqlite3.connect(self.db_path)
+ cursor = conn.cursor()
+ cursor.execute('''
+ CREATE TABLE IF NOT EXISTS students (
+ id_card TEXT PRIMARY KEY,
+ stu_id TEXT UNIQUE,
+ name TEXT,
+ gender BOOLEAN,
+ height INTEGER,
+ weight REAL,
+ enrollment_date TEXT,
+ class_name TEXT,
+ major TEXT,
+ age INTEGER,
+ birthday TEXT
+ )
+ ''')
+ conn.commit()
+ conn.close()
+
+ def get_by_id(self, id_card: str) -> Optional[Student]:
+ conn = sqlite3.connect(self.db_path)
+ cursor = conn.cursor()
+ cursor.execute("SELECT * FROM students WHERE id_card =?", (id_card,))
+ row = cursor.fetchone()
+ conn.close()
+ if row:
+ data = dict(zip([description[0] for description in cursor.description], row))
+ return Student.from_dict(data)
+ return None
+
+ def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
+ conn = sqlite3.connect(self.db_path)
+ cursor = conn.cursor()
+ cursor.execute("SELECT * FROM students WHERE stu_id =?", (stu_id,))
+ row = cursor.fetchone()
+ conn.close()
+ if row:
+ data = dict(zip([description[0] for description in cursor.description], row))
+ return Student.from_dict(data)
+ return None
+
+ def get_all(self) -> List[Student]:
+ conn = sqlite3.connect(self.db_path)
+ cursor = conn.cursor()
+ cursor.execute("SELECT * FROM students")
+ rows = cursor.fetchall()
+ conn.close()
+ return [Student.from_dict(dict(zip([description[0] for description in cursor.description], row))) for row in
+ rows]
+
+ def add(self, student: Student) -> bool:
+ if self.get_by_id(student.id_card) or self.get_by_stu_id(student.stu_id):
+ return False
+ conn = sqlite3.connect(self.db_path)
+ cursor = conn.cursor()
+ try:
+ cursor.execute('''
+ INSERT INTO students (id_card, stu_id, name, gender, height, weight, enrollment_date, class_name, major, age, birthday)
+ VALUES (?,?,?,?,?,?,?,?,?,?,?)
+ ''', (
+ student.id_card, student.stu_id, student.name, student.gender, student.height, student.weight,
+ student.enrollment_date.isoformat() if student.enrollment_date else None, student.class_name,
+ student.major, student.age, student.birthday.isoformat()))
+ conn.commit()
+ return True
+ except sqlite3.IntegrityError:
+ return False
+ finally:
+ conn.close()
+
+ def delete(self, id_card: str = None, stu_id: str = None) -> bool:
+ conn = sqlite3.connect(self.db_path)
+ cursor = conn.cursor()
+ if id_card:
+ cursor.execute("DELETE FROM students WHERE id_card =?", (id_card,))
+ elif stu_id:
+ cursor.execute("DELETE FROM students WHERE stu_id =?", (stu_id,))
+ rows_affected = cursor.rowcount
+ conn.commit()
+ conn.close()
+ return rows_affected > 0
+
+ def update(self, student: Student) -> bool:
+ conn = sqlite3.connect(self.db_path)
+ cursor = conn.cursor()
+ try:
+ cursor.execute('''
+ UPDATE students
+ SET stu_id =?, name =?, gender =?, height =?, weight =?, enrollment_date =?, class_name =?, major =?, age =?, birthday =?
+ WHERE id_card =?
+ ''', (
+ student.stu_id, student.name, student.gender, student.height, student.weight,
+ student.enrollment_date.isoformat() if student.enrollment_date else None, student.class_name,
+ student.major, student.age, student.birthday.isoformat(), student.id_card))
+ rows_affected = cursor.rowcount
+ conn.commit()
+ return rows_affected > 0
+ except sqlite3.IntegrityError:
+ return False
+ finally:
+ conn.close()
+
+
+# 业务逻辑层
+class StudentBLL:
+ def __init__(self, dal: IStudentDAL):
+ self.dal = dal
+
+ def add_student(self, student: Student) -> bool:
+ if not student.is_valid:
+ raise ValueError(f"学生数据无效: {', '.join(student.get_errors())}")
+ return self.dal.add(student)
+
+ def delete_student(self, id_card: str = None, stu_id: str = None) -> bool:
+ return self.dal.delete(id_card, stu_id)
+
+ def update_student(self, student: Student) -> bool:
+ if not student.is_valid:
+ raise ValueError(f"学生数据无效: {', '.join(student.get_errors())}")
+ return self.dal.update(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 query_by_name(self, name: str) -> List[Student]:
+ return [s for s in self.get_all_students() if name.lower() in s.name.lower()]
+
+ def query_by_class(self, class_name: str) -> List[Student]:
+ return [s for s in self.get_all_students() if class_name.lower() in s.class_name.lower()]
+
+ def query_by_major(self, major: str) -> List[Student]:
+ return [s for s in self.get_all_students() if major.lower() in s.major.lower()]
+
+ def get_student_count(self) -> int:
+ return len(self.get_all_students())
+
+ def get_major_count(self) -> dict:
+ major_count = {}
+ for student in self.get_all_students():
+ if student.major in major_count:
+ major_count[student.major] += 1
+ else:
+ major_count[student.major] = 1
+ return major_count
+
+ def get_avg_height_by_class(self, class_name: str) -> float:
+ students = [s for s in self.get_all_students() if s.class_name == class_name and s.height is not None]
+ if not students:
+ return 0
+ return sum(s.height for s in students) / len(students)
+
+ def get_avg_weight_by_class(self, class_name: str) -> float:
+ students = [s for s in self.get_all_students() if s.class_name == class_name and s.weight is not None]
+ if not students:
+ return 0
+ return sum(s.weight for s in students) / len(students)
+
+ def get_avg_height_by_major(self, major: str) -> float:
+ students = [s for s in self.get_all_students() if s.major == major and s.height is not None]
+ if not students:
+ return 0
+ return sum(s.height for s in students) / len(students)
+
+ def get_avg_weight_by_major(self, major: str) -> float:
+ students = [s for s in self.get_all_students() if s.major == major and s.weight is not None]
+ if not students:
+ return 0
+ return sum(s.weight for s in students) / len(students)
+
+ def import_from_csv(self, file_path):
+ with open(file_path, 'r', newline='') as f:
+ reader = csv.DictReader(f)
+ for row in reader:
+ student = Student.from_dict(row)
+ if student.is_valid:
+ self.add_student(student)
+
+ def import_from_json(self, file_path):
+ with open(file_path, 'r') as f:
+ data = json.load(f)
+ for row in data:
+ student = Student.from_dict(row)
+ if student.is_valid:
+ self.add_student(student)
+
+ def export_to_csv(self, file_path):
+ students = self.get_all_students()
+ with open(file_path, 'w', newline='') as f:
+ writer = csv.DictWriter(f, fieldnames=students[0].to_dict().keys() if students else [])
+ writer.writeheader()
+ for s in students:
+ writer.writerow(s.to_dict())
+
+ def export_to_json(self, file_path):
+ students = self.get_all_students()
+ with open(file_path, 'w') as f:
+ json.dump([s.to_dict() for s in students], f, indent=4)
+
+
+# 用户界面层
+class StudentUI:
+ def __init__(self, bll: StudentBLL):
+ self.bll = bll
+
+ def display_menu(self):
+ print("\n===== 学生信息管理系统 =====")
+ print("1. 添加学生信息")
+ print("2. 删除学生信息")
+ print("3. 修改学生信息")
+ print("4. 查看学生详细信息")
+ print("5. 查询学生信息")
+ print("6. 统计信息")
+ print("7. 数据导入")
+ print("8. 数据导出")
+ print("0. 退出系统")
+ print("==========================")
+
+ def display_query_menu(self):
+ print("\n===== 查询学生信息 =====")
+ print("1. 按学号查询")
+ print("2. 按姓名查询")
+ print("3. 按班级查询")
+ print("4. 按专业查询")
+ print("5. 返回上一级")
+ print("======================")
+
+ def display_stats_menu(self):
+ print("\n===== 统计信息 =====")
+ print("1. 学生总数")
+ print("2. 各专业学生人数")
+ print("3. 按班级计算平均身高")
+ print("4. 按班级计算平均体重")
+ print("5. 按专业计算平均身高")
+ print("6. 按专业计算平均体重")
+ print("7. 返回上一级")
+ print("====================")
+
+ def display_import_menu(self):
+ print("\n===== 数据导入 =====")
+ print("1. 从CSV导入")
+ print("2. 从JSON导入")
+ print("3. 返回上一级")
+ print("====================")
+
+ def display_export_menu(self):
+ print("\n===== 数据导出 =====")
+ print("1. 导出到CSV")
+ print("2. 导出到JSON")
+ print("3. 返回上一级")
+ print("====================")
+
+ def get_student_input(self, existing_student=None):
+ """获取学生输入,支持使用现有学生数据作为默认值"""
+ if existing_student:
+ print(f"当前值: {existing_student.name}")
+ name = input("请输入姓名 (直接回车保持不变): ") or (existing_student.name if existing_student else "")
+
+ if existing_student:
+ print(f"当前值: {existing_student.id_card}")
+ id_card = input("请输入身份证号 (直接回车保持不变): ") or (existing_student.id_card if existing_student else "")
+
+ if existing_student:
+ print(f"当前值: {existing_student.stu_id}")
+ stu_id = input("请输入学号 (直接回车保持不变): ") or (existing_student.stu_id if existing_student else "")
+
+ if existing_student and existing_student.gender is not None:
+ print(f"当前值: {existing_student.gender}")
+ gender_str = input("请输入性别 (True/False,直接回车保持不变): ")
+ gender = None
+ if gender_str:
+ gender = gender_str.lower() == 'true'
+ elif existing_student:
+ gender = existing_student.gender
+
+ if existing_student and existing_student.height is not None:
+ print(f"当前值: {existing_student.height}")
+ height_str = input("请输入身高 (cm,直接回车保持不变): ")
+ height = None
+ if height_str:
+ height = int(height_str)
+ elif existing_student:
+ height = existing_student.height
+
+ if existing_student and existing_student.weight is not None:
+ print(f"当前值: {existing_student.weight}")
+ weight_str = input("请输入体重 (kg,直接回车保持不变): ")
+ weight = None
+ if weight_str:
+ weight = float(weight_str)
+ elif existing_student:
+ weight = existing_student.weight
+
+ if existing_student and existing_student.enrollment_date:
+ print(f"当前值: {existing_student.enrollment_date.isoformat()}")
+ enrollment_date = input("请输入入学日期 (YYYY-MM-DD,直接回车保持不变): ") or (
+ existing_student.enrollment_date.isoformat() if existing_student and existing_student.enrollment_date else None
+ )
+
+ if existing_student:
+ print(f"当前值: {existing_student.class_name}")
+ class_name = input("请输入班级名称 (直接回车保持不变): ") or (
+ existing_student.class_name if existing_student else "")
+
+ if existing_student:
+ print(f"当前值: {existing_student.major}")
+ major = input("请输入专业 (直接回车保持不变): ") or (existing_student.major if existing_student else "")
+
+ return Student(name, id_card, stu_id, gender, height, weight, enrollment_date, class_name, major)
+
+ def run(self):
+ while True:
+ self.display_menu()
+ choice = input("请选择操作: ")
+
+ if choice == "1":
+ self.add_student()
+ elif choice == "2":
+ self.delete_student()
+ elif choice == "3":
+ self.update_student()
+ elif choice == "4":
+ self.view_student_details()
+ elif choice == "5":
+ self.query_student()
+ elif choice == "6":
+ self.statistics()
+ elif choice == "7":
+ self.import_data()
+ elif choice == "8":
+ self.export_data()
+ elif choice == "0":
+ print("感谢使用,再见!")
+ break
+ else:
+ print("无效选择,请重试!")
+
+ def add_student(self):
+ print("\n===== 添加学生信息 =====")
+ try:
+ student = self.get_student_input()
+ if self.bll.add_student(student):
+ print("添加成功!")
+ else:
+ print("添加失败,学号或身份证号可能已存在。")
+ except ValueError as e:
+ print(f"添加失败: {e}")
+
+ def delete_student(self):
+ print("\n===== 删除学生信息 =====")
+ choice = input("按学号删除请输入1,按身份证号删除请输入2: ")
+ if choice == "1":
+ stu_id = input("请输入要删除的学号: ")
+ if self.bll.delete_student(stu_id=stu_id):
+ print("删除成功!")
+ else:
+ print("删除失败,学号不存在。")
+ elif choice == "2":
+ id_card = input("请输入要删除的身份证号: ")
+ if self.bll.delete_student(id_card=id_card):
+ print("删除成功!")
+ else:
+ print("删除失败,身份证号不存在。")
+ else:
+ print("无效选择,请重试!")
+
+ def update_student(self):
+ print("\n===== 修改学生信息 =====")
+ stu_id = input("请输入要修改的学生学号: ")
+ student = self.bll.get_student_by_stu_id(stu_id)
+ if not student:
+ print("学号不存在。")
+ return
+
+ print(f"当前信息: {student.to_dict()}")
+ print("(直接回车表示不修改)")
+
+ # 使用现有的学生数据作为默认值
+ new_student = self.get_student_input(student)
+
+ # 确保不修改ID和学号
+ new_student.id_card = student.id_card
+ new_student.stu_id = student.stu_id
+
+ try:
+ if self.bll.update_student(new_student):
+ print("修改成功!")
+ else:
+ print("修改失败,可能是数据验证不通过。")
+ except ValueError as e:
+ print(f"修改失败: {e}")
+
+ def view_student_details(self):
+ print("\n===== 查看学生详细信息 =====")
+ stu_id = input("请输入要查看的学生学号: ")
+ student = self.bll.get_student_by_stu_id(stu_id)
+ if student:
+ print(student.to_dict())
+ else:
+ print("学号不存在。")
+
+ def query_student(self):
+ while True:
+ self.display_query_menu()
+ choice = input("请选择查询方式: ")
+ if choice == "1":
+ stu_id = input("请输入学号: ")
+ student = self.bll.get_student_by_stu_id(stu_id)
+ if student:
+ print(student.to_dict())
+ else:
+ print("未找到该学生。")
+ elif choice == "2":
+ name = input("请输入姓名: ")
+ students = self.bll.query_by_name(name)
+ if students:
+ for s in students:
+ print(s.to_dict())
+ else:
+ print("未找到匹配的学生。")
+ elif choice == "3":
+ class_name = input("请输入班级名称: ")
+ students = self.bll.query_by_class(class_name)
+ if students:
+ for s in students:
+ print(s.to_dict())
+ else:
+ print("未找到匹配的学生。")
+ elif choice == "4":
+ major = input("请输入专业名称: ")
+ students = self.bll.query_by_major(major)
+ if students:
+ for s in students:
+ print(s.to_dict())
+ else:
+ print("未找到匹配的学生。")
+ elif choice == "5":
+ break
+ else:
+ print("无效选择,请重试!")
+
+ def statistics(self):
+ while True:
+ self.display_stats_menu()
+ choice = input("请选择统计方式: ")
+ if choice == "1":
+ print(f"学生总数: {self.bll.get_student_count()}")
+ elif choice == "2":
+ major_count = self.bll.get_major_count()
+ for major, count in major_count.items():
+ print(f"{major}: {count}人")
+ elif choice == "3":
+ class_name = input("请输入班级名称: ")
+ avg_height = self.bll.get_avg_height_by_class(class_name)
+ print(f"{class_name} 班级平均身高: {avg_height:.2f} cm")
+ elif choice == "4":
+ class_name = input("请输入班级名称: ")
+ avg_weight = self.bll.get_avg_weight_by_class(class_name)
+ print(f"{class_name} 班级平均体重: {avg_weight:.2f} kg")
+ elif choice == "5":
+ major = input("请输入专业名称: ")
+ avg_height = self.bll.get_avg_height_by_major(major)
+ print(f"{major} 专业平均身高: {avg_height:.2f} cm")
+ elif choice == "6":
+ major = input("请输入专业名称: ")
+ avg_weight = self.bll.get_avg_weight_by_major(major)
+ print(f"{major} 专业平均体重: {avg_weight:.2f} kg")
+ elif choice == "7":
+ break
+ else:
+ print("无效选择,请重试!")
+
+ def import_data(self):
+ while True:
+ self.display_import_menu()
+ choice = input("请选择导入方式: ")
+ if choice == "1":
+ file_path = input("请输入CSV文件路径: ")
+ try:
+ self.bll.import_from_csv(file_path)
+ print("导入成功!")
+ except Exception as e:
+ print(f"导入失败: {e}")
+ elif choice == "2":
+ file_path = input("请输入JSON文件路径: ")
+ try:
+ self.bll.import_from_json(file_path)
+ print("导入成功!")
+ except Exception as e:
+ print(f"导入失败: {e}")
+ elif choice == "3":
+ break
+ else:
+ print("无效选择,请重试!")
+
+ def export_data(self):
+ while True:
+ self.display_export_menu()
+ choice = input("请选择导出方式: ")
+ if choice == "1":
+ file_path = input("请输入导出的CSV文件路径: ")
+ try:
+ self.bll.export_to_csv(file_path)
+ print("导出成功!")
+ except Exception as e:
+ print(f"导出失败: {e}")
+ elif choice == "2":
+ file_path = input("请输入导出的JSON文件路径: ")
+ try:
+ self.bll.export_to_json(file_path)
+ print("导出成功!")
+ except Exception as e:
+ print(f"导出失败: {e}")
+ elif choice == "3":
+ break
+ else:
+ print("无效选择,请重试!")
+
+
+if __name__ == "__main__":
+ # 选择数据存储方式
+ storage_choice = input("请选择数据存储方式 (1: CSV, 2: JSON, 3: SQLite): ")
+ if storage_choice == "1":
+ dal = CSVStudentDAL("students.csv")
+ elif storage_choice == "2":
+ dal = JSONStudentDAL("students.json")
+ elif storage_choice == "3":
+ dal = SQLiteStudentDAL("students.db")
+ else:
+ print("无效选择,默认使用SQLite。")
+ dal = SQLiteStudentDAL("students.db")
+
+ bll = StudentBLL(dal)
+ ui = StudentUI(bll)
+ print("欢迎使用学生信息管理系统!")
+ ui.run()
\ No newline at end of file