选课系统编写思路

资料下载

选课系统项目文件-CCS.zip

项目目的

  • 在ATM+购物车项目的所掌握的三层架构、软件开发目录之上,进一步拓展
  • 在项目中加入面向对象的封装、继承、多态、反射的要素,训练面向对象的思维

架构设计

在三层架构的基础上,做了一些简单的改变。

  • 用户交互层也进行了分叉,不同的用户类型将访问不同的功能

    分别是管理员、学生、教师三个视图。

  • 无论是什么视图,作为用户都有注册登录功能,实际上这段代码是大同小异的

    所以如果会面向对象之反射的话,就可以对每个视图的注册登录功能做统合,当然相应的也会有与接口来处理其与数据交互层的连接。

    下文会详细讲解。

  • 数据处理采取了对象存储的方法,采取了pickle模块,能将python的对象类型序列化存储到文件里,而对象中存储了对象独有的数据。

  • 每种对象数据都有存储功能,我们可以让它继承父类中的存储功能,使用类名和对象进行区别,做到‘分类’存储。

image

重要环节讲解

统合注册登录界面

auth_view.py 属于用户交互层,不同的用户类型只会登录它相应的登录态,如Admin视窗就只会改变login_user['Admin']的状态,得到一个管理员登录用户

from interface import auth_inf
from conf import settings
from lib import common


# 保存所有用户类型的登录状态
login_user = {
    'Admin': '',
    'Teacher': '',
    'Student': ''
}


def register(mode):
    ch_mode = settings.MODE_CH_DICT.get(mode)  # 每个类型的有不同的字符
    while True:
        username = input(f'请输入{ch_mode}用户名(q退出登录):').strip()
        if username == 'q':
            return False
        flag, msg = auth_inf.is_obj_exist_inf(username, mode=mode) 
        # 根据不同的用户类型,启用的接口也会有区别
        if flag:
            print(f'{ch_mode}用户【{username}】已被注册')
            continue
        password = input(f'请输入{ch_mode}密码:').strip()
        confirm_pwd = input(f'请确认{ch_mode}密码:').strip()
        if not password == confirm_pwd:
            print('两次输入的密码不一致')
            continue
        password = common.get_hash(password)
        # if mode == 'Student':
        flag, msg = auth_inf.register_inf(username, password, mode=mode)
        print(f'{ch_mode}【{username}】注册成功')
        break
    return True


def login(mode):
    ch_mode = settings.MODE_CH_DICT.get(mode)
    while True:
        username = input(f'请输入{ch_mode}用户名:').strip()
        if username == 'q':
            return False
        flag, msg = auth_inf.is_obj_exist_inf(username, mode=mode)
        if not flag:
            print(f'不存在{ch_mode}用户【{username}】')
            continue
        password = input(f'请输入{ch_mode}密码:').strip()
        password = common.get_hash(password)
        flag, msg = auth_inf.login_inf(username, password, mode=mode)
        if flag:
            print(f'{ch_mode}用户{username}登录成功')
            login_user[mode] = username
            return True
        print('密码错误')


def login_auth(mode):
    def outer(func):
        def inner(*args, **kwargs):
            name = login_user[mode]
            if name:
                if auth_inf.is_obj_valid(name, mode):
                    res = func(*args, **kwargs)
                    return res
                print(f'{name}已被管理员冻结,请联系管理员')
                return
            print('你还未登录,不能使用这些功能')
            login(mode)

        return inner

    return outer

auth_inf.py 专门转接认证方面的功能,用到了反射的功能,将字符串'Admin'转换为models模块中的类,每个类中都有相应的注册方法,校验密码方法等方法,而且同名,如校验密码都用了verify_pwd,这里就是用了多态的思想,如果大家都是干一件事,就同名,那么我们拿到不同对象时,无论是什么类的对象,都存在一个verify_pwd方法为我们校验密码

from db import models


def is_obj_exist_inf(username, *, mode):
    cls = getattr(models, mode)
    if cls.select(username):
        return True, f'{cls.__name__}对象【{username}】存在'
    return False, f'{cls.__name__}对象【{username}】不存在'


def register_inf(username, pwd, *, mode, **kwargs):
    cls = getattr(models, mode)
    cls(username, pwd, **kwargs)
    return True, f'{cls.__name__}【{username}】注册成功'


def login_inf(username, password, *, mode):
    cls = getattr(models, mode)
    user_obj = cls.select(username)
    if user_obj.verify_pwd(password):   # 校验密码的多态
        return True, '登录成功'
    return False, '登录失败'


def is_obj_valid(name, mode):
    cls = getattr(models, mode)
    if not hasattr(cls.select(name), 'valid'):
        return True
    return cls.select(name).valid


def modify_pwd(username, new_pwd, *, mode):
    cls = getattr(models, mode)
    user_obj = cls.select(username)
    user_obj.modify_pwd(new_pwd)
    return True, '密码修改成功'

models.Admin 数据层的admin类,使用了封装的思想,先将pwd隐藏起来,让用户不能读取,再开放接口进行校验。这个数据属于敏感数据,所以就使用了封装后开放接口的做法,来避免这个数据被滥用。(这里还有很多数据也可以采取这种思想,如名字作为唯一凭证应该也只读不能删改,这里就留给读者去思考)

class Admin(DBHandler):
    def __init__(self, name, pwd):
        self.name = name
        self.__pwd = pwd
        self.save()

    def verify_pwd(self, password):  # 校验密码只能通过这个接口
        return password == self.__pwd

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

    def lock_obj(self, cls, name):
        obj = cls.select(name)
        obj.valid = False
        obj.save()

    def unlock_obj(self, cls, name):
        obj = cls.select(name)
        obj.valid = True
        obj.save()

注册登录功能与核心功能的流程优化

student_view.py 的run函数

注册登录应该是单独的界面,让用户成功登录后再给他看所有的核心功能

为此,可以采取两个并列的循环来实现,在登录成功时,使第一循环break,就可以成功进入第二个循环,向用户展示核心功能,让用户可以循环使用

def run():
    while True:
        if auth_view.login_user[MODE]:
            break
        choice = input("""
        +----学生登录注册界面----+
        |       1 注册         |
        |       2 登录         |
        +---------------------+
        请输入编号(q返回首页):""")
        if choice == 'q':
            return
        if choice == '1':
            auth_view.register(MODE)
        elif choice == '2':
            flag = auth_view.login(MODE)
            if flag:
                print('登录成功')
                break
        else:
            print('输入1或2进入注册或者登录')
            continue
    student_user = auth_view.login_user[MODE]
    while True:
        choice = input(f'''
        =========您好,学生【{student_user}】=========
        1 修改密码
        2 绑定学校
        3 选择课程
        4 查看成绩单
        =============<学生首页>=============
        请输入功能编号(q返回首页)''').strip()
        if choice == 'q':
            return
        if choice in func_dict:
            func_dict[choice]()
        else:
            print('请输入正确的功能编号')

具体的效果如下:

image

对象是数据和功能的集合体

models.Teacher.py 上面的这句话怎么理解呢,实际上可以看到关于数据处理的逻辑都是在models的类中完成的,创建课程时,两者一定会互相绑定,让teacher对象的course_list中添加这个课程的名字,调用Course(course_name, price, school, self.name)创建并存储课程时也传入教师的名字,让课程对象保存到它的school属性中

而在接口层只是对这些对象简单的存取和调用对象中的功能罢了。

class Teacher(DBHandler):
    def __init__(self, name, pwd):
        self.name = name
        self.__pwd = pwd
        self.valid = True
        self.course_list = []
        self.save()

    def verify_pwd(self, password):
        return password == self.__pwd

    def modify_pwd(self, new_pwd):
        self.__pwd = new_pwd
        self.save()

    def create_course(self, course_name, price, school):
        Course(course_name, price, school, self.name)
        self.course_list.append(course_name)
        self.save()

    def get_course_infos_list(self):
        # ['课程名','价格','学校','地址','讲师'] * n
        return [Course.select(name).get_course_infos()
                for name in self.course_list]

    def modify_score(self, course_name, student_name, score):
        student_obj = Student.select(student_name)
        student_obj.scores_list[course_name] = score
        student_obj.save()

接口层 teacher_inf.py

def create_course_inf(teacher_user, school_name, course_name, price):
    teacher_obj = models.Teacher.select(teacher_user)
    teacher_obj.create_course(course_name, price, school_name)
    school_obj = models.School.select(school_name)
    school_obj.append_course(course_name)
    return True, f'您开设的课程【{course_name}】已在【{school_name}】开设'
posted @ 2022-11-13 20:54  leethon  阅读(33)  评论(0编辑  收藏  举报