版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第一章绪论1.1研究背景与研究意义随着社会经济的高速发展,社会中各个企业需要的专业人才日渐趋于多样化,各个高校的学生培养计划也开始有了更多的方向,因此高校中的教学专业、科目逐渐趋于复杂。这对于高校每年必须进行的考试考查的安排的难度和繁琐性也大幅度提升。人工的进行考试的安排,虽然可以安排出考试计划,但是花费的时间和精力较多,且老师和学生需要在大量的安排数据中找到自己的考试安排。本章围绕上述人工排考存在的问题,详细介绍设计实现考试安排系统的背景和意义。社会经济高速发展的今天,社会对高等教育人才需求量大大提高的同时,需要的高等教育人才类型也更加丰富,这促使高等院校对学生的培养计划和方向,也不再像以前一样简单,而是诞生了更多的专业和学习的科目,高校必须进行的考试科目也逐渐比以往更多,意味着高校考试安排的工作将变得更加繁琐和困难。期末考试是二级学院对学生一个学期学习成果的检测。期末考试涉及二级学院、专业、班级、教室、科目、监考老师、时间的分配。人工可以分配出合适的考试安排,但需要花费大量的时间和精力,且人工的安排往往使用Excel表格进行数据的展示,所以教师和学生就需要在大量的数据中找到自己的考试计划,对于老师和学生非常不友好。人工分配要想查看以及修改分配记录效率低且过程繁琐,耗时耗力,且不易对课程、教师、教室、学生、选课、授课以及每学期的考试安排计划等数据信息进行管理。所以为了提高考试安排计划的效率和考试安排信息的管理,考虑对“期末考试安排系统”进行设计与实现,以减少二级学院对期末考试安排的人力花费,提高考试安排工作的效率。考试的安排由系统进行安排,教师和学生可以通过系统在线查看自己的考试安排和监考计划,当考试计划安排结果出来后,系统会在特定的时间向监考教师发送监考任务提醒的短信,以便让监考教师及时注意到自己的监考工作。学生可以在线填写对特定考试科目的缓考申请,交由该科目的授课老师以及学院的辅导员填写意见后,由管理员参考意见进行缓考申请的审核。考试任务结束后,管理员可以通过系统查看学生考试考勤情况的可视化数据,也可以查看并导出每学期的考试考勤情况报表。1.2国内外研究现状排考系统属于教务安排工作的一部分,排考系统往往约束条件较多,且针对不同的高校考试计划各种约束条件也大不相同,因此这就导致了排考系统的复杂性较高并且很难设计出一种普遍应用的不同高校的通用的排考策略,最好的排考策略便是针对已知的约束条件和资源设计相应的排考策略。排考本质上就是对各种考试必要的数据在特定的约束条件下进行分配,通过对各种数据的判断比较,排除矛盾的决策,选出合适的决策。针对高校的排考系统的设计,国际上已经有了许多不同的设计策略,并将排考问题分为线性优化问题、NP完全问题等。国内也有学者通过建立图论模型对排考问题提出了优化,例如高等学校排考染色-匹配算法,基于遗传算法的排考算法[1]。但是这些排考算法仅针对于符合特定约束条件下的排考规则,并不适用于其他的排考规则。因此根据实际考试安排要求规定约束条件设计合适的排考算法。1.3论文的主要工作参考国内外对考试安排算法的设计,结合二级学院的实际情况设计出合适的考试安排算法并结合SMS服务对考试安排工作进行及时的通知。除此之外,将在系统中为用户提供考试安排涉及到的数据的管理功能、数据可视化功能、数据统计报表功能以及缓考功能。本论文的研究内容主要有一下几点:参考国内外排考算法的设计思想,再根据实际约束条件设计出合适的考试安排算法,并将算法运用到系统的主要功能考试安排功能。再围绕考试安排功能设计实现考试安排所需数据的管理功能、数据可视化、考试情况报表统计以及考生的缓考功能。根据系统的主要功能模块,确定对系统进行编码实现时,需要用到的开发技术以及相关技术理论。整个系统的编码开发主要使用SpringBoot、Vue进行系统功能的实现,并使用RabbitMQ、Nginx作为系统的中间件。运用软件工程的相关理论结合UML软件建模技术对系统进行理论化的分析和设计。主要从系统需求分析、系统总体设计、系统详细设计与实现以及系统测试几个软件开发阶段进行分析和实现。1.4本论文的结构安排 第一章:绪论。本章通过结合社会经济快速发展带来的社会企业对高等教育人才多样化的要求,引起高校对学生培养计划的教学方向的改变,高校教育方向日渐复杂多样化,因此学校的考试科目也增多,从安排考试科目的需求出发,阐述了考试安排系统设计实现的必要性。第二章:相关概念与技术理论综述。本章根据系统的功能需求,详细介绍了系统开发使用的理论知识和技术点。对相关技术 SpringBoot、mybatis-plus、RabbitMQ、vue、ElementUI以及相关理论死信队列、消息存活时间、延迟队列进行介绍以及分析为何选择使用这些理论技术。第三章:系统需求分析。本章通过对系统的所有业务进行详细的需求分析,在本章分析并阐述,系统每个功能具体要实现什么功能,功能的流程。最后再介绍系统的功能性需求与非功能性需求。第四章:系统的总体设计。本章从系统功能的角度出发,将本系统的功能从用户以及使用需求两个主要方面进行功能模块划分。系统功能模块包括基础数据管理模块、考生考试考勤数据可视化模块、考生考试考勤统计报表模块、考试计划安排模块、缓考申请功能模块、审核缓考申请功能模块。并对系统的数据库进行分析设计和实现。第五章:系统的详细设计,本章对系统的主要功能模块,使用软件工程相关理论进行详细设计,以及对系统进行编码实现,详细介绍主要功能的实现流程以及每个主要功能的设计思路以及所用到的技术方案。第六章:系统测试。本章介绍软件测试相关理论,并根据软件测试理论对系统的主要功能模块进行功能测试。第七章:总结与展望。本章结合系统的实现结果与论文的实际成果,对系统当前存在的问题进行说明,并提出今后运用何种方式或使用更好的设计思路去解决系统存在的问题。1.5本章小结本章对论文的研究背景和研究意义进行阐述,从而引出排考系统的整体功能,阐述了将使用系统去解决的问题,以及设计和实现系统的工作方案。介绍了本论文将要进行的工作并对本论文的总体结构进行分析概括,描述了论文每章需要进行的工作。第二章相关理论和技术为了降低视图代码与业务功能代码直接的依赖,系统的总体设计使用前后端分离的技术进行设计,使用Vue+ElementUI组件库进行前端页面的编写,使用Springboot+Mybatis-plus进行后端业务功能代码的编写。系统的主要功能为安排考试计划,并且在考试计划安排出结果后下发短信通知监考老师监考工作信息,很明显并不是排考结束后立马下发短信,而是尽量在考试工作开始前几天下发,因此引入消息中间件RabbitMQ实现该功能。文件导入解析功能,需要对多种不同类型文件进行处理,但本质都是对文件的解析然后保存数据,因此考虑使用策略模式实现该功能。2.1SpringBoot技术SpingBoot技术框架本质上是对Spring框架的扩展和延申,初衷是为了减少开发者在使用Spring框架时需要编写大量的配置文件带来的繁杂性。约定大于配置是一种软件设计理念,开发者通过遵循框架约定的默认规则,达到简化开发的目的。SpringBoot正是通过这个理念,极大的减少了开发人员进行开发框架搭建时需要编写的配置文件,并且SpringBoot提供了主流中间件的模板类,使得开发人员在整合其他中间件时,可以直接使用对应的模板类操作中间件相关接口实现对应功能。系统主要通过Springboot框架进行编码的实现。2.2RabbitMQ技术RabbitMQ是一个消息中间件。其主要模块有消息提供者provider,连接信道channel,转发消息的交换机Exchange,消息的消费者consumer,及存放消息的消息队列Queue。通过消息提供者把消费者产生的消息发送到消息交换机中,由消息交换机通过特定的工作模式将消息发送到消息队列中进行存储,再由消息消费者从消息队列中取出消息并进行消费,以上模块之间的通信通过建立信道channel进行消息的交互。其中每个消息都有一个属性TTL及存活时间。若不给消息设置TTL,则消息不会过期。给消息设置了TTL后,消息会在TTL时间之后死亡,并由一个特殊的交换机死信交换机将消息转发到一个特殊的消息队列即死信队列,通过将普通的消息队列与死信交换机进行绑定,即可构成一个死信队列。2.3Mybatis-plus技术Mybatis-plus是对持久化框架Mybatis的增强,与Mybatis相比,Mybatis-plus使用BaseMapper、IService接口及ServiceImpl实现类封装了对数据库进行简单操作的SQL语句,开发者只需要对接口这类进行实现和继承,便能直接使用封装好的方法去执行一些较简单的SQL语句,开发者只需将精力放在复杂SQL语句的编写,使得开发者需要编写的Mapper映射文件大大减少。且其内置分页插件,无需另外整合分页插件,只需要通过开启分页插件,根据需求编写自己的分页代码,提供给Mybatis-plus需要的分页参数即可实现分页功能。2.4Vue.js技术 Vue一个是用于构建用户前端界面的渐进式前端开发框架。Vue采用MVVM模式,即模型-视图-模式视图桥梁。通过属性v-model将界面中的元素与数据进行双向绑定,不需要通过JavaScript代码更新数据。Vue中页面跳转通过路由router实现。Vue有beforeCreate、activated等钩子函数,每个钩子函数对应着Vue组件中的不同生命周期,Vue组件会在不同的生命周期调用对应的钩子函数。开发者可以根据实际的需求灵活使用生命周期钩子,在不同的钩子函数中对自定义的函数进行调用,实现特定的功能和进行数据的渲染。2.5Redis技术Redis是一个高性能的基于内存的非关系型数据库,使用key-value及键值对的方式进行数据的存储。Redis常用来做数据的缓存,消息队列等功能,将用户经常访问的数据缓存到Redis以提高数据访问的速度。不仅如此,还可以通过设置Redis中的缓存过期时间从而实现某些功能,例如验证码有效期。本系统将使用Redis配合RabbitMQ实现短信下发功能。2.6设计模式理论与技术不同,设计模式是一种软件设计思想。模式是开发人员在长期的软件开发实践中设计软件、管理组织大量软件工作而提炼出来的经验总结,是重复利用设计方法、管理软件过程的有力工具。通过使用模式,有益于复杂系统中产生精巧、简洁的方法设计[2]。在系统中,针对特定场景运用设计模式,不但可以提高代码的可读还能提高系统功能的扩展性。2.7本章小结本章对系统设计实现将要使用到的主要开发技术和理论知识进行介绍。前端用户界使用Vue+ElementUI进行编写,后端业务功能代码使用SpringBoot+Mybatis-Plus+RabbitMQ进行实现。最后介绍了设计模式相关理论知识,在系统的文件数据导入模块中将会运用到设计模式去实现相应的功能代码。第三章系统需求分析本章开始,将以软件工程方法论对系统进行分析设计。软件工程的概念普遍适用于所有类型系统的开发。这些基本概念包括软件过程、可靠性、信息安全性、需求以及复用[3]。本章将对软件工程中的需求分析过程进行分析。软件需求分析的任务是确定系统必须完成哪些工作,也就是对目标系统提出完整、准确、清晰、具体的要求.它所做的工作是深入描述软件的功能和性能,确定软件设计的边界和软件同其他元素的接口,定义软件的其他有效性要求[4]。需求分析是软件开发中非常重要的一个阶段,正确的需求分析结果,能够知道软件设计开发往正确的方向逐步执行。3.1可行性分析通过对现有的考试安排、课程安排等教务范畴的系统进行比较分析发现,系统的功能需求在软件实施的整个过程中,都不会有太大的变化。因此系统设计实现的重点在于完成系统功能需求的同时,保证系统的可复用性和扩展性。系统使用相对成熟的后端开发技术SpringBoot相关的技术栈进行系统的编码实现,整合第三方中间件RabbitMQ、SMS服务以及kodo云存储服务进行部分功能的实现。3.2系统需求概述二级学院采用人工手动的方式,利用Excel文件对每个学期末二级学院的考试日程进行安排,虽然可以安排出合理的考试计划安排,但往往需要花费大量的时间和人力。并且教师和学生需要在Excel表格中从大量的考试计划数据中找到自己的监考计划和考试计划,不但不够直观而且对于老师和学生的体验不够友好。因此为了解决这些问题,考虑对期末考试安排系统进行设计和实现。系统主要解决的问题有如下几点:排考数据管理。基础数据管理是对安排考试计划必需的数据进行管理。包括二级学院、班级、教室、学生、教师、课程信息、专业信息的管理以及包括各数据的录入。考试安排:管理员通过选择考试科目以及考试时间,点击生成考试计划安排表后,由系统自动生成考试计划并返回界面。短信通知:考试计划安排生成后,系统将在每个监考老师监考的前一天通过短信的方式向老师发送监考计划,短信内容包括监考时间、监考地点、教师名称。缓考功能模块:该功能模块的流程为:由学生填写需要缓考的考试科目信息并提交;由任课老师和辅导员填写缓考建议;管理员通过任课老师和辅导员的建议进行缓考申请的审核。考试情况可视化模块:该模块以饼图的形式,向用户展示每次期末考试的情况,展示的数据包括考生人数、缓考人数、实际考试人数。用户可以通过选择考试科目、学年学期、班级进行数据的筛选。报表统计模块:该模块统计了每次期末考试的情况,用户可以选择导出期末考试情况报表。3.3系统角色分析系统角色是系统功能以外的部分,主要包括系统的使用者、参与者。通过对期末考试安排系统的分析,可以得出系统的主要角色包括管理员、教师、学生以及二级学院辅导员。各角色在系统中主要操作的功能如下:管理员:管理员主要使用系统进行考试安排中关系到的所有数据进行管理、对系统功能菜单的管理、系统用户角色信息管理、系统角色及操作权限的管理。管理员还需要根据要求,选择适当条件进行考试计划的安排。审核学生的缓考申请。查看并导出考试情况的报表。辅导员:辅导员在系统中是一个具有辅导员角色的教师。辅导员主要的职责为填写学生缓考申请的中的缓考建议。教师:教师的主要操作功能包括填写学生缓考申请建议、查看自己的监考工作计划。.学生:学生的主要功能包括查看自己的考试安排、填写缓考申请。3.4功能需求本小节通过使用UML建模中的用例图,对系统主要的功能用例进行分析与建模。下面将对系统的主要功能进行用例分析建模,并给出相应的用例图。3.4.1考试安排数据管理模块该模块的操作使用主要由系统的管理员进行使用和操作。系统的管理员可通过该模块对二级学院信息、班级、学生、教室、教师、考试科目、专业的信息进行管理。考试安排数据管理模块用例图如图3-1所示:图3-1考试安排数据管理用例图考试数据管理流程:管理员点击相应基础数据管理菜单。系统进入相应的基础数据管理菜单并显示数据列表。管理员在搜索框中输入筛选条件,点击查询。系统查询并现实符合条件的数据列表。管理员点击新增按钮。系统现实新增界面。管理员填写信息点击提交。系统保存提交的信息并提示操作成功。管理员选择要删除的数据点击删除。系统现实是否确认删除。管理员点击确定。系统删除选中的数据,并现实操作成功。管理员点击上传文件。系统现实文件上传框。管理员选择文件。系统上传文件到服务器中,并将文件数据导入到数据库中,显示操作成功。3.4.2考试安排模块管理员点击考试安排,系统进入考试安排界面,由管理员选择安排考试的条件后,点击生成考试计划安排按钮,系统生成考试安排后,获取有监考任务的教师信息,用于在监考前一天通知教师,并且将考试安排数据展示在当前界面。管理员可以在搜索框输入筛选条件,选择特定考试安排信息导出为Excel表格。用例图如图3-2所示:图3-2考试计划安排用例图考试计划安排功能流程: 1.管理员点击考试安排菜单。2.系统进入考试安排界面,并显示已经存在的考试安排计划。3.管理员选择考试科目以及考试时间,填写学年学期,点击生成考试计划安排按钮。4.系统接收参数,生成考试安排计划,获取有监考任务的教师并存储到消息队列中,将数据存储到数据库中,返回界面并提示操作成功。5..管理员点击导出考试计划安排。6.系统导出考试计划安排为Excel文件,并在浏览器提示管理员下载。7.管理员选择下载路径,确定下载。3.4.3缓考功能模块考试计划生成后,考生可以登录系统,点击考试计划菜单,系统进入考试计划界面并显示该考生的考试计划。考生点击申请缓考按钮,系统弹出缓考信息填写表,学生选择需要缓考的科目并点击提交。由任课老师和辅导员填写建议后交由管理员进行审核。学生只能删除未审核的缓考申请,已审核的缓考申请由管理员删除。缓考功能的用例图如图3-3所示图3-3缓考功能用例图缓考功能流程:学生操作(1)学生点击我的考试计划菜单。(2)系统进入考试计划界面并显示当前学生的考试计划。(3)学生点击缓考申请按钮。(4)系统显示缓考申请填写表格。(5)学生选择缓考科目,填写缓考理由后上传相关证明附件,点击提交。(6)系统保存学生的缓考申请记录,上传附件,并保存文件路径。提示操作成功。2.任课教师操作(1)任课教师点击学生缓考申请菜单。(2)系统显示该任课教师所授课程的缓考申请记录。(3)任课教师选择要填写建议的缓考申请,点击填写建议按钮。(4)系统显示该条缓考申请信息。(5)任课教师填写建议并提交。(6)系统保存本次提交记录并提示操作成功。3.辅导员操作(1)辅导员点击学生缓考申请菜单。(2)系统显示该属于该辅导员所在二级学院的学生缓考申请。(3)辅导员选中要填写建议的学生缓考申请并点击填写建议按钮。(4)系统显示该条缓考申请信息。(5)辅导员填写建议并提交。(6)系统保存本次提交记录,并提示操作成功。4.管理员操作(1)管理点击审核学生缓考申请菜单。(2)系统显示所有学生的缓考申请记录。(3)管理员选择要审核的缓考申请并点击审核按钮。(4)系统显示该缓考申请信息。(5)管理员填写审核结果并提交。(6)系统保存本次审核记录并提示操作成功。3.4.4报表统计模块管理员可以查看每次考试后的考试情况统计,通过输入筛选条件选择出要查看的考试情况,并可以将数据导出为Excel文件保存。报表统计模块的用例图如图3-4所示:图3-4报表统计用例图报表统计功能基本流程:管理员点击考试情况报表统计菜单。系统进入相应界面并显示所有科目的考试情况信息列表。管理员输入筛选条件,点击查询。系统查询并显示符合条件的考试情况信息列表。管理员点击导出报表按钮。系统将考试情况信息列表导出为Excel文件并提示下载。管理员选择下载路径确认下载。考试情况可视化模块考试情况可视化模块通过可视化图标组件Echarts实现。管理员进入界面后,可以根据条件统计考试情况的可视化统计信息。考试情况可视化模块的用例图如图3-5所示:图3-5考试情况可视化用例图考试情况可视化功能基本流程:管理员点击考试情况可视化菜单。系统进入考试情况可视化界面。管理员选择筛选条件,点击查询。系统以饼图的形式展示考试情况可视化信息。3.2非功能需求系统的非功能性需求主要包括系统的扩展性、易用性、可维护性、安全性。本系统的非功能需求有以下几点:易用性前端用户界面是系统与用户直接交互的一个部分,在设计用户界面时,应该将界面设计的简单,通过设计尽量降低用户需要进行的操作。在用户填写信息时,系统应该让用户少填信息,只需要用户填写必要信息,且尽量使用选择信息的方式替代填写信息,以提高用户的操作体验。安全性系统通过使用shiro安全框架,实现了系统用户的操作权限,没有得到授权的用户无法操作对应的功能模块,保证了非系统用户无法使用系统功能模块。并通过编写SQLFilter类验证并过滤SQL,防止SQL注入。编写XssFilter与XssHttpServletRequestWrapper类防止XSS攻击。扩展性与可维护性在编写系统代码时,应该严格遵循分层模式去编写代码,每一个类只对外暴露service层,即当一个类需要用它其他类的功能时,不能在该类中依赖其他类的dao类,只能引入service类,这样做能将各层的职责分开,是代码间的耦合度降低,易于代码的维护。尽量组合各个类来实现业务功能,当需要增加新的功能时,优先考虑增加新的代码实现,而不是修改既有代码,以提高代码的扩展性。3.3本章小结本章对二级学院期末考试安排系统的非功能性需求和功能性需求进行阐述。分析了考试安排系统中的主要功能需求分析并给出了系统的功能架构图,并且通过运用UML建模中的用例图分析了系统各个功能模块中的基本执行流程。最后分析阐述了系统的非功能性需求,主要通过系统的安全性、易用性、系统的扩展性和可维护性进行展开阐述。
第四章系统总体设计本章从期末考试安排系统的总体需求入手,分析了系统的技术架构和和物理架构以及业务功能架构图,并在本章对系统的数据库进行分析和设计,详细阐述系统数据库的分析设计过程。4.1系统总体功能设计对于功能较多的系统,一般遵循软件工程“模块化”的理论进行系统开发。“模块化”即把系统按功能模块进行划分,在设计实现系统时根据划分的功能模块进行开发。“模块化”的思想,通过对系统的主要功能进行分析和划分,在系统设计的过程中就将系统拆分成若干个模块,使得整个系统的功能架构变得清晰,便于系统功能的分析和实现。 根据需求规约、及系统的核心功能“考试计划安排”,可以将系统分为:考试计划安排所需的基础数据管理模块、考试安排模块、缓考功能模块、考试情况统计报表模块。其中系统的基础数据管理模块主要用于服务于考试安排模块。图4-1为系统的功能结构图。图4-1系统功能结构图4.2部署架构设计用户通过浏览器进入系统界面,再由http服务器nginx代理用户发出的请求转发到web应用服务器中,再由web应用服务器通过数据持久层mybatis-plus与数据库进行数据的传输交互。系统的物理架构图如图4-2所示。图4-2物理架构图4.3系统架构图系统前端界面使用Vue、elemmentUI、node.js编写用户交互界面,用户的请求通过nginx反向代理后发送给后端服务器,再由后端对用户的请求进行处理,系统架构图如图4-3所示。图4-3系统架构图系统架构设计说明:nginx是一个HTTP和反向代理web服务器。在本系统中使用nginx对用户请求进行代理,通过nginx将请求纷发到后端,解决系统的跨域请求问题。nginx反向代理配置如下:http{server{listen8001;server_namelocalhost;location/{proxy_pass:8080/;add_header"Access-Control-Allow-Origin""$http_origin";add_header"Access-Control-Allow-Credentials""true";add_header"Access-Control-Allow-Methods""GET,POST,OPTIONS,DELETE,PATCH,PUT,HEAD";add_header"Access-Control-Allow-Headers""DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type";}}其中8001为系统前端项目的端口,listen表示由nginx对8001端口进行监听,location/{}模块表示由nginx代理转发的端口,8080则为系统后端的端口号。当用户访问8001端口时,用户的所有请求都将由nginx代理后转发到8080端口。阿里云SMS服务用于实现短信的下发,当生成考试安排后,由后端调用相关服务接口实现短信发送业务功能。kodo云存储用于存储系统中用户上传的文件,用户上传文件时,系统将文件存储到kodo中,并将文件在kodo中的远程路径存到Mysql数据库中。RabbitMQ将需要下发短信的老师信息发送到队列中,并在特定时间执行下发短信的操作,以达到及时通知监考教师的目的。Redis在系统中的主要功能用于保证RabbitMQ消息队列中的消息不会被重复消费。4.4系统功能业务架构设计系统的业务功能架构是对系统功能的总体分析,通过系统的业务功能架构可以清晰的了解系统功能的设计流程,系统中各数据的流向和变换,好的系统业务功能架构设计,能清晰的指导系统的设计与开发。系统的整体业务功能的设计遵循MVC模式的规则,从视图层、控制层、业务层以及数据持久层的角度去对系统业务功能进行分析。系统业务功能架构图如图4-4所示图4-4系统业务功能架构图各层次主要功能如下:表示层:由视图层和数据交互部分组合而成。通过ElementUI编写视图层与用户进行交互,用户通过视图层填写修改数据,通过Vue和axios向后端发起HTTP请求并携带用户填写的数据。控制层在代码中表现为controller类,controller类通过SpringMVC提供的GetMapping、PostMapping等注解进行前后端之间的url映射,通过这个url映射找到相应的类和方法对用户请求进行处理。业务层对外暴露service业务接口,service封装了实现系统业务功能的主要业务逻辑实现代码,由controller对service进行调用从而实现系统所要达到的具体业务功能。数据持久层是用于对数据库系统操作的一层,对数据库进行操作的所有SQL代码都编写在此层中,代码中体现为dao接口,一个dao接口映射一个xml文件,起到将业务功能代码与SQL代码分离的好处,降低业务与数据库操作之间的耦合度。service层通过调用dao接口实现对数据库数据的查询更新操作。4.5数据库设计大多数系统都是对关系数据库的操作,区别在于业务功能的流程,最终系统仍要对数据库进行操作。数据库相当于一个系统的地基,设计出好的数据库对我们在实现系统的过程中是否能够顺利实现业务功能起着至关重要的作用。本小节对二级学院期末考试安排系统的数据库进行分析设计与实现。4.5.1数据库概念逻辑设计概念逻辑设计即对系统中涉及到的现实世界的数据实体建立抽象的概念数据模型,并表示出各模型之间存在的关系,下面对系统数据库概念逻辑设计过程进行分析并给出数据库系统的E-R图。数据库E-R图如图4-5所示:图4-5E-R图得到关系模式:管理员(管理员编号,管理员信息)。二级学院(二级学院编号,二级学院名称)。专业(专业编号,所属二级学院编号,专业名称)。班级(班级编号,所属专业编号,班级基本属性)。学生(学生学号,学生个人信息,所属班级编号)。教师(工号,个人信息,所属二级学院编号)。辅导员(工号,个人信息,所属二级学院编号)。课程(课程编号,课程信息)。授课关系(授课教师编号,教授课程编号)。选课关系(学号,课程编号)。附件(附件编号,url)。缓考申请(缓考申请编号,学号,缓考信息,附件编号)。填写缓考建议(工号,缓考申请编号,缓考建议)。审核缓考申请(管理员编号,缓考申请编号,审核结果)。4.5.2数据库物理设计在数据库的物理设计中,少数数据表之间的关系并没有严格遵循数据库设计范式,而是采用一种反范式的设计方式进行设计。字段重复在关系数据库中是最常见也是最有效的一种反范式的设计方法,是一种典型的以空间换时间的设计理念。这种设计理念随着硬件产品更新和降价不断加速,而被越来越多的人认可和采用[5]。为了便于业务功能的实现将数据库之间的关联在业务层解决,通过增加冗余字段减少数据表之间的关联操作。主要数据表的物理结构如下:授课信息表(tb_teacher_course)。授课信息表用于存储授课老师与教授课程之间的关系,用于在排考功能中挑选出任课老师以及非任课老师,是一个表示多对多关系的数据表,主要字段有课程编号、教师工号、课程名称和教师名称。表的具体结构如表格4-6所示:表4-6授课信息表字段名称类型是否为空备注索引id表自增idbigint否表自增主键主键teacher_num老师工号varchar(25)否外键course_num课程编号varchar(50)否外键teacher_name老师名称varchar(10)否course_name课程名称varchar(50)否选课信息表(tb_student_course)。选课信息表用于存储学生与课程之间的关系,用于在排考功能中选择出所有需要进行该课程考试的学生,是一个表示多对多关系的数据表。主要字段有课程编号、学号、学生姓名、课程名称。表的具体结构如表格4-7所示:表4-7选课信息表字段名称类型是否为空备注索引id表自增idbigint否表自增主键主键stu_id学号varchar(25)否外键course_num课程编号varchar(50)否外键stu_name学生姓名varchar(10)否course_name课程名称varchar(50)否考试计划安排表(tp_plan)。考试计划安排表用于存储系统生成的每一条考试安排记录,主要字段有考试时间、考生学号、考生姓名、教学楼、教室编号、主监考官编号、主监考官姓名、副监考官编号、副监考官姓名、考试科目编号、考试科目名称、学生班级编号、学生班级姓名、学年学期。表的具体结构如表格4-8所示:表4-8考试安排计划表字段名称类型是否为空备注id表自增idbigint否表自增主键test_time考试时间datetime否stu_id考生学号varchar(25)否stu_name考生姓名varchar(10)否classroom_location教学楼varchar(30)否classroom_num教室编号varchar(30)否main_invigilator_num主监考官编号varchar(25)否main_invigilator_name主监考官姓名varchar(10)否sec_invigilator_num副监考官编号varchar(25)否sec_invigilator_name副监考官姓名varchar(10)否course_num考试科目编号varchar(50)否course_name考试科目名称varchar(50)否class_num学生班级编号varchar(30)否class_name学生班级名称varchar(30)否term_year学年学期varchar(50)否表的索引如表格4-9所示:表4-9考试安排计划表索引索引名字段索引类型index01test_time,stu_id唯一索引index02stu_id,classroom_num,main_invigilator_num,sec_invigilator_num,course_num,year_term唯一索引index03stu_id,course_num,year_term唯一索引缓考申请处理表(tb_apply_handle):缓考申请处理表用于存储每一条由任课教师或辅导员填写缓考申请建议的信息。主要字段有缓考申请编号、工号、任课教师建议、辅导员建议。表的具体结构如表格4-10所示:表4-10缓考申请处理表字段名称类型是否为空备注索引id表自增idbigint否表自增主键主键apply_num缓考申请编号varchar(200)否user_num工号varchar(25)否teacher_opinion任课教师建议text是instructor_opinion辅导员建议text是缓考申请审核表(tb_apply_decide):缓考申请审核表用于存储管理员审核缓考申请记录信息。表的主要字段有缓考申请编号、管理员编号、审核结果、审核时间。表的具体结构如表格4-11所示:表4-11缓考申请审核表字段名称类型是否为空备注索引id表自增idbigint否表自增主键主键apply_num缓考申请编号varchar(200)否user_num管理员编号varchar(25)否status审核结果varchar(3)是decide_time审核时间datetime否4.6本章小结本章给出了系统的系统架构图以及结合系统业务功能结构图对系统业务功能结构进行分析,主要从视图层、控制层、业务层以及数据持久层描述了系统业务功能的总体执行流程。并在最后分析阐述了系统数据库表的设计过程,包括对概念逻辑设计、物理结构设计的分析,解释了主要数据表的用途和给出了数据表的具体结构。
第五章系统详细设计与实现系统架构设计的核心是选择并整合系统设计实现所需要用到的技术和理论,系统架构能够指导开发者在编码过程中选择何种技术去实现系统功能。本章将围绕系统架构设计中的技术,对系统主要功能模块进行详细的分析设计与实现。本章主要详细分析的系统功能有考试安排功能、文件解析存储功能、短信下发功能以及缓考功能。5.1考试安排功能设计与实现考试安排功能的核心,是排考算法。即对考试科目、考生、监考教师、教室、时间、班级进行合理的安排。为了使排考算法能有序合理的执行,需要约定几项原则:空闲座位多的教室优先匹配考生较多的考试科目。当教室的空闲座位无法完整容纳一个班级中需要考试的考生人数时,不在将该班级安排进入该教室。同一考试应当在同一时间段进行,若在该时间段,空闲教室座位数量不大于该科目考生数量,则不对该考试进行安排。.每个考场安排两名监考官,主监考官为该科目任课老师,副监考官为其他老师。若当前时间段安排的考试已有学生参加另一场考试,则自动将当前考试科目安排到下一天。排考算法的流程如图5-1所示:图5-1排考算法流程算法描述:系统接受用户输入的考试科目、考试时间。系统查出需要进行该考试的所有考生按班级归类并升序排序。系统找出该考试时间下所有的空闲教室升序排序计算并比较空闲座位数与考生人数。若空闲教室人数大于考生人数则执行步骤4,若空闲教室人数小于考生人数则终止排考,提示教室座位不足。将排序好的班级中的考生,按顺序加依次安排到教室中,每次安排后比较教室空闲座位是否大于下一个班级考生人数。每次安排后比较教室空闲座位数是否大于下一个班级考生人数,若大于则继续安排,知道空闲座位小于下一个班级考生人数,并将该教室从教室列表中移除。查出所有的监考官并分类为主监考官和副监考官,遍历监考官列表,若当前遍历监考官在该时间段有考试安排,则将其移出列表,没有则将监考官安排进入教室。将考试科目和考试时间安排到每一条已经分配有考生以及监考官的考试安排记录中并收集为一个考试安排列表。将考试安排列表存储到数据表中。显示考试安排记录并提示操作成功。算法具体实现代码://构造教室与考试班级关系
for(Iterator<ClassRoomEntity>it1=iterator4Classroom;it1.hasNext();){
ClassRoomEntityclassRoomEntity=it1.next();
iterator4List=list.listIterator();
//查出每个教室存在的考试记录,一条记录代表一个学生的一条考试记录
List<TestEntity>testEntityList=this.getStudentListByCourseNumAndTestTime4ClassroomNum(params.getCourseNum(),params.getTestTime(),classRoomEntity.getClassroomNum());
testEntityList4AllTest.addAll(testEntityList);
classRoomEntity.setRealSize(testEntityList.size());
for(Iterator<ClassEntity>it=iterator4List;it.hasNext();){
ClassEntityclassEntity=it.next();
if(classRoomEntity.getSize()-classRoomEntity.getRealSize()>classEntity.getSize4Test()){
classRoomEntity.getClassEntityList().listIterator().add(classEntity);
it.remove();
}
}
}//构建教室与学生关系
for(ClassRoomEntityclassRoomEntity:classRoomEntityList){
//获取班级学生信息for(ClassEntityclassEntity:classRoomEntity.getClassEntityList()){
List<StudentEntity>list4Student=this.studentCourseService.getStudentListByClassNum(classEntity.getClassNum(),params.getCourseNum());
classRoomEntity.getStudentEntityList().addAll(list4Student);
}}
for(StudentEntitystudentEntity:classRoomEntity.getStudentEntityList()){
if(this.testDao.getRecordsByClassNameAndTestTime(this.classService.getClassByNum(studentEntity.getClassNum()).getClassName(),params.getTestTime())!=null){
TestEntitytestEntity=newTestEntity();
testEntity.setClassroomNum(classRoomEntity.getClassroomNum());
testEntity.setClassroomLocation(classRoomEntity.getClassroomLocation());
testEntity.setCourseNum(params.getCourseNum());
testEntity.setCourseName(this.courseService.getCourseByNum(params.getCourseNum()).getCourseName());
testEntity.setStuId(studentEntity.getStuId());
testEntity.setStuName(studentEntity.getStuName());
testEntity.setTestTime(DateHandleUtils.changeDate(params.getTestTime()));
testEntity.setClassName(this.classService.getClassByNum(studentEntity.getClassNum()).getClassName());
testEntity.setClassNum(studentEntity.getClassNum());
testEntity.setYearTerm(params.getYearTerm());
testEntityList4Insert.add(testEntity);
}else{
//省略类似安排代码}
}
}//查询构建主监考官和副监考官//主监考官集合
List<TeacherEntity>teacherEntityList4Main=this.teacherService.getTeacherListByCourseNum(params.getCourseNum()).stream().sorted(Cparing(TeacherEntity::getWeight)).collect(Collectors.toList());//副监考官集合
List<TeacherEntity>teacherEntityList4Sec=this.teacherService.getTeacherList().stream().sorted(Cparing(TeacherEntity::getWeight)).collect(Collectors.toList());
teacherEntityList4Sec.removeAll(teacherEntityList4Main);//创建迭代器进行遍历Iterator<TeacherEntity>iterator4Main;
Iterator<TeacherEntity>iterator4Sec;//分配监考官
for(Iterator<ClassRoomEntity>it=iterator4ClassroomAnother;it.hasNext();){
ClassRoomEntityclassRoomEntity=it.next();
iterator4Main=teacherEntityList4Main.iterator();
iterator4Sec=teacherEntityList4Sec.iterator();
for(Iterator<TeacherEntity>it4Main=iterator4Main;it4Main.hasNext();){
TeacherEntityteacherEntity=it4Main.next();
TestEntitytestEntity=this.getRecord(params.getTestTime(),teacherEntity.getTeacherNum());
TestEntitytestEntitySec=this.getRecord4Sec(params.getTestTime(),teacherEntity.getTeacherNum());
if(testEntity==null&&testEntitySec==null&&classRoomEntity.getClassEntityList().isEmpty()==false){
doubleweight=teacherEntity.getWeight()+0.1;
teacherEntity.setWeight(weight);
this.teacherService.updateTeacher(teacherEntity);
classRoomEntity.setMainTeacher(teacherEntity);
it4Main.remove();
break;
}
}考试计划安排功能界面如图5-2所示:图5-2考试安排功能截图5.2导入Excel文件录入数据功能导入数据的特点是,系统中大多数数据信息都需要用到该功能,尽管每种数据类型不一致,但是系统对数据的操作都是解析用户上传的文件,封装为对应的实体类,然后存储到数据库中。因此使用设计模式中的策略模式实现该功能,提高该功能的扩展性和灵活性,减少代码中大量分支判断语句的编写。实现该功能的类图如图5-3所示。图5-3文件数据录入策略模式类图具体实现代码:1.策略接口publicinterfaceInputExcelStrategy{
voidinputDataBatch(StringlocalDir,finalClass<?extendsBaseRowModel>clazz,List<?extendsBaseRowModel>list,LonguserId);
}2.部分接口实现类:publicclassClassroomStrategyimplementsInputExcelStrategy{
@Resource
privateClassroomDaoclassroomDao;
@Override
publicvoidinputDataBatch(StringlocalDir,Class<?extendsBaseRowModel>clazz,List<?extendsBaseRowModel>list,LonguserId){
try(FileInputStreaminputStream=newFileInputStream(localDir)){
list=ExcelUtil.readExcel(newBufferedInputStream(inputStream),clazz);
}catch(IOExceptione){
e.printStackTrace();
}
this.classroomDao.saveBatch(list);
}
}3.策略选择控制类:publicclassStrategyContext{
privateInputExcelStrategyinputExcelStrategy;
publicStrategyContext(InputExcelStrategyinputExcelStrategy){
this.inputExcelStrategy=inputExcelStrategy;
}
publicRexecute(StringlocalDir,finalClass<?extendsBaseRowModel>clazz,List<?extendsBaseRowModel>list,LonguserId){
this.inputExcelStrategy.inputDataBatch(localDir,clazz,list,userId);
returnR.ok();
}5.3短信下发功能实现短信下发功能要求在老师监考任务前一天对老师进行监考任务的短信通知。传统的定时任务一般用于周期性的功能实现,若要实现特定条件触发,需要借助数据库通过不断遍历数据库中存储的任务时间进行实现,不够灵活且多次访问数据库会导致性能下降。在此利用RabbitMQ中的TTL和DLX特性,构建延迟队列实现短信下发功能。另外出于对特殊情况的考虑,例如网络问题,当消费者消费消息后,无法告诉队列已经消费过该消息,因此该消息仍会存在于队列中,重复消费。因此使用Redis对每条消息进行缓存,每次消费者消费消息前都到缓存中查看是否存在该条消息,若不存在,则消费者消费本条消息,并将消费的消息的唯一区别关键字key存入redis中进行缓存,若redis中已经存在本条消息的关键字则将该消息丢弃。实现原理:创建一个没有消费者消费的普通队列normalQueue。将老师的监考任务按时间顺序升序排序,消息提供者向普通队列发送老师监考任务消息,并对每一条消息设置TTL,TTL为每个老师监考工作的前一天。由于该队列没有消费者消费消息,因此队列中的消息在TTL时间之后,成为死信。为此队列绑定一个死信交换机dead-letter-exchange,创建一个新的队列deadQueue与死信交换机绑定,此时normalQueue中的死信由死信交换机重新发送到死信队列deadQueue中。创建消息消费者与死信队列deadQueue进行绑定,由该消费者消费死信队列中的教师监考计划消息,每次消费前都到Redis中查找该条消息,若消息已经存在则丢弃,不存在则发送短信。短信下发的时序图图如图5-4所示:图5-4短信下发功能时序图具体实现代码如下:1.RabbitMQ队列、交换机、生产者、消费者初始化代码。publicclassRabbitMQConfig{
publicstaticfinalStringDELAYED_QUEUE_NAME="delayed_queue";
publicstaticfinalStringDELAYED_EXCHANGE_NAME="delayed_exchange";
publicstaticfinalStringDELAYED_ROUTING_KEY="delayed_routing_key";
@Bean
publicMessageConvertermessageConverter(){
returnnewJackson2JsonMessageConverter();
}
@Bean
publicQueuedelayedQueue(){
returnnewQueue(DELAYED_QUEUE_NAME);
}
@Bean
publicCustomExchangedelayedExchange(){
Map<String,Object>arguments=newHashMap<>();
arguments.put("x-delayed-type","direct");
returnnewCustomExchange(DELAYED_EXCHANGE_NAME,"x-delayed-message",true,false,arguments);
}
@Bean
publicBindingbindingExchangeToQueue(@Qualifier("delayedQueue")QueuedelayedQueue,@Qualifier("delayedExchange")CustomExchangedelayedExchange){
returnBindingBuilder.bind(delayedQueue).to(delayedExchange).with(DELAYED_ROUTING_KEY).noargs();
}
}2.消息提供者publicclassSmsProvider{
publicstaticvoidsendMessage(TeacherAndTestDtoteacherAndTestDto,IntegerdelayTime,RabbitTemplaterabbitTemplate){
rabbitTemplate.convertAndSend(RabbitMQConfig.DELAYED_EXCHANGE_NAME,RabbitMQConfig.DELAYED_ROUTING_KEY,teacherAndTestDto,message->{//设置消息过期时间
message.getMessageProperties().setDelay(delayTime);
returnmessage;
});
}
}消息消费者publicclassSmsConsumer{
@Autowired
RedisUtilsredisUtils;
@RabbitListener(queues=RabbitMQConfig.DELAYED_QUEUE_NAME)
publicvoidreceiveMessage(TeacherAndTestDtoteacherAndTestDto){//消息key,用于验证Redis中是否存在消息
Stringkey=teacherAndTestDto.getTeacherName()+teacherAndTestDto.getTestTime()+teacherAndTestDto.getCourseName()+teacherAndTestDto.getLocation();
if(redisUtils.get(key)!=null){
System.out.println("丢弃消息");
}else{
SmsUtil.sendSms(teacherAndTestDto.getTeacherName(),teacherAndTestDto.getMobile(),teacherAndTestDto.getTestTime(),teacherAndTestDto.getLocation());
redisUtils.set(key,teacherAndTestDto,-1);
try{
Thread.sleep(10000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}
}5.4缓考功能缓考申请的流程为:由学生填写缓考申请,任课教师与辅导员进行缓考申请建议的填写,最后交由管理员审核。图5-5为缓考功能的状态图。图5-5缓考功能状态图具体实现代码:1.学生申请缓考applicationEntity.setCourseName(this.courseService.getCourseByNum(applicationEntity.getCourseNum()).getCourseName());
applicationEntity.setApplyTime(newDate());
this.applicationService.save(applicationEntity);
if(file.isEmpty()){
thrownewRRException("上传文件不能为空");
}
Stringsuffix=file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
Stringurl=OSSFactory.build().uploadSuffix(file.getBytes(),suffix);
FileEntityfileEntity=newFileEntity();
fileEntity.setApplyNum(applicationEntity.getApplyNum());
fileEntity.setUrl(url);
this.fileService.save(fileEntity);
System.out.println(file);
System.out.println(applicationEntity);
returnR.ok();}2.教师填写意见:publicRhandleApplication(ApplicationHandleEntityapplicationHandleEntity,StringuserNum){
applicationHandleEntity.setUserNum(userNum);
applicationHandleEntity.setApplyNum(this.getById(applicationHandleEntity.getId()).getApplyNum());
ApplicationHandleEntityapplicationHandleEntity4Db=this.applicationHandleDao.getByApplyNumAndUserNum(applicationHandleEntity.getApplyNum(),userNum);
if(applicationHandleEntity4Db!=null){
this.applicationHandleDao.updateByApplyNumAndUserNum(
applicationHandleEntity.getApplyNum(),
userNum,
applicationHandleEntity.getInstructorOpinion(),
applicationHandleEntity.getTeacherOpinion());
}else{
this.applicationHandleService.save(applicationHandleEntity);
}
returnR.ok();
}3.管理员审核缓考申请:publicRdecideApplication(ApplicationDecideEntityapplicationDecideEntity,StringuserNum){
applicationDecideEntity.setUserNum(userNum);
applicationDecideEntity.setDecideTime(newDate());
ApplicationDecideEntityapplicationDecideEntityDb=this.applicationDecideDao.getByApplyNumAndUserNum(applicationDecideEntity.getApplyNum(),userNum);
if(applicationDecideEntityDb!=null){
this.applicationDecideDao.updateByApplyNumAndUserNum(applicationDecideEntity);
}else{
this.save(applicationDecideEntity);
}
returnR.ok();
}缓考功能实现界面截图:学生填写缓考界面,截图如图5-6所示图5-6学生填写缓考申请界面实现结果截图2.任课教师填写建议界面,如图5-7所示:图5-7任课教师填写建议截图实现结果截图3.辅导员填写建议界面,如图5-8所示:图5-8辅导员填写建议界面4.管理员审核的系统界面,如图5-9所示:图5-9管理员审核缓考申请界面实现结果截图考试考勤情况可视化考试考勤情况可视化功能使用Echarts数据可视化图标库,在后端将要展示的数据构造成Echarts需要的数据类型,将数据返回前端,以饼图的形式展示数据。管理员还可以通过条件筛选特定数据。具体实现代码如下:1.提供给Echarts的数据类型publicclassEchartsEntity{
privateStringname;
privateIntegervalue;
publicEchartsEntity(Stringname,intvalue){
=name;
this.value=value;
}
}封装数据并返回前端publicRstatistics4StudentTestInit(){
//查出所有考试计划
List<TestEntity>listAll=this.testService.getAll();
//查出缓考申请通过的学生
List<TestEntity>listPass=this.applicationDecideService.getAll();
//实际参考人数
List<TestEntity>listTrue=newArrayList<>();
listTrue.addAll(listAll);
listTrue.removeAll(listPass);
//构造数据返回前端
List<EchartsEntity>list=newArrayList<>();
EchartsEntityechartsEntity4All=newEchartsEntity("考生人数",listAll.size());
EchartsEntityechartsEntity4Pass=newEchartsEntity("缓考人数",listPass.size());
EchartsEntityechartsEntityTrue=newEchartsEntity("实际考试人数",listTrue.size());
list.add(echartsEntity4All);
list.add(echartsEntity4Pass);
list.add(echartsEntityTrue);
returnR.ok().put("list",list);
}实现界面如图5-9所示图5-10考试考勤情况数据可视化实现结果截图5.6考试情况报表统计考试情况报表统计模块,以列表的形式展示期末考试的考试情况,主要展示的数据为考试人数、实际考试人数、缓考人数。管理员可以将数据导出为Excel格式文件。主要实现代码如下所示:publicRreportFormAll(Map<String,Object>params){//查出所有考生考试记录
List<TestEntity>testEntityListAll=this.testService.getByCondition(params);//通过stream流对考试记录信息以班级名称、课程名称、学年学期组合而成关键字进行分组
Map<String,List<TestEntity>>map=testEntityListAll.stream().collect(Collectors.groupingBy(o->o.getClassName()+o.getCourseN
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 某跨境贸易项目延期进度报告提交联系函6篇范文
- 2026 年雷电灾害安全防护知识宣讲课件
- 年度财务盘点进度汇报催告通知(5篇)范文
- 共建零欺凌校园弘扬友善风尚小学主题班会课件
- 【初三】【九年级】【数学】秋季开学第一课主题班会【课件】
- 基于AI的农业教育培训体系构建方案
- 高中生物 第二章 克隆技术 2.2 植物的克隆教案 浙科版选修3
- 七年级生物下册 第四单元 第五章 人体内废物的排出教案1 (新版)新人教版
- 讨论办公设备采购项目延期函7篇范本
- 小学语文人教部编版二年级下册7一匹出色的马第二课时教案设计
- 2025校招:硬件工程师笔试题目及答案
- 数控加工中心操作编程练习图纸60张
- 三角函数(一轮复习教案)
- 沪教版六年级上册数学练习题
- 血管炎患者的护理
- 架线跨越果林施工方案
- 16G362钢筋混凝土结构预埋件(详细书签)图集
- 价值型销售(技能篇)
- T-CECS120-2021套接紧定式钢导管施工及验收规程
- 医学实验风险评估报告
- MR355.臂丛神经规范化扫描方案
评论
0/150
提交评论