基于Springboot+Vue+Sercurity实现的大学生健康管理平台(上)

简介: 基于Springboot+Vue+Sercurity实现的大学生健康管理平台

软件架构


健康管理平台的总目标是:在计算机网络,数据库和先进的开发平台上,利用现有的软件,配置一定的硬件,开发一个具有开放体系结构的、易扩充的、易维护的、具有良好人机交互界面的大学生健康管理平台。

具体的要求如下:


  • 大学生可以方便的使用五大基本功能
  • 独立于具体的数据库平台。对于与数据库交互的部分充分考虑兼容性
  • 容易在原先基础上进行二次开发
  • 中间基础平台的无型性。尽量采用开放的标准技术,达到跨平台运行的效果


为了达到以上的开发目的,健康管理平台应遵循一定的设计和开发与原则:

  • 模块接口定义清晰
  • 基础类库和接口设计合理,尽量建立叮扩展的接口和抽象类
  • 数据库操作使用标准SQL语句,如果一定要使用有差别的SQL语句,则尽量集中在一起,以减少将来维护和移植的难度
  • 采用开放的标准和工具等

2.2 系统的功能要求


2.2.1 前台功能

在前台的功能实现上,可以分为以下几个部分:

  • 大学生健康阅读:每日可以在首页进行健康资料阅读
  • 大学生健康日志:用户可以记录自己健康日记,记录每一天的睡眠,饮酒,吸烟,血压,体重等情况
  • 大学生体检管理:大学生可通过该平台查询每次的体检结果,显示部分项目随时间变化的曲线
  • 疾病预测评估:针对糖尿病和高血压两种疾病,利用数学线性回归的方法,模拟计算出大学生患病概率


2.2.2 后台功能

后台主要有四个功能:

  • 管理员用户登录功能:通过编号登录系统
  • 管理员管理大学生功能:可以增删改查大学生基本信息
  • 管理员管理体检结果:可以增删改查大学生体检记录
  • 管理员对体检表,健康文档,体检数据图标展示等进行管理,以及权限管理:指定不同科室医生进行不同的操作

2.3 系统的性能需求


健康管理平台的特性要求网站系统需要具备以下几个主要特性:


  • 多样性:健康管理平台在设计时必须遵循的一个重要原则,就是以用户为系统设计的中心。网络的发展大大降低了信息咨询的成本,用户面临更多的选择,所以,健康管理平台除了降低成本以外,还必须在外观,功能的多样性上下工夫,才能够得到用户的认可。具体来说,应用系统应能实现用户特定的需求,操作界面友好、方便,功能设置符合用户的习惯和要求
  • 易维护:管理员能够简单方便地管理系统,定义新的应用,并对系统进行日常维护;随着健康管理平台信息的变更或增加、数据规模的增大,系统升级、维护应易于操作;各子系统、功能模块的设计应具有良好的灵活性,保证在健康管理平台系统建设过程中后期系统功能的实现不影响前期已投入使用的系统功能
  • 安全性:系统应充分考虑信息、设备、运行和管理的安全性,建立完善的多层次的安全保障体系,保证用户的个人信息等资料的数据安全
  • 可扩展:健康管理平台的用户从事着各种行业,他们的需求各不相同,系统在设计时应充分考虑到用户需求不断变化的需要,降低系统在扩展新的业务功能时的复杂度[5]


2.4 系统的数据要求


2.4.1 数据的性质


数据录入和处理的准确性和实时性


数据的输入是否准确是数据处理的前提,错误的输入会导致系统输出的不正确和不可用,从而使系统的工作失去意义。数据的输入来源是用户的手工输入。手工输入要通过系统界面上的安排系统具有容错性,并且对用户操作进行实时的跟踪和错误提示。


在系统中,数据的输入往往是大量的,因此系统要有一定的处理能力,以保证迅速的处理数据。


数据的一致性与完整性


由于网站的数据是共享的,所以如何保证这些数据的一致性,是网站必须解决的问题。要解决这一问题,要有一定的人员维护数据的一致性,在数据录入处控制数据的去向,并且要求对数据库的数据完整性进行严格的约束。


对于输入的数据,要为其定义完整性规则,如果不能符合完整性约束,网站应该拒绝该数据。


数据的共享与独立性


整个健康管理平台的数据是共享的。然而,从网站开发的角度上看,共享会给设计和调试带来困难。因此,应该提供灵活的配置,使各个分系统能够独立运行,而通过人工干预的手段进行系统数据的交换。这样,也能提供系统的强壮性。


2.4.2 数据字典


健康管理平台主要涉及到真实姓名数据字典,如表2.1所示:


真实姓名数据字典


8be97ba0b2d579e642e3bd61eb95d858.png

系统信息表


b409ba597a4a68ea86a75aa374c3d5ff.png


报告信息表



7611eff79a45a435887cd9f9ce59afad.png

权限表


060dd8595a69853c6649118a106f7667.png

权限资源表

c825b5fa28cd4395041132fca7621679.png


资源表


becd2cc5ac27cbff5c4633d2371d9eae.png

健康阅读表


ca7edc118ada309ba77ff8e8736549a2.png

检查报告表


cf9155341bd209d82fb3e92e01c75c4b.png

2.4.3 实体联系图


c725bad244e1c2b4f646eb1ae2dd0b0a.png


3.项目实现

3.1 项目说明


项目是采用SpringBoot + Mybatis + Shiro 的搭建的单体后台服务。同时通过引入通用mapper减少代码量,已达到重点关注业务的开发。

<dependency><groupId>tk.mybatis</groupId><artifactId>mapper-spring-boot-starter</artifactId><version>${mapper-starter-version}</version><exclusions><exclusion><groupId>javax.persistence</groupId><artifactId>persistence-api</artifactId></exclusion></exclusions></dependency>

3.1.1 实体


在创建实体时,首先定义了一个BaseEntity的类,所有的实体均集成该类,同时该类需要加上@MappedSuperclass注解。BaseEntity里面包含了所有实体的公共字段,如:id、createTime、updateTime。这样做是为了减少代码的冗余。


实体上配置了@Table注解,以及实体字段配置了 @Column注解是为了搭配jpa的自动建表,已达到添加字段时,重新运行项目,数据库表字段也会自动添加。

但是存在缺点: 更改字段类型或删除字段,数据库表不能自动修改或删除字段,需要手动去变更。


3.1.2 Dao层


再创建相关的dao层接口时,需要接口继承自定义的BaseDao接口,该接口集成了通用mapper相关的接口和注解,便于我们自定义一些通用接口。


3.1.3 Service层


所有的service接口需要继承自定义的BaseService接口,该接口定义了一些常用到的接口,例如增、删、改、查等等。


publicinterfaceBaseService<T>{List<T>selectAll();TselectByKey(Serializablekey);intinsert(Tentity);intdeleteByKey(Serializablekey);intdeletes(List<String>keys);intdelete(Tentity);intupdate(Tentity);List<T>selectByExample(Exampleexample);List<T>select(Conditioncondition);PageInfo<T>selectPage(Conditioncondition);}


同时创建接口实现类时需要继承自定义的BaseServiceImpl类,该类实现了BaseService接口的所有方法,这样可以达到不用书写这些接口,就可在需要的时候调用,如果需要自定义某个方法时,只需要实通过@Override重写某个方法即可。

@Slf4j@Transactional(propagation=Propagation.SUPPORTS,readOnly=true,rollbackFor=Exception.class)publicabstractclassBaseServiceImpl<T,MextendsBaseDao<T>>implementsBaseService<T>{privateClass<?>clazz=(Class<?>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];@AutowiredprotectedMentityDao;@OverridepublicList<T>selectAll(){returnentityDao.selectAll();}@OverridepublicTselectByKey(Serializablekey){log.info("执行筛选==>参数为【"+key+"】");returnentityDao.selectByPrimaryKey(key);}@Override@Transactional(rollbackFor=Exception.class)publicintinsert(Tentity){log.info("执行插入==>参数为【"+entity+"】");returnentityDao.insertSelective(entity);}@Override@Transactional(rollbackFor=Exception.class)publicintdeleteByKey(Serializablekey){log.info("执行删除==>参数为【"+key+"】");returnentityDao.deleteByPrimaryKey(key);}@Override@Transactional(rollbackFor=Exception.class)publicintdeletes(List<String>keys){log.info("执行批量删除==>参数为【"+keys+"】");StringkeyName=null;Field[]fields=clazz.getDeclaredFields();for(Fieldfield:fields){if(field.isAnnotationPresent(Id.class)){keyName=field.getName();break;}}Exampleexample=newExample(clazz);example.createCriteria().andIn(keyName,keys);returnentityDao.deleteByExample(example);}@Override@Transactional(rollbackFor=Exception.class)publicintdelete(Tentity){log.info("执行删除==>参数为【"+entity+"】");if(entity!=null){returnentityDao.delete(entity);}return0;}@Override@Transactional(rollbackFor=Exception.class)publicintupdate(Tentity){log.info("执行修改==>参数为【"+entity+"】");returnentityDao.updateByPrimaryKeySelective(entity);}@OverridepublicList<T>selectByExample(Exampleexample){returnentityDao.selectByExample(example);}@OverridepublicList<T>select(Conditioncondition){log.info("执行条件查询===>参数为【"+JSONObject.toJSONString(condition)+"】");Map<String,EntityColumn>propertyMap=EntityHelper.getEntityTable(clazz).getPropertyMap();Exampleexample=newExample(clazz);List<Clause>clauses=condition.getClauses();if(!CollectionUtils.isEmpty(clauses)){Example.Criteriacriteria=example.createCriteria();clauses.forEach(clause->{if(!StringUtils.isEmpty(clause.getColumn())){if("isNull".equalsIgnoreCase(clause.getOperation())){criteria.andIsNull(clause.getColumn());}if("isNotNull".equalsIgnoreCase(clause.getOperation())){criteria.andIsNotNull(clause.getColumn());}else{if(!StringUtils.isEmpty(clause.getValue())){if("between".equalsIgnoreCase(clause.getOperation())){ArrayList<?>list=(ArrayList<?>)clause.getValue();criteria.andBetween(clause.getColumn(),list.get(0),list.get(1));}if("like".equalsIgnoreCase(clause.getOperation())){criteria.andLike(clause.getColumn(),"%"+clause.getValue()+"%");}if("=".equalsIgnoreCase(clause.getOperation())){criteria.andCondition(propertyMap.get(clause.getColumn()).getColumn()+clause.getOperation(),clause.getValue());}}}}});}Map<String,Object>sortMap=condition.getSortMap();if(sortMap.size()>0){StringBuffersb=newStringBuffer();sortMap.forEach((k,v)->{if(!StringUtils.isEmpty(k)&&!StringUtils.isEmpty(v)){sb.append(propertyMap.get(k).getColumn());sb.append(" ");sb.append(v);sb.append(",");}});if(sb.toString().endsWith(",")){sb.deleteCharAt(sb.length()-1);}example.setOrderByClause(sb.toString());}returnselectByExample(example);}@OverridepublicPageInfo<T>selectPage(Conditioncondition){PageHelper.startPage(condition.getPageNum(),condition.getPageSize());List<T>list=select(condition);returnnewPageInfo<>(list);}}

3.1.4 Web层


在web层,项目定义了一个BaseController的抽象类,里面实体了基本的增删改查的接口定义。再我们创建某个模块的Controller时只需要继承该类就 可达到不用在创建的类下面定义接口,也会自动拥有相关接口,如果需要对某个接口方法做定制时,只需要重新某个方法就可以了。

@Slf4jpublicabstractclassBaseController<SextendsBaseService<T>,T,IDextendsSerializable>{@AutowiredprotectedSservice;@ApiOperation(value="基础接口: 新增操作")@PostMapping(value="add")publicResponseEntity<T>save(@RequestBodyTentity){try{if(service.insert(entity)<1){thrownewMyException(ExceptionEnums.ADD_ERROR);}}catch(Exceptione){log.error(e.getMessage());thrownewMyException(ExceptionEnums.ADD_ERROR);}returnResponseEntity.ok(entity);}@ApiOperation(value="基础接口: 返回指定ID的数据")@GetMapping(value="get/{id}")publicResponseEntity<T>get(@PathVariable("id")IDid){Tt=null;try{t=service.selectByKey(id);}catch(Exceptione){log.error(e.getMessage());thrownewMyException(ExceptionEnums.GET_ITEM);}returnResponseEntity.ok(t);}@ApiOperation(value="基础接口: 返回所有数据")@GetMapping(value="all")publicResponseEntity<List<T>>all(){List<T>list;try{list=service.selectAll();}catch(Exceptione){log.error(e.getMessage());thrownewMyException(ExceptionEnums.GET_LIST_ERROR);}returnResponseEntity.ok(list);}@ApiOperation(value="基础接口: 分页返回数据")@PostMapping(value="page")publicResponseEntity<PageInfo<T>>page(@RequestBodyConditioncondition){PageInfo<T>page;try{page=service.selectPage(condition);}catch(Exceptione){log.error(e.getMessage());thrownewMyException(ExceptionEnums.GET_LIST_ERROR);}returnResponseEntity.ok(page);}@ApiOperation(value="基础接口: 修改数据")@PostMapping(value="update")publicResponseEntity<T>update(@RequestBodyTentity){try{if(service.update(entity)<1){thrownewMyException(ExceptionEnums.UPDATE_ERROR);}}catch(Exceptione){log.error(e.getMessage());thrownewMyException(ExceptionEnums.UPDATE_ERROR);}returnResponseEntity.ok(entity);}@ApiOperation(value="基础接口: 删除指定ID的数据")@GetMapping(value="delete/{id}")publicResponseEntity<String>delete(@PathVariable("id")IDid){try{if(service.deleteByKey(id)<1){thrownewMyException(ExceptionEnums.DELETE_ERROR);}}catch(Exceptione){log.error(e.getMessage());thrownewMyException(ExceptionEnums.DELETE_ERROR);}returnResponseEntity.ok("删除成功");}}

3.2 权限相关


权限主要采用apache shiro,需要引入相关依赖


<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.3.2</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.3.2</version></dependency>

在使用shiro时,需要创建一个realm进行进行权限的授权和认证,同时需要创建一个自定义的凭证验证器来校验密码是否一致。配置好这些后需要定义一个shiro的配置类ShiroConfiguration来配置shiro的权限过滤工厂beanShiroFilterFactoryBean,在里面引入自定义的AuthRealmbean以达到权限的校验。


因为项目采用的是前后端分离,会产生跨域请求等问题,所以需要开放所有来源对该服务的请求,所以需要配置相关过滤器CorsFilter。


publicclassCorsFilterimplementsFilter{@Overridepublicvoidinit(FilterConfigfilterConfig)throwsServletException{}@OverridepublicvoiddoFilter(ServletRequestservletRequest,ServletResponseservletResponse,FilterChainfilterChain)throwsIOException,ServletException{servletRequest.setCharacterEncoding("utf-8");HttpServletResponsehttpServletResponse=(HttpServletResponse)servletResponse;HttpServletRequesthttpServletRequest=(HttpServletRequest)servletRequest;//是否支持cookie跨域
httpServletResponse.setHeader("Access-Control-Allow-Credentials","true");//指定允许其他域名访问
httpServletResponse.setHeader("Access-Control-Allow-Origin",httpServletRequest.getHeader("Origin"));//响应头设置
httpServletResponse.setHeader("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept,client_id, uuid, Authorization,user-agent,X-XSRF-TOKEN");// 设置过期时间
httpServletResponse.setHeader("Access-Control-Max-Age","3600");//响应类型
httpServletResponse.setHeader("Access-Control-Allow-Methods","POST, GET, PUT, OPTIONS, DELETE");// 支持HTTP1.1.
httpServletResponse.setHeader("Cache-Control","no-cache, no-store, must-revalidate");// 支持HTTP 1.0. 
httpServletResponse.setHeader("Pragma","no-cache");httpServletResponse.setHeader("Allow","GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");if("OPTIONS".equals(httpServletRequest.getMethod())){httpServletResponse.setStatus(204);}filterChain.doFilter(servletRequest,servletResponse);}@Overridepublicvoiddestroy(){}}

使用shiro权限验证时,第一次请求为option方式,所以要配置shiro过滤掉option方式,不对此方式做权限拦截。故配置ShiroFilter

@Slf4jpublicclassShiroFiterextendsFormAuthenticationFilter{@OverrideprotectedbooleanisAccessAllowed(ServletRequestrequest,ServletResponseresponse,ObjectmappedValue){booleanallowed=super.isAccessAllowed(request,response,mappedValue);if(!allowed){// 判断请求是否是options请求
Stringmethod=WebUtils.toHttp(request).getMethod();if(StringUtils.equalsIgnoreCase("OPTIONS",method)){returntrue;}}returnallowed;}@OverrideprotectedbooleanonAccessDenied(ServletRequestrequest,ServletResponseresponse)throwsException{returnsuper.onAccessDenied(request,response);}}

3.3 其他


项目中定义了全局异常捕获,再项目中需要抛出异常时只需要抛出自定义的异常MyException,就可对该异常进行捕获,当用户请求某个接口如果出现异常时,后台服务就会返回自定义的错误代码,不会出现乱码的情况,以便于前端对返回结果做统一处理或错误拦截。同时我们也可预先自定义需要抛出的错误,在前段可对错误code进行区别,采用不同的样式提示。

@Getter@NoArgsConstructor@AllArgsConstructorpublicenumExceptionEnums{/**
     * 错误返回信息
     */NO_CHECK_INFO(400,"暂无健康档案表,无法进行数据分析"),CHOOSE_FILE(400,"文件未上传"),IS_NOT_LOGIN(400,"用户未登录"),UPLOAD_FAIL(400,"上传失败"),RESOURCE_FOUNT_FAIL(400,"获取菜单权限失败!"),NOT_AUTHORIZED(403,"您暂无权限访问该资源!"),ACCOUNT_IS_EXIT(400,"用户名已存在"),PASSWORD_WRONG(400,"密码错误"),ACCOUNT_IS_NOT_EXIT(400,"用户名不存在"),UPDATE_ERROR(400,"更新失败"),ADD_ERROR(400,"新增失败"),DELETE_ERROR(400,"删除失败"),GET_LIST_ERROR(400,"获取列表失败"),GET_ITEM(400,"获取对象失败"),NO_WEIGHT_HEIGHT(400,"当前档案未完善身高体重信息");/**
     * 代码值
     */privateintcode;/**
     * 消息
     */privateStringmsg;}


相关文章
|
26天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue实现的留守儿童爱心网站设计与实现(计算机毕设项目实战+源码+文档)
博主是一位全网粉丝超过100万的CSDN特邀作者、博客专家,专注于Java、Python、PHP等技术领域。提供SpringBoot、Vue、HTML、Uniapp、PHP、Python、NodeJS、爬虫、数据可视化等技术服务,涵盖免费选题、功能设计、开题报告、论文辅导、答辩PPT等。系统采用SpringBoot后端框架和Vue前端框架,确保高效开发与良好用户体验。所有代码由博主亲自开发,并提供全程录音录屏讲解服务,保障学习效果。欢迎点赞、收藏、关注、评论,获取更多精品案例源码。
61 10
|
26天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue实现的家政服务管理平台设计与实现(计算机毕设项目实战+源码+文档)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
46 8
|
26天前
|
JavaScript 搜索推荐 Java
基于SpringBoot+Vue实现的家乡特色推荐系统设计与实现(源码+文档+部署)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
57 8
|
26天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue实现的高校食堂移动预约点餐系统设计与实现(源码+文档+部署)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
70 3
|
29天前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
233 17
Spring Boot 两种部署到服务器的方式
|
29天前
|
Dart 前端开发 JavaScript
springboot自动配置原理
Spring Boot 自动配置原理:通过 `@EnableAutoConfiguration` 开启自动配置,扫描 `META-INF/spring.factories` 下的配置类,省去手动编写配置文件。使用 `@ConditionalXXX` 注解判断配置类是否生效,导入对应的 starter 后自动配置生效。通过 `@EnableConfigurationProperties` 加载配置属性,默认值与配置文件中的值结合使用。总结来说,Spring Boot 通过这些机制简化了开发配置流程,提升了开发效率。
61 17
springboot自动配置原理
|
1月前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
87 11
|
1月前
|
缓存 安全 Java
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
369 12
|
1月前
|
Java 测试技术 应用服务中间件
Spring Boot 如何测试打包部署
本文介绍了 Spring Boot 项目的开发、调试、打包及投产上线的全流程。主要内容包括: 1. **单元测试**:通过添加 `spring-boot-starter-test` 包,使用 `@RunWith(SpringRunner.class)` 和 `@SpringBootTest` 注解进行测试类开发。 2. **集成测试**:支持热部署,通过添加 `spring-boot-devtools` 实现代码修改后自动重启。 3. **投产上线**:提供两种部署方案,一是打包成 jar 包直接运行,二是打包成 war 包部署到 Tomcat 服务器。
47 10
|
1月前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
84 8

热门文章

最新文章