0


基于Springboot的校园疫情管理系统

一、基于Springboot+MybatisPlus+Echarts+Mysql实现校园疫情管理系统

Coding路人王:从0到1

项目简介:

​ 该项目基于springboot框架实现了疫情背景下的校园管理,主要涵盖了中国疫情确诊分布地图(对接腾讯API接口)、中国实时疫情新闻播报、以及对疫情数据的饼图、折线图和柱状图展示。系统角色可以进行增删改查,为角色分配菜单权限,大致分为学生、教师、院系级系统管理员,集成了shiro框架实现了不同的角色可以赋予不同的菜单栏权限,可以完成菜单栏的动态增删改查,实现了动态的权限设置。

​ 在校园疫情数据管理中,基于mybatiplus框架实现了疫情数据的带有条件查询及分页的增删改查,并实现了拖拽式上传excel数据和导出疫情数据。其中相关功能主要包含疫情数据管理,疫情新闻管理,疫情图表展示管理,学生健康打卡管理,院系管理、班级管理、核酸检测管理,疫苗接种管理,学生请假管理等等。系统管理中主要包含了用户管理、角色管理和菜单管理。

​ 在数据库设计中,主要设计了用户与角色之间的多对多实体关系,角色与菜单之间的多对多关系设计。其中,学生请假功能设计了详细的审批流,学生提交审批流后,教师审批后方为院系审批,为串行审批流程,设计了审批节点的状态与审批流的审批逻辑。

​ 在缓存设计中,主要将访问量较高的中国疫情地图和新闻播报首页进行了redis数据缓存,为了保证redis缓存与mysql数据的一致性,每当定时任务触发去解析腾讯API接口数据时候,每更新一次数据库数据,就要删除一次redis缓存,这样就可以保证客户端每次查询数据查不到就可以进行访问数据库更新最新的缓存数据。

​ 在接口设计中,采用了RestFul风格的架构,使请求路径变得更加简洁,传递、获取参数值更加方便,通过请求路径中直接传递参数值,不会暴露传递给方法的参数变量名,接口也变得更加安全。

开发环境:

Java:JDK1.8

开发工具:Idea、Navicat、Maven3.6

前端框架:layui框架

后端框架:Springboot、MybatisPlus、Shiro、Httpclient爬虫

数据库:Mysql5.7及其以上

源码:
讲解地址:https://www.bilibili.com/video/BV1aY411c7d1?share_source=copy_web

请添加图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.1 构建springboot项目

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.1.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency>
spring:
 datasource:
  username: root
  password: 123456
  url: jdbc:mysql://localhost:3306/nocv?serverTimezone=UTC&useSSL=false&characterEncoding=utf-8
  driver-class-name: com.mysql.jdbc.Driver

1.2 引入Echarts地图

1.官网:https://echarts.apache.org/zh/ 下载JS文件引入项目
2.查看图例
3.快速使用

<!DOCTYPEhtml><html><head><metacharset="utf-8"/><!-- 引入刚刚下载的 ECharts 文件 --><scriptsrc="echarts.js"></script></head></html>
<!DOCTYPEhtml><html><head><metacharset="utf-8"/><title>ECharts</title><!-- 引入刚刚下载的 ECharts 文件 --><scriptsrc="echarts.js"></script></head><body><!-- 为 ECharts 准备一个定义了宽高的 DOM --><divid="main"style="width: 600px;height:400px;"></div><scripttype="text/javascript">// 基于准备好的dom,初始化echarts实例var myChart = echarts.init(document.getElementById('main'));// 指定图表的配置项和数据var option ={title:{text:'ECharts 入门示例'},tooltip:{},legend:{data:['销量']},xAxis:{data:['衬衫','羊毛衫','雪纺衫','裤子','高跟鞋','袜子']},yAxis:{},series:[{name:'销量',type:'bar',data:[5,20,36,10,10,20]}]};// 使用刚指定的配置项和数据显示图表。
   myChart.setOption(option);</script></body></html>

地图社区图例http://www.isqqw.com/

1.3 创建数据库

DROPTABLEIFEXISTS`nocv_data`;CREATETABLE`nocv_data`(`id`int(11)NOTNULLAUTO_INCREMENT,`name`varchar(255)DEFAULTNULL,`value`int(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=35DEFAULTCHARSET=utf8;-- ------------------------------ Records of nocv_data-- ----------------------------INSERTINTO`nocv_data`VALUES('1','澳门','95');INSERTINTO`nocv_data`VALUES('2','香港','35');INSERTINTO`nocv_data`VALUES('3','台湾','153');INSERTINTO`nocv_data`VALUES('4','新疆','56');INSERTINTO`nocv_data`VALUES('5','宁夏','26');INSERTINTO`nocv_data`VALUES('6','青海','26');

1.4 编写代码

springboot

contRoller: /query

service:

dao:

entity:

1.5 展示数据

$.ajax({
    url: "/query",
    dataType: "json",
    success: function (data) {
      // 某种意义上来说,数组也是object
      for (let i in data) {
        dataList[i] = data[i];
      }

      myChart.setOption({
        series: [
          {
            name: "确诊病例",
            type: "map",
            geoIndex: 0,
            data: dataList
          }
       ]
      });
    }
  });

七、中国疫情地图增删改查

7.1 分页配置MybatisPlusConfig

@Configuration@ConditionalOnClass(value ={PaginationInterceptor.class})publicclassMybatisPlusConfig{@BeanpublicPaginationInterceptorpaginationInterceptor(){returnnewPaginationInterceptor();}}

7.2 layui返回的数据格式 DataView

@Data@AllArgsConstructor@NoArgsConstructorpublicclassDataView{privateInteger code =0;privateString msg ="";privateLong count =0L;privateObject data;publicDataView(Long count,Object data){this.count = count;this.data = data;}publicDataView(Object data){this.data = data;}}

八、疫情打卡上报管理

8.1 引入静态打卡HTML页面

九、实现拖拽Excel导入疫情数据功能

9.1 引入pom依赖

<!--引入poi--><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.0.0</version></dependency>

9.2 引入layui 的拖拽上传Excel组件

* Excel的拖拽或者点击上传
* 1.前台页面发送一个请求,上传文件MutilpartFile HTTP
* 2.Controller,上传文件MutilpartFile 参数
* 3.POI 解析文件,里面的数据一行一行全部解析出来
* 4.每一条数据插入数据库
* 5.mybatiplus 批量saveBatch(list)
<divclass="layui-upload-drag"id="test10"><iclass="layui-icon"></i><p>点击上传,或将文件拖拽到此处</p><divclass="layui-hide"id="uploadDemoView"><hr><imgsrc=""alt="上传成功后渲染"style="max-width: 196px"></div></div>

layui.use(['upload','jquery'],function(){
        var layer = layui.layer //弹层
            ,$ = layui.jquery
            ,upload = layui.upload
            
//拖拽上传
        upload.render({
            elem: '#test10'
            ,url: '/excelImport'
            ,accept: 'file' //普通文件
            ,done: function(res){
                layer.msg('上传成功');
                console.log(res);
            }
        });

9.2 编写Controller

// Excel数据导入@RequestMapping(value ="/excelImport", method =RequestMethod.POST)@ResponseBodypublicDataViewuploadExcel(@RequestParam("file")MultipartFile file){DataView dataView =newDataView();if(file.isEmpty()){
        dataView.setMsg("文件为空");return dataView;}try{//根据路径获取这个操作excel的实例HSSFWorkbook wb =newHSSFWorkbook(file.getInputStream());HSSFSheet sheet = wb.getSheetAt(0);//实体类集合List<NocvData> listData =newArrayList<>();HSSFRow row =null;//循环sesheet页中数据从第二行开始,第一行是标题for(int i =0; i < sheet.getPhysicalNumberOfRows(); i++){//获取每一行数据
            row = sheet.getRow(i);NocvData data =newNocvData();
            data.setName(row.getCell(0).getStringCellValue());
            data.setValue(Integer.valueOf((int) row.getCell(1).getNumericCellValue()));
            listData.add(data);}//循环展示导入的数据,实际应用中应该校验并存入数据库
        indexService.saveBatch(listData);
        dataView.setCode(200);
        dataView.setMsg("导入成功");return dataView;}catch(Exception e){
        e.printStackTrace();}
    dataView.setCode(100);
    dataView.setMsg("导入失败");return dataView;}

9.4 数据导出Excel功能【中国疫情数据】

1.前端发送请求 /

2.后端查询数据库,封装数据Excel实体

3.返回数据建立输出,写出浏览器文件

// 导出疫情数据
form.on("submit(doExport)",function (){
    window.location.href="/excelOutportChina";//这里是接口的地址})<button type="button"class="layui-btn layui-btn-sm layui-btn-radius" lay-submit="" lay-filter="doExport"><i class="layui-icon layui-icon-search layui-icon-normal"></i>导出中国疫情数据Excel</button>@RequestMapping("/excelOutportChina")@ResponseBodypublicvoidexcelOutportChina(HttpServletResponse response){
        response.setCharacterEncoding("UTF-8");List<NocvData> list = indexService.list();HSSFWorkbook wb =newHSSFWorkbook();//2-创建sheet页,设置sheet页的名字HSSFSheet sheet = wb.createSheet("中国数据表");//3-创建标题行HSSFRow titleRow = sheet.createRow(0);
        titleRow.createCell(0).setCellValue("城市名称");
        titleRow.createCell(1).setCellValue("确诊数量");//4-遍历将数据集合将数据放到对应的列中for(NocvData data : list){HSSFRow dataRow = sheet.createRow(sheet.getLastRowNum()+1);
            dataRow.createCell(0).setCellValue(data.getName());
            dataRow.createCell(1).setCellValue(data.getValue());}// 5.建立输出OutputStream os =null;try{//6-设置Excel的名称
            response.setContentType("application/octet-stream;charset=utf-8");
            response.setHeader("Content-Disposition","attachment;filename="+newString("中国疫情数据表".getBytes(),"iso-8859-1")+".xls");
            os = response.getOutputStream();
            wb.write(os);
            os.flush();}catch(Exception e){
            e.printStackTrace();}finally{try{if(os !=null){
                    os.close();}}catch(Exception e){
                e.printStackTrace();}}}

十、对接腾讯API接口实现疫情数据实时更新

十、打开腾讯数据网址腾讯实时疫情

主页网址:https://news.qq.com/zt2020/page/feiyan.htm#/global

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-66dqclsu-1655813236195)(C:\Users\15067\AppData\Local\Temp\1654132815620.png)]

腾讯数据接口:https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5

网易数据接口:https://c.m.163.com/ug/api/wuhan/app/data/list-total

10.1 网络爬虫对接 【腾讯】API接口

<!--httpClient客户端--><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.56</version></dependency>
@ComponentpublicclassHttpUtils{@BeanpublicStringgetData()throwsIOException{//请求参数设置RequestConfig requestConfig =RequestConfig.custom()//读取目标服务器数据超时时间.setSocketTimeout(10000)//连接目标服务器超时时间.setConnectTimeout(10000)//从连接池获取连接的超时时间.setConnectionRequestTimeout(10000).build();CloseableHttpClient httpClient =null;HttpGet request =null;CloseableHttpResponse response =null;try{//创建HttpClient
            httpClient =HttpClients.createDefault();//使用url构建get请求
            request =newHttpGet("https://c.m.163.com/ug/api/wuhan/app/data/list-total");//填充请求设置
            request.setConfig(requestConfig);//发送请求,得到响应
            response = httpClient.execute(request);//获取响应状态码int statusCode = response.getStatusLine().getStatusCode();//状态码200 正常if(statusCode ==200){//解析响应数据HttpEntity entity = response.getEntity();//字符串格式数据String string =EntityUtils.toString(entity,"UTF-8");System.out.println("字符串格式:"+ string);return string;}else{thrownewHttpResponseException(statusCode,"响应异常");}}finally{if(response !=null){
                response.close();}if(request !=null){
                request.releaseConnection();}if(httpClient !=null){
                httpClient.close();}}}}

10.2 对接定时程序数据入库

@ControllerpublicclassTengXunApi{publicstaticvoidmain(String[] args)throwsException{HttpUtils httpUtils =newHttpUtils();String string = httpUtils.getData();System.out.println("string:"+string);//json格式数据JSONObject jsonObject =JSONObject.parseObject(string);Object data = jsonObject.get("data");System.out.println(data.toString());System.out.println("====================================");//=========================JSONObject jsonObject1 =JSONObject.parseObject(data.toString());ChinaTotal chinaTotal =(ChinaTotal) jsonObject1.get("chinaTotal");System.out.println(chinaTotal);}}

10.3 对接腾讯API实现省份数据的自动刷新

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-62249eu4-1655813236199)(C:\Users\15067\AppData\Local\Temp\1654302358239.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oRCwv28N-1655813236201)(C:\Users\15067\AppData\Local\Temp\1654302429447.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3134rPJg-1655813236206)(C:\Users\15067\AppData\Local\Temp\1654303017808.png)]

// 各个省份的数据
// 3.世界各个国家及地区的所有数据
Object areaTree = jsonObjectData.get("areaTree");
System.out.println("areaTree:"+areaTree);

JSONArray areaTree1 = jsonObjectData.getJSONArray("areaTree");
Object[] objects = areaTree1.toArray();
// 所有国家的名字
for (int i = 0; i < objects.length; i++) {
    JSONObject jsonObject1 = JSONObject.parseObject(objects[i].toString());
    Object name = jsonObject1.get("name");
    //System.out.println(name);
}

// 数组中第三个为中国省份数据
JSONObject jsonObject1 = JSONObject.parseObject(objects[2].toString());
JSONArray children1 = jsonObject1.getJSONArray("children");
Object[] objects1 = children1.toArray();

// 遍历中国地区的数据
List<NocvData> list = new ArrayList<>();
SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

for (int i = 0; i < objects1.length; i++) {
    NocvData nocvData = new NocvData();
    JSONObject jsonObject2 = JSONObject.parseObject(objects1[i].toString());
    Object name = jsonObject2.get("name");//省份名称
    Object total2 = jsonObject2.get("total");
    JSONObject totalJson = JSONObject.parseObject(total2.toString());
    Object confirm2 = totalJson.get("confirm");//quzhen数量
    System.out.println(name+":"+confirm2);
    //获取省份更新的时间
    Object extData = jsonObject2.get("extData");
    JSONObject extDataJson = JSONObject.parseObject(extData.toString());
    Object lastUpdateTime1 = extDataJson.get("lastUpdateTime");
    //封装数据
    nocvData.setName(name.toString());
    nocvData.setValue(Integer.parseInt(confirm2.toString()));
    String s = String.valueOf(lastUpdateTime1);
    if (lastUpdateTime1 == null){

        nocvData.setUpdateTime(new Date());

    }else {

        nocvData.setUpdateTime(format2.parse(s));
    }

    list.add(nocvData);
}

// 插入数据库各个省份的数据
indexService.saveBatch(list);

十一、完成系统登录和验证码

11.1 简单登录过程

前台:User: username password 【javabean

后台:SQL:select * from user where username = ? and password = ?

USER: ID USERNAME PASSWORD IMG ROLE BANJI

session 浏览器第一次访问程序服务端,会产生一个session,会帮你生成一个唯一的ID 【缓存、数据库】

浏览器:cookies, sessionid ------- sesssion

缺点:

1.服务端压力【存储】

session保存在服务端,一个用户保存,百万级别的用户,都不保存在服务端。 session sessionid,浏览器保存id

2.局限性,浏览器禁用cookie

问题:user信息

token: 没有状态,username password 字符串

11.2 Shiro框架

登录:认证,,授权

认证:登录,学生—》学校 大门口进门

授权:学生=男生 【男生宿舍 男生厕所】

role【学生 老师 管理员】

学生:查看

老师:修改

管理员:删除

11.3 验证码比较难?

1.先要判断验证码对不对?

2.username password SQL

3.SHIRO 权限【角色】

吃饭时候:跑外面 砍一棵大树 一双筷子

使用。

11.2 验证码逻辑

推荐

<!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>4.6.8</version></dependency>
@RequestMapping("/getCode")
    public void getCode(HttpServletResponse response, HttpSession session) throws IOException {
        //HuTool定义图形验证码的长和宽,验证码的位数,干扰线的条数
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(116, 36,4,10);
        session.setAttribute("code",lineCaptcha.getCode());
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            lineCaptcha.write(outputStream);
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

不推荐 麻烦

@WebServlet("/checkCode")publicclassCheckCodeServletextendsHttpServlet{publicvoiddoGet(HttpServletRequest request,HttpServletResponse response)throwsServletException,IOException{//服务器通知浏览器不要缓存
        response.setHeader("pragma","no-cache");
        response.setHeader("cache-control","no-cache");
        response.setHeader("expires","0");//在内存中创建一个长80,宽30的图片,默认黑色背景//参数一:长//参数二:宽//参数三:颜色int width =80;int height =30;BufferedImage image =newBufferedImage(width,height,BufferedImage.TYPE_INT_RGB);//获取画笔Graphics g = image.getGraphics();//设置画笔颜色为灰色
        g.setColor(Color.GRAY);//填充图片
        g.fillRect(0,0, width,height);//产生4个随机验证码,12EyString checkCode =getCheckCode();//将验证码放入HttpSession中
        request.getSession().setAttribute("CHECKCODE_SERVER",checkCode);//设置画笔颜色为黄色
        g.setColor(Color.YELLOW);//设置字体的小大
        g.setFont(newFont("黑体",Font.BOLD,24));//向图片上写入验证码
        g.drawString(checkCode,15,25);//将内存中的图片输出到浏览器//参数一:图片对象//参数二:图片的格式,如PNG,JPG,GIF//参数三:图片输出到哪里去ImageIO.write(image,"PNG",response.getOutputStream());}/**
     * 产生4位随机字符串 
     */privateStringgetCheckCode(){String base ="0123456789ABCDEFGabcdefg";int size = base.length();Random r =newRandom();StringBuffer sb =newStringBuffer();for(int i=1;i<=4;i++){//产生0到size-1的随机值int index = r.nextInt(size);//在base字符串中获取下标为index的字符char c = base.charAt(index);//将c放入到StringBuffer中去
            sb.append(c);}return sb.toString();}publicvoiddoPost(HttpServletRequest request,HttpServletResponse response)throwsServletException,IOException{this.doGet(request,response);}}

十一、 集成Shiro完成登录和资源控制

11.1 简介

登陆之前:除了登录页面和静态资源之外全部拦截掉

Subject****:主体,应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject , 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

SecurityManager****:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;

Realm****:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

11.2 pom依赖

<!--shiro--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.2</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.4.2</version></dependency><!--shiro和thymeleaf集成的扩展依赖,为了能在页面上使用xsln:shiro的标签--><dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version></dependency>

11.3 编写UserRealm

publicclassUserRealmextendsAuthorizingRealm{@Autowired@LazyprivateUserService userService;@OverridepublicStringgetName(){returnthis.getClass().getSimpleName();}/**
     * 认证
     */@OverrideprotectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationToken token)throwsAuthenticationException{//1.查询数据库QueryWrapper<User> queryWrapper =newQueryWrapper<>();
        queryWrapper.eq("username",token.getPrincipal().toString());User user = userService.getOne(queryWrapper);if(null!= user){//盐 时用户uuid生成的//ByteSource salt = ByteSource.Util.bytes(user.getSalt());SimpleAuthenticationInfo info =newSimpleAuthenticationInfo(user,user.getPassword(),this.getName());return info;}returnnull;}/**
     * 授权
     */@OverrideprotectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principalCollection){SimpleAuthorizationInfo authorizationInfo =newSimpleAuthorizationInfo();returnnull;}}

11.4 ShiroConfig

@Configuration@ConditionalOnWebApplication(type =Type.SERVLET)@ConditionalOnClass(value ={SecurityManager.class})@ConfigurationProperties(prefix ="shiro")@DatapublicclassShiroAutoConfiguration{privatestaticfinalString SHIRO_DIALECT ="shiroDialect";privatestaticfinalString SHIRO_FILTER ="shiroFilter";// 加密方式privateString hashAlgorithmName ="md5";// 散列次数privateint hashIterations =2;// 默认的登陆页面privateString loginUrl ="/index.html";privateString[] anonUrls;// 放行的路径privateString logOutUrl;// 登出的地址privateString[] authcUlrs;// 拦截的路径/**
     * 声明凭证匹配器
     *//*@Bean("credentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName(hashAlgorithmName);
        credentialsMatcher.setHashIterations(hashIterations);
        return credentialsMatcher;
    }*//**
     * 声明userRealm
     */@Bean("userRealm")publicUserRealmuserRealm(){UserRealm userRealm =newUserRealm();return userRealm;}/**
     * 配置SecurityManager
     */@Bean("securityManager")publicSecurityManagersecurityManager(UserRealm userRealm){DefaultWebSecurityManager securityManager =newDefaultWebSecurityManager();// 注入userRealm
        securityManager.setRealm(userRealm);return securityManager;}/**
     * 配置shiro的过滤器
     */@Bean(SHIRO_FILTER)publicShiroFilterFactoryBeanshiroFilterFactoryBean(SecurityManager securityManager){ShiroFilterFactoryBean factoryBean =newShiroFilterFactoryBean();// 设置安全管理器
        factoryBean.setSecurityManager(securityManager);// 设置未登陆的时要跳转的页面
        factoryBean.setLoginUrl(loginUrl);Map<String,String> filterChainDefinitionMap =newHashMap<>();// 设置放行的路径if(anonUrls !=null&& anonUrls.length >0){for(String anon : anonUrls){
                filterChainDefinitionMap.put(anon,"anon");System.out.println(anon);}}// 设置登出的路径if(null!= logOutUrl){
            filterChainDefinitionMap.put(logOutUrl,"logout");}// 设置拦截的路径if(authcUlrs !=null&& authcUlrs.length >0){for(String authc : authcUlrs){
                filterChainDefinitionMap.put(authc,"authc");}}Map<String,Filter> filters=newHashMap<>();
        factoryBean.setFilters(filters);
        factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return factoryBean;}/**
     * 注册shiro的委托过滤器,相当于之前在web.xml里面配置的
     * @return
     */@BeanpublicFilterRegistrationBean<DelegatingFilterProxy>delegatingFilterProxy(){FilterRegistrationBean<DelegatingFilterProxy> filterRegistrationBean =newFilterRegistrationBean<DelegatingFilterProxy>();DelegatingFilterProxy proxy =newDelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName(SHIRO_FILTER);
        filterRegistrationBean.setFilter(proxy);return filterRegistrationBean;}/* 加入注解的使用,不加入这个注解不生效--开始 *//**
     *
     * @param securityManager
     * @return
     */@BeanpublicAuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor(SecurityManager securityManager){AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =newAuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}@BeanpublicDefaultAdvisorAutoProxyCreatorgetDefaultAdvisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator =newDefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);return advisorAutoProxyCreator;}/* 加入注解的使用,不加入这个注解不生效--结束 *//**
     * 这里是为了能在html页面引用shiro标签,上面两个函数必须添加,不然会报错
     *
     * @return
     */@Bean(name = SHIRO_DIALECT)publicShiroDialectshiroDialect(){returnnewShiroDialect();}}

11.5 static下的静态index.html跳转页面

<!--用来跳转-->
<script type="text/javascript">
    window.location.href="/toLogin";
</script>

11.6 编写yml配置过滤路径

#shiro的配置
shiro:
  anon-urls:
    - /toLogin*
    - /login.html*
    - /login/login
    - /login/getCode
    - /css/**
    - /echarts/**
    - /images/**
    - /layui/**
  login-url: /index.html
  log-out-url: /login/logout*
  authc-ulrs:
    - /**

十二、设计菜单、角色、班级、学院、老师等数据库设计

12.1 数据库设计

1.menu 菜单
idpidtypetitlepremissioniconhrefopenordernumavailable10menu疫管理menu:select/menu11121menu饼图menu:select/pie021
2.role 角色
idnameremark1超级管理员拥有所有权限2老师查看新增修改3学生查看
3.role_menu 关联关系表
ridmid1112
4.user 用户表【老师,学生,管理员】
idusernamepassword…role_idban_ji_idxue_yuan_idteacher_id1admin1234561110
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iB4WlpBu-1655813236209)(C:\Users\15067\AppData\Local\Temp\1654489985017.png)]

5.ban_ji 班级表
idnamexue_yuan_id1软件工程1班1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HjmAyZJe-1655813236211)(C:\Users\15067\AppData\Local\Temp\1654490055131.png)]

6.xue_yuan学院表
idname1计算机系

12.2 Java实体编写

十三、 修改index主题样式、菜单的增删改查

13.1 【dtree属性菜单和下拉实现查询所有】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LIcAKCP1-1655813236214)(C:\Users\15067\AppData\Local\Temp\1654494627502.png)]

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency>

13.1 菜单的插入

TreeNode

packagecom.example.demo.controller;importcom.fasterxml.jackson.annotation.JsonProperty;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;importjava.util.ArrayList;importjava.util.List;/**
 * @Author:
 * @Date: 2019/11/22 15:25
 */@Data@AllArgsConstructor@NoArgsConstructorpublicclassTreeNode{privateInteger id;@JsonProperty("parentId")//返回的json的名称 parentId ,为了确定层级关系privateInteger pid;privateString title;privateString icon;privateString href;privateBoolean spread;privateList<TreeNode> children =newArrayList<TreeNode>();/**
     * 0为不选中  1为选中
     */privateString checkArr="0";/**
     * 首页左边导航菜单的构造器
     */publicTreeNode(Integer id,Integer pid,String title,String icon,String href,Boolean spread){this.id = id;this.pid = pid;this.title = title;this.icon = icon;this.href = href;this.spread = spread;}/**
     * 部门 dtree的构造器
     * @param id id
     * @param pid 父亲parentId
     * @param title 名称
     * @param spread 是否展开
     */publicTreeNode(Integer id,Integer pid,String title,Boolean spread){this.id = id;this.pid = pid;this.title = title;this.spread = spread;}/**
     * 给角色分配权限的构造器
     */publicTreeNode(Integer id,Integer pid,String title,Boolean spread,String checkArr){this.id = id;this.pid = pid;this.title = title;this.spread = spread;this.checkArr = checkArr;}}

TreeBuilder

publicclassTreeNodeBuilder{publicstaticList<TreeNode>build(List<TreeNode> treeNodes,Integer topPid){List<TreeNode> nodes =newArrayList<TreeNode>();for(TreeNode n1 : treeNodes){if(n1.getPid()==topPid){
                nodes.add(n1);}for(TreeNode n2 : treeNodes){if(n1.getId()==n2.getPid()){
                    n1.getChildren().add(n2);}}}return nodes;}}

13.2 dtree菜单下拉回显和展示所有

父级菜单ID为:0【必填】

13.3 菜单栏的编辑

menuService.updateById(menu);

13.4 菜单栏的删除

删除逻辑的时候:

@RequestMapping("/checkMenuHasChildrenNode")@ResponseBodypublicMap<String,Object>checkChildrenNode(Menu menu){Map<String,Object> map =newHashMap<>();QueryWrapper<Menu> queryWrapper =newQueryWrapper<>();
        queryWrapper.eq("pid",menu.getId());List<Menu> list = menuService.list(queryWrapper);if(list.size()>0){
            map.put("value",true);}else{
            map.put("value",false);}return map;}

1.子类ID,不能删除

2.没有子类ID,直接删掉

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JZr1SKfj-1655813236216)(C:\Users\15067\AppData\Local\Temp\1654516334725.png)]

真正的删除

@RequestMapping("/deleteMenu")@ResponseBodypublicDataViewdeleteMenu(Menu menu){
    menuService.removeById(menu.getId());DataView dataView =newDataView();
    dataView.setCode(200);
    dataView.setMsg("删除菜单成功!");return dataView;}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-320hLrXh-1655813236217)(C:\Users\15067\AppData\Local\Temp\1654521334106.png)]

13.5 修改index主菜单栏为动态查库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UTCDOIpj-1655813236219)(C:\Users\15067\AppData\Local\Temp\1654561883304.png)]

1.修改样式 引入 js css

2.配置yml放行js包

3.原项目修改index.html 为 china.html 删除 commonmenu.html 引入 静态资源包里面的 index.html

**4.去掉其它页面 的 引入,添加 **

去掉

<!--layui公共模块-->
<div th:include="commonmenu :: menu"></div>

class="layui-body"

5.修改 indexcontroller 的请求/路径,添加一个/toChina

6.修改数据库 /toChina

7.编写Controller

/**
 * 加载最外层index菜单
 */@RequestMapping("loadIndexLeftMenuJson")@ResponseBodypublicDataViewloadIndexLeftMenuJson(Menu permissionVo){//查询所有菜单QueryWrapper<Menu> queryWrapper =newQueryWrapper<>();List<Menu> list = menuService.list();List<TreeNode> treeNodes =newArrayList<>();for(Menu p : list){Integer id =p.getId();Integer pid = p.getPid();String title = p.getTitle();String icon = p.getIcon();String href = p.getHref();Boolean spread = p.getOpen().equals(1)?true:false;
        treeNodes.add(newTreeNode(id,pid,title,icon,href,spread));}//构造层级关系List<TreeNode> list2 =TreeNodeBuilder.build(treeNodes,0);returnnewDataView(list2);}

十四、角色【管理员、学生、老师】CRUD,分配菜单权限

14.1 角色的增删改查【条件查询带有分页】

1.引入role的静态页面

页面进行菜单的增加

2…

3…

4…

14.2 为角色分配菜单权限

1.分配权限 menu【菜单的操作资源】id

2.分配角色 role【用户 管理员 学生 教师】id

3.关联表role_menu:全都可以为空,不能有主键,都是外键属性

rid mid

1 1

1 2

select mid from role_menu where rid = ?

List 所具有的菜单栏权限

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eRt6OwUJ-1655813236222)(C:\Users\15067\AppData\Local\Temp\1654588068035.png)]

/**
 * 1.初始化下拉列表的权限
 */@AutowiredprivateMenuService menuService;@RequestMapping("/initPermissionByRoleId")@ResponseBodypublicDataViewinitPermissionByRoleId(Integer roleId){//查询所有菜单和权限QueryWrapper<Menu> queryWrapper =newQueryWrapper<>();List<Menu> allPermissions = menuService.list();//1.首先根据角色id查询出当前角色所拥有的所有菜单的ID和权限的IDList<Integer> currentRolePermissions = roleService.queryRolePermissionIdsByRid(roleId);//2.根据查询出来的菜单ID和权限ID,再查询出菜单的数据和权限的数据List<Menu> currentPermissions =null;//如果根据角色id查询出来了菜单ID或权限ID,就去查询if(currentRolePermissions.size()>0){
        queryWrapper.in("id",currentRolePermissions);
        currentPermissions = menuService.list(queryWrapper);}else{
        currentPermissions =newArrayList<>();}//3.构造List<TreeNode>List<TreeNode> nodes =newArrayList<>();for(Menu allPermission : allPermissions){String checkArr ="0";for(Menu currentPermission : currentPermissions){if(allPermission.getId().equals(currentPermission.getId())){
                checkArr ="1";break;}}Boolean spread =(allPermission.getOpen()==null||allPermission.getOpen()==1)?true:false;
        nodes.add(newTreeNode(allPermission.getId(),allPermission.getPid(),allPermission.getTitle(),spread,checkArr));}returnnewDataView(nodes);}
@Select("select mid from role_menu where rid = #{roleId}")List<Integer>queryRolePermissionIdsByRid(Integer roleId);
分配菜单权限【角色与菜单之间的关系】

// 1.分配菜单栏之前删除所有的rid数据@Delete("delete from role_menu where rid = #{rid}")voiddeleteRoleByRid(Integer rid);// 2.保存分配 角色 与 菜单 的关系@Insert("insert into role_menu(rid,mid) values (#{rid},#{mid})")voidsaveRoleMenu(Integer rid,Integer mid);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0s4wJIY2-1655813236223)(C:\Users\15067\AppData\Local\Temp\1654592413047.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MaV5uqsm-1655813236224)(C:\Users\15067\AppData\Local\Temp\1654592505578.png)]

用户管理【增删改查、分配角色】

14.1 用户的增删改查,上传头像

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i8F02faY-1655813236225)(C:\Users\15067\AppData\Local\Temp\1654602632710.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5hE5w2pp-1655813236226)(C:\Users\15067\AppData\Local\Temp\1654602643229.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bhrCB3kB-1655813236228)(C:\Users\15067\AppData\Local\Temp\1654602658000.png)]

1.引入以页面

2.编写代码

查询所有带有分页 带有查询条件

第一种办法:如何连表查询????????

自定义方法:

// 1.第一种办法//if (StringUtils.isNotBlank(userVo.getUsername())){//    userService.loadUserByLeftJoin(userVo.getUsername(),userVo.getPage(),userVo.getLimit());//}// 2.mapper//@Select("select a.username,b.name FROM user as a where a.username = #{} LEFT JOIN ban_ji as b ON a.ban_ji_id = b.id limit #{},#{}")

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m4nt7xIX-1655813236229)(C:\Users\15067\AppData\Local\Temp\1654658187253.png)]

sql:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VhZoXnAU-1655813236236)(C:\Users\15067\AppData\Local\Temp\1654658432315.png)]

// 2.第二种办法

1.ipage【User所有数据】—> banjiID ----->ban_ji 表 名字给ipage对象进行赋值

2.添加属性

// 非数据库列 班级名字@TableField(exist =false)privateString banJiName;// 非数据库列 学院名字@TableField(exist =false)privateString xueYuanName;// 非数据库列 老师名字@TableField(exist =false)privateString teacherName;
// 2.第二种办法// 查到所有的数据 1 1 1for(User user : iPage.getRecords()){// 为班级名字进行赋值if(user.getBanJiId()!=null){// 班级banJiService查库BanJi banji = banJiService.getById(user.getBanJiId());
                user.setBanJiName(banji.getName());}// 为学院名字进行赋值if(user.getXueYuanId()!=null){XueYuan xueYuan = xueYuanService.getById(user.getXueYuanId());
                user.setXueYuanName(xueYuan.getName());}// 为老师名字进行赋值if(user.getTeacherId()!=null){User teacher = userService.getById(user.getTeacherId());
                user.setTeacherName(teacher.getUsername());}}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fjaAX5j5-1655813236238)(C:\Users\15067\AppData\Local\Temp\1654660307518.png)]

14.2 用户新增或编辑和删除

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5uhkQLKX-1655813236240)(C:\Users\15067\AppData\Local\Temp\1654665124918.png)]

填充数据【下拉列表】

//初始化下拉列表【班级】
$.get("/user/listAllBanJi",function (res) {
    var banji = res;
    var dom_banji=$("#banji");
    var html = "<optionvalue=''>选择班级</option>";
    $.each(banji,function (index,item) {
        html+="<optionvalue='"+item.id+"'>"+item.name+"</option>";
    });
    dom_banji.html(html);
    form.render("select");
})

//初始化下拉列表【学院】
$.get("/user/listAllXueYuan",function (res) {
    var xueyuan = res;
    var dom_xueyuan=$("#xueyuan");
    var html = "<optionvalue=''>选择学院</option>";
    $.each(xueyuan,function (index,item) {
        html+="<optionvalue='"+item.id+"'>"+item.name+"</option>";
    });
    dom_xueyuan.html(html);
    form.render("select");
})
/**
 * 初始化下拉列表的数据【班级】
 */@RequestMapping("/listAllBanJi")@ResponseBodypublicList<BanJi>listAllBanJi(){List<BanJi> list = banJiService.list();return list;}/**
 * 初始化下拉列表的数据【学院】
 */@RequestMapping("/listAllXueYuan")@ResponseBodypublicList<XueYuan>listAllXueYuan(){List<XueYuan> list = xueYuanService.list();return list;}

熙增编辑和删除

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mCjxh4WB-1655813236244)(C:\Users\15067\AppData\Local\Temp\1654669217432.png)]

14.2 重置密码、修改密码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HpxAemem-1655813236245)(C:\Users\15067\AppData\Local\Temp\1654669197299.png)]

14.3 给用户分配角色【一个用户可能多重角色 1:m】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N2lkmwDw-1655813236247)(C:\Users\15067\AppData\Local\Temp\1654680325599.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nXINgZEG-1655813236248)(C:\Users\15067\AppData\Local\Temp\1654680700125.png)]

1.创建一张角色与用户的维护表 user_role

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PFewXu4O-1655813236249)(C:\Users\15067\AppData\Local\Temp\1654680810232.png)]

2.初始化点击的角色列表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w7bNuflq-1655813236252)(C:\Users\15067\AppData\Local\Temp\1654682183307.png)]

//controller@RequestMapping("/initRoleByUserId")@ResponseBodypublicDataViewinitRoleByUserId(Integer id){// 1.查询所有角色List<Map<String,Object>> listMaps = roleService.listMaps();// 2.查询当前登录用户所拥有的角色List<Integer> currentUserRoleIds = roleService.queryUserRoleById(id);// 3.让你的前端 变为选中状态for(Map<String,Object> map : listMaps){Boolean LAY_CHECKED =false;Integer roleId =(Integer) map.get("id");for(Integer rid : currentUserRoleIds){if(rid.equals(roleId)){
                    LAY_CHECKED =true;break;}}
            map.put("LAY_CHECKED",LAY_CHECKED);}returnnewDataView(Long.valueOf(listMaps.size()),listMaps);}// mapper 根据用户id查询所有的角色@Select("select rid from user_role where uid = #{id}")List<Integer>queryUserRoleById(Integer id);

3.确认分配角色保存角色分配关系

1.删除之前的用户与角色关系

2.保存用户与角色的关系

publicvoidsaveUserRole(Integer uid,Integer[] ids){
    roleMapper.deleteRoleUserByUid(uid);if(ids!=null&&ids.length>0){for(Integer rid : ids){
            roleMapper.saveUserRole(uid,rid);}}}
// 1. 先删除之前的用户与角色关系@Delete("delete from user_role where uid = #{uid}")voiddeleteRoleUserByUid(Integer uid);//2. 保存分配的用户与角色之间的关系@Insert("insert into user_role(uid,rid) values(#{uid},#{rid})")voidsaveUserRole(Integer uid,Integer rid);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f0lZ1SzI-1655813236253)(C:\Users\15067\AppData\Local\Temp\1654683042820.png)]

十五、不同的用户【角色】登录看到不同的菜单栏

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L5tTWQ4G-1655813236254)(C:\Users\15067\AppData\Local\Temp\1654689887283.png)]

用户 : 角色 : 菜单

id ---- List

role — List

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-podtUbrh-1655813236256)(C:\Users\15067\AppData\Local\Temp\1654690134405.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W3SNeaZG-1655813236257)(C:\Users\15067\AppData\Local\Temp\1654690116614.png)]

加载左侧主页菜单栏的时候进行条件查询【OK】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yo1ioyDe-1655813236258)(C:\Users\15067\AppData\Local\Temp\1654691392426.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RRdlseGs-1655813236259)(C:\Users\15067\AppData\Local\Temp\1654691492269.png)]

不同角色展现不同的菜单实现逻辑

// 查询的所有菜单栏 按照条件查询【管理员,学生 老师【条件查询】】QueryWrapper<Menu> queryWrapper =newQueryWrapper<>();List<Menu> list =null;// 1.取出session中的用户IDUser user =(User) session.getAttribute("user");Integer userId = user.getId();// 2.根据用户ID查询角色IDList<Integer> currentUserRoleIds = roleService.queryUserRoleById(userId);// 3.去重Set<Integer> mids =newHashSet<>();for(Integer rid : currentUserRoleIds){// 3.1.根据角色ID查询菜单IDList<Integer> permissionIds = roleService.queryAllPermissionByRid(rid);// 3.2.菜单栏ID和角色ID去重
    mids.addAll(permissionIds);}// 4.根据角色ID查询菜单IDif(mids.size()>0){
    queryWrapper.in("id",mids);
    list = menuService.list(queryWrapper);}

十六、实时刷新china疫情新闻播报+轮播图

16.1 引入layui轮播图面板和轮播图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LctU8lCd-1655813236260)(C:\Users\15067\AppData\Local\Temp\1654731382207.png)]

<!--轮播图--><divclass="layui-carousel"id="test1"lay-filter="test1"><divcarousel-item=""><div><imgstyle="height: 100%;width: 100%"th:src="@{/images/banner3.jpg}"></div><div><imgstyle="height: 100%;width: 100%"th:src="@{/images/banner2.jpg}"></div><div><imgstyle="height: 100%;width: 100%"th:src="@{/images/banner1.jpg}"></div></div></div>

layui.use(['carousel','element', 'layer', 'util'], function(){
        var element = layui.element
            ,layer = layui.layer
            ,carousel = layui.carousel
            ,util = layui.util
            ,$ = layui.$;

        //头部事件
        util.event('lay-header-event', {
            //左侧菜单事件
            menuLeft: function(othis){
                layer.msg('展开左侧菜单的操作', {icon: 0});
            }
            ,menuRight: function(){
                layer.open({
                    type: 1
                    ,content: '<divstyle="padding: 15px;">处理右侧面板的操作</div>'
                    ,area: ['260px', '100%']
                    ,offset: 'rt' //右上角
                    ,anim: 5
                    ,shadeClose: true
                });
            }
        });
        //轮播图
        //常规轮播
        carousel.render({
            elem: '#test1'
            ,arrow: 'always'
            ,height:'220px'
            ,width:'350px'
        });

    });

16.2 实时播报新闻引入layui【时间线样式】

@Data@TableName("nocv_news")publicclassNocvNews{@TableId(value ="id", type =IdType.AUTO)privateInteger id;privateString title;privateString content;privateString publishby;privateDate createTime;}

16.3 疫情新闻的增删改查

十七、校园系统管理

17.1 学院 班级 的快速增删改查

十八、校园防疫管理

18.1 健康打卡管理

18.2 核酸检测管理

时间选择器的使用【】

<divclass="layui-form"><divclass="layui-form-item"><divclass="layui-inline"><labelclass="layui-form-label">检测时间</label><divclass="layui-input-inline"><inputtype="text"name="createTime"class="layui-input"lay-verify="required"id="test1"placeholder="yyyy-MM-dd"></div></div><divclass="layui-inline"><labelclass="layui-form-label">报告时间</label><divclass="layui-input-inline"><inputtype="text"name="updateTime"class="layui-input"id="test2"placeholder="yyyy-MM-dd"></div></div></div></div>

layui.extend({
        dtree: '/layui_ext/dtree/dtree'
    }).use(['jquery', 'form', 'layer', 'laydate', 'table', 'layedit','dtree'], function () {

        var laydate = layui.laydate;

18.3 疫苗接种管理

十九、引入Redis为项目增添光彩

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMO3hjBg-1655813236261)(C:\Users\15067\AppData\Local\Temp\1655171447022.png)]

19.1 为什么要引入redis数据库?

1.单机并发量高 10W读写速度 tomcat:??? 150????Mysql数据库???16384

2.安全起见,全部查询缓存,不要去查询mysql数据库

3.面试必问!!!!!!!!!

如何学习?

19.2 引入的问题

1.有缓存:返回数据【基本】

2.没有缓存:没有–》查询mysql–>更新redis缓存【非常快!】;【全国人民】

3.对接腾讯疫情数据接口API【实时】

redis老数据 mysql【查询api接口—>插入mysql数据库】

需要的时最新的疫情数据?????????

解决方案:更新数据---->删除缓存【最基本】

19.3 redis客户端

1.Jedis

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.7.1</version><!--版本号可根据实际情况填写--></dependency>

测试连接redis服务器

1.双击启动redis服务器

2.写程序

packagecom.example.demo.redis;importredis.clients.jedis.Jedis;importjava.util.Iterator;importjava.util.List;importjava.util.Set;/**
 * @author Coding路人王
 * @date 2022/6/14 10:39
 */publicclassTestConnRedis{publicstaticvoidmain(String[] args){//连接本地的 Redis 服务Jedis jedis =newJedis("localhost");// 1.String类型【确诊:222,死亡:00】
        jedis.set("nocv","Coding路人王");System.out.println(jedis.get("nocv"));// 2.List集合【新闻列表】// 存储数据到列表中
        jedis.lpush("site-list","Runoob");
        jedis.lpush("site-list","Google");
        jedis.lpush("site-list","Taobao");// 获取存储的数据并输出List<String> list = jedis.lrange("site-list",0,2);for(int i=0; i<list.size(); i++){System.out.println("列表项为: "+list.get(i));}// 3.Set无序集合,去重
        jedis.sadd("nocvset","111");
        jedis.sadd("nocvset","111");
        jedis.sadd("nocvset","111");
        jedis.sadd("nocvset","111");
        jedis.sadd("nocvset","222");Set<String> nocvlist = jedis.smembers("nocvlist");for(String s : nocvlist){System.out.println(s);}// 4.Sorted Set 有序集合【排名,排序,获取排序码】
        jedis.zadd("nocvset2",86.9,"1111");
        jedis.zadd("nocvset2",56.8,"2222");
        jedis.zadd("nocvset2",86.5,"3333");
        jedis.zadd("nocvset2",88.9,"4444");
        jedis.zadd("nocvset2",100,"5555");Set<String> nocvset2 = jedis.zrange("nocvset2",0,-1);for(String s : nocvset2){System.out.println(s);}Long nocvset21 = jedis.zrank("nocvset2","4444");System.out.println(nocvset21);System.out.println("=========================");// 返回分数区间内的个数Long nocvset22 = jedis.zremrangeByScore("nocvset2",88,100);System.out.println(nocvset22);// 返回有序集中,成员的分数值Double nocvset23 = jedis.zscore("nocvset2","5555");System.out.println(nocvset23);// 88.9}}

2.springboot-starter

<!-- 自动版本控制,帮你引入了jedis客户端 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

19.4 china页面完成redis的String缓存功能【全国区镇 死亡等数据缓存】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yy5LxjAZ-1655813236262)(C:\Users\15067\AppData\Local\Temp\1655186492688.png)]

/**
 * // redis查询数据库的逻辑
 * 1.查询redis缓存,【有数据,直接返回】【没有数据,查询Mysql数据库,更新缓存,返回客户端】
 */Jedis jedis =newJedis("127.0.0.1");// 2.1拿到客户端连接【你没有使用redis】if(jedis!=null){String confirm = jedis.get("confirm");String input = jedis.get("input");String heal = jedis.get("heal");String dead = jedis.get("dead");String updateTime = jedis.get("updateTime");// 2.2 缓存里面有数据if(StringUtils.isNotBlank(confirm)&&StringUtils.isNotBlank(input)&&StringUtils.isNotBlank(heal)&&StringUtils.isNotBlank(dead)&&StringUtils.isNotBlank(updateTime)){ChinaTotal chinaTotalRedis =newChinaTotal();
        chinaTotalRedis.setConfirm(Integer.parseInt(confirm));
        chinaTotalRedis.setInput(Integer.parseInt(input));
        chinaTotalRedis.setHeal(Integer.parseInt(heal));
        chinaTotalRedis.setDead(Integer.parseInt(dead));// 格式调整 String ----> Date
        chinaTotalRedis.setUpdateTime(newDate());System.out.println("redis中的数据:"+ chinaTotalRedis);// 扔回前台
        model.addAttribute("chinaTotal",chinaTotalRedis);// 3.疫情播报新闻List<NocvNews> newsList = nocvNewsService.listNewsLimit5();
        model.addAttribute("newsList",newsList);return"china";}else{// 2.3 缓存里面没有数据 查询数据ChinaTotal chinaTotal = chinaTotalService.getById(id);
        model.addAttribute("chinaTotal",chinaTotal);// 3.疫情播报新闻List<NocvNews> newsList = nocvNewsService.listNewsLimit5();
        model.addAttribute("newsList",newsList);// 2.4 更新缓存
                jedis.set("confirm",String.valueOf(chinaTotal.getConfirm()));
                jedis.set("input",String.valueOf(chinaTotal.getInput()));
                jedis.set("heal",String.valueOf(chinaTotal.getHeal()));
                jedis.set("dead",String.valueOf(chinaTotal.getDead()));
                jedis.set("updateTime",String.valueOf(chinaTotal.getUpdateTime()));return"china";}}
// 7.删除缓存【非常重要】mysql ---  redis 已执行的一个简单套路Jedis jedis =newJedis("127.0.0.1");if(jedis!=null){
            jedis.flushDB();}

19.5 全国各个省份的数据库Redis缓存【redis的List数据结构】

@RequestMapping("/query")@ResponseBodypublicList<NocvData>queryData()throwsParseException{// 每天更新一次的数据使用场景/*QueryWrapper<NocvData> queryWrapper = new QueryWrapper<>();
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    String format1 = format.format(new Date());
    queryWrapper.ge("update_time",format.parse(format1));*/// 1.先查redis缓存[List] 有数据返回即可Jedis jedis =newJedis("127.0.0.1");if(jedis!=null){// 1.1 有缓存数据 返回数据即可List<String> listRedis = jedis.lrange("nocvdata",0,33);List<NocvData> dataList =newArrayList<>();if(listRedis.size()>0){for(int i=0; i<listRedis.size(); i++){System.out.println("列表项为: "+listRedis.get(i));String s = listRedis.get(i);JSONObject jsonObject =JSONObject.parseObject(s);Object name = jsonObject.get("name");Object value = jsonObject.get("value");NocvData nocvData =newNocvData();
                nocvData.setName(String.valueOf(name));
                nocvData.setValue(Integer.parseInt(value.toString()));
                dataList.add(nocvData);}// 查询redis缓存数据库 返回的数据return dataList;}else{// 1.2 redis没有数据 查Mysql数据库,更新缓存List<NocvData> list = indexService.listOrderByIdLimit34();for(NocvData nocvData : list){
                jedis.lpush("nocvdata",JSONObject.toJSONString(nocvData));}// 返回的数据中的数据return list;}}// 默认没有连接redis的返回数据库【兼容有没有安装redis】List<NocvData> list = indexService.listOrderByIdLimit34();return list;}

二十、RestFul API风格架构

RESTful就是资源定位和资源操作的风格。不是标准也不是协议。

REST即Representational State Transfer的缩写,可译为"表现层状态转化”。RESTful最大的特点为:资源、统一接口、URI和无状态。

rest式的web服务是一种ROA(The Resource-Oriented Architecture)(面向资源的架构)

20.1 说引入前后的比较

在Restful之前的操作:
http://127.0.0.1/user/query/name=1 GET 根据用户id查询用户数据
http://127.0.0.1/user/save POST 新增用户
http://127.0.0.1/user/update POST 修改用户信息
http://127.0.0.1/user/delete/1 GET/POST 删除用户信息

RESTful用法:
http://127.0.0.1/user/1 GET 根据用户id查询用户数据
http://127.0.0.1/user POST 新增用户
http://127.0.0.1/user PUT 修改用户信息
http://127.0.0.1/user DELETE 删除用户信息

1.http请求地址的变化

@RestControllerpublicclassRESTfulController{//传统方式:http://localhost:8080/h1?a=1&b=11@RequestMapping("h1")publicStringtest1(int a,int b ,Model model){int rslt=a+b;
        model.addAttribute("msg","结果为:"+rslt);return"hello";}//RESTful:http://localhost:8080/h2/1/11@RequestMapping("h2/{a}/{b}")publicStringtest2(@PathVariableint a,@PathVariableint b ,Model model){int rslt=a+b;
        model.addAttribute("msg","结果为:"+rslt);return"hello";}}

2.url不变,使用method属性区分

@RequestMapping(value ="h3/{a}/{b}",method =RequestMethod.GET)publicStringtest3(@PathVariableint a,@PathVariableint b ,Model model){int rslt=a+b;
    model.addAttribute("msg","get结果为:"+rslt);return"hello";}@RequestMapping(value ="h3/{a}/{b}",method =RequestMethod.POST)publicStringtest4(@PathVariableint a,@PathVariableint b ,Model model){int rslt=a+b;
    model.addAttribute("msg","post结果为:"+rslt);return"hello";}

3.3、使用@GetMapping、@PostMapping

@GetMapping("h3/{a}/{b}")publicStringtest5(@PathVariableint a,@PathVariableint b ,Model model){int rslt=a+b;
    model.addAttribute("msg","get结果为:"+rslt);return"hello";}@PostMapping("h3/{a}/{b}")publicStringtest6(@PathVariableint a,@PathVariableint b ,Model model){int rslt=a+b;
    model.addAttribute("msg","post结果为:"+rslt);return"hello";}

总结

  • 使请求路径变得更加简洁
  • 传递、获取参数值更加方便,框架会自动进行类型转换
  • 通过路径变量@PathVariable的类型,可以约束访问参数。 - 若参数值与定义类型不匹配,则访问不到对应的方法,报错400错误的请求。
  • 安全,请求路径中直接传递参数值,并用斜线/分隔,不会暴露传递给方法的参数变量名。

二十一、请假审批功能设计

21.1 规矩正式的审批流设计

  1. 流程概念分析通常的流程一般分为2个层次来讲:流程、节点

流程就是某一具体的业务流程(如请假审批流程,财务报销审批流程),它由若干节点组成;
节点就是一种特定业务类型的封装,包括节点基本信息、参与者、时间限制、工作任务信息、触发事件、启动策略等信息

  1. 流程节点状态分析 首先我们分析业务工单状态,从操作人流程节点状态这三个角度分析
  2. 操作人串行: 上一处理人指定某一处理人时,其他拥有此步骤权限的操作员不可进行查看和操作,必须当前处理人处理完毕后,流程才能继续;并行: 由上一处理人指定固定多个处理人时,由任一员工处理即可,不分前后顺序,全部处理完成,进入下一步骤;

并签、或签的逻辑

  1. 执行动作 一套完整的审批流程参考钉钉审批事件信息的处理上包含如下5类:

申请:针对当前业务表单信息发起申请,开始审批流程的传递。分为2部分:由申请提交人手动发起申请、由程序自动判断满足触发规则的数据自动发起申请;另外还要注意的2点:是否允许提交人撤销(是、否)、记录编辑(不可编辑、管理员可编辑、管理员和审批人都可编辑 );
通过:当前步骤处理通过,进入下一步骤,若为末步骤,则流程处理完成;
退回:将步骤退回至上一步骤,即返回至上一处理人处,若为首步骤,则不进行退回;
否决:将步骤直接结束,执行结束动作拒绝活动,不再进行操作,或者回退至第一步骤;
撤回:若当前步骤已处理,下一处理人未处理的情况下可进行撤回操作。

节点流程描述

  1. 节点状态****提交人: 未提交、已提交、处理中、已结束。【枚举类型】处理人: 待处理、已处理【通过,驳回】。

21.2 请假流程分析

流程:学生发起请假申请---->老师审核----->院系审核【结束】

操作人:学生,老师,院系【角色】

节点:

学生【未提交、已提交、处理中、已结束】

老师、院系【待处理、已处理】

21.2 数据库设计

请假审批流程表【approval_process】
iduidreasonaddressdayphonenode_statuscreate_timeupdate_timeiduid请假理由去向天数电话节点状态创建时间修改时间
【node_status 列属性】

0:学生未提交

1:学生已提交

2:老师审核通过

3:老师驳回

4:院系审核通过

5:院系驳回

6:已结束

标签: spring boot java 后端

本文转载自: https://blog.csdn.net/wyn_365/article/details/125397649
版权归原作者 Coding路人王 所有, 如有侵权,请联系我们删除。

“基于Springboot的校园疫情管理系统”的评论:

还没有评论