高校宿舍管理系统
1.项目概况
提到宿舍,现在无论是学校,还是工厂,都是在熟悉不过的了,学生宿舍楼,职工教员工,职工宿舍楼等等,每一栋楼房都有很多的房间,每个宿舍分配了多少个床位,住着什么人,那些床位是空的,那些房间是空的,这些都需要人员去维护,曾经的我们,通过纸张登记,查看房间信息,房间床位信息,房间少的情况下还好,我们可以一个个进行比对,看登记信息去查看房间信息,床位信息,但是房间多,登记信息多的情况下,或者有人员进行变动,退房,换房的情况下,可就麻烦了我们的宿舍管理员了,我们的宿舍管理员,需要慢慢的进行查找,浪费了时间成本不说,还容易出错,新来的学生或员工需要等待很长时间才能进去入住,在加上现在楼层在逐渐的向高层发展,人员手动管理已经不能满足社会的需要,因而,社会需要更好的工具去管理这些枯燥的的宿舍管理工作,从而宿舍管理系统应用而生。
宿舍管理系统,解决了当前人力成本,时间成本问题,可以快速的定位到房间号,宿舍床位号,通过宿舍管理系统,我们只要根据系统的要求去操作,就可以分分钟的时间解决某个宿舍是否满员,可以方便的把人员和宿舍,床位进行绑定,就像我们的身份号似的,都是唯一的,查询起来很方便,相比较起传统的宿舍纸张登记来说,现代化的宿舍管理系统更快捷,更高效,更适应人们快捷化的生活。
现在高校的发展和体制不断的进行了完善,所以学校的服务也逐渐有了变化,对于学校宿舍的管理有了新形势的管理模式。学校根据自身的实际情况,对于学生宿舍生活管理的服务进行改善。有些学校因为管理工作的错误,影响了学校里学生的现况,甚至给整个学校造成了负面的影响,而且现在学校管理问题普遍存在。所以现在学校也在建设信息化管理系统,这种管理系统,可以为学校的管理工作和宿舍管理工作带来很好的效果。
2.项目目标
2.1新生分配与入住
在高校新生入住和分配上面,宿舍管理系统优势尽显,通常宿舍管理系统可通过新生预分配和新生入住两个模块实现对新生宿舍入住和宿舍分配的管理,这不但可保证分配入住的科学性,还能提升学生的入住效率,既减轻了人工分配的负担,也简化了学生的入住手续,更好地促进了学校和新生之间的和谐。
2.2宿舍的全面管理
通过对宿舍管理系统进行优化,高校宿舍的管理工作可细化成若干个系统的小模块,宿舍管理工作的细化在利于学生便捷高校入住的同时还让高校宿舍管理的智能化和信息化得到了进一步的彰显,通过对宿舍管理系统的全面优化,从整栋宿舍楼的整体管理到每一个床位的每一个角落都会实现整齐划一的管理。
楼->单元->层->宿舍->床位
2.3数据科学统计和分析
宿舍管理系统中记载着整栋宿舍楼的和全部入住学生的相关信息,通过其完善智能的信息统计功能,高校或者宿舍管理者可对每个楼层的入住情况、每个房间的入住信息数据实现一键查看,不用再通过一步步、一级级地层层询问来获取相关信息,这在很大程度上促进了宿舍信息的共享。
2.4精细化管理
宿舍考勤,管理员,多角色管理,一目了然。寄宿生管理,班级,楼层、宿舍号精确考勤。床位分配管理,楼、栋管理员都有相对应的权限。就寝时间段可设置,班主班可随时关注住宿学生情况及时报表发送至班主任手机。
2.5接口拓展功能
系统预留的接口可与宿舍其他的智能设施实现对接,如宿舍管理系统可连接宿舍的考勤门禁系统和迎新收费系统,可真正通过计算机管理系统的运用实现智能化校园的建设。
3.项目功能
3.1 项目角色(参与者)
-
超级管理员(拥有系统最高的权限)
-
宿管员(宿舍管理员:对宿舍进行管理,按楼宇进行管理)
-
学生(在线预选宿舍)
详见思维导图(xmind文件 )
3.1 项目功能
- 基础资料
- 用户管理(管理员、宿管员管理)
- 年级管理
- 学院、系、专业、班级(统称为组织机构)
- 学生管理
- 公告管理
- 宿舍管理
- 楼宇管理
- 楼层管理
- 宿舍管理(批量初始化、CURD)
- 床位管理(入住、调换、退宿)
- 预选宿舍
- 预选设置
- 预选操作
- 缺勤、请假管理
- 缺勤登记(影响床位状态)
- 请假登记(影响床位状态)
- 来访人员管理
- 登记来访信息
- 查询来访信息
- 报修管理
- 报修申请
- 状态审批
- 报修查询
- 报表/图表管理
- excel报表
- echarts图表显示
4.数据库设计
数据库设计软件PowerDesigner
安装:百度一下PowerDesigner的安装教程
PowerDesigner与Navicate的区别
- PowerDesigner重在可视化的设计
- Navicate就是一个数据库的可视化客户端工具
详见pdm图
5.项目技术路线
后端采用SpringBoot
前端采用LayUI(在开发前端期间不涉及任何前后端页面跳转,只通过json数据传输,前后数据校验采用JWT)
数据库采用MySQL(5.5至8.0)
开发IDE:Idea、webstorme
请求校验:JWT
该项目支持三个版本
- 前后端分离(SpringBoot+LayUI)(前后端来回切换台费劲,容易混淆)
- SpringBoot版本前后端不分离(快速开发调试)
- SSM版前后端不分离(需要将SpringBoot的配置重新按照xml的 格式配置一遍)
6.项目搭建
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
-
public:静态html文件
-
static:静态css、js等文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dormitory?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
server:
port: 8888
servlet:
context-path: /dormitory
7.整合Mybatis
在pom.xml中引入starter
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
配置Mapper目录
mybatis:
mapper-locations: com/yanzhen/mapper/*.xml
打印SQL输出到控制台
logging:
level:
com:
yanzhen:
mapper: debug
8.Mybatis分页
引入相关的jar
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
配置yml文件
pagehelper:
helper-dialect: mysql
public class Entity {
private Integer page;//页码数
private Integer limit = 10;//页面大小,默认的10
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getLimit() {
return limit;
}
public void setLimit(Integer limit) {
this.limit = limit;
}
}
9.代码生成器
entity
package ${data.packageName}modules.${data.moduleName}.entity;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
import com.yanzhen.utils.Entity;
import java.util.Date;
/**
* ${data.classAnnotation}
* @author ${data.author}
* @time ${data.createTime}
*/
public class ${data.entityName} extends Entity{
<#list data.datas as item>
/**
* ${item.annotation}
*/
<#if item.type == "String" && item.field !="id">
@Length(max = ${item.typeLength?c})
</#if>
private ${item.type} ${item.field};
</#list>
<#list data.datas as item>
public ${item.type} get${item.fieldUpper}() {
return ${item.field};
}
public void set${item.fieldUpper}(${item.type} ${item.field}) {
this.${item.field} = ${item.field};
}
</#list>
}
mapper
package ${data.packageName}.mapper;
import java.util.List;
import java.util.Map;
import ${data.packageName}.entity.${data.entityName};
public interface ${data.entityName}Mapper {
public int create(${data.entityName} ${data.objectName});
public int delete(Integer id);
public int update(${data.entityName} ${data.objectName});
public int updateSelective(${data.entityName} ${data.objectName});
public List<${data.entityName}> query(${data.entityName} ${data.objectName});
public ${data.entityName} detail(Integer id);
public int count(${data.entityName} ${data.objectName});
}
mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${data.packageName}.mapper.${data.entityName}Mapper">
<resultMap type="${data.packageName}.entity.${data.entityName}" id="${data.entityName}">
<id column="id" property="id"/>
<#list data.datas as item>
<#if item.column == "id"><#continue></#if>
<result column="${item.column}" property="${item.field}"/>
</#list>
</resultMap>
<insert id="create" keyProperty="id" useGeneratedKeys="true" parameterType="${data.packageName}.entity.${data.entityName}">
insert into ${data.tableName}(
<#list data.datas as item>
<#if item.column == "id"><#continue></#if>
${item.column}<#if item_index!=(data.datas?size-1)>,</#if>
</#list>
)values(
<#list data.datas as item>
<#if item.column == "id"><#continue></#if>
<#noparse>#{</#noparse>${item.field}<#noparse>}</#noparse><#if item_index!=(data.datas?size-1)>,</#if>
</#list>
)
</insert>
<select id="query" resultMap="${data.entityName}">
select * from ${data.tableName}
<include refid="${data.entityName}FindCriteria"/>
</select>
<select id="count" resultType="int">
select count(1) from ${data.tableName}
<include refid="${data.entityName}FindCriteria"/>
</select>
<select id="detail" resultMap="${data.entityName}">
select * from ${data.tableName} where id = <#noparse>#{id}</#noparse>
</select>
<delete id="delete">
delete from ${data.tableName} where id = <#noparse>#{id}</#noparse>
</delete>
<update id="update">
update ${data.tableName} set
<#list data.datas as item>
<#if item.column == "id"><#continue></#if>
${item.column}=<#noparse>#{</#noparse>${item.field}<#noparse>}</#noparse><#if item_index!=(data.datas?size-1)>,</#if>
</#list>
where id = <#noparse>#{id}</#noparse>
</update>
<update id="updateSelective">
update ${data.tableName} set
<#list data.datas as item>
<#if item.column == "id"><#continue></#if>
<#if item.type == "String" || item.type == "Date">
<if test="${item.field} != null and ${item.field} != ''"> ${item.column} = <#noparse>#{</#noparse>${item.field}<#noparse>}</#noparse></if><#if item_index!=(data.datas?size-1)>,</#if>
<#else>
<if test="${item.field} != null">${item.column} = <#noparse>#{</#noparse>${item.field}<#noparse>}</#noparse></if><#if item_index!=(data.datas?size-1)>,</#if>
</#if>
</#list>
where id = <#noparse>#{id}</#noparse>
</update>
<sql id="${data.entityName}FindCriteria">
<where>
<#list data.datas as item><#if item.type == "Date"><#continue></#if>
<#if item.type == "String">
<if test="${item.field} != null and ${item.field} != ''">and ${item.column} = <#noparse>#{</#noparse>${item.field}<#noparse>}</#noparse></if>
<#else>
<if test="${item.field} != null">and ${item.column} = <#noparse>#{</#noparse>${item.field}<#noparse>}</#noparse></if>
</#if>
</#list>
</where>
</sql>
</mapper>
service
package ${data.packageName}.service;
import com.yanzhen.mapper.${data.entityName}Mapper;
import com.yanzhen.entity.${data.entityName};
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ${data.entityName}Service {
@Autowired
private ${data.entityName}Mapper ${data.objectName}Mapper;
public int create(${data.entityName} ${data.objectName}) {
return ${data.objectName}Mapper.create(${data.objectName});
}
public int delete(Integer id) {
return ${data.objectName}Mapper.delete(id);
}
public int update(${data.entityName} ${data.objectName}) {
return ${data.objectName}Mapper.update(${data.objectName});
}
public int updateSelective(${data.entityName} ${data.objectName}) {
return ${data.objectName}Mapper.updateSelective(${data.objectName});
}
public PageInfo<${data.entityName}> query(${data.entityName} ${data.objectName}) {
if(${data.objectName} != null && ${data.objectName}.getPage() != null){
PageHelper.startPage(${data.objectName}.getPage(),${data.objectName}.getLimit());
}
return new PageInfo<${data.entityName}>(${data.objectName}Mapper.query(${data.objectName}));
}
public ${data.entityName} detail(Integer id) {
return ${data.objectName}Mapper.detail(id);
}
public int count(${data.entityName} ${data.objectName}) {
return ${data.objectName}Mapper.count(${data.objectName});
}
}
10.前端
Layui+Layuimini 单页版V2
Layuimini:基于Layui编写的一套最简洁、易用的后台框架模板(基本上与LayuiAdminPro功能上类似)
整合部分详见视频+代码
11.前后端分离项目
- ajax(前端实现类似拦截器这样的效果比较差,改动代码太多)
- axios(易用、简洁且高效的http库)
http://www.axios-js.com/zh-cn/docs/
Promise(不太了解的同学看一下即可)
特性
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
12.Layui集成Axios
//前后端分离的项目 数据传输的验证采用token
axios.interceptors.request.use(function(config){
config.headers.token="zzzzzzzzzzzzzzzz";//与后端交互的时候还需要处理
return config;
},function(error){
return Promise.reject(error);
});
axios.interceptors.response.use(function(response){
console.log(response.headers['content-type']);与后端交互的时候还需要处理
if(response.status==200){
const res = response.data;
if(res.code==0){ //后端返回的数据为0的时候为正常数据
return res;
}else{
return Promise.reject(res.msg);
}
}else{
return Promise.reject(response.statusText);
}
},function(error){
return Promise.reject(error);
});
13.登录页面与主页
- 前端项目的分包结构
- 掌握调整js、css、images文件路径
14.登录功能
form.on('submit(login)', function (data) {
data = data.field;
console.log(data);
axios({
method: 'post',
url: 'http://localhost:8888/dormitory/login',
data: data
}).then(function(data){
console.log(data);
}).catch(function(error){
console.log(data);
});
return false;
});
15.跨域处理
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
//使用CorsFilter解决跨域的问题
@Bean
public CorsFilter corsFilter(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
//允许跨域请求的域名
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedMethod("*");
//允许任何头部
corsConfiguration.addAllowedHeader("*");
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration);
CorsFilter corsFilter = new CorsFilter(urlBasedCorsConfigurationSource);
return corsFilter;
}
}
16.基于Token的身份验证
参考网页
https://www.cnblogs.com/Jason-Xiang/p/9808596.html
http://tool.chinaz.com/Tools/Base64.aspx
-
HTTP Basic Auth
HTTP Basic Auth简单点说明就是每次请求API时都提供用户的username和password,简言之,Basic Auth是配合RESTful API 使用的最简单的认证方式,只需提供用户名密码即可,但由于有把用户名密码暴露给第三方客户端的风险,在生产环境下被使用的越来越少。因此,在开发对外开放的RESTful API时,尽量避免采用HTTP Basic Auth。
-
Cookie Auth
Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象(jsessionId);通过客户端带上的Cookie对象来与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie会被删除。但可以通过修改cookie 的expire time使cookie在一定时间内有效。
-
基于 Token 的身份验证方法
使用基于 Token 的身份验证方法,大概的流程是这样的:
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端会签发一个 Token然后保存(缓存或者数据库),再把这个 Token 发送给客户端
- 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
- 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
- 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
Token机制相对于Cookie机制又有什么好处呢?
- 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输.
- 无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.
- 更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可.
- 去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.
- 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
- CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。
- 性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多.
- 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).
基于JWT(JSON WebToken)的Token认证机制实现
实现Token 验证的方法挺多的,还有一些标准规范,其中JSON Web Token(JWT)是一个非常轻巧的规范 。JWT 标准的 Token 有三个部分:
- header(头部)
- payload(数据)
- signature(签名)
中间用点分隔开,并且都会使用 Base64 编码,所以真正的 Token 看起来像这样:
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
Header
每个 JWT token 里面都有一个 header,也就是头部数据。里面包含了使用的算法,这个 JWT 是不是带签名的或者加密的。主要就是说明一下怎么处理这个 JWT token 。
头部里包含的东西可能会根据 JWT 的类型有所变化,比如一个加密的 JWT 里面要包含使用的加密的算法。唯一在头部里面要包含的是 alg 这个属性,如果是加密的 JWT,这个属性的值就是使用的签名或者解密用的算法。如果是未加密的 JWT,这个属性的值要设置成 none。
示例:
{ "alg": "HS256" }
意思是这个 JWT 用的算法是 HS256。上面的内容得用 base64url 的形式编码一下,所以就变成这样:
eyJhbGciOiJIUzI1NiJ9
Payload
Payload 里面是 Token 的具体内容,这些内容里面有一些是标准字段,你也可以添加其它需要的内容。下面是标准字段:
- iss:Issuer,发行者
- sub:Subject,主题
- aud:Audience,观众
- exp:Expiration time,过期时间
- nbf:Not before
- iat:Issued at,发行时间
- jti:JWT ID
比如下面这个 Payload ,用到了 iss 发行人,还有 exp 过期时间这两个标准字段。另外还有两个自定义的字段,一个是 name ,还有一个是 admin 。
{ "iss": "nihao.net", "exp": "1438955445", "name": "wanghao", "admin": true }
使用 base64url 编码以后就变成了这个样子:
eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ
Signature
JWT 的最后一部分是 Signature ,这部分内容有三个部分,先是用 Base64 编码的 header.payload ,再用加密算法加密一下,加密的时候要放进去一个 Secret ,这个相当于是一个密码,这个密码秘密地存储在服务端。
- header
- payload
- secret
const encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); HMACSHA256(encodedString, 'secret');
处理完成以后看起来像这样:
SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
最后这个在服务端生成并且要发送给客户端的 Token 看起来像这样:
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
客户端收到这个 Token 以后把它存储下来,下回向服务端发送请求的时候就带着这个 Token 。服务端收到这个 Token ,然后进行验证,通过以后就会返回给客户端想要的资源。
JWT的JAVA实现
Java中对JWT的支持可以考虑使用JJWT开源库;JJWT实现了JWT, JWS, JWE 和 JWA RFC规范
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
jwtutils通用类的封装
package com.yanzhen.framework.jwt; import com.yanzhen.entity.User; import com.yanzhen.framework.exception.MyException; import io.jsonwebtoken.*; import org.springframework.util.StringUtils; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * jwt的工具类 */ public class JWTUtil { public static String token = "token"; //秘钥 public static String jwt_secret="yanzhen@cms@cc596183363."; //过期时长 public static long jwt_expr = 3600*24*1000; //1、生成token public static String sign(User user){ //1、指定签名的时候使用的签名算法 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; //2、生成签发时间 long nowMillis = System.currentTimeMillis(); Date date = new Date(nowMillis); //3、创建playLoad的私有声明 Map<String,Object> claims = new HashMap<>(); claims.put("id",user.getId()); claims.put("userName",user.getUserName()); //4、生成签发人 String subject = user.getUserName(); JwtBuilder builder = Jwts.builder() .setClaims(claims) .setId(UUID.randomUUID().toString()) .setIssuedAt(date) .setSubject(subject) .signWith(signatureAlgorithm,jwt_secret); //设置过期时间 Date exprDate = new Date(nowMillis + jwt_expr); builder.setExpiration(exprDate); return builder.compact(); } //2、验证token public static boolean verify(String token){ try { if(StringUtils.isEmpty(token)){ return false; } Jwts.parser().setSigningKey(jwt_secret).parseClaimsJws(token).getBody(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } //3、获取用户信息 public static User getUser(String token){ try { if(StringUtils.isEmpty(token)){ throw new MyException("token不能为空"); } if(verify(token)){ Claims claims = Jwts.parser().setSigningKey(jwt_secret).parseClaimsJws(token).getBody(); User user = new User(); user.setId(Integer.parseInt(claims.get("id")+"")); user.setUserName(claims.get("userName")+""); return user; }else{ throw new MyException("超时或不合法token"); } } catch (Exception e) { throw new MyException("超时或不合法token"); } } public static void main(String[] args) { User user = new User(); user.setId(1); user.setUserName("admin"); System.out.println(sign(user)); } }
17.token应用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader(JWTUtil.token);
//根据token获取user对象
User user = JWTUtil.getUser(token);
if(user == null){
throw new MyException("超时或不合法的token");
}
String newToken = JWTUtil.sign(user);
response.setHeader(JWTUtil.token,newToken);
//很重要
response.setHeader("Access-Control-Expose-Headers", JWTUtil.token);
request.setAttribute("user",user);
return true;
}
axios.interceptors.response.use(function(response){
if(response.headers.token){
store.setToken(response.headers.token);
}
if(response.status==200){
const res = response.data;
if(res.code==200){ //后端返回的数据为200的时候为正常数据
return res;
}else{
return Promise.reject(res.msg);
}
}else{
return Promise.reject(response.statusText);
}
},function(error){
return Promise.reject(error);
});
18.用户管理
19.年级管理
20.组织机构
- ztree
- layui集成ztree
- 实现对ztree节点的curd
- 数据库设计方面满足tree型结构(parentId)
- 所有的叶子节点都是班级
layui集成ztree
layui.define(['jquery'],function(exports){
let jQuery = layui.jquery;
...
layui.link(layui.cache.base+'ztree/zTreeStyle.css')
exports("ztree",jQuery.fn.zTree)
});
layui.config({
base: rootPath + "lay-module/",
version: true
}).extend({
miniAdmin: "layuimini/miniAdmin", // layuimini后台扩展
miniMenu: "layuimini/miniMenu", // layuimini菜单扩展
miniPage: "layuimini/miniPage", // layuimini 单页扩展
miniTheme: "layuimini/miniTheme", // layuimini 主题扩展
miniTongji: "layuimini/miniTongji", // layuimini 统计扩展
step: 'step-lay/step', // 分步表单扩展
treetable: 'treetable-lay/treetable', //table树形扩展
tableSelect: 'tableSelect/tableSelect', // table选择扩展
iconPickerFa: 'iconPicker/iconPickerFa', // fa图标选择扩展
echarts: 'echarts/echarts', // echarts图表扩展
echartsTheme: 'echarts/echartsTheme', // echarts图表主题扩展
wangEditor: 'wangEditor/wangEditor', // wangEditor富文本扩展
layarea: 'layarea/layarea', // 省市县区三级联动下拉选择器
axios: 'axios/axios', // axios
store: 'store/store', // store
ztree: 'ztree/ztree', // axios
});
ztree操作
- 实现树形的CURD
- 树形采用异步还非异步
21.功能菜单权限设置
- 管理员设置
- 所有功能菜单、数据权限
- 宿管员设置
- 为每个宿管员单独设置功能菜单权限
- 拥有自己的数据权限
- 楼宇管理
- 楼层
- 宿舍
- 学生
- …
- 楼宇管理
22.学生管理
- 基础信息(管理员)