学生选课系统项目开发

1.项目需求

1. 创建北京、上海 2 所学校
2. 创建linux , python , go 3个课程 , linux\py 在北京开, go 在上海开
3. 课程包含,周期,价格,通过学校创建课程
4. 通过学校创建班级, 班级关联课程、讲师
5. 创建学员时,选择学校,关联班级
6. 创建讲师角色时要关联学校,
7. 提供两个角色接口,一个管理接口

2.项目分析

各部分需要完成的任务

管理员视图:
    1. 注册
    2. 登录
    3. 创建讲师
    4. 创建学校
    5. 创建学生

讲师视图:
    1. 登录
    2. 讲课
    3. 查看学生成绩

学生视图:
    1. 注册
    2. 登录
    3. 选择课程
    4. 查看分数

分析:

  1. 和上一个atm相似.本项目也是采用用户层. 接口层. 数据库层三层架构
  2. 代码中要用到面向对象编程写. 具体的代码逻辑如下图

文件夹结构预览:

3.代码部分

本文篇幅较长. 可使用导航栏点击标题部分查看:

bin文件夹

start.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 10:20

# @author: Maxs_hu
import os
import sys

BASE_PATH = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_PATH)  # 直接将项目根目录加上. 后面全部可以直接调用. 一劳永逸
from core.src import run

if __name__ == '__main__':
   run()

conf文件夹

settings.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 10:36
# @author: Maxs_hu
import os


BASE_PATH = os.path.dirname(os.path.dirname(__file__))
DB_PATH = os.path.join(BASE_PATH, 'db')
SCHOOL_DB_PATH = os.path.join(DB_PATH, 'school')


core文件夹

admin.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 10:37
# @author: Maxs_hu
from interface import common_interface, admin_interface
from lib import common


# 设置用户登录状态
admin_status = {
   "name": None
}


def admin_register():
   while True:
       print('管理员注册')
       username = input('请输入用户名>>>').strip()
       password = input('请输入密码>>>').strip()
       if password:
           flag, msg = admin_interface.admin_register_interface(username, password)  # 调接口
           if not flag:
               print(msg)
           else:
               print(msg)
               break


def admin_login():
   while True:
       print('管理员登录')
       username = input('请输入用户名>>>').strip()
       password = input('请输入密码>>>')
       if admin_status['name'] == username:
           print('已登录完成. 无需重复登录')
           break
       if password:
           flag, msg = common_interface.login_interface(username, password, 'admin')  # 调接口
           if not flag:
               print(msg)
           else:
               admin_status["name"] = username
               print(msg)
               break


@common.auth_login(user_type='admin')
def create_teacher():
   print('创建讲师')
   teacher_name = input('请输入老师名称>>>').strip()
   teacher_password = input('请设置老师密码>>>').strip()
   if teacher_name:
       flag, msg = admin_interface.create_teacher_interface(admin_status['name'], teacher_name, teacher_password)
       if flag:
           print(msg)
       else:
           print(msg)
   else:
       print('老师名不能为空')


@common.auth_login(user_type='admin')
def create_school():
   print('创建学校')
   school = input('请输入学校名称>>>').strip()
   addr = input('请输入学校地址>>>').strip()
   if school:
       flag, msg = admin_interface.create_school_interface(admin_status['name'], school, addr)
       if flag:
           print(msg)
       else:
           print(msg)
   else:
       print('学校名称不能为空')


@common.auth_login(user_type='admin')
def create_course():
   while True:
       print('创建课程')
       # 先查看学校. 再创建课程. 最后在学校中添加课程
       school_list = common_interface.check_school_interface()
       for i, school in enumerate(school_list):
           print("%s: %s" % (i+1, school))
       choice = input('请输入需要选择的校区>>>').strip()
       course_name = input('请输入需要添加的课程>>>').strip()
       course_fee = input('请输入课程费用>>>').strip()
       if choice.isdigit():
           choice = int(choice)
           if 0 < choice <= len(school_list):
               flag, msg = admin_interface.create_course_interface(admin_status['name'], course_name, course_fee,  school_list[choice-1])
               if flag:
                   print(msg)
                   break
               else:
                   print(msg)
           else:
               print('请输入有效数字')
       else:
           print('请输入数字')


func_dic = {
   '1': admin_register,
   '2': admin_login,
   '3': create_teacher,
   '4': create_school,
   '5': create_course,
}


def admin_view():
   while True:
       print("""
1. 注册
2. 登录
3. 创建讲师
4. 创建学校
5. 创建课程
       """)
       choice = input('请输入的业务序号(q退出)>>>').strip()
       if choice not in func_dic:
           print('没有业务信息')
           break
       if choice == "q":
           break
       func_dic[choice]()

src.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 10:37
# @author: Maxs_hu
from core import admin, student, teacher

identify_dic = {
   "1": admin.admin_view,
   "2": teacher.teacher_view,
   "3": student.student_view,
}


def run():
   while 1:
       print("""
>>>欢迎进入选课系统<<<
   1. 管理员
   2. 讲师
   3. 学生
       """)
       choice = input('请输入身份(q退出)>>>').strip()
       if choice == 'q':
           print('选课系统已关闭')
           break
       if not choice in identify_dic:
           print('无相关身份')
           break
       identify_dic[choice]()  # 直接加括号调用

student.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 10:37
# @author: Maxs_hu
from interface import student_interface, common_interface
from lib import common

# 用户登录状态
student_status = {
   'name': None
}


def student_register():
   print('学生注册')
   while True:
       username = input('请输入用户名>>>').strip()
       password = input('请输入密码>>>').strip()
       if password:
           flag, msg = student_interface.student_register_interface(username, password)
           if flag:
               print(msg)
               break
           else:
               print(msg)
       else:
           print('用户名或密码不能为空')


def student_login():
   print('学生登录')
   while True:
       username = input('请输入用户名>>>').strip()
       password = input('请输入密码>>>').strip()
       if student_status['name'] == username:
           print('已登录完成. 无需重复登录')
           break
       if password:
           flag, msg = common_interface.login_interface(username, password, 'student')
           if flag:
               student_status['name'] = username
               print(msg)
               break
           else:
               print(msg)
       else:
           print('用户名和密码不能为空')


@common.auth_login(user_type='student')
def choose_school():
   while True:
       print('选择学校')
       school_list = student_interface.check_all_school_interface()
       for i, school in enumerate(school_list):
           print('%s: %s' % (i+1, school))
       choice = input('请输入你想选择的学校>>>').strip()
       if choice.isdigit():
           choice = int(choice)
           if 0 < choice <= len(school_list):
               flag, msg = student_interface.choose_school_interface(student_status['name'], school_list[choice-1])
               print(msg)
               break
           else:
               print('请输入有效数字')
       else:
           print('请输入数字')


@common.auth_login(user_type='student')
def choose_course():
   while True:
       print('选择课程')
       # 先查看可以选择的课程. 在进行选课
       flag, msg = student_interface.check_avaliable_course_interface(student_status['name'])
       if flag:
           for i, course in enumerate(msg):
               print('%s: %s' % (i+1, course))
           choice = input('请输入你想选择的课程>>>').strip()
           if choice.isdigit():
               choice = int(choice)
               if 0 < choice <= len(msg):
                   flag, msg = student_interface.choose_course_interface(student_status['name'], msg[choice-1])
                   if flag:
                       print(msg)
                       break
                   else:
                       print(msg)
               else:
                   print('请输入有效数字')
           else:
               print('请输入数字')
       else:
           print('没有可以选择的课程')
           break


@common.auth_login(user_type='student')
def check_score():
   print('查看成绩')
   flag, msg = student_interface.check_score_interface(student_status['name'])
   print(msg)


func_dic = {
   '1': student_register,
   '2': student_login,
   '3': choose_school,
   '4': choose_course,
   '5': check_score,
}


def student_view():
   while True:
       print('''
1 注册
2 登录
3 选择学校
4 选择课程
5 查看成绩
       ''')
       choice = input('请选择办理的业务>>>').strip()
       if choice not in func_dic:
           print('无此业务')
           break
       func_dic[choice]()

teacher.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 10:37
# @author: Maxs_hu
from interface import teacher_interface, common_interface
from lib import common

# 用户状态
teacher_status = {
   'name': None
}


def teacher_register():
   print('讲师注册')
   while True:
       username = input('请输入用户名>>>').strip()
       password = input('请输入密码>>>').strip()
       if password:
           flag, msg = teacher_interface.teacher_register_interface(username, password)
           if flag:
               print(msg)
               break
           else:
               print(msg)
       else:
           print('用户名和密码不能为空')


def teacher_login():
   print('讲师登录')
   while True:
       username = input('请输入用户名>>>').strip()
       password = input('请输入密码>>>').strip()
       if teacher_status['name'] == username:
           print('已登录完成. 无需重复登录')
           break
       if password:
           flag, msg = common_interface.login_interface(username, password, 'teacher')
           if flag:
               teacher_status['name'] = username
               print(msg)
               break
           else:
               print(msg)
       else:
           print('用户名或密码不能为空')


@common.auth_login(user_type='teacher')
def choose_course():
   while True:
       print('选择课程')
       # 先查看所有的课程. 在添加课程到teacher类中
       course_list = teacher_interface.choose_course_interface()
       for i, course in enumerate(course_list):
           print('%s: %s' % (i+1, course))
       choice = input('请输入你想选择的课程>>>').strip()
       if choice.isdigit():
           choice = int(choice)
           if 0 < choice <= len(course_list):
               flag, msg = teacher_interface.add_course_interface(teacher_status['name'], course_list[choice-1])
               if flag:
                   print(msg)
                   break
               else:
                   print(msg)
           else:
               print('请输入有效数字')
       else:
           print('请输入数字')


@common.auth_login(user_type='teacher')
def check_course():
   while True:
       print('查看课程')
       course_list = teacher_interface.check_course_interface(teacher_status['name'])
       if course_list:
           for course in course_list:
               print(course)
           break
       else:
           print("请先选择课程")


@common.auth_login(user_type='teacher')
def check_student():
   while True:
       print('查看学生')
       # 先查看课程. 再查看课程对应的学生
       course_list = teacher_interface.check_course_interface(teacher_status['name'])
       for i, course in enumerate(course_list):
           print('%s: %s' % (i+1, course))
       choice = input('请选择想要查看的课程.课程对应的学生会被返回>>>').strip()
       if choice.isdigit():
           choice = int(choice)
           if 0 < choice <= len(course_list):
               student_list = teacher_interface.check_student_interface(course_list[choice-1])
               if student_list:
                   for student in student_list:
                       print(student)
                   break
               else:
                   print('暂时没有学生')
                   break
           else:
               print('请输入有效的数字')
       else:
           print('请输入数字')


@common.auth_login(user_type='teacher')
def modify_score():
   while True:
       print('修改学生成绩')
       # 先查看课程. 再查看课程对应的学生. 在对学生进行打分
       course_list = teacher_interface.check_course_interface(teacher_status['name'])
       for i, course in enumerate(course_list):
           print('%s: %s' % (i + 1, course))
       choice = input('请选择想要查看的课程.课程对应的学生会被返回>>>').strip()
       if choice.isdigit():
           choice = int(choice)
           if 0 < choice <= len(course_list):
               course = course_list[choice-1]
               student_list = teacher_interface.check_student_interface(course)
               if student_list:
                   for i, student in enumerate(student_list):
                       print('%s: %s' % (i + 1, student))
                   choice = input('请输入学生名称并对其进行打分>>>').strip()
                   score = int(input('请输入要打的分数>>>').strip())
                   if choice.isdigit():
                       choice = int(choice)
                       if 0 < choice <= len(student_list):
                           flag, msg = teacher_interface.modify_score_interface(student_list[choice-1], course, score, )
                           print(msg)
                           break
                       else:
                           print('请输入有效数字')
                   else:
                       print('请输入数字')
               else:
                   print('暂时没有学生')
           else:
               print('请输入有效的数字')
       else:
           print('请输入数字')


func_dic = {
   '1': teacher_register,
   '2': teacher_login,
   '3': choose_course,
   '4': check_course,
   '5': check_student,
   '6': modify_score
}


def teacher_view():
   while True:
       print("""
1. 注册
2. 登录
3. 选择课程
4. 查看课程
5. 查看学生
6. 修改学生成绩
           """)
       choice = input('请输入想要办理的业务>>>').strip()
       if choice not in func_dic:
           print('无此业务')
           break
       func_dic[choice]()

db文件夹

db_handler.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 13:44
# @author: Maxs_hu
from conf import settings
import os
import pickle


def save(obj):
   db_dir = settings.DB_PATH
   db_path = os.path.join(db_dir, obj.__class__.__name__.lower())  # /admin
   if not os.path.exists(db_path):
       os.makedirs(db_path)  # 创建文件夹
   detail_path = os.path.join(db_path, obj.name)
   with open(detail_path, 'wb') as f:
       pickle.dump(obj, f)  # 直接将对象写到pickle中
       f.flush()  # 刷入硬盘


def select(name, type_dir):
   db_dir = settings.DB_PATH
   db_path = os.path.join(db_dir, type_dir)
   if not os.path.exists(db_path):
       os.makedirs(db_path)  # 创建文件夹
   detail_path = os.path.join(db_path, name)
   if os.path.exists(detail_path):  # 判断文件夹是否存在
       with open(detail_path, 'rb') as f:
           return pickle.load(f)  # 将查询到的信息返回
   else:
       return None

moudle.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 13:44
# @author: Maxs_hu
from db import db_handler


class BaseClass:
   def save(self):
       db_handler.save(self)

   @classmethod
   def get_obj_by_name(cls, name):
       return db_handler.select(name, cls.__name__.lower())  # 直接将类的名字进行传参


class Admin(BaseClass):
   def __init__(self, name, password):
       self.name = name
       self.password = password
       self.save()  # 这样将Admin实例化可以直接调用保存功能

   # 管理员功能设置
   def create_teacher(self, name, password):
       # 直接初始化一个类即可
       Teacher(name, password)

   def create_school(self, name, addr):
       School(name, addr)

   def create_course(self, name, fee):
       Course(name, fee)


class Teacher(BaseClass):
   def __init__(self, name, password):
       self.name = name
       self.password = password
       self.course_list = []
       self.save()

   def add_course(self, name):
       self.course_list.append(name)
       self.save()


class Student(BaseClass):
   def __init__(self, name, password):
       self.name = name
       self.password = password
       self.school = None
       self.score = {}
       self.course_list = []
       self.save()

   def choose_school(self, school):
       self.school = school
       self.save()

   def get_school(self):
       return self.school

   def choose_course(self, course):
       self.course_list.append(course)
       self.score[course] = 0  # 初始化为0分
       self.save()

   def modify_score(self):
       self.save()


class School(BaseClass):
   def __init__(self, name, addr):
       self.name = name
       self.addr = addr
       self.course_list = []
       self.save()

   def add_course(self, name):  # 课程和学校绑定
       self.course_list.append(name)
       self.save()


class Course(BaseClass):
   def __init__(self, name, fee):
       self.name = name
       self.fee = fee
       self.student_list = []
       self.save()

   def add_student(self, name):
       self.student_list.append(name)
       self.save()

# 这里存在一个报错案例: 原因在于我程序没编写完成之前做了测试. 后期填写的属性都没有在已保存的类型中. 导致报错no attribute

interface文件夹

admin_interface.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 14:21
# @author: Maxs_hu
from db import moudle


def admin_register_interface(username, password):
   admin_obj = moudle.Admin.get_obj_by_name(username)
   if admin_obj:
       return False, '管理员已注册'
   else:
       moudle.Admin(username, password)  # 直接实例化__init__触发save()
       return True, f'{username}已注册'


def create_teacher_interface(username, teacher_name, teacher_password):
   teacher_obj = moudle.Teacher.get_obj_by_name(teacher_name)  # 查看老师是否存在
   if teacher_obj:
       return False, '老师已存在'
   else:
       obj = moudle.Admin.get_obj_by_name(username)
       obj.create_teacher(teacher_name, teacher_password)  # 通过对象调用绑定对象
       return True, f'{teacher_name}创建完成'


def create_school_interface(username, school_name, school_addr):
   school_obj = moudle.School.get_obj_by_name(school_name)  # 判断学校是否存在
   if school_obj:
       return False, '学校已经存在'
   else:
       obj = moudle.Admin.get_obj_by_name(username)
       obj.create_school(school_name, school_addr)
       return True, f'{school_name}创建完成'


def create_course_interface(username, course_name, course_fee, school):
   course_obj = moudle.Course.get_obj_by_name(course_name)
   if course_obj:
       return False, '课程已存在'
   else:
       # 先创建课程
       obj = moudle.Admin.get_obj_by_name(username)
       obj.create_course(course_name, course_fee)
       # 将课程和学校绑定
       school_obj = moudle.School.get_obj_by_name(school)
       school_obj.add_course(course_name)
       return True, f'{course_name}创建完成'

student_interface.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 17:11
# @author: Maxs_hu
from db import moudle
import os
from conf import settings


def student_register_interface(username, password):
   student_obj = moudle.Student.get_obj_by_name(username)
   if student_obj:
       return False, '该用户已存在'
   else:
       moudle.Student(username, password)
       return True, f'{username}已注册'


def check_all_school_interface():
   db_path = settings.DB_PATH
   school_path = os.path.join(db_path, 'school')
   if os.path.exists(school_path):
       return os.listdir(school_path)
   else:
       return '请联系管理员创建学校'


def choose_school_interface(username, school):
   student_obj = moudle.Student.get_obj_by_name(username)
   student_obj.choose_school(school)
   return True, '学校选择完毕'


def check_score_interface(username):
   obj = moudle.Student.get_obj_by_name(username)
   score = obj.score
   if score:
       return True, score
   else:
       return False, '请联系老师进行打分'


def check_avaliable_course_interface(username):
   student_obj = moudle.Student.get_obj_by_name(username)
   school = student_obj.get_school()
   if school:
       school_obj = moudle.School.get_obj_by_name(school)
       return True, school_obj.course_list
   else:
       return False, '请先选择学校'


def choose_course_interface(username, course):
   obj = moudle.Student.get_obj_by_name(username)
   if course in obj.course_list:
       return False, '您已选过该课程'
   obj.choose_course(course)
   course_obj = moudle.Course.get_obj_by_name(course)
   course_obj.add_student(username)
   return True, '选课成功'

teacher_interface.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 17:11
# @author: Maxs_hu
from db import moudle
from conf import settings
import os


def teacher_register_interface(username, password):
   if moudle.Teacher.get_obj_by_name(username):
       return False, '用户已存在'
   else:
       moudle.Teacher(username, password)
       return True, f'{username}用户已注册'


def choose_course_interface():
   path = settings.DB_PATH
   course_path = os.path.join(path, 'course')
   if os.path.exists(course_path):
       course_list = os.listdir(course_path)
       return course_list
   else:
       return '请联系管理员创建课程'


def add_course_interface(username, course):
   teacher_obj = moudle.Teacher.get_obj_by_name(username)
   if course in teacher_obj.course_list:
       return False, '该课程已经选过了'
   else:
       teacher_obj.add_course(course)
       return True, f'{course}选择完成'


def check_course_interface(name):
   teacher_obj = moudle.Teacher.get_obj_by_name(name)
   return teacher_obj.course_list


def check_student_interface(course):
   obj = moudle.Course.get_obj_by_name(course)
   return obj.student_list


def modify_score_interface(name, course, score):
   student_obj = moudle.Student.get_obj_by_name(name)
   student_obj.score[course] = score
   student_obj.modify_score()
   return True, '打分完成'

common_interface.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 21:23
# @author: Maxs_hu
from db import moudle
import os
from conf import settings


def login_interface(name, password, user_type):
   global obj
   if user_type == "admin":
       obj = moudle.Admin.get_obj_by_name(name)  # 直接拿到admin的对象. 方便后面调用
   elif user_type == "teacher":
       obj = moudle.Teacher.get_obj_by_name(name)
   elif user_type == "student":
       obj = moudle.Student.get_obj_by_name(name)
   else:
       return False, '没有这个用户类型'
   if obj:
       if obj.password == password:
           return True, '%s登录成功' % name
       else:
           return False, '密码错误'
   else:
       return False, '用户不存在'


def check_school_interface():
   school_path = settings.SCHOOL_DB_PATH
   if os.path.exists(school_path):
       school_list = os.listdir(school_path)
       return school_list
   else:
       return None

lib文件夹

common.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/22 10:36
# @author: Maxs_hu


# 写一个加参装饰器
def auth_login(user_type):
   from core import admin, teacher, student  # 在局部变量内导入. 每次需要时才使用. 防止交叉调用

   def auth(func):
       def wrapper(*args, **kwargs):
           if user_type == 'admin':
               if not admin.admin_status['name']:
                   admin.admin_login()  # 直接调用登录接口进行登录
               else:
                   res = func(*args, **kwargs)
                   return res
           elif user_type == 'student':
               if not student.student_status['name']:
                   student.student_login()
               else:
                   return func(*args, **kwargs)

           elif user_type == 'teacher':
               if not teacher.teacher_status['name']:
                   teacher.teacher_login()
               else:
                   return func(*args, **kwargs)
           else:
               raise TypeError('没有此类型')
       return wrapper
   return auth

4.运行效果及总结

以上是部分运行截图. 在代码完成过后还发生过一些错误:

  1. 最后测试的时候一直报类的no attribute. 找了半天发现原因是在编程中途存的数据. 在那个类中还没有存放那个属性. 所以一定要注意在程序开发中 -> 之前的测试数据可能跟后期的不匹配
  2. 程序健壮性问题: 一个程序可能有很多中报错的类型. 这就需要在编程的时候足够细心. 尽量将报错方式都想到. 实在不行可以使用try…except捕获异常
  3. 变量及程序命名问题: 如果命名出现相同可能会导致程序之间发生交叉调用. 我们要尽量避免这种命名相同. 让每一个变量都有自己的独特的含义
  4. 数据储存问题: 每一次调用到数据库中的类去改变数据之后不能仅仅只是表面上改变. 而要去调永db_handler中的save方法. 将数据保存到本地地址里面. 这样后面在使用的时候才会有记录
  5. 最后一个就是程序之间的调用一定要逻辑清晰. 稍有不慎可能就调错了类或者传错了名字. 这就需要程序命名可读性高. 编码的时候保持细心. 思路清晰.