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

news2024/9/30 9:27:18

这是博主自己在使用的一套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;
    }

}

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

社交媒体危机管理:Facebook的公共关系之道

社交媒体的崛起为信息传播带来了前所未有的便利&#xff0c;然而&#xff0c;与之相伴而生的是社交媒体危机。作为全球最大的社交媒体平台之一&#xff0c;Facebook曾多次面临各种危机&#xff0c;这时候&#xff0c;危机管理成为了其公共关系的关键一环。本文将深入探讨Facebo…

卷积神经网络和深度神经网络的区别与联系

DNN是指深度神经网络&#xff0c;它是一个很广的概念&#xff0c;某种意义上CNN、RNN、GAN等都属于其范畴之内。DNN与CNN&#xff08;[卷积神经网络&#xff09;的区别是DNN特指全连接的神经元结构&#xff0c;并不包含卷积单元或是时间上的关联。其中最初的神经网络的所有隐含…

空间计算时代催生新一波巨大算力市场需求

什么是空间计算&#xff1f; 空间计算是一种整合虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;、混合现实&#xff08;MR&#xff09;等技术的计算模式&#xff0c;旨在将数字信息与真实世界融合在一起。这种融合创造了一个全新的计算环境&#xff…

【开源】基于JAVA语言的陕西非物质文化遗产网站

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 设计目标2.2 研究内容2.3 研究方法与过程2.3.1 系统设计2.3.2 查阅文献2.3.3 网站分析2.3.4 网站设计2.3.5 网站实现2.3.6 系统测试与效果分析 三、系统展示四、核心代码4.1 查询民间文学4.2 查询传统音乐4.3 增改传统舞…

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

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

openpose之使用摄像头检测并输出到json文件

编程如画&#xff0c;我是panda&#xff01; 前言 之前给大家分享了如何搭建openpose环境&#xff0c;并进行了测试案例&#xff0c;但是如果要使用摄像头的话&#xff0c;还需要修改一下运行文件&#xff0c;并且这次会教大家如何输出到json文件 。 如果环境还没有搭建好&am…

k8s之对外服务ingress

一、service 1、service作用 ①集群内部&#xff1a;不断跟踪pod的变化&#xff0c;不断更新endpoint中的pod对象&#xff0c;基于pod的IP地址不断变化的一种服务发现机制&#xff08;endpoint存储最终对外提供服务的IP地址和端口&#xff09; ②集群外部&#xff1a;类似负…

简单的推箱子游戏实战

目录 项目分析 地图初始化 背景图片 游戏场景图片: 热键控制 按键设置 确定人物位置 实现人物移动(非箱子,目的地) 推箱子控制 游戏结束 最终代码 合法性判断: 项目分析 墙:0,地板:1,箱子目的地:2,小人:3,箱子:4,箱子命中目标:5 地图初始化 背景图片 #include <…

开发实践6_缓存^中间件

以下学习 朔宁夫 开发工程师 课程。 缓存可提高程序响应速度。数据库缓存(可过期)/ Redis缓存(Key:Value)/ Memcacheed缓存/ 程序层缓存。 一 缓存 1. 数据库缓存 创建缓存数据表 // python manage.py createcachetable cache_table setting // # 缓存配置 CACHES {def…

怎么样的布局是符合可制造性的PCB布局?

满足可制造性、可装配性、可维修性要求&#xff0c;方便调试的时候于检测和返修&#xff0c;能够方便的拆卸器件&#xff1a; 1&#xff09;极性器件的方向不要超过2种&#xff0c;最好都进行统一方向等要求&#xff0c;如图1-1所示&#xff1b; 图1-1 极性器件方向统一摆放 2…

【C语言】ipoib模块 - ipoib_send_rss函数

一、ipoib_send_rss函数定义 int ipoib_send_rss(struct net_device *dev, struct sk_buff *skb,struct ib_ah *address, u32 dqpn) {struct ipoib_dev_priv *priv ipoib_priv(dev);struct ipoib_tx_buf *tx_req;struct ipoib_send_ring *send_ring;u16 queue_index;int hlen…

SQL Povit函数使用及实例

PIVOT函数常用于数据的行转列&#xff0c;同时也可以用此函数实现类似于Excel中的数据透视表的效果。 PIVOT函数 PIVOT 函数的基本语法如下&#xff1a; -- PIVOT 语法 SELECT <非透视的列>,[第一个透视的列] AS <列名称>,[第二个透视的列] AS <列名称>,.…

Oracle 数据库备份与恢复的重要性与最佳实践

文章目录 一、备份的重要性二、备份工具-RMAN四、比较备份策略五、实例恢复六、完全恢复与不完全恢复七、备份与恢复脚本 引言&#xff1a; 在现代信息时代&#xff0c;数据已成为组织和企业最重要的资产之一。保护和恢复数据的能力对于确保业务连续性和减少潜在风险至关重要。…

探索 2024 年新副业:无人饮品机的新风向

随着科技的迅猛发展&#xff0c;无人饮品机作为一种全新的商业模式&#xff0c;正逐渐成为 2024 年副业的新风向。如果你还没有了解过这种全新的副业&#xff0c;那么现在是时候深入了解一下了。 D咖无人饮品机的优势在于其 24 小时不间断的营业模式&#xff0c;它可以在你睡觉…

【成功案例】首日ROI超70%!ROI目标超150%!看 NetMarvel如何助力棋牌游戏出海?

今天无论是线上线下&#xff0c;中国游戏广告在海外市场上屡见不鲜&#xff0c;甚至还会出现包场式营销&#xff0c;“中国人敢花钱”的印象已经深入人心&#xff0c;特别是休闲游戏的爆发给众多众多厂商带来新的增长机遇&#xff0c;大家花钱的态势更猛了。 棋牌类属于休闲体…

以后要做GIS开发的话是学GIS专业还是学计算机专业好一些?

GIS开发其实严格来说分为前后端以及底层开发。不同的方向&#xff0c;代表了不同的开发语言。 所以大家首先要了解自己具体要做的岗位类型是什么&#xff0c;其次才是选择专业侧重点。 但是严格来说&#xff0c;选择某个专业&#xff0c;到就业方向这个过程&#xff0c;并不是…

大疆笔试题目(2023-08-13)

1. 输出无重复3位数 时间限制&#xff1a; 3000MS 内存限制&#xff1a; 65536KB 题目描述&#xff1a; 从{1,2,3,4,5,6,7,8,9}中随机挑选不重复的5个数字作为输入数组’selectedDigits’&#xff0c;能组成多少个互不相同且无重复数字的3位数&#xff1f;请编写程序&#xff…

大创项目推荐 深度学习的水果识别 opencv python

文章目录 0 前言2 开发简介3 识别原理3.1 传统图像识别原理3.2 深度学习水果识别 4 数据集5 部分关键代码5.1 处理训练集的数据结构5.2 模型网络结构5.3 训练模型 6 识别效果7 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习…

基于小波多普勒变换的回波信号检测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1小波变换基础 4.2 多普勒效应与多普勒变换 4.3 小波多普勒变换 4.4 回波信号检测 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 %回波…

QT自定义控件0-360°刻度尺

支持0到360&#xff0c;360到0的过度。 直接上代码&#xff0c;可以直接用&#xff0c;使用的paintevent事件实现的&#xff0c;没啥好讲的。 .cpp void Widget::drawCourse(QPainter& p,QPen pen,QFont font) {double currentNumber m_ang;p.setBrush(Qt::black);p.dra…