毕业设计 高校排课系统

需求分析

一般情况下,需求分析在整个软件开发过程中占的比例大约在70%左右,而代码编写则只占了30%左右,由此可见在开发项目之前,需求分析是十分重要且复杂的工作。对于高校排课系统,我们主要是要理清系统实体间的关系,排课业务的需求等。

1 排课需求分析

1.1 排课流程

对于普通高校的排序业务流程大致如下:
1.学期前安排并确定各个专业和班级
2.管理员为每个班级根据学校资源进行排课
3.排课结果公示
4.学期按照课表进行授课
系统设计的关键在于如何分配教学资源,做到合理科学。特别是多条件下课表的安排以及学生,教室,教师等资源的调度是本系统设计的重点。
在这里插入图片描述
排课流程图

1.2 排课原则

对于不同场景下,排课规则一般不同,本系统模拟这样一个场景:
1.每次排课只排一周的课时,但是所有班级和专业都要进行排课。
2.有两种类型的课程,必修课和选修课,通常情况下,必修课在白天授课,选修课一般安排在晚上。
3.一天按照11节课时进行排课,但要注意的是通常一节课占用两个课时段。
4.一名教师可以带相关专业的至多三门专业课程。
5.同一班级同一课程一周中不同时段的授课老师应保持一致。
6.每次排课的时间应该在10s之内。
注意:对于该场景我们应设置一些前置条件,如下。
1.学校的教学资源,如教师,教室,班级等信息应该提前确定并且应该保证满足学校招生的需求。
2.对于教师,教室,班级等信息,通过有差别的编码形式进行定义,方便程序处理。

2 管理员端业务需求分析

2.1 教师管理

教师模块应该包含对教师的新增,删除,更新信息,查询等操作。
删除老师:当老师还有课要上,则不能删除。如果老师没有课程要上的话,则要先将老师与课程的关联解除,然后再删除该老师的信息。
新增老师:同样如果新增一名老师也需要与相关课程进行关联(不超过三个)
更新老师:更新老师信息时,规定只更新非关联信息。
查询老师:根据老师相关信息进行查询,注意管理员可以查看每个老师的详细信息。

2.2 教室管理

教室管理包括:教室的新增,更新信息,删除,查询等功能。
注意:教室可能会与排课信息关联,因此对于教室的删除操作时要注意其关联信息的处理。例如:教室如果还有课要上,则不能删除。

2.3 学生管理

学生管理相对于系统相对独立,在排课过程中属于前置条件,对系统核心设计影响不大,但是学生作为一个实体,与系统其他实体有个众多关联,因此管理员对其进行操作时,要注意关联信息的处理,具体操作如下:
添加学生:添加一个学生,并分配该学生的班级。
删除学生:删除一个学生,但注意,学生与班级有关联,因此还要在学生所在的班级中将学生信息删除。
更新学生:更新学生非关联信息时,直接修改即可,但是当修改学生的班级信息时,要注意将学生信息从原班级删除,并在新班级中添加该学生信息。
查找学生:管理员可以根据学生的相关信息进行查询,并且可以查看学生的详细信息。

2.4 管理员管理

对于超级管理员,他对于普通管理员具有管理权限,而普通管理员无对管理员进行管理的权限,超级管理员的权限包括:
限制管理员:限制普通管理员的系统操作权限。
更新管理员:更新普通管理员信息。
查看管理员:查看普通管理员信息。

2.5 排课管理

该模块是排课系统的核心,管理员对该模块有如下操作:
查看所有已排课班级
查看已排课班级的排课详细信息,班级详细信息
修改排课班级非关联信息,如名称
删除排课班级,注意班级的排课信息与排课老师,排课教室,排课班级都有关联,因此删除排课的班级,需要解除该课表与关联班级,教室,老师的关联
重新排课,如果对当前班级的排课效果不满意,可以重新排课,直至满意为止。

2.6 班级管理

管理员可以对班级进行如下管理:
新增班级:新增一个班级信息,注意新增班级的时候同时必须要指明该班级所需上的课程
查询班级:根据班级信息查询班级,以及班级的详细信息。
删除班级:删除一个班级,注意如果该班级还有课程或还有学生则无法删除。
更新班级:更新班级的非关联信息,如名称等

3 学生端业务需求分析

3.1 查看并修改个人信息

学生可以通过账号密码登录系统,查看个人信息,并可以对登录密码进行修改。

3.2 查看课表信息

学生正确登录后可以查看个人的课表信息。

4 教师端业务需求分析

4.1 查看并修改个人信息

教师可以通过账号密码登录系统,查看个人信息,并可以对登录密码进行修改。

4.2 查看课表信息

教师正确登录后可以查看个人的课表信息。

5 数据库设计

系统的数据库设计,尤其是系统实体间的关联关系十分重要,他是我们对业务分析之后得出的一个实例结论,是我们进行代码开发前的最后一步,它直接反映着系统的业务需求,因此,数据库的设计的完成可以说是完成了一半的系统设计。

5.1 ER图

以排课实体为中心,排课实体对象关联了老师实体,教室实体,班级实体,班级实体与学生实体关联。
在这里插入图片描述
系统ER图

5.2实体属性表
Admin
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	NAME	varchar(100) NULL	
	PASSWORD	varchar(100) NULL	
	sex	varchar(10) NULL	

Classroom
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	NAME	varchar(100) NULL	
	state	varchar(10) NULL	

Classroom_time
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	classroom_id	varchar(100) NULL	
	time_num	int(11) NULL	
	state	int(11) NULL	

Major
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	NAME	varchar(100) NULL	
	state	varchar(10) NULL	

Major_subject
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	major_id	varchar(100) NULL	
	subject_id	varchar(100) NULL	

Paike
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	major_id	varchar(100) NULL	
	subject_id	varchar(100) NULL	
	classroom_id	varchar(100) NULL	
	teacher_id	varchar(100) NULL	
	time_num	int(11) NULL	

Student
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	NAME	varchar(100) NULL	
	PASSWORD	varchar(100) NULL	
	sex	varchar(10) NULL	
	age	int(11) NULL	
	state	varchar(20) NULL	

Student_major
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	student_id	varchar(100) NULL	
	major_id	varchar(100) NULL	

Subject
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	name	varchar(100) NULL	
	type	varchar(10) NULL	
	numOfWeek	varchar(10) NULL	

Teacher
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	NAME	varchar(100) NULL	
	PASSWORD	varchar(100) NULL	
	sex	varchar(10) NULL	
	age	int(11) NULL	

Teacher_subject
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	teacher_id	varchar(100) NULL	
	subject_id	varchar(100) NULL	

Teacher_time
	Field	Type	Comment
	id	varchar(100) NOT NULL	key
	teacher_id	varchar(100) NULL	
	time_num	int(11) NULL	
	state	int(11) NULL	
5.3实体属性图

Admin
在这里插入图片描述
Classroom
在这里插入图片描述
Classroom_time
在这里插入图片描述
Major
在这里插入图片描述
Major_subject
在这里插入图片描述

Paike
在这里插入图片描述

Student
在这里插入图片描述

Student_major
在这里插入图片描述

Subject
在这里插入图片描述

Teacher
在这里插入图片描述

Teacher_subject
在这里插入图片描述

Teacher_time
在这里插入图片描述

系统设计

在通过前面的调研和需求分析后,我们对系统的主要功能有了详细明确的认识,下面进行系统的具体设计和代码实现以及效果展示。

1 登录与退出模块

1.1 登录模块

登录模块是系统的入口因此登录时的验证十分重要,用户登陆时要先判断用户是否存在,如果用户存在再判断密码是否正确,如果密码也正确,则验证通过,并用session记录用户的类型,如果用户登录失败则需要给出对应的提示信息。登录成功后,主页会随着用户类型不同而展示不同的页面。
具体流程如下。
在这里插入图片描述
用户登录流程
界面展示。
在这里插入图片描述

登录界面
具体实现代码如下。

@RequestMapping("/login")
public String login(String name, String password, Model model, Integer login_type, HttpSession session){
// 打印用户登录信息
        System.out.println(name+" "+password+" "+login_type);
        if (login_type==0){//student
            Student student = studentMapper.findStudentByNameAndPassword(name,password);
            if (student!=null){
                session.setAttribute("user",student);
                session.setAttribute("type",0);
                return "redirect:index";
            }else{
                model.addAttribute("msg","用户名或密码错误");
                return "login";
            }
        }else if (login_type==1){//teacher
            Teacher teacher = teacherMapper.findTeacherByNameAndPassword(name,password);
            if (teacher!=null){
                session.setAttribute("user",teacher);
                session.setAttribute("type",1);
                return "redirect:index";
            }else{
                model.addAttribute("msg","用户名或密码错误");
                return "login";
            }
        }else{//admin
            Admin admin = adminMapper.findAdminByNameAndPassword(name,password);
            if (admin!=null){
                // 将用户信息保存到session域中
                session.setAttribute("user",admin);
                session.setAttribute("type",2);
                // 重定向到index
                return "redirect:index";
            }else{
                // 用户名或密码错误
                model.addAttribute("msg","用户名或密码错误");
                return "login";
            }
        }
}

主页展示(管理员)。
在这里插入图片描述

管理员主页

1.2 退出模块

登录的用户可以选择退出系统,退出时要注意先清除当前用户的session信息,再退出系统,跳转到系统首页。
流程图如下。
在这里插入图片描述

退出流程

具体代码如下。

@RequestMapping("/logout")
    public String logout(HttpSession session){
        // 清除当前会话信息
        session.invalidate();
        return "redirect:index";
}

主页(未登录)展示。
在这里插入图片描述

未登录主页

2 管理员模块

用户以管理员账号登录后,进入系统管理员模式。

2.1 班级管理模块

管理员可以给班级安排课程,也可以给已经排好课的班级重新排课,具体效果如下。
点击进入班级管理。
在这里插入图片描述

班级管理
点击添加按钮可以为班级自动排课。
在这里插入图片描述

班级管理列表
在这里插入图片描述

添加班级后自动为班级排课

点击查看排课,即可查看当前班级的课表,同时也可以对班级进行更新,查看详情,删除的操作。
在这里插入图片描述

查看详情
排课详情以用户友好的课表的形式展示,方便查阅。点击重新排课即刻快速地给当前班级重新排课。
在这里插入图片描述

课表
查看班级详情,展示班级信息包括:班级名称,总课程数,总课时,学生人数等具体信息,方便阅读。
在这里插入图片描述

班级详情
更新班级信息,只可以更新班级名称。
在这里插入图片描述

更新班级
管理员可以更具班级关键字查找相关班级信息。
在这里插入图片描述

班级搜索
搜索结果同样可以进行更新和删除操作。
在这里插入图片描述

班级搜索结果
上面是页面展示效果,下面来详细分析一下具体的代码实现步骤,这里着重讲解一下排课过程的实现。
排课实现逻辑如下。
1.根据当前班级,找到班级所有的课程(学期前安排好的)
2.为每一个课程进行排课,具体过程:
2.1根据课程找到授课老师,并确定授课时间(老师够用)
2.2根据授课时间找一个满足条件的教室(教室够用)
这样循环将每个课程排好后,就生成了一个班级的课表。
排课流程图如下。
在这里插入图片描述

排课流程
具体代码如下。


@RequestMapping("/to_paike")
    public String to_paike(@ModelAttribute("id") String id,RedirectAttributes attributes) {
        /*
        根据专业id找专业对应的课程
         */
        List<String> subjects = majorSubjectMapper.findSubjectsByMajorID(id);
        /*
         存储当前班级所有已安排课程的时间段,通过比较防止后面的课程时间与前面的课程时间重复
         */
        List<String> timeList = new ArrayList<>();

        /*
        遍历该班级的所有课程进行排课
        条件:课程分为必修和选修课程,必修课程优先安排在白天,选修课程优先安排在晚上
         */
        for (String sid :
                subjects) {
            // 获取课程一周的课时量
            String num = subjectMapper.findSubjectById(sid).getNumOfWeek();
            // 获取课程的类别:必修,选修
            String type = subjectMapper.findSubjectById(sid).getType();
            // 按照每个课程一周的课程量排课
            for (int i = 0; i < Integer.parseInt(num); i++) {
                // 定义变量: 每个课程排好后都有教师,教室,时间
                Teacher teacher = null;
                Classroom classroom = null;
                String timeNum = null;

                // 存储待选的授课老师
                List<String> teachers = new ArrayList<>();
                // 要求:同一课程同一老师
                if (i >= 1) {// 如果这门课之前排过,则找到之前的这门课老师
                    List<String> teacherId = paikeMapper.findTeacherIdByMajorIdAndSubjectId(id, sid);
                    if (teacherId.size()!=0)teachers.add(teacherId.get(0));
                } else {// 如果第一次排这门课,则根据课程id获取所有的授课老师
                    teachers = teacherSubjectMapper.findTeacherIdBySubjectId(sid);
                }
                // 洗牌
                Collections.shuffle(teachers);
                // 循环待选老师找出满足条件的老师
                for (String tid :
                        teachers) {
                    // 找到老师的有空时间
                    List<TeacherTime> teacherTimes = teacherTimeMapper.findTeacherTimesByTeacherId(tid);
                    // 预处理找出空闲时间(可选)
                    Collections.shuffle(teacherTimes);
                    boolean fa = false;
                    for (TeacherTime tt :
                            teacherTimes) {
                        // 判断必修,如果是必修,但当前老师空闲时间是晚上则继续选老师的下一个时间段
                        if (type.equals("1") & Integer.parseInt(tt.getTimeNum()) % 5 == 0) {
                            continue;
                        }
                        if (type.equals("0") & Integer.parseInt(tt.getTimeNum()) % 5 != 0) {
                            continue;
                        }
                        // 判断该老师空闲时间上是否与之前的课程重复了
                        boolean fb = false;
                        for (String tl :
                                timeList) {
                            if (tl.equals(tt.getTimeNum())) {
                                fb = true;
                                break;
                            }
                        }
                        // 如果当前老师空闲时间与之前课程时间冲突则继续
                        if (fb) {
                            continue;
                        }
                        // 时间不重复继续,判断是否空闲
                        if (tt.getState().equals("0")) {
                            // 将该时间存储到timeList中
                            timeList.add(tt.getTimeNum());
                            // 根据老师时间找教室
                            List<ClassroomTime> classroomTimes = classroomTimeMapper.findClassroomTimesByTimeNum(tt.getTimeNum());
                            Collections.shuffle(classroomTimes);
                            boolean fc = false;
                            for (ClassroomTime ct :
                                    classroomTimes) {
                                // 教室可用
                                if (ct.getState().equals("0")) {
                                    classroom = classroomMapper.findClassroomById(ct.getClassroomId());
                                    classroomTimeMapper.updateClassroomStateByIdAndTimeNum(classroom.getId(), tt.getTimeNum(), "1");
                                    timeNum = tt.getTimeNum();
                                    fc = true;
                                    break;
                                }
                            }
                            // 如果教室可用就更新教师该时间段状态,否则继续
                            if (fc) {
                                teacher = teacherMapper.findTeacherById(tt.getTeacherId());
                                teacherTimeMapper.updateTeacherStateByIdAndTimeNum(tt.getTeacherId(), tt.getTimeNum(), "1");
                                fa = true;
                                break;
                            }
                        }
                    }
                    // 如果该课程的老师和教室都找到了,则将这个课表对象存到数据库中,否则继续
                    if (fa) {
                        paikeMapper.addPaike(new Paike(IDGenerator.getUniqueID(),
                                id, sid, classroom.getId(), teacher.getId(), timeNum));
                        majorMapper.updateMajorStateById(id, "1");
                        break;
                    }
                }
            }
        }
        // 当前班级所有课程都排好后,根据班级id获取班级的全部课表信息
        List<Paike> paikes = paikeMapper.findPaikesByMajorId(id);
        // 按照时间先后顺序排序
        paikes.sort(Comparator.comparingInt(o -> Integer.parseInt(o.getTimeNum())));
        // 填充没课的时间段
        List<String> list = new ArrayList<>();
        for (Paike paike :
                paikes) {
            list.add(paike.getTimeNum());
        }
        for (int i = 0; i < 35; i++) {
            if (!list.contains(String.valueOf(i+1))){
                paikeMapper.addPaike(new Paike(IDGenerator.getUniqueID(),
                        id, null, null, null, (i+1)+""));
            }
        }
        attributes.addFlashAttribute("id",id);
        return "redirect:to_paike_info";
}

在课表排好后需要进行格式化显示,效果如下。
在这里插入图片描述

格式化显示课表
实现逻辑如下。
将每个课表的表示的一周中35个时间段按天分成7份单独展示在table中的每一列中即可。
具体代码如下。


/**
     * 格式化显示步骤1
     * @param model
     * @param paikeno
     */
    private void addList(Model model, List<Paike> paikeno) {
        // 调用格式化显示步骤2
        getList(model, paikeno, majorMapper,subjectMapper,
                teacherMapper,
                classroomMapper);
    }

    /**
     * 格式化显示步骤2
     * @param model
     * @param paikeno
     * @param majorMapper
     * @param subjectMapper
     * @param teacherMapper
     * @param classroomMapper
     */
    public static void getList(Model model, List<Paike> paikeno, MajorMapper majorMapper,
                               SubjectMapper subjectMapper,
                               TeacherMapper teacherMapper,
                               ClassroomMapper classroomMapper) {
        // 定义各行元素
        List<Dto> one = new ArrayList<>();
        List<Dto> two = new ArrayList<>();
        List<Dto> three = new ArrayList<>();
        List<Dto> four = new ArrayList<>();
        List<Dto> five = new ArrayList<>();
        // 遍历一周中35个课表对象进行处理
        for (int i = 0; i < 35; i++) {
            // 调用格式化显示步骤3
            getLists(paikeno, one, two, three, four, five, i,subjectMapper,teacherMapper,classroomMapper,
                    majorMapper
                    );
        }

        // 将处理结果封装到model里
        model.addAttribute("ones",one);
        model.addAttribute("twos",two);
        model.addAttribute("threes",three);
        model.addAttribute("fours",four);
        model.addAttribute("fives",five);
    }

    /**
     * 格式化显示步骤3
     * @param paikeno
     * @param one
     * @param two
     * @param three
     * @param four
     * @param five
     * @param i
     * @param subjectMapper
     * @param teacherMapper
     * @param classroomMapper
     * @param majorMapper
     */
    private static void getLists(List<Paike> paikeno,
                                 List<Dto> one,
                                 List<Dto> two,
                                 List<Dto> three,
                                 List<Dto> four,
                                 List<Dto> five, int i,SubjectMapper subjectMapper,
                                 TeacherMapper teacherMapper,
                                 ClassroomMapper classroomMapper,
                                 MajorMapper majorMapper) {
        // 将排课对象转化成dto
        Paike paike = paikeno.get(i);
        Subject subject = subjectMapper.findSubjectById(paike.getSubjectId());
        Teacher teacher = teacherMapper.findTeacherById(paike.getTeacherId());
        Classroom classroom = classroomMapper.findClassroomById(paike.getClassroomId());

        // 按行添加
        if (i%5==0){
            // 这里使用dto来传输需要的数据
            one.add(new Dto(subject,teacher,classroom,paike,majorMapper.findMajorById(paike.getMajorId())));
        }
        if (i%5==1){
            two.add(new Dto(subject,teacher,classroom,paike,majorMapper.findMajorById(paike.getMajorId())));
        }
        if (i%5==2){
            three.add(new Dto(subject,teacher,classroom,paike,majorMapper.findMajorById(paike.getMajorId())));
        }
        if (i%5==3){
            four.add(new Dto(subject,teacher,classroom,paike,majorMapper.findMajorById(paike.getMajorId())));
        }
        if (i%5==4){
            five.add(new Dto(subject,teacher,classroom,paike,majorMapper.findMajorById(paike.getMajorId())));
        }
    }

    /**
     * 课表展示
     * @param id
     * @param model
     * @return
     */
    @RequestMapping("/to_paike_info")
    public String to_paike_info(@ModelAttribute("id") String id, Model model) {
        /*
        当前班级所有课程都排好后,根据班级id获取
        班级的全部课表信息,传递到前段展示即可
         */
        List<Paike> paikes = paikeMapper.findPaikesByMajorId(id);
        // 统计有效课时
        int temp =0;
        for (Paike paike :
                paikes) {
            if (paike.getClassroomId()==null|paike.getSubjectId()==null|paike.getTeacherId()==null){
                temp++;
            }
        }
        // 按照时间先后顺序排序
        paikes.sort(Comparator.comparingInt(o -> Integer.parseInt(o.getTimeNum())));

        // 调用格式化显示方法,将需要的数据放到model里面
        addList(model, paikes);
        Major major = majorMapper.findMajorById(id);
        model.addAttribute("major",major);
        model.addAttribute("numOfSub",paikes.size()-temp);
        return "paike_index";
}
2.2 课程管理模块

课程管理包括:课程信息查询,课程搜索,课程更新,课程添加等功能。下面来具体看下效果和实现过程。
进入课程管理模块后,展示如下。
在这里插入图片描述

我们可以对课程进行更新操作,如下。
在这里插入图片描述

具体代码如下。

@RequestMapping("/subject_update")
    @Transactional
    public String subject_update(Subject subject,RedirectAttributes attributes){
        subjectMapper.updateSubjectById(subject);
        attributes.addFlashAttribute("msg","更新成功");
        return "redirect:subject_index";
}

我们可以对课程进行删除操作,删除操作在系统中比较重要,因此给出提示信息,如下。
在这里插入图片描述

如果删除的课程,还有其他实体与之关联,则无法删除,并给出提示,如下。
在这里插入图片描述

具体代码如下。


@RequestMapping("/to_subject_delete")
    @Transactional
    public String to_subject_delete(@RequestParam("id") String id,RedirectAttributes attributes){
        List<String> subjectIds = paikeMapper.findSubjectBySubjectId(id);
        if (subjectIds.size()>0){
            attributes.addFlashAttribute("msg","正在上课,无法删除");
            return "redirect:subject_index";
        }
        majorSubjectMapper.deleteSubjectFromMajor(id);
        teacherSubjectMapper.deleteTeacherAndSubjectBySubjectId(id);
        subjectMapper.deleteSubjectById(id);
        attributes.addFlashAttribute("msg","删除成功");
        return "redirect:subject_index";
    }

3 其他模块

由于篇幅有限,其他模块功能就不进行详细介绍了,具体的实现过程在项目中都有注释,结合运行效果可以很快理解。

4 源码地址

说明:资源为CSDN账号BirdMan98,即本人所有,受版权保护,商业应用必追究责任,引用需征得同意,为了保护版权,设置了收费,大家请酌情购买

源码地址

在这里插入图片描述

  • 11
    点赞
  • 135
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BirdMan98

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值