1一、基于Springboot+MybatisPlus+Echarts+Mysql实现各种疫情地图及管理系统
功能继续更新中【BILIBILI.........】
【Coding路人王:从0到1】
功能继续更新中【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);
}