1一、基于Springboot+MybatisPlus+Echarts+Mysql实现各种疫情地图及管理系统

功能继续更新中【BILIBILI.........】

【Coding路人王:从0到1】

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


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


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


基于Springboot的校园疫情管理系统_数据_04


基于Springboot的校园疫情管理系统_数据_05



基于Springboot的校园疫情管理系统_数据_06


基于Springboot的校园疫情管理系统_数据_07


基于Springboot的校园疫情管理系统_数据_08


基于Springboot的校园疫情管理系统_上传_09


基于Springboot的校园疫情管理系统_上传_10


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


基于Springboot的校园疫情管理系统_数据_12


基于Springboot的校园疫情管理系统_上传_13


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


基于Springboot的校园疫情管理系统_数据_15


基于Springboot的校园疫情管理系统_上传_16


基于Springboot的校园疫情管理系统_上传_17


基于Springboot的校园疫情管理系统_数据_18


基于Springboot的校园疫情管理系统_数据_19


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


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


基于Springboot的校园疫情管理系统_上传_22


基于Springboot的校园疫情管理系统_数据_23


功能继续更新中【BILIBILI.........】

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.快速使用

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8" />
  <!-- 引入刚刚下载的 ECharts 文件 -->
  <script src="echarts.js"></script>
 </head>
</html>
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8" />
  <title>ECharts</title>
  <!-- 引入刚刚下载的 ECharts 文件 -->
  <script src="echarts.js"></script>
 </head>
 <body>
  <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
  <div id="main" style="width: 600px;height:400px;"></div>
  <script type="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>

地图社区图例

1.3 创建数据库

DROP TABLE IF EXISTS `nocv_data`;
CREATE TABLE `nocv_data` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(255) DEFAULT NULL,
 `value` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of nocv_data
-- ----------------------------
INSERT INTO `nocv_data` VALUES ('1', '澳门', '95');
INSERT INTO `nocv_data` VALUES ('2', '香港', '35');
INSERT INTO `nocv_data` VALUES ('3', '台湾', '153');
INSERT INTO `nocv_data` VALUES ('4', '新疆', '56');
INSERT INTO `nocv_data` VALUES ('5', '宁夏', '26');
INSERT INTO `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})
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return  new PaginationInterceptor();
    }
}

7.2 layui返回的数据格式 DataView

@Data
@AllArgsConstructor
@NoArgsConstructor
public class DataView {
    private Integer code = 0;
    private String msg = "";
    private Long count = 0L;
    private Object data;

    public DataView(Long count,Object data){
        this.count = count;
        this.data = data;
    }

    public DataView(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)
<div class="layui-upload-drag" id="test10">
    <i class="layui-icon"></i>
    <p>点击上传,或将文件拖拽到此处</p>
    <div class="layui-hide" id="uploadDemoView">
        <hr>
        <img src="" 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)
@ResponseBody
public DataView uploadExcel(@RequestParam("file") MultipartFile file) {
   
    DataView dataView = new DataView();
    if (file.isEmpty()) {
        dataView.setMsg("文件为空");
        return dataView;
    }
    try {

        //根据路径获取这个操作excel的实例
        HSSFWorkbook wb = new HSSFWorkbook(file.getInputStream());
        HSSFSheet sheet = wb.getSheetAt(0);

        //实体类集合
        List<NocvData> listData = new ArrayList<>();
        HSSFRow row = null;

        //循环sesheet页中数据从第二行开始,第一行是标题
        for (int i = 0; i < sheet.getPhysicalNumberOfRows(); i++) {

            //获取每一行数据
            row = sheet.getRow(i);
            NocvData data = new NocvData();
            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")
    @ResponseBody
    public void excelOutportChina(HttpServletResponse response){
        response.setCharacterEncoding("UTF-8");
        List<NocvData> list = indexService.list();
        HSSFWorkbook wb = new HSSFWorkbook();
        //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="
                    + new String("中国疫情数据表".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

腾讯数据接口: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>
@Component
public class HttpUtils {

    @Bean
    public String getData() throws IOException {
        //请求参数设置
        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 = new HttpGet("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 {
                throw new HttpResponseException(statusCode, "响应异常");
            }
        } finally {
            if (response != null) {
                response.close();
            }
            if (request != null) {
                request.releaseConnection();
            }
            if (httpClient != null) {
                httpClient.close();
            }
        }

    }

}

10.2 对接定时程序数据入库

@Controller
public class TengXunApi {

    public static void main(String[] args) throws Exception {
        HttpUtils httpUtils = new HttpUtils();
        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实现省份数据的自动刷新


// 各个省份的数据
// 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")
public class CheckCodeServlet extends HttpServlet {
 public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, 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 = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
  
  //获取画笔
  Graphics g = image.getGraphics();
  //设置画笔颜色为灰色
  g.setColor(Color.GRAY);
  //填充图片
  g.fillRect(0,0, width,height);
  
  //产生4个随机验证码,12Ey
  String checkCode = getCheckCode();
  //将验证码放入HttpSession中
  request.getSession().setAttribute("CHECKCODE_SERVER",checkCode);
  
  //设置画笔颜色为黄色
  g.setColor(Color.YELLOW);
  //设置字体的小大
  g.setFont(new Font("黑体",Font.BOLD,24));
  //向图片上写入验证码
  g.drawString(checkCode,15,25);
  
  //将内存中的图片输出到浏览器
  //参数一:图片对象
  //参数二:图片的格式,如PNG,JPG,GIF
  //参数三:图片输出到哪里去
  ImageIO.write(image,"PNG",response.getOutputStream());
 }
 /**
  * 产生4位随机字符串 
  */
 private String getCheckCode() {
  String base = "0123456789ABCDEFGabcdefg";
  int size = base.length();
  Random r = new Random();
  StringBuffer sb = new StringBuffer();
  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();
 }
 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  this.doGet(request,response);
 }
}

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

12.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,即安全数据源。

12.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>

12.3 编写UserRealm

public class UserRealm extends AuthorizingRealm {

    @Autowired
    @Lazy
    private UserService userService;


    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }


    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //1.查询数据库
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",token.getPrincipal().toString());
        User user = userService.getOne(queryWrapper);
        if (null != user){
            //盐 时用户uuid生成的
            //ByteSource salt = ByteSource.Util.bytes(user.getSalt());
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
            return info;
        }
        return null;
    }

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        return null;
    }


}

12.4 ShiroConfig

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(value = { SecurityManager.class })
@ConfigurationProperties(prefix = "shiro")
@Data
public class ShiroAutoConfiguration {

    private static final String SHIRO_DIALECT = "shiroDialect";
    private static final String SHIRO_FILTER = "shiroFilter";
    // 加密方式
    private String hashAlgorithmName = "md5";
    // 散列次数
    private int hashIterations = 2;
    // 默认的登陆页面
    private String loginUrl = "/index.html";

    private String[] anonUrls; // 放行的路径
    private String logOutUrl; // 登出的地址
    private String[] authcUlrs; // 拦截的路径

    /**
     * 声明凭证匹配器
     */
    /*@Bean("credentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName(hashAlgorithmName);
        credentialsMatcher.setHashIterations(hashIterations);
        return credentialsMatcher;
    }*/

    /**
     * 声明userRealm
     */
    @Bean("userRealm")
    public UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        return userRealm;
    }

    /**
     * 配置SecurityManager
     */
    @Bean("securityManager")
    public SecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入userRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /**
     * 配置shiro的过滤器
     */
    @Bean(SHIRO_FILTER)
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        factoryBean.setSecurityManager(securityManager);
        // 设置未登陆的时要跳转的页面
        factoryBean.setLoginUrl(loginUrl);
        Map<String, String> filterChainDefinitionMap = new HashMap<>();
        // 设置放行的路径
        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=new HashMap<>();
        factoryBean.setFilters(filters);
        factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return factoryBean;
    }

    /**
     * 注册shiro的委托过滤器,相当于之前在web.xml里面配置的
     * @return
     */
    @Bean
    public FilterRegistrationBean<DelegatingFilterProxy> delegatingFilterProxy() {
        FilterRegistrationBean<DelegatingFilterProxy> filterRegistrationBean = new FilterRegistrationBean<DelegatingFilterProxy>();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName(SHIRO_FILTER);
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }

   /* 加入注解的使用,不加入这个注解不生效--开始 */
    /**
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
   /* 加入注解的使用,不加入这个注解不生效--结束 */

    /**
     * 这里是为了能在html页面引用shiro标签,上面两个函数必须添加,不然会报错
     *
     * @return
     */
    @Bean(name = SHIRO_DIALECT)
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

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

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

12.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:
    - /**

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

13.1 数据库设计

1.menu 菜单

id

pid

type

title

premission

icon

href

open

ordernum

available

1

0

menu

疫管理

menu:select


/menu

1

1

1

2

1

menu

饼图

menu:select


/pie

0

2

1

2.role 角色

id

name

remark

1

超级管理员

拥有所有权限

2

老师

查看新增修改

3

学生

查看

3.role_menu 关联关系表

rid

mid

1

1

1

2

4.user 用户表【老师,学生,管理员】

id

username

password

...

role_id

ban_ji_id

xue_yuan_id

teacher_id

1

admin

123456


1

1

1

0


5.ban_ji 班级表

id

name

xue_yuan_id

1

软件工程1班

1


6.xue_yuan学院表

id

name

1

计算机系

13.2 Java实体编写

十四、 菜单的增删改查

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


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

14.1 菜单的插入

TreeNode

package com.example.demo.controller;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author:
 * @Date: 2019/11/22 15:25
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TreeNode {

    private Integer id;
    @JsonProperty("parentId") //返回的json的名称 parentId ,为了确定层级关系
    private Integer pid;
    private String title;
    private String icon;
    private String href;
    private Boolean spread;
    private List<TreeNode> children = new ArrayList<TreeNode>();

    /**
     * 0为不选中  1为选中
     */
    private String checkArr="0";

    /**
     * 首页左边导航菜单的构造器
     */
    public TreeNode(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 是否展开
     */
    public TreeNode(Integer id, Integer pid, String title, Boolean spread) {
        this.id = id;
        this.pid = pid;
        this.title = title;
        this.spread = spread;
    }

    /**
     * 给角色分配权限的构造器
     */
    public TreeNode(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

public class TreeNodeBuilder {
    public static List<TreeNode> build(List<TreeNode> treeNodes, Integer topPid) {
        List<TreeNode> nodes = new ArrayList<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;
    }
}

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

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

14.3 菜单栏的编辑

menuService.updateById(menu);

14.4 菜单栏的删除

删除逻辑的时候:

@RequestMapping("/checkMenuHasChildrenNode")
    @ResponseBody
    public Map<String,Object> checkChildrenNode(Menu menu){
        Map<String,Object> map = new HashMap<>();
        QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
        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,直接删掉


真正的删除

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


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


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")
@ResponseBody
public DataView loadIndexLeftMenuJson(Menu permissionVo){
    //查询所有菜单
    QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
    List<Menu> list = menuService.list();

    List<TreeNode> treeNodes = new ArrayList<>();
    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(new TreeNode(id,pid,title,icon,href,spread));
    }

    //构造层级关系
    List<TreeNode> list2 = TreeNodeBuilder.build(treeNodes,0);
    return new DataView(list2);

}

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

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

1.引入role的静态页面

页面进行菜单的增加

2....

3...

4....

15.2 为角色分配菜单权限

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

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

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

rid  mid

1     1

1     2

select mid from role_menu where rid = ?

List 所具有的菜单栏权限

/**
 * 1.初始化下拉列表的权限
 */
@Autowired
private MenuService menuService;

@RequestMapping("/initPermissionByRoleId")
@ResponseBody
public DataView initPermissionByRoleId(Integer roleId){
    //查询所有菜单和权限
    QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
    List<Menu> allPermissions = menuService.list();
    //1.首先根据角色id查询出当前角色所拥有的所有菜单的ID和权限的ID
    List<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 = new ArrayList<>();
    }
    //3.构造List<TreeNode>
    List<TreeNode> nodes = new ArrayList<>();
    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(new TreeNode(allPermission.getId(),allPermission.getPid(),allPermission.getTitle(),spread,checkArr));
    }
    return new DataView(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}")
void deleteRoleByRid(Integer rid);

// 2.保存分配 角色 与 菜单 的关系
@Insert("insert into role_menu(rid,mid) values (#{rid},#{mid})")
void saveRoleMenu(Integer rid, Integer mid);


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

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


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 #{},#{}")


sql:


// 2.第二种办法

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

2.添加属性

// 非数据库列 班级名字
@TableField(exist = false)
private String banJiName;

// 非数据库列 学院名字
@TableField(exist = false)
private String xueYuanName;

// 非数据库列 老师名字
@TableField(exist = false)
private String teacherName;
// 2.第二种办法
// 查到所有的数据 1 1 1
for (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());
            }
        }


16.2 用户新增或编辑和删除


填充数据【下拉列表】

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

熙增编辑和删除


16.2 重置密码、修改密码


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



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


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


//controller
@RequestMapping("/initRoleByUserId")
    @ResponseBody
    public DataView initRoleByUserId(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);
        }
        return new DataView(Long.valueOf(listMaps.size()),listMaps);
    }



// mapper 根据用户id查询所有的角色
@Select("select rid from user_role where uid = #{id}")
List<Integer> queryUserRoleById(Integer id);

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

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

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

public void saveUserRole(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}")
void deleteRoleUserByUid(Integer uid);

//2. 保存分配的用户与角色之间的关系
@Insert("insert into user_role(uid,rid) values(#{uid},#{rid})")
void saveUserRole(Integer uid, Integer rid);


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


用户  : 角色  :菜单

id ---- List

role --- List



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


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

// 查询的所有菜单栏 按照条件查询【管理员,学生 老师【条件查询】】
QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
List<Menu> list = null;

// 1.取出session中的用户ID
User user = (User) session.getAttribute("user");
Integer userId = user.getId();
// 2.根据用户ID查询角色ID
List<Integer> currentUserRoleIds = roleService.queryUserRoleById(userId);
// 3.去重
Set<Integer> mids = new HashSet<>();
for (Integer rid : currentUserRoleIds){
    // 3.1.根据角色ID查询菜单ID
    List<Integer> permissionIds = roleService.queryAllPermissionByRid(rid);
    // 3.2.菜单栏ID和角色ID去重
    mids.addAll(permissionIds);
}
// 4.根据角色ID查询菜单ID
if (mids.size()>0){
    queryWrapper.in("id",mids);
    list = menuService.list(queryWrapper);
}