1.list.jsp中初始化数据表格
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="/WEB-INF/layout/header.jsp" %>
<!-- Theme style -->
<link rel="stylesheet" href="static/dist/css/adminlte.min.css">
<link rel="stylesheet" href="static/plugins/bootstrap-table/bootstrap-table.min.css">
</head>
<body style="background-color: #e3e3e3;">
<div class="container-fluid mt-2">
<div class="content">
<div class="card">
<section class="card-header">
<h3 class="card-title">权限配置</h3>
<span class="float-right">
<a class="refresh-page text-dark" href="javascript:window.location.reload();" title="刷新当前页面">
<i class="fa fa-sync"></i>
</a>
</span>
</section>
<section class="card-body">
<div id="toolbar" class="input-group">
<button class="btn btn-success add-item mr-2">新增内容</button>
<button class="btn btn-danger del-check">删除选中</button>
</div>
<table id="bootstrapTable"></table>
</section>
</div>
</div>
</div>
<%@include file="/WEB-INF/layout/footer.jsp" %>
<script src="static/plugins/bootstrap-table/bootstrap-table.min.js"></script>
<script src="static/plugins/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
<script>
($=>{
$(()=>{
const table = $("#bootstrapTable"); //获取Dom对象
//初始化数据表格
table.bootstrapTable({
toolbar:"#toolbar", //指定自定义工具栏所在dom的筛选
buttonsClass:"secondary",//指定表格内按钮的默认样式btn-default.info,success,warning,danger,secondary
search:true, //开启搜索框
searchOnEnterKey:true, //当在搜索框输入内容后,按回车键进行搜索
pagination:true, //开启数据分页模式
sidePagination: "server", //client:在客户端进行分页(第一次请求就直接获取所有数据缓存在浏览器中,等待翻页使用js进行数据的切换)
//server:在服务器端实现分页(切换页面会重新请求服务器,服务器只会返回当前页面的数据,其余的不反应)
pageNumber:1,//默认加载的页面是第一页
pageSize:10, //默认每一页加载的数据条数
pageList:[10,20,50,100,200], // 用于切换每一页可加载的数据条数的待选项
sortName:"addTime", //默认参与排序的字段
sortOrder:"DESC", //默认排序方式
showColumns: true, // 显示工具按钮:用于控制正在显示的列,可以隐藏或显示某些列
showRefresh: true, // 显示刷新表格数据按钮
striped:true, // 数据各行显示不同背景色
cache:false, // 关闭ajax的请求缓存
method:"post", //ajax请求获取数据的请求方法
contentType:"application/json;charset=utf-8",
uniqueId:"id",//从服务器获取数据用于区分不同数据的关键字段--主键
url:"master/userRuleData.do", // 获取数据的ajax请求接口
queryParams(params){
return JSON.stringify($.extend({},params,{
extras:{} // 附加的额外请求参数
}));
},
columns:[{ //代表一列的数据
checkbox: true
},{ //代表一列的数据
field:"id", // 数据对象的属性名称,当前列展示当前属性的值
title:"编号",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
},{ //代表一列的数据
field:"pid", // 数据对象的属性名称,当前列展示当前属性的值
title:"父级权限",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
},{ //代表一列的数据
field:"ruleName", // 数据对象的属性名称,当前列展示当前属性的值
title:"权限名称",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
},{ //代表一列的数据
field:"ruleUrl", // 数据对象的属性名称,当前列展示当前属性的值
title:"访问路径",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
},{ //代表一列的数据
field:"ruleMethod", // 数据对象的属性名称,当前列展示当前属性的值
title:"方法",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
},{ //代表一列的数据
field:"ruleType", // 数据对象的属性名称,当前列展示当前属性的值
title:"类型",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
},{ //代表一列的数据
field:"ruleIcon", // 数据对象的属性名称,当前列展示当前属性的值
title:"图标",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
},{ //代表一列的数据
field:"ruleDes", // 数据对象的属性名称,当前列展示当前属性的值
title:"描述信息",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
},{ //代表一列的数据
field:"addTime", // 数据对象的属性名称,当前列展示当前属性的值
title:"添加时间",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
},{ //代表一列的数据
field:"updateTime", // 数据对象的属性名称,当前列展示当前属性的值
title:"修改时间",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
visible:false, //默认隐藏当前列
},{ //代表一列的数据
field:"delTime", // 数据对象的属性名称,当前列展示当前属性的值
title:"操作栏",//当前列的标题名称
align:"center", //left center right 单元格文字的对齐方式
events:"actionEvents", //指定单元格内事件处理的对象名称 必须保存
formatter(value,row,index){ //用于格式化显示内容
/*
value:当前字段对应的值
row:这一行的数据对象(ajax获取到的原始数据对象)
index:这一行的行号,每一页都从零开始
*/
return [
'<a class="text-info edit"><i class="fa fa-edit"></i></a>',
'<a class="text-danger delete"><i class="fa fa-trash"></i></a>'
].join("");
}
}], //是一个数组,用于定义列的名称,列的显示方式,列的显示字段
});
//设置筛选监听
$("#toolbar").on("click","button",function (e) {
//需要用到this对象所以不用箭头函数
//console.log(this,e.target,e.currentTarget);
const target= $(this); //获取被点击对象
if (target.hasClass("add-item")){
//新增新内容
} else if (target.hasClass("del-checked")){
//删除所选项
}
})
});
//设置表格内按钮的点击事件
window.actionEvents = {
"click .edit":function (e,value,row,index) {
//修改栏目操作
},
"click .delete":function (e,value,row,index) {
//删除当前栏目操作
},
}; //优先于bootstrapTable初始化前定义执行
})(jQuery);
</script>
</body>
</html>
2.db-service中添加三个接口
BaseService
UserRuleService
UserMasterService
3.新建BootsTableRequest.java
package peanut.bean;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import peanut.utils.PeanutUtils;
import java.util.Map;
import java.util.regex.Pattern;
@Getter
@Setter
@ToString
public class BootsTableRequest {
private Integer limit; //限制每次查询获取到的数据条数
private Integer offset; //从第几条数据开始查询
private String order; //排序方式ASC DESC
private String search; //参与排序的字段
private String sort; //参与搜索的字段
private Map<String,Object> extras; //额外的请求参数
public void setLimit(Integer limit){
this.limit = limit == null ? 1:limit > 1 ?limit :1; //限定最少查询一条数据
}
public void setOffset(Integer offset) {
this.offset = offset == null ? 0 : offset > 0 ? offset : 0;//限定最小值为0
}
public void setOrder(String order) {
this. order ="ASC".equalsIgnoreCase(order) ? "ASC" : "DESC";//排序方式只有两种
}
public void setSort(String sort) {
//参与排序的字段只允许出现数字 字母 下划线
this.sort = sort;
if (Pattern.matches("[0-9a-zA-Z_]+", sort)){
//如果是模糊查询需要拼接%%
this.sort =PeanutUtils.humpToLine(sort);
}else{
this.sort = null;
}
}
public void setSearch(String search) {
//只能输入数字 字母 下划线 中文字符
if (Pattern.matches("[0-9a-zA-Z_\u4300-\u9fa5]+", search)){
//如果是模糊查询需要拼接%%
this.search = "%"+ search + "%";
}else{
this.search = null;
}
}
}
4…新建BootsTableResponse.java
package peanut.bean;
import com.github.pagehelper.PageInfo;
import db.model.BaseModel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
@Getter
@Setter
@ToString
public class BootsTableResponse<T extends BaseModel> {
private long total;//记录了系统数据总条数
private List<T> rows;//返回的真实数据列表
public BootsTableResponse(List<T> rows) {
PageInfo<T> info = new PageInfo<>(rows);
this.total = info.getTotal();
this.rows = rows;
}
}
5.PeanutUtils.java中添加驼峰命名转化为下划线
private static final Pattern humpPattern = Pattern.compile("[A-Z]");
/**
* 驼峰命名转化为下划线
* @param str 待转换的字符串
* @return 转换后的字符串,如果原始字符串为空则返回NULL
*/
public static String humpToLine(String str){
if (StringUtils.isEmpty(str)) return null;
Matcher matcher = humpPattern.matcher(str);
StringBuffer buffer = new StringBuffer();
while (matcher.find()){
matcher.appendReplacement(buffer,"_"+matcher.group(0).toLowerCase());
}
matcher.appendTail(buffer);
return buffer.toString();
}
6.UserMasterServiceImpl.java中导包
package edu.td.peanut.service;
import db.mapper.UserMasterMapper;
import db.model.UserMaster;
import db.service.UserMasterService;
//import org.mybatis.dynamic.sql.select.QueryExpressionDSL;
//import org.mybatis.dynamic.sql.select.SelectDSLCompleter;
//import org.mybatis.dynamic.sql.select.SelectModel;
//import org.mybatis.dynamic.sql.util.Buildable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import peanut.bean.BootsTableRequest;
import peanut.bean.BootsTableResponse;
import static org.mybatis.dynamic.sql.SqlBuilder.*;
import static db.mapper.UserMasterDynamicSqlSupport.*;
@Service//声明当前类是用于数据查询的服务类
@Primary//当实现接口MasterService有多个实现类的时候,@Primary用于声明当前类是默认实现类,同一个接口的众多实现类中只有一个实现类可以添加@Primary注解
public class UserMasterServiceImpl implements UserMasterService {
@Autowired
private UserMasterMapper masterMapper;//初始化查询接口
@Override
public UserMaster findByPrimaryKey(Integer key){
if (key == null ||key < 1) return null;
return masterMapper.selectByPrimaryKey(key).orElse(null);
}
@Override
public BootsTableResponse<UserMaster> bootsData(BootsTableRequest tableRequest) {
return null;
}
@Override
public UserMaster findMasterByUsername(String userName){
if (StringUtils.isEmpty(userName))return null;
/*
return masterMapper.selectOne(new SelectDSLCompleter() {
@Override
public Buildable<SelectModel> apply(QueryExpressionDSL<SelectModel> selectModelQueryExpressionDSL) {
return selectModelQueryExpressionDSL
.where(UserMasterDynamicSqlSupport.delTime,isEqualTo(0))
.and(UserMasterDynamicSqlSupport.masterUsername,isEqualTo(userName));
}
}).orElse(null);
*/
return masterMapper.selectOne(entity->entity
.where(delTime,isEqualTo(0))
.and(masterUsername,isEqualTo(userName))
).orElse(null);
}
}
7.UserRuleServiceImpl中实现分页查询
package edu.td.peanut.service;
import com.github.pagehelper.PageHelper;
import db.mapper.UserRuleMapper;
import db.model.UserRule;
import db.service.UserRuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import peanut.bean.BootsTableRequest;
import peanut.bean.BootsTableResponse;
import static org.mybatis.dynamic.sql.SqlBuilder.*;
import static db.mapper.UserRuleDynamicSqlSupport.*;
@Service
@Primary
public class UserRuleServiceImpl implements UserRuleService {
@Autowired
private UserRuleMapper ruleMapper;
@Override
public UserRule findByPrimaryKey(Integer key) {
if (key == null || key < 1) return null;
return ruleMapper.selectByPrimaryKey(key).orElse(null);
}
@Override
public BootsTableResponse<UserRule> bootsData(BootsTableRequest tableRequest) {
if (tableRequest==null)return null;
//添加查询分页
PageHelper.offsetPage(tableRequest.getOffset(),tableRequest.getLimit());
return new BootsTableResponse<>(ruleMapper.select(entity->{
//添加花括号是为了实现多行编码
entity.where(delTime,isEqualTo(0))
.and(
ruleName,isLikeWhenPresent(tableRequest.getSearch()),
or(ruleMethod,isLikeWhenPresent(tableRequest.getSearch())),
or(ruleType,isLikeWhenPresent(tableRequest.getSearch())),
or(ruleUrl,isLikeWhenPresent(tableRequest.getSearch()))
);
//添加排序规则
if (tableRequest.getSort()==null) {
//使用默认排序规则
entity.orderBy(addTime.descending());
}else if ("ASC".equals(tableRequest.getOrder())) {
//升序排列
entity.orderBy(userRule.column(tableRequest.getSort()));
}else {
//降序排列
entity.orderBy(userRule.column(tableRequest.getSort()).descending());
}
return entity;
}));
}
}
8.添加debug
package edu.td.peanut.interceptor;
import db.model.UserMaster;
import db.service.UserMasterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 拦截器必须实现接口HandlerInterceptor
* preHandle 进入拦截器后优先执行判断的方法,如果返回值为True,则继续向后执行下一个拦截器或者执行Contrller,如果返回值是False则不再向后执行
* postHandle postHandle返回值是True且controller没有发生异常时执行的方法
* afterCompletion postHandle返回值是True且controller执行完成后(无论有无异常情况)执行的方法
*
* 登录拦截只需要实现preHandle方法
*/
public class MasterLoginInterceptor extends HandlerInterceptorAdapter {
@Value("${system.debug}")
private boolean debug;
@Autowired
private UserMasterService masterService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断登录 使用session
HttpSession session = request.getSession();
if (session.getAttribute("loginUser") == null){
if (debug){
UserMaster master = masterService.findByPrimaryKey(1);
session.setAttribute("loginUser",master);
return true;
}
//用户未登录-重定向用户到登录页面
response.sendRedirect("/master/login.do");
return false;
}
return true;
}
}
9.打印返回信息
project.properties中添加系统配置
新建log4j.properties
log4j.rootLogger=TRACE, stdout
log4j.logger.db.mapper=DEBUG
#定义打印的样式
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L -%m%n
#关闭插件内的log4j日志信息
log4j.logger.org=OFF
log4j.logger.com=OFF
10.添加org.apache.log4j.Logger
package edu.td.peanut.controller;
import org.apache.log4j.Logger;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
import peanut.utils.PeanutUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public abstract class PublicController {
protected Logger LOG = Logger.getLogger(this.getClass());
protected HttpServletRequest request; //controller与jsp页面共用一个request和session对象
protected HttpServletResponse response; //controller与jsp页面共用一个request和session对象
protected HttpSession session; //controller与jsp页面共用一个request和session对象
protected ModelMap modelMap; //用于将控制器内的数据传递到模板页面中
@ModelAttribute //在执行controller目标方法前被调用的方法,下列对象由Spring框架赋值
public void _init(HttpServletRequest request,HttpServletResponse response,HttpSession session,ModelMap modelMap){
this.request=request;
this.response=response;
this.session=session;
this.modelMap=modelMap;
}
protected void createToken(){
//生成随机字符串
String sessionToken = PeanutUtils.randomStr(12);
//记录sessionToken于session中
session.setAttribute("sessionToken",sessionToken);
//将sessionToken传递到页面
modelMap.addAttribute("token",sessionToken);
}
protected void createCode(){
//生成随机字符串
String codeKey = PeanutUtils.randomStr(6,"0123456789");
//记录codeKey于session中
session.setAttribute("codeKey",codeKey);
//将codeKey传递到页面
modelMap.addAttribute("code",codeKey);
}
protected boolean checkToken(String token){
if (StringUtils.isEmpty(token)) return false;
//从session中获取sessionToken
String sessionToken = (String) session.getAttribute("sessionToken");
session.removeAttribute("sessionToken");
return token.equals(sessionToken);
}
protected boolean checkCode(String code){
if (StringUtils.isEmpty(code)) return false;
String codeKey = (String) session.getAttribute("codeKey");
return code.equalsIgnoreCase(codeKey); //验证码的验证需要忽略字符大小写
}
}