分享一个基于easyui前端框架开发的后台管理系统模板

这是博主自己在使用的一套easyui前端框架的后台管理系统模版,包含了后端的Java代码,已经实现了菜单控制、权限控制功能,可以直接拿来使用。

springboot + mybatis + mybatis-plus实现的增删查改完整项目,前端使用了easyui前端框架。icon-default.png?t=N7T8https://gitee.com/he-yunlin/easyui-crud.git

目录

功能介绍

一、菜单管理

菜单列表

角色-菜单列表

二、权限管理

权限列表

角色-权限列表

三、用户管理

用户列表

用户-角色列表

四、歌曲管理

歌曲列表

五、系统功能

系统设置

初始化权限

代码介绍

响应状态码

统一响应实体类

全局异常处理类

统一数据格式处理类

datagrid数据格式对象

获取用户登录信息的工具类

基于easyui的表格过滤插件功能实现

基础的分页器

基础的排序器


功能介绍

这是对本系统的一些简单的功能介绍。

一、菜单管理

菜单列表

点击表格头部工具栏的【添加】按钮,会添加一条模板数据

/**
 * 添加
 */
function insert() {
	requestUrl = "/menu/insert";

	ajaxPost(requestUrl, {
		type: 1,
		name: "xxx",
		url: "/html/xxx_list.html",
		icon: "icon-script"
	}, function (response) {
		showMsg(response.message);

		$("#menu_list").datagrid("reload");
	}, error);
}

修改功能是基于easyui的表格行内编辑完成的,鼠标选择一行数据,点击头部工具栏的【修改】按钮,会开启该行的编辑。

点击保存会向后台控制器提交修改后的数据,点击取消则只会取消行内编辑。

通过给表格添加结束编辑事件,当表格行结束编辑,也就是调用了endEdit方法时触发事件,会把data修改为修改后的行数据。

let data = {};

/**
 * 保存
 */
function save() {
	if (editingId != null) {
		let datagrid = $("#menu_list");

		// 只有结束编辑才能获取到最新的值
		datagrid.datagrid("endEdit", editingId);

		ajaxPost(requestUrl, data, function () {
			editingId = null;

			datagrid.datagrid("reload");
		}, error);
	}
}

$(document).ready(function() {    
    $("#menu_list").datagrid({
		url: "/menu/selectByPage",
		method: "get",
		height: 680,
		fitColumns: true,
		pagination: true,
		onAfterEdit: function (index, row, changes) {
			data = {
				id: row.id,
				type: row.type,
				parentId: row.parentId,
				url: changes.url ? changes.url : row.url,
				name: changes.name ? changes.name : row.name,
				icon: changes.icon ? changes.icon : row.icon
			};
		},
        .....
    };

};

删除功能比较简单,就不介绍了~

角色-菜单列表

就是对角色的菜单进行管理,目前只是基于父级菜单实现,只需要给角色添加对应的父类菜单即可让该角色获得该菜单下的所有子菜单的权限。

二、权限管理

权限列表

对系统资源权限(也就是控制器接口权限)进行管理

父级权限的编号格式为为服务名_控制器名,子级权限的编号为服务名_控制器名_方法名。

权限初始化功能:一键自动完成权限的初始化功能,会先删除原来的权限,然后扫描控制器类的包,获取所有控制器接口信息,并保存到数据库。

涉及的后端代码

    @Override
    public void resources() throws ClassNotFoundException {
        // 删除原来的权限
        permissionMapper.delete(null);

        // 扫描路径
        String basePackage = "cn.edu.sgu.www.controller";
        // 获取扫描结果
        List<Permission> permissions = resourceScanner.scan(basePackage);

        for (Permission permission : permissions) {
            permissionMapper.insert(permission);
        }
    }

扫描工具类的代码

package cn.edu.sgu.www.util;

import cn.edu.sgu.www.EasyuiCrud;
import cn.edu.sgu.www.annotation.AnonymityAccess;
import cn.edu.sgu.www.entity.Permission;
import cn.edu.sgu.www.enums.RequestMethod;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 接口资源扫描工具类
 * @author heyunlin
 * @version 1.0
 */
@Component
public class ResourceScanner {

    /**
     * 服务名
     */
    @Value("${spring.application.name}")
    private String SERVICE_NAME;

    private static List<String> classPaths = new ArrayList<>();
    private static final List<Permission> resources = new ArrayList<>();

    /**
     * 扫描controller包下的目录,生成权限
     * @param basePackage controller包
     * @return List<Permission>
     * @throws ClassNotFoundException 类找不到时抛出异常
     */
    public List<Permission> scan(String basePackage) throws ClassNotFoundException {
        // 删除掉上一次的数据
        if (!resources.isEmpty()) {
            resources.clear();
        }
        if (!classPaths.isEmpty()) {
            classPaths.clear();
        }

        String classpath = EasyuiCrud.class.getResource("/").getPath();
        String searchPath = classpath + basePackage.replace(".", "/");

        classpath = classpath.replaceFirst("/", "");
        classPaths = getClassPaths(new File(searchPath));

        for(String classPath : classPaths) {
            // 得到类的全限定名
            classPath = classPath.replace(classpath.replace("/", "\\")
                    .replaceFirst("\\\\", ""), "")
                    .replace("\\", ".")
                    .replace(".class", "");
            classpath = classPath.substring(classPath.indexOf(basePackage));

            // 通过反射获取类的信息
            Class<?> cls = Class.forName(classpath);

            // 获取标注在类上的@RequestMapping注解
            RequestMapping requestMapping = cls.getAnnotation(RequestMapping.class);

            // 构建父权限
            Permission parent = new Permission();
            // 控制器类上的路径
            String prefix = "";

            if(requestMapping != null) {
                // path或者value
                prefix = requestMapping.value().length > 0 ? requestMapping.value()[0] : requestMapping.path()[0];

                parent.setType(0);
                parent.setUrl(prefix);
                parent.setId(SERVICE_NAME + "_" + cls.getSimpleName());

                // 设置name
                if (cls.isAnnotationPresent(Api.class)) {
                    Api api = cls.getAnnotation(Api.class);

                    if (api != null) {
                        // 类的接口文档@Api注解的tags属性值
                        String name = api.tags()[0];

                        parent.setName(name);
                    }
                }

                resources.add(parent);
            }

            Method[] methods = cls.getDeclaredMethods();

            for (Method method : methods) {
                getClassAnnotation(method, prefix, cls.getSimpleName(), parent.getId());
            }
        }

        return resources;
    }

    /**
     * 得到类上面的注解信息
     * @param method Method
     * @param prefix String 控制器类上@RequestMapping注解指定的路径
     * @param controllerName 控制器名称
     * @param parentId String 父级权限ID
     */
    public void getClassAnnotation(Method method, String prefix, String controllerName, String parentId) {
        // 构建子权限
        Permission permission = new Permission();
        String url = null;

        // 获取url
        if (method.isAnnotationPresent(RequestMapping.class)) {
            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
            url = prefix + (requestMapping.value().length > 0 ? requestMapping.value()[0] : requestMapping.path()[0]);
            String requestMethod = requestMapping.method().length > 0 ? requestMapping.method()[0].name() : "get";

            permission.setMethod(RequestMethod.getValueByName(requestMethod));
        } else if (method.isAnnotationPresent(GetMapping.class)) {
            GetMapping getMapping = method.getAnnotation(GetMapping.class);
            url = prefix + getMapping.value()[0];

            permission.setMethod(RequestMethod.GET.getValue());
        } else if (method.isAnnotationPresent(PostMapping.class)) {
            PostMapping postMapping = method.getAnnotation(PostMapping.class);
            url = prefix + postMapping.value()[0];

            permission.setMethod(RequestMethod.POST.getValue());
        }

        // 处理URL
        if(url != null && url.endsWith("/")) {
            url = url.substring(0, url.length() - 1);
        }
        permission.setUrl(url);

        // 设置value
        if (method.isAnnotationPresent(ApiOperation.class)) {
            ApiOperation operation = method.getAnnotation(ApiOperation.class);

            if (operation != null) {
                String name = operation.value();

                permission.setName(name);
            }
        }
        // 默认值0
        permission.setAnonymity(0);

        if (method.isAnnotationPresent(AnonymityAccess.class)) {
            AnonymityAccess annotation = method.getAnnotation(AnonymityAccess.class);

            if (annotation != null) {
                permission.setAnonymity(annotation.value() ? 1 : 0);
            }
        }

        permission.setType(1);
        permission.setParentId(parentId);
        permission.setId(SERVICE_NAME + "_" + controllerName + "_" + method.getName());

        resources.add(permission);
    }

    private List<String> getClassPaths(File path) {
        if (path.isDirectory()) {
            File[] files = path.listFiles();

            if (files != null) {
                for (File file : files) {
                    getClassPaths(file);
                }
            }
        } else {
            if (path.getName().endsWith(".class")) {
                classPaths.add(path.getPath());
            }
        }

        return classPaths;
    }

}

角色-权限列表

角色权限的维护,包括简单的增删改和基于easyui树实现的授权功能,以及超管账户的权限初始化功能。

授权功能:通过简单的复选框勾选/取消勾选来给角色分配权限

权限初始化功能:其实非常简单,就是查询所有的资源权限,然后分配给超管用户。

    @Override
    public void init(String userId) {
        // 删除用当前户所有角色的权限
        rolePermissionMapper.deleteByUserId(userId);

        // 查询全部子权限
        List<Permission> list = permissionMapper.selectByType(PermissionType.ZQX.getValue());

        list.forEach(permission -> {
            RolePermission rolePermission = new RolePermission();

            rolePermission.setId(null);
            rolePermission.setRoleId(1);
            rolePermission.setPermissionId(permission.getId());

            rolePermissionMapper.insert(rolePermission);
        });
    }

三、用户管理

因为这个部分的功能很简单,只有简单的crud,不做过多介绍。

用户列表

用户-角色列表

四、歌曲管理

歌曲列表

歌曲的维护、歌单导入/导出功能。

五、系统功能

系统设置

鼠标移动到右上方的下拉菜单,点击【系统设置】打开系统设置窗口。

修改密码功能

 对密码进行加密存储

    @Override
	public void updatePassword(UserPassUpdateDTO userPassUpdateDTO) {
		// 用户名
		String username = userPassUpdateDTO.getUsername();
		// 旧密码
		String oldPass = userPassUpdateDTO.getOldPass();
		// 新密码
		String password = userPassUpdateDTO.getPassword();

		// 验证两次输入的密码是否相等
		if (password.equals(userPassUpdateDTO.getRePass())) {
			// 查询用户信息
			String encodedPassword = selectByUsername(username).getPassword();

			// 验证输入的旧密码是否正确
			if (PasswordEncoder.matches(oldPass, encodedPassword)) {
				UpdateWrapper<User> wrapper = new UpdateWrapper<>();

				wrapper.eq("username", username);
				wrapper.set("password", PasswordEncoder.encode(password));

				userMapper.update(wrapper.getEntity(), wrapper);
			} else {
				throw new GlobalException(ResponseCode.FORBIDDEN, "输入的密码不正确");
			}
		} else {
			throw new GlobalException(ResponseCode.FORBIDDEN, "两次输入的密码不一样");
		}
	}

菜单控制功能:就是控制左侧菜单的显示,勾选/取消勾选对应的菜单,然后点击窗口右下角的【确定】按钮提交修改。

初始化权限

这个按钮的功能和权限列表的【初始化】按钮是一样的。

代码介绍

前面已经对这个系统做了一些简单的介绍,接下来介绍一下博主经过多次实践产出的一部分公共的Java代码,可以直接使用。

响应状态码

在枚举中自定义了几种响应状态码

package cn.edu.sgu.www.restful;

/**
 * 响应状态码
 * @author heyunlin
 * @version 1.0
 */
public enum ResponseCode {

    /**
     * 请求成功
     */
    OK(200),
    /**
     * 失败的请求
     */
    BAD_REQUEST(400),
    /**
     * 未授权
     */
    UNAUTHORIZED(401),
    /**
     * 禁止访问
     */
    FORBIDDEN(403),
    /**
     * 找不到
     */
    NOT_FOUND(404),
    /**
     * 不可访问
     */
    NOT_ACCEPTABLE(406),
    /**
     * 冲突
     */
    CONFLICT(409),
    /**
     * 服务器发生异常
     */
    ERROR(500);

    private final Integer value;

    ResponseCode(Integer value) {
        this.value = value;
    }

    public Integer getValue() {
        return value;
    }

}

统一响应实体类

包含提示信息、响应数据和响应状态码的web响应实体类。

package cn.edu.sgu.www.restful;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;

import java.io.Serializable;

/**
 * 响应实体类
 * @param <T>
 * @author heyunlin
 * @version 1.0
 */
@Data
public class JsonResult<T> implements Serializable {
    private static final long serialVersionUID = 18L;

    /**
     * 响应数据
     */
    private T data;

    /**
     * 响应状态码
     */
    private Integer code;

    /**
     * 响应提示信息
     */
    private String message;

    /**
     * 成功提示
     */
    private static final String successMessage = "请求成功";

    public static JsonResult<Void> success() {
        return success(successMessage);
    }

    public static JsonResult<Void> success(String message) {
        return success(message, null);
    }

    public static <T> JsonResult<T> success(String message, T data) {
        JsonResult<T> jsonResult = new JsonResult<>();

        jsonResult.setCode(ResponseCode.OK.getValue());
        jsonResult.setMessage(message);
        jsonResult.setData(data);

        return jsonResult;
    }

    public static JsonResult<Void> error(String message) {
        JsonResult<Void> jsonResult = new JsonResult<>();

        jsonResult.setCode(ResponseCode.ERROR.getValue());
        jsonResult.setMessage(message);

        return jsonResult;
    }

    public static JsonResult<Void> error(ResponseCode responseCode, Throwable e) {
        return error(responseCode, e.getMessage() != null ? e.getMessage() : "系统发生异常,请联系管理员!");
    }

    public static JsonResult<Void> error(ResponseCode responseCode, String message) {
        JsonResult<Void> jsonResult = new JsonResult<>();

        jsonResult.setCode(responseCode.getValue());
        jsonResult.setMessage(message);

        return jsonResult;
    }

    public static <T> JsonResult<JsonPage<T>> restPage(Page<T> page) {
        JsonPage<T> jsonPage = JsonPage.restPage(page);

        return success(successMessage, jsonPage);
    }

}

全局异常处理类

package cn.edu.sgu.www.restful.handler;

import cn.edu.sgu.www.exception.GlobalException;
import cn.edu.sgu.www.restful.JsonResult;
import cn.edu.sgu.www.restful.ResponseCode;
import cn.edu.sgu.www.util.UserUtils;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * 全局异常处理类
 * @author heyunlin
 * @version 1.0
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理GlobalException
     * @param e GlobalException
     * @return JsonResult<Void>
     */
    @ExceptionHandler
    public JsonResult<Void> handleGlobalException(GlobalException e) {
        printMessage(e);

        HttpServletResponse response = UserUtils.getResponse();
        response.setStatus(e.getResponseCode().getValue());

        return JsonResult.error(e.getResponseCode(), e);
    }

    /**
     * 处理BindException
     * @param e BindException
     * @return JsonResult<Void>
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public JsonResult<Void> handleBindException(BindException e) {
        printMessage(e);

        BindingResult bindingResult = e.getBindingResult();
        FieldError fieldError = bindingResult.getFieldError();
        String defaultMessage = Objects.requireNonNull(fieldError).getDefaultMessage();

        return JsonResult.error(ResponseCode.BAD_REQUEST, defaultMessage);
    }

    /**
     * 处理Exception
     * @param e Exception
     * @return JsonResult<Void>
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public JsonResult<Void> handleException(Exception e) {
        printMessage(e);

        return JsonResult.error(ResponseCode.ERROR, e);
    }

    private void printMessage(Exception e) {
        e.printStackTrace();
    }

}

统一数据格式处理类

这个类需要注意的是,knife4j的接口路径不能被处理,否则接口文档的页面内容不能正常显示。

# 配置统一数据格式返回处理类忽略的路径
response:
  ignore:
    - /error
    - /v2/api-docs
    - /swagger-resources
package cn.edu.sgu.www.restful.handler;

import cn.edu.sgu.www.config.property.ResponseProperties;
import cn.edu.sgu.www.restful.JsonResult;
import cn.edu.sgu.www.util.UserUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.List;

/**
 * 统一数据格式返回处理类
 * @author heyunlin
 * @version 1.0
 */
@Slf4j
@RestControllerAdvice
public class GlobalResponseHandler implements ResponseBodyAdvice {

    private final List<String> ignore;

    @Autowired
    public GlobalResponseHandler(ResponseProperties responseProperties) {
        ignore = responseProperties.getIgnore();
    }

    @Override
    public boolean supports(MethodParameter parameter, Class type) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter parameter, MediaType mediaType, Class type
            , ServerHttpRequest request, ServerHttpResponse response) {
        // 返回值类型为JsonResult,则直接返回
        if (body instanceof JsonResult) {
            return body;
        }

        // 忽略的请求地址
        String requestURI = UserUtils.getRequest().getRequestURI();

        if (ignore.contains(requestURI)) {
            return body;
        }

        log.debug("接口{}的返回值为:{}", requestURI, body.toString());

        // 将返回值类型修改为JsonResult
        JsonResult<Object> jsonResult = JsonResult.success(null, body);

        if (body instanceof String) {
            return JSON.toJSONString(jsonResult);
        }

        return jsonResult;
    }

}

datagrid数据格式对象

基于easyui的datagrid组件要求返回的数据格式,封装成的对象。

package cn.edu.sgu.www.restful;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;

import java.io.Serializable;
import java.util.List;

/**
 * easyui datagrid数据格式对象
 * @param <T>
 * @author heyunlin
 * @version 1.0
 */
@Data
public class JsonPage<T> implements Serializable {
    private static final long serialVersionUID = 18L;

    /**
     * 总记录数
     */
    private Long total;

    /**
     * 查询结果
     */
    private List<T> rows;

    /**
     * 页脚数据
     */
    private T footer;

    public static <T> JsonPage<T> restPage(Page<T> page) {
        JsonPage<T> jsonPage = new JsonPage<>();

        jsonPage.setTotal(page.getTotal());
        jsonPage.setRows(page.getRecords());

        return jsonPage;
    }

}

获取用户登录信息的工具类

基于shiro的获取用户登录信息的工具类

package cn.edu.sgu.www.util;

import cn.edu.sgu.www.entity.User;
import cn.edu.sgu.www.exception.GlobalException;
import cn.edu.sgu.www.restful.ResponseCode;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 获取用户信息的工具类
 * @author heyunlin
 * @version 1.0
 */
public class UserUtils {

    /**
     * 得到Subject对象
     * @return Subject
     */
    public static Subject getSubject() {
        return SecurityUtils.getSubject();
    }

    /**
     * 获取登录的用户信息
     * @return User
     */
    public static User getUserInfo() {
        Object object =  getSubject().getPrincipal();

        if (object == null) {
            throw new GlobalException(ResponseCode.BAD_REQUEST, "获取登录信息失败,当前没有用户登录。");
        }

        return (User) object;
    }

    /**
     * 获取登录用户的ID
     * @return String
     */
    public static String getUserId() {
        return getUserInfo().getId();
    }

    /**
     * 获取登录的用户名
     * @return String
     */
    public static String getLoginUsername() {
        return getUserInfo().getUsername();
    }

    public static HttpServletRequest getRequest() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();

        if (attributes != null ) {
            return ((ServletRequestAttributes) attributes).getRequest();
        }

        throw new GlobalException(ResponseCode.ERROR, "获取request对象失败");
    }

    public static HttpServletResponse getResponse() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();

        if (attributes != null ) {
            return ((ServletRequestAttributes) attributes).getResponse();
        }

        throw new GlobalException(ResponseCode.ERROR, "获取response对象失败");
    }

}

基于easyui的表格过滤插件功能实现

package cn.edu.sgu.www.base;

import lombok.Data;

import java.io.Serializable;

/**
 * 过滤规则
 * @author heyunlin
 * @version 1.0
 */
@Data
public class FilterRule implements Serializable {
    private static final long serialVersionUID = 18L;

    /**
     * 字段名
     */
    private String field;

    /**
     * 比较符
     */
    private Operator op;

    /**
     * 字段值
     */
    private String value;
}
package cn.edu.sgu.www.base;

/**
 * 比较符
 * @author heyunlin
 * @version 1.0
 */
public enum Operator {
    /**
     * 包含
     */
    contains,
    /**
     * 等于
     */
    equal,
    /**
     * 不等于
     */
    notequal,
    /**
     * 以...开始
     */
    beginwith,
    /**
     * 以...结尾
     */
    endwith,
    /**
     * 小于
     */
    less,
    /**
     * 小于或等于
     */
    lessorequal,
    /**
     * 大于
     */
    greater,
    /**
     * 大于或等于
     */
    greaterorequal
}

基础的分页器

提供了分页的功能,需要分页功能的接口的参数类型只需要集成该类即可,将自动获得分页功能。

package cn.edu.sgu.www.base;

import cn.edu.sgu.www.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;
import java.util.List;

/**
 * 基础分页参数对象,包含页数和每页的记录数
 * @author heyunlin
 * @version 1.0
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class Pager<T> extends Sorter implements Serializable {
    private static final long serialVersionUID = 18L;

    /**
     * 页数
     */
    private Integer page = 1;

    /**
     * 每页记录数
     */
    private Integer rows = 10;

    /**
     * 过滤规则
     */
    private List<FilterRule> filterRules;

    /**
     * 根据Pager创建Page对象
     * @param pager Pager
     * @return Page
     */
    public static <T> Page<T> ofPage(Pager<T> pager) {
        return new Page<>(pager.getPage(), pager.getRows());
    }

    /**
     * 根据Pager创建QueryWrapper对象
     * @param pager Pager
     * @return QueryWrapper<T>
     */
    public static <T> QueryWrapper<T> getQueryWrapper(Pager<T> pager, boolean enableSort) {
        QueryWrapper<T> wrapper = new QueryWrapper<>();

        List<FilterRule> filterRules = pager.getFilterRules();

        if (filterRules != null && !filterRules.isEmpty()) {
            for (FilterRule filterRule : filterRules) {
                // 字段名:转为小写字母+下划线的格式
                String field = StringUtils.toLower(filterRule.getField());
                // 字段值
                String value = filterRule.getValue();

                if (StringUtils.isNotEmpty(value)) {
                    switch (filterRule.getOp()) {
                        case less:
                            wrapper.lt(field, value);
                            break;
                        case equal:
                            wrapper.eq(field, value);
                            break;
                        case greater:
                            wrapper.gt(field, value);
                            break;
                        case notequal:
                            wrapper.ne(field, value);
                            break;
                        case lessorequal:
                            wrapper.le(field, value);
                            break;
                        case greaterorequal:
                            wrapper.ge(field, value);
                            break;
                        case beginwith:
                            wrapper.likeLeft(field, value);
                            break;
                        case endwith:
                            wrapper.likeRight(field, value);
                            break;
                        case contains:
                            wrapper.like(field, value);
                            break;
                        default:
                            break;
                    }
                }
            }
        }

        if (enableSort) {
            // 得到order by语句
            String statement = getOrderByStatement(pager);

            if (StringUtils.isNotEmpty(statement)) {
                wrapper.last(statement);
            }
        }

        return wrapper;
    }

}

基础的排序器

因为前端的基础分页器Pager已经继承了Sorter,所以开启了分页功能后自动获得排序功能。

package cn.edu.sgu.www.base;

import cn.edu.sgu.www.exception.GlobalException;
import cn.edu.sgu.www.restful.ResponseCode;
import cn.edu.sgu.www.util.StringUtils;
import lombok.Data;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * 基础排序对象,包含排序字段和排序方式
 * @author heyunlin
 * @version 1.0
 */
@Data
public class Sorter implements Serializable {
    private static final long serialVersionUID = 18L;

    /**
     * 空字符串
     */
    private static final String EMPTY_STR = "";

    /**
     * 分割符
     */
    private static final String SEPARATOR = ",";

    /**
     * 排序方式
     */
    private static final List<String> ORDER_STYLES = new ArrayList<>(2);

    static {
        ORDER_STYLES.add("asc");
        ORDER_STYLES.add("desc");
    }

    /**
     * 排序字段
     */
    private String sort;

    /**
     * 排序方式:asc/desc
     */
    private String order;

    /**
     * 根据查询条件拼接得到order by语句
     * @param sorter 分页查询条件
     * @return String
     */
    public static String getStatement(Sorter sorter) {
        String sort;
        String sortColumn = sorter.getSort();

        // 处理排序字段
        String[] sortArray = {};

        if (StringUtils.isNotEmpty(sortColumn)) {
            // 驼峰命名转为下划线
            sort = StringUtils.toLower(sortColumn);

            if (sort.contains(SEPARATOR)) {
                sortArray = sort.split(SEPARATOR);
            }
        } else {
            return EMPTY_STR;
        }

        // 处理排序方式
        String[] orderArray = {};
        String order = sorter.getOrder();

        if (StringUtils.isNotEmpty(order)) {
            if (order.contains(SEPARATOR)) {
                orderArray = order.split(SEPARATOR);
            }
        } else {
            return EMPTY_STR;
        }

        StringBuilder statement = new StringBuilder();

        if (sortArray.length > 0 && orderArray.length > 0) {
            int length = sortArray.length;

            for (int i = 0; i < length; i++) {
                String pagerSort = sortArray[i];
                String pagerOrder = orderArray[i];

                boolean result = validate(pagerSort, pagerOrder);

                if (result) {
                    statement.append(pagerSort);
                    statement.append(" ");
                    statement.append(pagerOrder);

                    if (i < length - 1 ) {
                        statement.append(", ");
                    }
                }
            }
        } else {
            // " #{sort} #{order}“
            statement.append(sort);
            statement.append(" ");
            statement.append(order);
        }

        return statement.toString();
    }

    /**
     * 根据查询条件拼接得到order by语句
     * @param sorter 分页查询条件
     * @return String
     */
    public static String getOrderByStatement(Sorter sorter) {
        String statement = getStatement(sorter);

        if (StringUtils.isNotEmpty(statement)) {
            return " order by " + statement;
        } else {
            return EMPTY_STR;
        }
    }

    /**
     * 往Pager的排序字段中添加排序
     * @param pager Pager Pager对象
     * @param sort String 排序字段
     * @param order String 排序方式
     * @return Pager<?> 返回重新设置排序字段和排序方式后的Pager对象
     */
    public static Pager<?> append(Pager<?> pager, String sort, String order) {
        boolean result = validatePager(pager);

        if (result) {
            String pagerSort = pager.getSort();
            String pagerOrder = pager.getOrder();

            pager.setSort(pagerSort.concat(SEPARATOR).concat(sort));
            pager.setOrder(pagerOrder.concat(SEPARATOR).concat(order));

            return pager;
        }

        return null;
    }

    /**
     * 验证Pager对象的sort和order的值是否合法
     * @param pager Pager<?>
     * @return boolean
     */
    private static boolean validatePager(Pager<?> pager) {
        String sort = pager.getSort();
        String order = pager.getOrder();

        return validate(sort, order);
    }

    /**
     * 验证sort和order的值是否合法
     * @param sort 排序字段
     * @param order 排序方式
     * @return boolean
     */
    private static boolean validate(String sort, String order) {
        if (StringUtils.isEmpty(sort)) {
            throw new GlobalException(ResponseCode.FORBIDDEN, "排序字段不允许为空!");
        } else if (StringUtils.isEmpty(order)) {
            throw new GlobalException(ResponseCode.FORBIDDEN, "排序方式不允许为空!");
        } else if(!ORDER_STYLES.contains(order.toLowerCase())) {
            throw new GlobalException(ResponseCode.FORBIDDEN, "排序方式不合法!");
        }

        return true;
    }

}

好了,文章就分享到这里了,自己的一个小成果,分享给大家,希望对大家有所帮助~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/332990.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

怎么在桌面查看备忘录新的提醒事项?方法教程

在这个信息爆炸的时代&#xff0c;我们每天都面临着无数的任务和提醒。作为一名忙碌的职场人&#xff0c;我经常需要依赖备忘录来记录重要的待办事项&#xff0c;以免遗漏。备忘录&#xff0c;就像我生活中的小助手&#xff0c;帮我记下工作会议、生日提醒、购物清单等等&#…

基于 Hologres+Flink 的曹操出行实时数仓建设

本文整理自曹操出行实时计算负责人林震基于 HologresFlink 的曹操出行实时数仓建设的分享&#xff0c;内容主要分为以下六部分&#xff1a; 曹操出行业务背景介绍曹操出行业务痛点分析HologresFlink 构建企业级实时数仓曹操出行实时数仓实践曹操出行业务成果分析未来展望 一、曹…

基于Vue+Canvas实现的画板绘画以及保存功能,解决保存没有背景问题

基于VueCanvas实现的画板绘画以及保存功能 本文内容设计到的画板的js部分内容来源于灵感来源引用地址&#xff0c;然后我在此基础上&#xff0c;根据自己的需求做了修改&#xff0c;增加了其他功能。 下面展示了完整的前后端代码 这里写目录标题 基于VueCanvas实现的画板绘…

OpenAI GPT应用商城正式上线!超300万个GPT应用供选择

原创 | 文 BFT机器人 千呼万唤始出来&#xff0c;终于在北京时间1月11日凌晨&#xff0c;OpenAI在官网发布了令人振奋的消息&#xff1a;备受瞩目的GPT store正式上线&#xff01; 这个商店旨在让团体和企业用户轻松找到那些既实用又热门的GPT应用。在这里&#xff0c;用户可以…

python基础知识

python基础语法 python基础精讲 http://t.csdnimg.cn/HdKdi 本专栏主要针对python基础语法&#xff0c;帮助学习者快速接触并掌握python大部分最重要的语法特征。 1、基本数据类型和变量 2、分支结构与循环结构 3、函数与异常处理 4、类与模块 5、文件读写 通过本专栏可以快…

Unity 编辑器篇|(十)Handles (全面总结 | 建议收藏)

目录 1. 前言2 参数总览3 Handles两种使用方式3.1 基于Editor类的OnSceneGUI3.2 基于EditorWindow 4 Handles绘制4.1 Draw&#xff1a;绘制元几何体(点、线、面)4.1.1 抗锯齿&#xff1a; DrawAAPolyLine 、 DrawAAConvexPolygon4.1.2 绘制实线: DrawLine 、 DrawLines 、DrawP…

(2)(2.1) Andruav Android Cellular(一)

文章目录 前言 1 Andruav 是什么&#xff1f; 2 Andruav入门 3 Andruav FPV 4 Andruav GCS App​​​​​​​ 前言 Andruav 是一个基于安卓的互联系统&#xff0c;它将安卓手机作为公司计算机&#xff0c;为你的无人机和遥控车增添先进功能。 1 Andruav 是什么&#xff…

门禁监控如何提升安全系数?这个技术,学习一下!

随着社会的不断发展和科技的快速进步&#xff0c;安全管理成为各个领域至关重要的议题。在这一背景下&#xff0c;门禁监控系统逐渐崭露头角&#xff0c;成为保障建筑物和场所安全的关键工具。 门禁监控系统不仅在提高安全性方面发挥着积极作用&#xff0c;而且通过智能化的技术…

《模拟龙生》|500行Go代码写一个随机冒险游戏|巨龙修为挑战开启

一、前言 新年就要到了&#xff0c;祝大家新的一年&#xff1a;&#x1f432; 龙行龘龘&#xff0c;&#x1f525; 前程朤朤&#xff01; 白泽花了点时间&#xff0c;用 500行 Go 代码写了一个控制台的小游戏&#xff1a;《模拟龙生》&#xff0c;在游戏中你将模拟一条新生的…

Linux系统三剑客之grep和正则表达式的介绍(一)

1.正则表达式 目录 1.正则表达式 1.什么是正则表达式 &#xff1f; 2.正则表达式的使用场景 3.正则表达式字符表示 4.它们之间的区别 2.grep命令 作用&#xff1a; 语法&#xff1a; 说明&#xff1a; 选项&#xff1a;options 重点 实例 3.后面的下次再更新。 …

C语言位域定义与使用

参考文章&#xff1a; 【C语言】详解位域定义与使用_c 语言定义位-CSDN博客 代码有修改&#xff0c;主要是变量初始化&#xff0c;原程序可能相应内存不能写。且第二个字节F不好区分各位。 #include <stdio.h>typedef struct {unsigned short b1 : 1;unsigned short b…

情人节专属--HTML制作情人节告白爱心

💕效果展示 💕html展示 <!DOCTYPE html> <html lang="en" > <head>

ros2仿真学习04 -turtlebot3实现cartographer算法建图演示

安装看这里 https://blog.csdn.net/hai411741962/article/details/135619608?spm1001.2014.3001.5502 虚拟机配置&#xff1a; 内存16g cpu 4 核 磁盘40G,20G 不够 启动仿真 ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py启动成功如下 启动建图 重新开一个…

浅谈情绪的分类合集

什么是情绪分类 情绪分类&#xff0c;是指区分或者对比一种情绪与另一种情绪的方法&#xff0c;目前在情绪研究&#xff08;emotion research&#xff09;与情感科学&#xff08;affective science&#xff09;是具有争议的问题。有两个讨论情绪分类的基本观点&#xff1a; 情…

【计算机组成与体系结构Ⅱ】Tomasulo 算法模拟和分析(实验)

实验5&#xff1a;Tomasulo 算法模拟和分析 一、实验目的 1&#xff1a;加深对指令级并行性及开发的理解。 2&#xff1a;加深对 Tomasulo 算法的理解。 3&#xff1a;掌握 Tomasulo 算法在指令流出、执行、写结果各阶段对浮点操作指令以及 load 和 store 指令进行了什么处…

CC工具箱使用指南:【用地用海指标汇总】

一、简介 在湘源上画的规划用地图&#xff0c;想导出规划指标表是很容易的。 但是现在很多用地图最终是要在ArcGIS中处理的&#xff0c;想再导出指标表就比较麻烦。 这个工具的主要功能就是在ArcGIS中汇总统计&#xff0c;生成类似湘源的Excel格式用地指标表。 二、工具参数…

20240117在本地机器识别OCR法语电影的字幕效果PK

20240117在本地机器识别OCR法语电影的字幕效果PK 2024/1/17 11:18 1959 - Jirai Cracher Sur Vos Tombes [Gast, Vian].avi https://www.pianbar.net//drama/52892.html 1959[我唾弃你的坟墓]Jirai cracher sur vos tombes[BT下载/迅雷下载] magnet:?xturn:btih:7c9c99d9d048…

【备战蓝桥杯】图论重点 敲黑板啦!

蓝桥杯备赛 | 洛谷做题打卡day11 文章目录 蓝桥杯备赛 | 洛谷做题打卡day11杂务题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 题解代码我的一些话 杂务 题目描述 John 的农场在给奶牛挤奶前有很多杂务要完成&#xff0c;每一项杂务都需要一定的时间来完成它。比如&a…

canvas绘制不同样式的六角星(示例源代码)

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

Python如何便捷的执行JavaScript代码,调用JS函数

文章目录 📖 介绍 📖🏡 环境 🏡📒 实现方法 📒📝 安装📝 使用⚓️ 相关链接 ⚓️📖 介绍 📖 Python在写一些爬虫代码的时候经常需要接触到JS逆向,其中如何让Python便捷的执行JS代码就成了一个很关键的问题,本文分享一种便捷的方式实现这个需求。 🏡 环…