From 55688c03a280ca8f0391c4efc5deb0ed612b17e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=9826745?= <2674517883@qq.com> Date: Thu, 26 Jun 2025 10:08:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=8B=E6=89=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- student UI.py | 1318 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1318 insertions(+) create mode 100644 student UI.py diff --git a/student UI.py b/student UI.py new file mode 100644 index 0000000..6f7b2a5 --- /dev/null +++ b/student UI.py @@ -0,0 +1,1318 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Jun 24 11:10:36 2025 + +@author: 26745 +""" + +from abc import ABC, abstractmethod +from typing import List, Optional, Dict, Union +from datetime import date, datetime +import csv +import json +import os +import re + + +class IStudentDAL(ABC): + """学生信息数据访问层接口""" + + @abstractmethod + def add(self, student: 'Student') -> bool: + """添加学生信息""" + pass + + @abstractmethod + def delete(self, id_card: str) -> bool: + """根据身份证号删除学生信息""" + pass + + @abstractmethod + def update(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 search_by_name(self, name: str) -> List['Student']: + """根据姓名模糊查询学生信息""" + pass + + @abstractmethod + def search_by_class(self, class_name: str) -> List['Student']: + """根据班级模糊查询学生信息""" + pass + + @abstractmethod + def search_by_major(self, major: str) -> List['Student']: + """根据专业模糊查询学生信息""" + pass + + @abstractmethod + def clear_all(self) -> bool: + """清空所有学生数据""" + pass + + +class StudentCSVDAL(IStudentDAL): + """CSV文件存储实现""" + + def __init__(self, file_path: str = 'students.csv'): + 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, '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[Dict]: + """从CSV文件读取所有学生数据""" + try: + with open(self.file_path, 'r', newline='', encoding='utf-8') as f: + reader = csv.DictReader(f) + return list(reader) + except FileNotFoundError: + self._ensure_file_exists() + return [] + + def _write_all(self, students: List[Dict]): + """将所有学生数据写入CSV文件""" + 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() + writer.writerows(students) + + def add(self, student: 'Student') -> bool: + students = self._read_all() + + # 检查身份证号或学号是否已存在 + for s in students: + if s['id_card'] == student.id_card: + return False + if s['stu_id'] == student.stu_id: + return False + + students.append(student.to_dict()) + self._write_all(students) + return True + + def delete(self, id_card: str) -> bool: + students = self._read_all() + original_count = len(students) + + students = [s for s in students if s['id_card'] != id_card] + + if len(students) < original_count: + self._write_all(students) + return True + return False + + def update(self, student: 'Student') -> bool: + students = self._read_all() + updated = False + + for i, s in enumerate(students): + if s['id_card'] == student.id_card: + students[i] = student.to_dict() + updated = True + break + + if updated: + self._write_all(students) + return True + return False + + def get_by_id(self, id_card: str) -> Optional['Student']: + students = self._read_all() + for s in students: + 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 = self._read_all() + for s in students: + if s['stu_id'] == stu_id: + return Student.from_dict(s) + return None + + def get_all(self) -> List['Student']: + students = self._read_all() + return [Student.from_dict(s) for s in students] + + def search_by_name(self, name: str) -> List['Student']: + students = self._read_all() + return [Student.from_dict(s) for s in students if name.lower() in s['name'].lower()] + + def search_by_class(self, class_name: str) -> List['Student']: + students = self._read_all() + return [Student.from_dict(s) for s in students + if s['class_name'] and class_name.lower() in s['class_name'].lower()] + + def search_by_major(self, major: str) -> List['Student']: + students = self._read_all() + return [Student.from_dict(s) for s in students + if s['major'] and major.lower() in s['major'].lower()] + + def clear_all(self) -> bool: + """清空所有学生数据""" + self._ensure_file_exists() + return True + + +class StudentJSONDAL(IStudentDAL): + """JSON文件存储实现""" + + def __init__(self, file_path: str = 'students.json'): + 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, 'w', encoding='utf-8') as f: + json.dump([], f) + + def _read_all(self) -> List[Dict]: + """从JSON文件读取所有学生数据""" + try: + with open(self.file_path, 'r', encoding='utf-8') as f: + return json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + self._ensure_file_exists() + return [] + + def _write_all(self, students: List[Dict]): + """将所有学生数据写入JSON文件""" + with open(self.file_path, 'w', encoding='utf-8') as f: + json.dump(students, f, ensure_ascii=False, indent=2) + + def add(self, student: 'Student') -> bool: + students = self._read_all() + + # 检查身份证号或学号是否已存在 + for s in students: + if s['id_card'] == student.id_card: + return False + if s['stu_id'] == student.stu_id: + return False + + students.append(student.to_dict()) + self._write_all(students) + return True + + def delete(self, id_card: str) -> bool: + students = self._read_all() + original_count = len(students) + + students = [s for s in students if s['id_card'] != id_card] + + if len(students) < original_count: + self._write_all(students) + return True + return False + + def update(self, student: 'Student') -> bool: + students = self._read_all() + updated = False + + for i, s in enumerate(students): + if s['id_card'] == student.id_card: + students[i] = student.to_dict() + updated = True + break + + if updated: + self._write_all(students) + return True + return False + + def get_by_id(self, id_card: str) -> Optional['Student']: + students = self._read_all() + for s in students: + 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 = self._read_all() + for s in students: + if s['stu_id'] == stu_id: + return Student.from_dict(s) + return None + + def get_all(self) -> List['Student']: + students = self._read_all() + return [Student.from_dict(s) for s in students] + + def search_by_name(self, name: str) -> List['Student']: + students = self._read_all() + return [Student.from_dict(s) for s in students if name.lower() in s['name'].lower()] + + def search_by_class(self, class_name: str) -> List['Student']: + students = self._read_all() + return [Student.from_dict(s) for s in students + if s['class_name'] and class_name.lower() in s['class_name'].lower()] + + def search_by_major(self, major: str) -> List['Student']: + students = self._read_all() + return [Student.from_dict(s) for s in students + if s['major'] and major.lower() in s['major'].lower()] + + def clear_all(self) -> bool: + """清空所有学生数据""" + self._ensure_file_exists() + return True + + +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, + ): + 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 enrollment_date is None: + self._enrollment_date = None + elif isinstance(enrollment_date, date): + self._enrollment_date = enrollment_date + else: + try: + self._enrollment_date = date.fromisoformat(enrollment_date) + except (ValueError, TypeError): + self._enrollment_date = None + + @property + def name(self) -> str: + """获取学生姓名""" + return self._name + + @name.setter + def name(self, value: str): + """设置学生姓名""" + if not isinstance(value, str): + raise TypeError("姓名必须是字符串") + self._name = value + + @property + def id_card(self) -> str: + """获取身份证号""" + return self._id_card + + @id_card.setter + def id_card(self, value: str): + """设置身份证号""" + if not isinstance(value, str): + raise TypeError("身份证号必须是字符串") + self._id_card = value + + @property + def stu_id(self) -> str: + """获取学号""" + return self._stu_id + + @stu_id.setter + def stu_id(self, value: str): + """设置学号""" + if not isinstance(value, str): + raise TypeError("学号必须是字符串") + self._stu_id = value + + @property + def enrollment_date(self) -> Optional[date]: + """获取入学日期""" + return self._enrollment_date + + @enrollment_date.setter + def enrollment_date(self, value: Union[date, str, None]): + """设置入学日期""" + if value is None: + self._enrollment_date = None + elif isinstance(value, date): + self._enrollment_date = value + else: + try: + self._enrollment_date = date.fromisoformat(value) + except (ValueError, TypeError): + self._enrollment_date = None + + @property + def birthday(self) -> Optional[date]: + """从身份证号中提取出生日期""" + if not self.id_card or len(self.id_card) != 18: + return None + + try: + birth_date_str = self.id_card[6:14] + return date.fromisoformat(f"{birth_date_str[:4]}-{birth_date_str[4:6]}-{birth_date_str[6:8]}") + except (ValueError, IndexError): + 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 (2 <= len(self.name) <= 20): + errors['name'] = "姓名长度必须在2-20个字符之间" + elif not re.match(r'^[\u4e00-\u9fa5A-Za-z]+$', self.name): + errors['name'] = "姓名只能包含中文或英文字符" + + # 验证身份证号 + id_card_errors = self._validate_id_card() + if id_card_errors: + errors['id_card'] = id_card_errors + + # 验证学号 + if not self.stu_id: + errors['stu_id'] = "学号不能为空" + elif not re.match(r'^\d{8,20}$', self.stu_id): + errors['stu_id'] = "学号必须是8-20位数字" + + # 验证身高 + if self.height is not None and not (50 <= self.height <= 250): + errors['height'] = "身高必须在50-250厘米之间" + + # 验证体重 + if self.weight is not None: + if not (5 <= self.weight <= 300): + errors['weight'] = "体重必须在5-300千克之间" + elif len(str(self.weight).split('.')[-1]) > 1: + errors['weight'] = "体重最多保留一位小数" + + # 验证入学日期 + if self.enrollment_date is not None: + if self.enrollment_date > date.today(): + errors['enrollment_date'] = "入学日期不能晚于当前日期" + if self.birthday and self.enrollment_date < self.birthday: + 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 self.id_card: + return "身份证号不能为空" + + # 长度检查 + if len(self.id_card) != 18: + return "身份证号长度必须为18位" + + # 前17位必须为数字 + if not self.id_card[:17].isdigit(): + return "身份证号前17位必须为数字" + + # 最后一位可以是数字或X + last_char = self.id_card[-1].upper() + if not (last_char.isdigit() or last_char == 'X'): + return "身份证号最后一位必须是数字或X" + + # 校验码验证 + check_code = self._get_check_code() + if check_code != last_char: + return f"身份证号校验位错误,应为{check_code}" + + # 出生日期验证 + birth_date_str = self.id_card[6:14] + if not self._validate_date(birth_date_str): + return "身份证号中的出生日期无效" + + return None + + @staticmethod + def _validate_date(date_str: str) -> bool: + """验证日期字符串(YYYYMMDD)的有效性""" + if len(date_str) != 8 or not date_str.isdigit(): + return False + + try: + year = int(date_str[:4]) + month = int(date_str[4:6]) + day = int(date_str[6:8]) + + # 简单的日期有效性检查 + if not (1900 <= year <= datetime.now().year): + return False + if not (1 <= month <= 12): + return False + if not (1 <= day <= 31): + return False + + # 更精确的日期检查 + date.fromisoformat(f"{year}-{month:02d}-{day:02d}") + return True + except (ValueError, IndexError): + return False + + def _get_check_code(self) -> str: + """计算身份证号的校验码""" + # 加权因子 + weights = [2 ** (17 - i) % 11 for i in range(17)] + + # 计算加权和 + total = sum(int(digit) * weight for digit, weight in zip(self.id_card[:17], weights)) + + # 计算校验码 + check_code_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_code_map[total % 11] + + 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, + 'enrollment_date': self.enrollment_date.isoformat() if self.enrollment_date else None, + 'class_name': self.class_name, + 'major': self.major + } + return data + + @classmethod + def from_dict(cls, data: Dict) -> 'Student': + """从字典创建学生对象""" + data = data.copy() + + # 处理入学日期 + if 'enrollment_date' in data and isinstance(data['enrollment_date'], str): + try: + data['enrollment_date'] = date.fromisoformat(data['enrollment_date']) + except ValueError: + data['enrollment_date'] = None + + return cls(**data) + + def __repr__(self) -> str: + """返回对象的字符串表示""" + return (f"Student(name='{self.name}', id_card='{self.id_card}', stu_id='{self.stu_id}', " + f"gender={'男' if self.gender else '女' if self.gender is not None else None}, " + f"height={self.height}, weight={self.weight}, " + f"enrollment_date={self.enrollment_date.isoformat() if self.enrollment_date else None}, " + f"class_name='{self.class_name}', major='{self.major}')") + + +class StudentService: + """学生信息业务逻辑层""" + + def __init__(self, dal: IStudentDAL): + self.dal = dal + + def add_student(self, student_data: Dict) -> Dict: + """添加学生信息""" + student = Student.from_dict(student_data) + + if not student.is_valid: + return {'success': False, 'message': '学生信息不合法', 'errors': student.errors} + + # 检查身份证号是否已存在 + if self.dal.get_by_id(student.id_card): + return {'success': False, 'message': '身份证号已存在'} + + # 检查学号是否已存在 + if self.dal.get_by_stu_id(student.stu_id): + return {'success': False, 'message': '学号已存在'} + + if self.dal.add(student): + return {'success': True, 'message': '添加成功'} + return {'success': False, 'message': '添加失败'} + + def delete_student(self, id_card: str) -> Dict: + """删除学生信息""" + if self.dal.delete(id_card): + return {'success': True, 'message': '删除成功'} + return {'success': False, 'message': '学生不存在'} + + from typing import Dict, List, Optional, Union + from datetime import date + import re + + def update_student(self, identifier: Dict[str, str], update_data: Dict) -> Dict: + """ + 通用更新学生信息方法 + :param identifier: 识别学生的条件字典,如 {'id_card': '123...'} 或 {'stu_id': '2023001'} + :param update_data: 要更新的数据字典 + :return: 操作结果字典 + """ + # 1. 根据条件查找学生 + student = self._find_student_by_identifier(identifier) + if not student: + return {'success': False, 'message': '未找到匹配的学生'} + + # 2. 合并更新数据 + updated_student = self._merge_student_data(student, update_data) + + # 3. 验证数据 + if not updated_student.is_valid: + return {'success': False, 'message': '更新数据不合法', 'errors': updated_student.errors} + + # 4. 检查学号冲突(如果更新了学号) + if 'stu_id' in update_data and update_data['stu_id'] != student.stu_id: + existing = self.dal.get_by_stu_id(update_data['stu_id']) + if existing and existing.id_card != student.id_card: + return {'success': False, 'message': '学号已被其他学生使用'} + + # 5. 执行更新 + if self.dal.update(updated_student): + return {'success': True, 'message': '更新成功'} + return {'success': False, 'message': '更新失败'} + + def _find_student_by_identifier(self, identifier: Dict[str, str]) -> Optional[Student]: + """根据不同的识别条件查找学生""" + if 'id_card' in identifier: + return self.dal.get_by_id(identifier['id_card']) + elif 'stu_id' in identifier: + return self.dal.get_by_stu_id(identifier['stu_id']) + elif 'name' in identifier: + students = self.dal.search_by_name(identifier['name']) + if len(students) == 1: + return students[0] + return None + + def _merge_student_data(self, original: Student, update_data: Dict) -> Student: + """合并原始学生数据和更新数据""" + original_data = original.to_dict() + merged_data = {**original_data, **update_data} + return Student.from_dict(merged_data) + + def batch_update_students(self, condition: Dict[str, str], update_data: Dict) -> Dict: + """ + 批量更新学生信息 + :param condition: 筛选条件,如 {'class_name': '计算机23-1'} + :param update_data: 要更新的数据 + :return: 操作结果字典 + """ + # 1. 根据条件查找学生 + students = self._find_students_by_condition(condition) + if not students: + return {'success': False, 'message': '未找到匹配的学生'} + + results = { + 'total': len(students), + 'success': 0, + 'failed': 0, + 'details': [] + } + + # 2. 逐个更新 + for student in students: + result = self.update_student( + {'id_card': student.id_card}, + update_data + ) + if result['success']: + results['success'] += 1 + else: + results['failed'] += 1 + results['details'].append({ + 'id_card': student.id_card, + 'success': result['success'], + 'message': result.get('message', '') + }) + + return { + 'success': True, + 'message': f'批量更新完成,成功{results["success"]}个,失败{results["failed"]}个', + 'results': results + } + + def _find_students_by_condition(self, condition: Dict[str, str]) -> List[Student]: + """根据条件查找多个学生""" + if 'class_name' in condition: + return self.dal.search_by_class(condition['class_name']) + elif 'major' in condition: + return self.dal.search_by_major(condition['major']) + elif 'name' in condition: + return self.dal.search_by_name(condition['name']) + return [] + + 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_students_by_name(self, name: str) -> List[Student]: + """根据姓名模糊查询学生信息""" + return self.dal.search_by_name(name) + + def search_students_by_class(self, class_name: str) -> List[Student]: + """根据班级模糊查询学生信息""" + return self.dal.search_by_class(class_name) + + def search_students_by_major(self, major: str) -> List[Student]: + """根据专业模糊查询学生信息""" + return self.dal.search_by_major(major) + + def get_student_count(self) -> int: + """获取学生总数""" + return len(self.dal.get_all()) + + def get_major_statistics(self) -> Dict[str, int]: + """统计各专业学生人数""" + students = self.dal.get_all() + stats = {} + + for student in students: + if student.major: + stats[student.major] = stats.get(student.major, 0) + 1 + + return stats + + def get_height_statistics(self) -> Dict[str, float]: + """计算平均身高""" + students = self.dal.get_all() + total_height = 0 + count = 0 + + for student in students: + if student.height: + total_height += student.height + count += 1 + + return {'average_height': total_height / count if count > 0 else 0} + + def get_weight_statistics(self) -> Dict[str, float]: + """计算平均体重""" + students = self.dal.get_all() + total_weight = 0.0 + count = 0 + + for student in students: + if student.weight: + total_weight += student.weight + count += 1 + + return {'average_weight': total_weight / count if count > 0 else 0} + + def export_to_csv(self, file_path: str) -> bool: + """导出数据到CSV文件""" + try: + students = self.dal.get_all() + data = [student.to_dict() for student in students] + + with open(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() + writer.writerows(data) + + return True + except Exception as e: + print(f"导出失败: {e}") + return False + + def import_from_csv(self, file_path: str) -> Dict: + """从CSV文件导入数据""" + try: + with open(file_path, 'r', newline='', encoding='utf-8') as f: + reader = csv.DictReader(f) + imported = 0 + skipped = 0 + + for row in reader: + student = Student.from_dict(row) + + if not student.is_valid: + skipped += 1 + continue + + # 检查是否已存在 + if self.dal.get_by_id(student.id_card) or self.dal.get_by_stu_id(student.stu_id): + skipped += 1 + continue + + if self.dal.add(student): + imported += 1 + else: + skipped += 1 + + return { + 'success': True, + 'message': f'导入完成 (成功: {imported}, 跳过: {skipped})', + 'imported': imported, + 'skipped': skipped + } + except Exception as e: + return { + 'success': False, + 'message': f'导入失败: {e}' + } + + def clear_all_data(self) -> bool: + """清空所有学生数据""" + return self.dal.clear_all() + + +class StudentManagementUI: + """学生信息管理系统用户界面""" + + def __init__(self): + # 默认使用JSON存储 + self.dal = StudentJSONDAL() + self.service = StudentService(self.dal) + + 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 run(self): + """运行系统""" + while True: + self.display_menu() + choice = input("请选择操作: ") + + if choice == '1': + self.add_student_ui() + elif choice == '2': + self.delete_student_ui() + elif choice == '3': + self.update_student_ui() + elif choice == '4': + self.query_students_ui() + elif choice == '5': + self.statistics_ui() + elif choice == '6': + self.import_export_ui() + elif choice == '7': + self.switch_storage_ui() + elif choice == '8': + self.clear_all_data_ui() + elif choice == '0': + print("感谢使用学生信息管理系统,再见!") + sys.exit() + else: + print("无效的选择,请重新输入!") + + def add_student_ui(self): + """添加学生信息界面""" + print("\n===== 添加学生信息 =====") + student_data = {} + + student_data['name'] = input("姓名: ") + student_data['id_card'] = input("身份证号: ") + student_data['stu_id'] = input("学号: ") + + gender = input("性别(男/女/跳过): ").strip() + if gender == '男': + student_data['gender'] = True + elif gender == '女': + student_data['gender'] = False + + height = input("身高(cm,跳过请直接回车): ").strip() + if height: + try: + student_data['height'] = int(height) + except ValueError: + print("身高必须是整数") + return + + weight = input("体重(kg,跳过请直接回车): ").strip() + if weight: + try: + student_data['weight'] = float(weight) + except ValueError: + print("体重必须是数字") + return + + enrollment_date = input("入学日期(YYYY-MM-DD,跳过请直接回车): ").strip() + if enrollment_date: + student_data['enrollment_date'] = enrollment_date + + student_data['class_name'] = input("班级名称(跳过请直接回车): ").strip() or None + student_data['major'] = input("专业名称(跳过请直接回车): ").strip() or None + + result = self.service.add_student(student_data) + if result['success']: + print("添加学生信息成功!") + else: + print(f"添加失败: {result['message']}") + if 'errors' in result: + for field, error in result['errors'].items(): + print(f"{field}: {error}") + + def delete_student_ui(self): + """删除学生信息界面""" + print("\n===== 删除学生信息 =====") + id_card = input("请输入要删除的学生身份证号: ") + + result = self.service.delete_student(id_card) + if result['success']: + print("删除学生信息成功!") + else: + print(f"删除失败: {result['message']}") + + def update_student_ui(self): + """更新学生信息界面""" + print("\n===== 更新学生信息 =====") + print("请选择查找学生的方式:") + print("1. 按身份证号") + print("2. 按学号") + print("3. 按姓名") + print("4. 批量更新(按班级)") + print("5. 批量更新(按专业)") + print("0. 返回上级菜单") + + choice = input("请选择操作: ") + + if choice == '1': + self._update_by_id_card() + elif choice == '2': + self._update_by_stu_id() + elif choice == '3': + self._update_by_name() + elif choice == '4': + self._batch_update_by_class() + elif choice == '5': + self._batch_update_by_major() + elif choice == '0': + return + else: + print("无效的选择!") + + def _update_by_id_card(self): + """按身份证号更新""" + id_card = input("请输入学生身份证号: ") + student = self.service.get_student_by_id(id_card) + if not student: + print("未找到该学生!") + return + + print("当前学生信息:") + self.display_student_details(student) + self._perform_update({'id_card': id_card}) + + def _update_by_stu_id(self): + """按学号更新""" + stu_id = input("请输入学生学号: ") + student = self.service.get_student_by_stu_id(stu_id) + if not student: + print("未找到该学生!") + return + + print("当前学生信息:") + self.display_student_details(student) + self._perform_update({'stu_id': stu_id}) + + def _update_by_name(self): + """按姓名更新""" + name = input("请输入学生姓名: ") + students = self.service.search_students_by_name(name) + + if not students: + print("未找到该学生!") + return + elif len(students) > 1: + print(f"找到{len(students)}个同名学生,请使用身份证号或学号更新") + return + + print("当前学生信息:") + self.display_student_details(students[0]) + self._perform_update({'id_card': students[0].id_card}) + + def _batch_update_by_class(self): + """按班级批量更新""" + class_name = input("请输入要更新的班级名称: ") + self._perform_batch_update({'class_name': class_name}) + + def _batch_update_by_major(self): + """按专业批量更新""" + major = input("请输入要更新的专业名称: ") + self._perform_batch_update({'major': major}) + + def _perform_update(self, identifier: Dict[str, str]): + """执行单个学生更新""" + print("\n请输入要更新的信息(直接回车保持原值):") + update_data = {} + + name = input("姓名: ").strip() + if name: + update_data['name'] = name + + stu_id = input("学号: ").strip() + if stu_id: + update_data['stu_id'] = stu_id + + gender = input("性别(男/女/跳过): ").strip() + if gender == '男': + update_data['gender'] = True + elif gender == '女': + update_data['gender'] = False + + height = input("身高(cm): ").strip() + if height: + try: + update_data['height'] = int(height) + except ValueError: + print("身高必须是整数") + return + + weight = input("体重(kg): ").strip() + if weight: + try: + update_data['weight'] = float(weight) + except ValueError: + print("体重必须是数字") + return + + enrollment_date = input("入学日期(YYYY-MM-DD): ").strip() + if enrollment_date: + update_data['enrollment_date'] = enrollment_date + + class_name = input("班级名称: ").strip() + if class_name: + update_data['class_name'] = class_name or None + + major = input("专业名称: ").strip() + if major: + update_data['major'] = major or None + + result = self.service.update_student(identifier, update_data) + if result['success']: + print("更新成功!") + else: + print(f"更新失败: {result['message']}") + if 'errors' in result: + for field, error in result['errors'].items(): + print(f"{field}: {error}") + + def _perform_batch_update(self, condition: Dict[str, str]): + """执行批量更新""" + print("\n请输入要批量更新的信息:") + update_data = {} + + class_name = input("班级名称(直接回车跳过): ").strip() + if class_name: + update_data['class_name'] = class_name or None + + major = input("专业名称(直接回车跳过): ").strip() + if major: + update_data['major'] = major or None + + enrollment_date = input("入学日期(YYYY-MM-DD,直接回车跳过): ").strip() + if enrollment_date: + update_data['enrollment_date'] = enrollment_date + + if not update_data: + print("没有提供任何更新数据!") + return + + confirm = input(f"确定要批量更新{condition}的学生信息吗?(y/n): ").lower() + if confirm != 'y': + print("操作已取消") + return + + result = self.service.batch_update_students(condition, update_data) + print(result['message']) + + if result['success'] and result['results']['failed'] > 0: + print("\n失败详情:") + for detail in result['results']['details']: + if not detail['success']: + print(f"身份证号: {detail['id_card']}, 原因: {detail['message']}") + + def query_students_ui(self): + """查询学生信息界面""" + while True: + print("\n===== 查询学生信息 =====") + print("1. 查询所有学生") + print("2. 按身份证号查询") + print("3. 按学号查询") + print("4. 按姓名查询") + print("5. 按班级查询") + print("6. 按专业查询") + print("7. 返回上一级") + print("======================") + + choice = input("请选择查询方式: ") + + if choice == '1': + self.query_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': + break + else: + print("无效的选择,请重新输入!") + + def query_all_students(self): + """查询所有学生""" + students = self.service.get_all_students() + if not students: + print("没有学生信息!") + return + + print("\n所有学生信息:") + print("-" * 80) + print(f"{'姓名':<10}{'学号':<15}{'身份证号':<20}{'班级':<15}{'专业':<20}") + print("-" * 80) + + for student in students: + print(f"{student.name:<10}{student.stu_id:<15}{student.id_card:<20}" + f"{student.class_name or '未设置':<15}{student.major or '未设置':<20}") + + print("-" * 80) + print(f"共 {len(students)} 名学生") + + def query_by_id_card(self): + """按身份证号查询""" + id_card = input("请输入身份证号: ") + student = self.service.get_student_by_id(id_card) + + if student: + self.display_student_details(student) + else: + print("未找到该学生!") + + def query_by_stu_id(self): + """按学号查询""" + stu_id = input("请输入学号: ") + student = self.service.get_student_by_stu_id(stu_id) + + if student: + self.display_student_details(student) + else: + print("未找到该学生!") + + def query_by_name(self): + """按姓名查询""" + name = input("请输入姓名(支持模糊查询): ") + students = self.service.search_students_by_name(name) + + if students: + print(f"\n找到 {len(students)} 名学生:") + for student in students: + self.display_student_details(student) + print("-" * 40) + else: + print("未找到匹配的学生!") + + def query_by_class(self): + """按班级查询""" + class_name = input("请输入班级名称(支持模糊查询): ") + students = self.service.search_students_by_class(class_name) + + if students: + print(f"\n找到 {len(students)} 名学生:") + for student in students: + self.display_student_details(student) + print("-" * 40) + else: + print("未找到匹配的学生!") + + def query_by_major(self): + """按专业查询""" + major = input("请输入专业名称(支持模糊查询): ") + students = self.service.search_students_by_major(major) + + if students: + print(f"\n找到 {len(students)} 名学生:") + for student in students: + self.display_student_details(student) + print("-" * 40) + else: + print("未找到匹配的学生!") + + def display_student_details(self, student: Student): + """显示学生详细信息""" + print("\n学生详细信息:") + print(f"姓名: {student.name}") + print(f"身份证号: {student.id_card}") + print(f"学号: {student.stu_id}") + print(f"性别: {'男' if student.gender else '女' if student.gender is not None else '未设置'}") + print(f"出生日期: {student.birthday.isoformat() if student.birthday else '未知'}") + print(f"年龄: {student.age if student.age else '未知'}") + print(f"身高: {student.height}cm" if student.height else "身高: 未设置") + print(f"体重: {student.weight}kg" if student.weight else "体重: 未设置") + print(f"入学日期: {student.enrollment_date.isoformat() if student.enrollment_date else '未设置'}") + print(f"班级: {student.class_name or '未设置'}") + print(f"专业: {student.major or '未设置'}") + + def statistics_ui(self): + """统计分析界面""" + while True: + print("\n===== 统计分析 =====") + print("1. 学生总数") + print("2. 平均身高") + print("3. 平均体重") + print("4. 专业统计") + print("5. 返回上一级") + print("===================") + + choice = input("请选择统计项目: ") + + if choice == '1': + count = self.service.get_student_count() + print(f"\n学生总数: {count}") + elif choice == '2': + stats = self.service.get_height_statistics() + print(f"\n平均身高: {stats['average_height']:.1f}cm") + elif choice == '3': + stats = self.service.get_weight_statistics() + print(f"\n平均体重: {stats['average_weight']:.1f}kg") + elif choice == '4': + stats = self.service.get_major_statistics() + print("\n各专业学生人数:") + for major, count in stats.items(): + print(f"{major}: {count}人") + elif choice == '5': + break + else: + print("无效的选择,请重新输入!") + + def import_export_ui(self): + """数据导入导出界面""" + while True: + print("\n===== 数据导入导出 =====") + print("1. 导出数据到CSV") + print("2. 从CSV导入数据") + print("3. 返回上一级") + print("======================") + + choice = input("请选择操作: ") + + if choice == '1': + file_path = input("请输入导出文件路径(默认: students_export.csv): ").strip() or "students_export.csv" + if self.service.export_to_csv(file_path): + print(f"数据已成功导出到 {file_path}") + else: + print("导出失败!") + elif choice == '2': + file_path = input("请输入要导入的CSV文件路径: ").strip() + result = self.service.import_from_csv(file_path) + print(result['message']) + elif choice == '3': + break + else: + print("无效的选择,请重新输入!") + + def switch_storage_ui(self): + """切换存储方式界面""" + print("\n===== 切换存储方式 =====") + print(f"当前存储方式: {'JSON' if isinstance(self.dal, StudentJSONDAL) else 'CSV'}") + print("1. 切换到JSON存储") + print("2. 切换到CSV存储") + print("3. 返回上一级") + print("======================") + + choice = input("请选择存储方式: ") + + if choice == '1': + self.dal = StudentJSONDAL() + self.service = StudentService(self.dal) + print("已切换到JSON存储") + elif choice == '2': + self.dal = StudentCSVDAL() + self.service = StudentService(self.dal) + print("已切换到CSV存储") + elif choice == '3': + return + else: + print("无效的选择!") + + def clear_all_data_ui(self): + """清空所有数据界面""" + confirm = input("确定要清空所有学生数据吗?此操作不可恢复!(y/n): ").strip().lower() + if confirm == 'y': + if self.service.clear_all_data(): + print("所有学生数据已清空!") + else: + print("清空数据失败!") + else: + print("操作已取消") + + +if __name__ == '__main__': + ui = StudentManagementUI() + ui.run() \ No newline at end of file