基于SpringBoot+Mybatisplus+Shiro的大学生健康档案管理系统代码+数据库,JAVA健康档案管理系统代码
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
大学生健康档案管理系统后端代码
介绍
大学生健康档案管理系统体检表健康文档体检数据图标展示等进行管理以及权限管理指定不同科室医生进行不同的操作。
完整代码下载 地址JAVA健康档案管理系统代码
软件架构
软件架构说明
- springboot
- mysql
- mybatis
- jpa
- swagger-ui
- lombok
项目说明
项目是采用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>
实体
在创建实体时首先定义了一个BaseEntity
的类所有的实体均集成该类同时该类需要加上@MappedSuperclass
注解。BaseEntity
里面
包含了所有实体的公共字段如id、createTime、updateTime。这样做是为了减少代码的冗余。
实体上配置了@Table
注解以及实体字段配置了 @Column
注解是为了搭配jpa的自动建表已达到添加字段时重新运行项目数据库表字段也会自动添加。
但是存在缺点: 更改字段类型或删除字段数据库表不能自动修改或删除字段需要手动去变更。
Dao层
再创建相关的dao层接口时需要接口继承自定义的BaseDao
接口该接口集成了通用mapper相关的接口和注解便于我们自定义一些通用接口。
Service层
所有的service接口需要继承自定义的BaseService
接口该接口定义了一些常用到的接口例如增、删、改、查等等。
package com.cqjtu.studentdocument.service;
import com.cqjtu.studentdocument.utils.dto.Condition;
import com.github.pagehelper.PageInfo;
import tk.mybatis.mapper.entity.Example;
import java.io.Serializable;
import java.util.List;
/**
* @author pengyangyan
*/
public interface BaseService<T> {
/**
* 查询所有数据
* @return 结果List
*/
List<T> selectAll();
/**
* 主键查询
*
* @param key 主键
* @return T
*/
T selectByKey(Serializable key);
/**
* 插入数据
* @param entity 传入实体
* @return 受影响行数
*/
int insert(T entity);
/**
* 主键删除
* @param key 主键
* @return 受影响行数
*/
int deleteByKey(Serializable key);
/**
* 批量删除
* @return 受影响行数
*/
int deletes(List<String> keys);
/**
* 删除数据
*
* @param entity 实体
* @return 受影响行数
*/
int delete(T entity);
/**
* 更新数据
*
* @param entity 实体
* @return 受影响行数
*/
int update(T entity);
/**
* mapper Example原生查询方式
*
* @param example example
* @return 结果List
*/
List<T> selectByExample(Example example);
/**
* 条件排序查询
* 1.查询条件clauses和排序条件sortMap都可以为空
* 2.如果查询条件和排序map都为空则返回所有数据
* 3.pageSize和pageNum该方法无效
* 4.clauses定义查询条件
* 5.sortMap定义排序条件key为属性字段value="DESC" 为倒序value="ASC"为正序。
*
* @param condition 条件
* @return 结果List
*/
List<T> select(Condition condition);
/**
* 条件分页排序查询
* 1.查询条件clauses和排序条件sortMap都可以为空
* 2.如果查询条件和排序map都为空则返回所有数据
* 3.clauses定义匹查询条件
* 4.sortMap定义排序条件key为属性字段value="DESC" 为倒序value="ASC"为正序。
*
* @param condition 条件
* @return 结果List
*/
PageInfo<T> selectPage(Condition condition);
}
同时创建接口实现类时需要继承自定义的BaseServiceImpl
类该类实现了BaseService
接口的所有方法这样可以达到不用书写这些接口就可在需要的时候
调用如果需要自定义某个方法时只需要实通过@Override
重写某个方法即可。
package com.cqjtu.studentdocument.service.Impl;
import com.alibaba.fastjson.JSONObject;
import com.cqjtu.studentdocument.dao.BaseDao;
import com.cqjtu.studentdocument.service.BaseService;
import com.cqjtu.studentdocument.utils.dto.Clause;
import com.cqjtu.studentdocument.utils.dto.Condition;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.mapperhelper.EntityHelper;
import javax.persistence.Id;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author pengyangyan
*/
@Slf4j
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true,rollbackFor = Exception.class)
public abstract class BaseServiceImpl<T, M extends BaseDao<T>> implements BaseService<T> {
private Class<?> clazz = (Class<?>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
@Autowired
protected M entityDao;
@Override
public List<T> selectAll() {
return entityDao.selectAll();
}
@Override
public T selectByKey(Serializable key) {
log.info("执行筛选==>参数为【"+key+"】");
return entityDao.selectByPrimaryKey(key);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int insert(T entity) {
log.info("执行插入==>参数为【"+entity+"】");
return entityDao.insertSelective(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteByKey(Serializable key) {
log.info("执行删除==>参数为【"+key+"】");
return entityDao.deleteByPrimaryKey(key);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int deletes(List<String> keys) {
log.info("执行批量删除==>参数为【"+keys+"】");
String keyName = null;
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Id.class)) {
keyName = field.getName();
break;
}
}
Example example = new Example(clazz);
example.createCriteria().andIn(keyName,keys);
return entityDao.deleteByExample(example);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int delete(T entity) {
log.info("执行删除==>参数为【"+entity+"】");
if (entity != null){
return entityDao.delete(entity);
}
return 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int update(T entity) {
log.info("执行修改==>参数为【"+entity+"】");
return entityDao.updateByPrimaryKeySelective(entity);
}
@Override
public List<T> selectByExample(Example example) {
return entityDao.selectByExample(example);
}
@Override
public List<T> select(Condition condition) {
log.info("执行条件查询===>参数为【"+JSONObject.toJSONString(condition) +"】");
Map<String, EntityColumn> propertyMap = EntityHelper.getEntityTable(clazz).getPropertyMap();
Example example = new Example(clazz);
List<Clause> clauses = condition.getClauses();
if (!CollectionUtils.isEmpty(clauses)) {
Example.Criteria criteria = 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) {
StringBuffer sb = new StringBuffer();
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());
}
return selectByExample(example);
}
@Override
public PageInfo<T> selectPage(Condition condition) {
PageHelper.startPage(condition.getPageNum(), condition.getPageSize());
List<T> list = select(condition);
return new PageInfo<>(list);
}
}
Web层
在web层项目定义了一个BaseController
的抽象类,里面实体了基本的增删改查的接口定义。再我们创建某个模块的Controller时只需要继承该类就
可达到不用在创建的类下面定义接口也会自动拥有相关接口如果需要对某个接口方法做定制时只需要重新某个方法就可以了。
package com.cqjtu.studentdocument.controller;
import com.cqjtu.studentdocument.advice.ExceptionEnums;
import com.cqjtu.studentdocument.advice.MyException;
import com.cqjtu.studentdocument.service.BaseService;
import com.cqjtu.studentdocument.utils.dto.Condition;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.io.Serializable;
import java.util.List;
/**
* @author pengyangyan
*/
@Slf4j
public abstract class BaseController<S extends BaseService<T>,T,ID extends Serializable> {
@Autowired
protected S service;
@ApiOperation(value = "基础接口: 新增操作")
@PostMapping(value = "add")
public ResponseEntity<T> save(@RequestBody T entity){
try {
if ( service.insert(entity)<1){
throw new MyException(ExceptionEnums.ADD_ERROR);
}
}catch (Exception e){
log.error(e.getMessage());
throw new MyException(ExceptionEnums.ADD_ERROR);
}
return ResponseEntity.ok(entity);
}
@ApiOperation(value = "基础接口: 返回指定ID的数据")
@GetMapping(value = "get/{id}")
public ResponseEntity<T> get(@PathVariable("id")ID id){
T t = null;
try {
t = service.selectByKey(id);
}catch (Exception e){
log.error(e.getMessage());
throw new MyException(ExceptionEnums.GET_ITEM);
}
return ResponseEntity.ok(t);
}
@ApiOperation(value = "基础接口: 返回所有数据")
@GetMapping(value = "all")
public ResponseEntity<List<T>> all(){
List<T> list;
try {
list = service.selectAll();
}catch (Exception e){
log.error(e.getMessage());
throw new MyException(ExceptionEnums.GET_LIST_ERROR);
}
return ResponseEntity.ok(list);
}
@ApiOperation(value = "基础接口: 分页返回数据")
@PostMapping(value = "page")
public ResponseEntity<PageInfo<T>> page(@RequestBody Condition condition){
PageInfo<T> page ;
try {
page = service.selectPage(condition);
}catch (Exception e){
log.error(e.getMessage());
throw new MyException(ExceptionEnums.GET_LIST_ERROR);
}
return ResponseEntity.ok(page);
}
@ApiOperation(value = "基础接口: 修改数据")
@PostMapping(value = "update")
public ResponseEntity<T> update(@RequestBody T entity){
try {
if (service.update(entity)<1){
throw new MyException(ExceptionEnums.UPDATE_ERROR);
}
}catch (Exception e){
log.error(e.getMessage());
throw new MyException(ExceptionEnums.UPDATE_ERROR);
}
return ResponseEntity.ok(entity);
}
@ApiOperation(value = "基础接口: 删除指定ID的数据")
@GetMapping(value = "delete/{id}")
public ResponseEntity<String> delete(@PathVariable("id")ID id){
try {
if ( service.deleteByKey(id)<1){
throw new MyException(ExceptionEnums.DELETE_ERROR);
}
} catch (Exception e){
log.error(e.getMessage());
throw new MyException(ExceptionEnums.DELETE_ERROR);
}
return ResponseEntity.ok("删除成功");
}
}
权限相关
权限主要采用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
,在里面引入自定义的AuthRealm
bean以达到权限的校验。
因为项目采用的是前后端分离会产生跨域请求等问题所以需要开放所有来源对该服务的请求所以需要配置相关过滤器CorsFilter
。
package com.cqjtu.studentdocument.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CorsFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
HttpServletRequest httpServletRequest = (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);
}
@Override
public void destroy() {
}
}
使用shiro权限验证时第一次请求为option方式所以要配置shiro过滤掉option方式不对此方式做权限拦截。故配置ShiroFilter
package com.cqjtu.studentdocument.filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@Slf4j
public class ShiroFiter extends FormAuthenticationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
boolean allowed = super.isAccessAllowed(request, response, mappedValue);
if (!allowed) {
// 判断请求是否是options请求
String method = WebUtils.toHttp(request).getMethod();
if (StringUtils.equalsIgnoreCase("OPTIONS", method)) {
return true;
}
}
return allowed;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
return super.onAccessDenied(request, response);
}
}
其他
项目中定义了全局异常捕获再项目中需要抛出异常时只需要抛出自定义的异常MyException
,就可对该异常进行捕获当用户请求某个接口如果出现异常时
后台服务就会返回自定义的错误代码不会出现乱码的情况,以便于前端对返回结果做统一处理或错误拦截。同时我们也可预先自定义需要抛出的错误在前段可对错误code进行区别采用不同的样式提示。
package com.cqjtu.studentdocument.advice;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum ExceptionEnums {
/**
* 错误返回信息
*/
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,"当前档案未完善身高体重信息")
;
/**
* 代码值
*/
private int code;
/**
* 消息
*/
private String msg;
}
安装教程
- 下载项目
- mysql本地创建数据库导入sql文件
- 修改项目yml数据库配置文件为自己的配置
- 运行项目