疫情上报系统
一、项目需求
- 通过在线网络,及时掌握学生每日健康状况。
- 解决传统健康信息上报问题。传统上报,复杂繁琐,上报信息不能及时汇总,异常信息不能及时传达,效率低下。
- 预防新冠疫情,能够对异常信息及时排除、早发现、早治疗、早隔离、早诊断。
二、项目功能
学生端:
-
用户登录
-
信息上报
职工端:
-
信息查看
-
提醒上报 [未开发]
-
人员管理 [增删改未开发]
-
名单录入
-
报单导出
管理员端:[未开发]
- 对职工的管理功能
- …
三、概要设计
1. 学生端
手机或电脑登录上报网址,进行信息上报。
学生端流程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IkBMYTAJ-1592224227665)(/doc/业务流程图.png)]
2. 职工端
项目的重点设计主要是在职工端,职工端涉及的功能模块也比较多。因为要完成对学生信息的录入,上报信息的管理,以及异常信息的处理。
功能模块图如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NzxYq6SF-1592224227669)(/doc/staff_guid.png)]
3. 数据库表结构设计
初步设计
- 上报信息表(report_record):存储学生登记信息。
- 学生信息表(student):记录学生基本信息,默认学号即为学生的账号,不再为学生建立账户信息。(不存在注册操作,职工导入学生名单后,账户即生效!)
- 职工表(staff):存储职工基本信息,学生的管理员。
- 院系表(department):存储部门信息。
- 平台账号信息表(local_auth):记录用户密码账号等信息。(学生存在默认账号,职工账号记录需要存在其中,并且存在相应的权限字段,用以区分是否为super_admin)
四、详细设计
1. 前台(学生端)
-
登录模块
学生使用学号进行登录,登录成功,跳转至登记信息页面;否则,重新登录。
-
信息登记
学生登录成功后,登记页面有显示学生的基本信息,包括学号部门等,当然学生可以对自己的班级进行选择,比如调班了不属于该班级了(情况很少,一般不建议改动)。学生填报完所有带有红色*星标的字段信息之后,方可提交,否则提交失败,若提交成功后,会给予成功提示!
若用户重复上报,最后一次的上报信息会覆盖前一次的上报信息或者只允许用户上报一次(使用拦截器解决用户重复上报问题和未登录就访问上报网址等问题)。
2. 后台(职工|管理员)
-
登录模块
职工端利用超级管理员下发的账号进行登录,该账号关联了职工的基本信息,登录成功后跳转至后台首页。
-
后台首页
-
部门上报信息概览
展示职工所属部门今日上报情况,已上报、未上报、体温异常等信息。
-
班级上报信息概览
列出职工所在部门下所有班级的上报情况,并以进度条的形式展示当前班级的上报进度。职工仅可对自己的班级进行点击查看上报情况,不允许查看其他班级的详细上报情况。
-
-
学生端
- 上报信息
- 信息查看:职工可查看所管理班级的上报情况,并支持分页查询。
- 上报通知:职工可对未上报的学生进行通知(发送邮件信息、或者发送短信息)
- 信息导出:导出指定班级下的今日上报信息。
- 人员管理
- 增删改查:人员的增删改查基本操作,以对话框的形式进行设计。
- 名单录入:职工可对学生名单为空的班级进行录入,多次录入的判断。
- 上报信息
-
管理员
- 未开发
3. 数据库详细设计
刚开始初步设计的表结构和实体对开发不是那么有利,刚开始单独几个表单单几个字段没多大用处,随着项目集成越来越多的功能,之前的表结构字段不得不进行改造,截止到现在,表结构暂且不是一个绊脚石,各个字段都各有用处。
表结构展示
1)department表
2)grade表
3)student表
4)staff表
5)report_record表
6)local_auth表
MysqlWorkBench逆向工程生成的表之间的关联图表
五、编程实现
1. 环境搭建
maven构建项目添加SSH框架所需依赖,添加每一层相关的配置文件,主要有struts.xml、applicationContext.xml、hibernate.cfg.xml,如果配置了连接池、日志等还需要添加相应的配置,结合自己项目所需。每一层相关的配置文件可以借助IDE实现配置。(如果导入别人的项目,可以通过左上角的加号对项目进行配置)
项目框架配置如下:
2. Spring集成struts2和hibernate
hibernate:applicationContext中可以对hibernate进行统一配置,可以将.hbm.xml映射文件配置到applicationContext中,事务管理,将hibernate的事务交于spring,本项目中采用的是编程式事务控制,通过@Transactional注解来实现的事务管理,这其中有很多坑,详情见项目总结。
struts2:applicationContext中并没有见struts2相关的配置,难道不需要集成struts不需要配置?其实环境搭建的时候导入了struts2-spring-plugin-2.2.1.1.jar,这个jar包就实现了spring和Struts2的整合,以后可以直接在aciton层实现属性注入,前提是必须在web.xml中加载了spring的配置。
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
3. 功能的实现
通过借助struts2的action,hibernate的映射和关联,spring的DI和AOP,实现业务需求。
项目总结
一、项目技术栈
-
前端
html、css、JavaScript、JQuery、Bootstrap、vue
-
后端(java)
struts2、spring、hibernate、druid、log4j、EasyExce、mysql、tomcat
二、项目开发工具
-
maven
构建项目,管理项目所需依赖。
-
git
版本控制。
-
gitee
在gitee上团队协作,实现代码远程托管。
三、git版本控制
1.git push rejected
远程仓库代码版本领先于本地仓库,本地落后于一个或者若干个版本;比如A写完代码之后push到了仓库,B在A写完之后,没有merge就push了,这时会被拒绝。解决方法,先pull merge之后在进行push即可。
2. 代码覆盖问题
git强制拉去远程仓库导致的代码覆盖,git冲突没有解决导致的代码覆盖(较常见)。所以说不要强制拉去或者不解决冲突。
3. Git冲突*
git冲突的产生:对两个或多个分支已经提交的分支的相同文件相同位置的不同操作进行了合并。
解决:
1)在修改文件之前先merge或者更新一下本地master。
2)有冲突手动解决冲突。
参考:
四、hibernate相关
1. spring集成hibernate实现事务控制
-
hibernate更新数据没反应
hibernate更新数据无反应,因为hibernate默认存在缓存,更新默认都是更新缓存中的数据并不会直接写入数据库,可以使用session.flush将缓存清空写入。
-
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread 不能获得同步的事务session
原因:
- SessionFactory的getCurrentSession并不能保证在没有当前Session的情况下会自动创建一个新的,这取决于CurrentSessionContext的实现,而在Spring提供的CurrentSessionContext的实现SpringSessionContext中,会在开始事务之前通过AOP的方式为当前线程创建Session,此时调用getCurrentSession()将得到正确结果。当Hibernate与Spring集成时,将使用该SessionContext,故此时调用getCurrentSession()的效果完全依赖于SpringSessionContext的实现。(hibernate的session是从spring上下文中获取,上下文中有则拿过来为己所用,否则抛出异常)【注:service层类上必须添加相应的事务注解,以确保当前session能被spring所管理】
- 在不使用Spring的情况下使用Hibernate,需要在Hibernate配置类中设置current_session_context_class为thread,也即使用了ThreadLocalSessionContext,那么我们在调用getCurrentSession()时,如果当前线程没有Session存在,则会创建一个绑定到当前线程。
- 在Spring中使用Hibernate,如果我们配置了TransactionManager,那么我们就不应该调用SessionFactory的openSession()来获得Sessioin,因为这样获得的Session并没有被事务管理。openSession和getCurrentSession区别
2. 懒加载异常LazyInitializationException
懒加载:hibernate配置对象关联时一对多多对多时,对关联对象采取的一种加载策略。
前台使用JSON和后台懒加载的数据进行交互时带来的异常:
## 异常信息
org.apache.struts2.json.JSONException: org.apache.struts2.json.JSONException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ncov.model.Department.grades, could not initialize proxy - no Session
1)~~JSONException,因为hibernate懒加载获得的是代理对象(session.load方法),,当向前台传递了这个代理对象的JSON数据时会报异常(空指针…),因为对象在转JSON时会调用get方法,而代理对象是没有get方法的。~~说法存在错误,不是这里的原因!
具体原因如下:
Hibernate 允许对关联对象、属性进行延迟加载,但是必须保证延迟加载的操作限于同一个 Hibernate Session 范围之内进行。如果 Service 层返回一个启用了延迟加载功能的领域对象给 Web 层,当 Web 层访问到那些需要延迟加载的数据时,由于加载领域对象的 Hibernate Session 已经关闭,这些导致延迟加载数据的访问异常。而Spring为我们提供的OpenSessionInViewFilter过滤器为我们很好的解决了这个问题。OpenSessionInViewFilter的主要功能是使每个请求过程绑定一个 Hibernate Session,即使最初的事务已经完成了,也可以在 Web 层进行延迟加载的操作。OpenSessionInViewFilter 过滤器将 Hibernate Session 绑定到请求线程中,它将自动被 Spring 的事务管理器探测到。所以 OpenSessionInViewFilter 适用于 Service
延迟初始化错误是运用Hibernate开发项目时最常见的错误。如果对一个类或者集合配置了延迟检索策略,那么必须当代理类实例或代理集合处于持久化状态(即处于Session范围内)时,才能初始化它。如果在游离状态时才初始化它,就会产生延迟初始化错误。
摘自-懒加载异常分析
总结:在session关闭时,调用了获取关联对象的方法。
解决方案:LazyInitializationException 多种配置方案。
个人解决方法:取消懒加载机制,那我设置懒加载有何用!因为我暂时不用到这些数据才设置了懒加载,又让我及时加载,对于我来说,不可取!在懒加载的对象上添加@JSON(serializable = flase)注解,含义就是对象转JSON时,添加该注解的属性不会被序列化即不会将其转化为JSON,对于任何请求来说这个是绝对的,如果这样,那么当某个前台功能用到这个数据时却获取不到,这也不行。我采取了下面这种解决方案,如果前台需要该JOSN格式的对象数据,那么在service层手动调用该对象关联的对象方法,获得关联对象,目的获取真实的关联对象,这样在转JSON时就不会存在代理对象了;如果不需要,那么将该对象的关联对象设置为null,断开关联。(上面的解决方法不合常规,这里仅仅为了记录一下当初在没有弄明白原理时,所采取的方案。)
注意点:
为什么在service层调用相关联的对象方法?如果web层调用会出现session closed...
的异常信息,因为@Transactional事务配置在service层,web层不在管理范围,所以说web层再次调用相关联的对象方法时,session已经关闭。
3. hibernate 一对多多对多的关联
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OFZEiJGa-1592224227690)(/doc/hibernate_relation.png)]
通过设置对象之间的关联,可以进行级联操作,级联查询就很常用,当我对department进行查询时,结合懒加载设置在需要的时候去加载数据,调用department属性方法就会得到部门下的grade和staff实例。
五、java Excel小白式操作
1. EasyExcel
官网 语雀也有文档,当初是看着语雀学习的,上手挺简单的,下面列举的就是刚开始使用出现的问题。
1)asm.jar、cglib包版本冲突【jar冲突的原因关注这两个】
2)测试EasyExcel读取文件并向DB中存储数据的时候,突然存储到一半,就一直在那转个不停,也不会再向数据库存储记录。
原因:
1)从池中的的连接都已经用完,没有可以利用的连接,可以设置连接池的属性,来增大池中的连接数量。
2)如果不设置连接池属性,可以通过增大每次读取的数量,即BATCH_SIZE(最先想到的是这一种,当时估摸着是不是池中的连接没有了,才会出现上述不会在运行但不会存储记录的情况)。数据量大的情况下,这种设置增加了内存的压力。
六、vue总结
本次开发前端框架用到的是vue,感觉用它如虎添翼,比JQuery操作DOM元素方便多去了,双向绑定,MVVM(Model-View-ViewModel),初次上手,感觉也踩了很多坑。
1)vue死循环问题
You may have an infinite update loop in a component render function.
出现上述情况的原因主要是因为,for循环判断条件中调用了某个函数,函数内部又对vue实例中的属性值进行了修改,导致vue重新render,从而dead loop.
2)this对象问题
我们通常会为每个页面定义一个vue实例,this值通常也为vue,但是比如我用了axios发送请求获取响应的时候,这时候的this不在是vue实例而是一个window对象,还有就是list.forEach(function(item, index) {…})forEachfunction中this也不再是vue实例,开发中常常这样定义let _this = this保存vue实例!从而在其他函数内部使用vue实例。
七、mysql 定时任务(定时事件)
项目为什么会用到定时任务?
为了统计每日上报数量,如果仔细看表,你会发现department和grade会有一个submitted_count字段,这个字段记录了当日上报的人数,如果第二天进行上报,需要将该字段清零,所以我最先考虑到用定时任务来去做这一个事情。同样如果项目扩展开来,可以用定时任务控制每日表单的填报时间等等。因为对java定时任务不太了解,这里用了mysql的Event来处理。
处理方式:
1)将student表中的report_id外键字段置空后,清空report_record表的记录,department、grade下的headcount归0。
2)将student表中的report_id外键字段置空,保留report_record表中的字段,下次只会更新相应的记录,department、grade下的headcount归0。
置空student表中的report_id原因就是update或者insert就是和report建立关联,之后headcount才会增一。
这里采取(2)
create EVENT clear_zero
on SCHEDULE EVERY 1 DAY STARTS '2020-06-15 00:00:00'
DO
UPDATE department
SET submitted_count = 0
WHERE
submitted_count != 0;
UPDATE grade
SET submitted_count = 0
WHERE
submitted_count != 0;
UPDATE student
SET report_id = NULL
WHERE
report_id IS NOT NULL;
可以利用navicate图形化工具创建event或者手写sql,事件的具体操作,网上都有介绍,这里不再陈述。
八、项目待做
-
后台登录验证码
-
后台首页部门下班级概览那地方,所属职工可通过超链接的形式链接到本班的上报信息界面,不属于自己管理的班级不可查看。(拦截器实现)
-
首页体温异常模块
-
上报信息展示,感染疑似和正常可以用html样式加以区分
-
上报信息的编辑操作:删除和通知,通知可以以邮件形式或者手机短信息通知。
-
学生信息的增删改
-
职工信息的信息的个人设置
-
超级管理员模块的开发
-
高级管理员指定每个职工所管理的班级,每个班级所属职工就固定了
-
缓存问题导致的拦截器失效!(缓存严重!待解决)有时候影响正常功能的使用,拦截器不能轻易使用!网址要变化,不然还是有缓存!
项目部署注意点
-
项目依赖了线上的vue库,也就是说如果电脑没有联网的情况下,vue.js库不会加载成功,导致页面变形或者功能失效。
-
前端页面用了vue和jquery,如果利用vue的方法去处理事件可能会出现事件失效,原因就是作用域不在vue范围内;另外如果项页面中点击事件失效,可能是JQuery和vue库并存导致的原因,为什么,网上都有有所说,这里不再细说,就是vue会重塑dom树,之前利用JQuery在dom节点上添加过的事件都会失效。
-
global.properties定义了全局属性配置,将数据库的用户名密码配置抽取到了这里面,部署根据自己的mysql进行配置。