SpringBoot项目快速开发框架JeecgBoot——Web处理!

发布时间:2025-06-26 20:17  浏览量:2

Jeecg Boot框架主要用于Web开发领域。下面介绍Jeecg Boot在Web开发中的常用功能,如控制器、登录、系统菜单、权限模块的角色管理和用户管理。

首先启动后台项目,将其导入IDEA中,并利用Maven自动加载依赖。然后把数据库脚本jeecg-boot目录下的db/jeecgboot-mysql-5.7.sql导入本地数据库中,并修改数据库的配置文件为本地配置。最后启动本地Redis服务,最后启动后台项目,请务必按顺序启动,否则会报错。

启动前端项目时,首先需要在本地安装并配置好Node.js,然后切换到前端代码目录ant-design-jeecg-vue下,在控制台使用npm install -y yarn命令安装yarn,并使用yarn install命令下载依赖,最后使用yarn run serve命令启动前端项目,或者可以先使用yarn run build命令编译项目后再启动。

启动前后端项目之后,在浏览器中访问http://localhost:3000/,可以看到登录页面,使用账号admin和密码123456登录,可以看到系统首页,如图8.4所示。访问http://localhost:8080/jeecg-boot/,可以查看系统的所有接口文档。

图8.4 系统首页

Jeecg Boot的控制器全部保存在包org.jeecg.modules.system.controller下,其中有一个是CommonController.java,它是文件上传下载的统一入口方法。在每个控制器上都有以下注解:

@Slf4j

@RestController

@RequestMapping("/sys/common")

以上3个注解的作用分别是记录日志、标记本类为控制器、设置请求的路径。控制器的返回值为固定格式,它返回Result类的实例对象。使用Result类能够非常快速地构建返回结果的对象。Result类的部分代码如下:

package org.jeecg.common.api.vo;

import com.fasterxml.jackson.annotation.JsonIgnore;

import io.swagger.annotations.ApiModel;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import org.jeecg.common.constant.CommonConstant;

import java.io.Serializable;

/**

* 接口返回数据格式

* @author scott

* @email jeecgos@163.com

* @date 2019年1月19日

*/

@Data@ApiModel(value="接口返回对象", description="接口返回对象")

public class Resultimplements Serializable {

/**

* 成功标志

*/

@ApiModelProperty(value = "成功标志")

private boolean success = true;

/**

* 返回处理消息

*/

@ApiModelProperty(value = "返回处理消息")

private String message = "操作成功!";

/**

* 返回代码

*/

@ApiModelProperty(value = "返回代码")

private Integer code = 0;

/**

* 返回数据对象data

*/

@ApiModelProperty(value = "返回数据对象")

private T result;

/**

* 时间戳

*/

@ApiModelProperty(value = "时间戳")

private long timestamp = System.currentTimeMillis;

public Result {

}

public Resultsuccess(String message) {

this.message = message;

this.code = CommonConstant.SC_OK_200;

this.success = true;

return this; }

@Deprecated

Resultr = new Result;

r.setSuccess(true);

r.setCode(CommonConstant.SC_OK_200);

r.setMessage("成功");

return r;

}

public staticResultOK {

Resultr = new Result;

public staticResultOK(T data) {

Resultr = new Result;

r.setSuccess(true);

r.setCode(CommonConstant.SC_OK_200);

r.setResult(data);

return r;

}

public staticResultOK(String msg, T data) {

Resultr = new Result;

r.setSuccess(true);

r.setCode(CommonConstant.SC_OK_200);

r.setMessage(msg);

r.setResult(data);

return r;

}

public staticResulterror(String msg, T data) {

Resultr = new Result;

r.setSuccess(false);

r.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);

r.setMessage(msg); r.setResult(data);

return r;

}

public static Result error(String msg) {

return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);

}

Result;

r.setCode(code);

r.setMessage(msg);

r.setSuccess(false);

return r;

}

public Resulterror500(String message) {

this.message = message;

this.code = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;

this.success = false;

return this;

}

/**

* 无权限访问返回结果

*/

noauth(String msg) {

return error(CommonConstant.SC_JEECG_NO_AUTHZ, msg);

}

@JsonIgnore

private String onlTable;

}

返回结果全部都使用Result进行包装后再返回,前端有统一的返回格式对结果进行处理。

在Jeecg Boot项目中,登录控制器的类是LoginController,该类包含所有的登录控制方法,包括登录、退出、获取访问量、获取登录人的信息、选择用户当前部门、短信API登录接口、手机号登录接口、获取加密字符串、后台生成图形验证码、App登录和图形验证码入口等。登录用到的相关数据库用户的表为sys_user。

下面详细说明登录方法的代码逻辑。其中,Controller登录方法的代码如下:

@ApiOperation("登录接口")

@RequestMapping(value = "/login", method = RequestMethod.POST)

public Resultlogin(@RequestBody SysLoginModel

sysLoginModel){

Result;

String username = sysLoginModel.getUsername;

String password = sysLoginModel.getPassword;

//update-begin--Author:scott Date:20190805 for:暂时注释掉密码加密逻

辑,

目前存在一些问题

//前端进行密码加密,后端进行密码解密

//password = AesEncryptUtil.desEncrypt(sysLoginModel.getPassword.

replaceAll("+", "\\+")).trim; //密码解密

辑,

目前存在一些问题

//update-begin-author:taoyan date:20190828 for:校验验证码

String captcha = sysLoginModel.getCaptcha;

if(captcha==null){

result.error500("验证码无效");

return result;

}

String lowerCaseCaptcha = captcha.toLowerCase;

String realKey =

MD5Util.MD5Encode(lowerCaseCaptcha+sysLoginModel.getCheckKey, "utf-

8");

Object checkCode = redisUtil.get(realKey); //当进入登录页面时,有一定概率出现验证码错误

if(checkCode==null ||

!checkCode.toString.equals(lowerCaseCaptcha)) {

result.error500("验证码错误");

return result;

}

//update-end-author:taoyan date:20190828 for:校验验证码

//1. 校验用户是否有效

LambdaQueryWrapperqueryWrapper = new LambdaQueryWrapper

;

queryWrapper.eq(SysUser::getUsername,username);

SysUser sysUser = sysUserService.getOne(queryWrapper);

result = sysUserService.checkUserIsEffective(sysUser);

if(!result.isSuccess) {

return result;

}

//2. 校验用户名和密码是否正确

String userpassword = PasswordUtil.encrypt(username, password,

sysUser.getSalt);

String syspassword = sysUser.getPassword;

if (!syspassword.equals(userpassword)) {

result.error500("用户名或密码错误");

return result;

}

//用户登录信息

userInfo(sysUser, result);

//update-begin--Author:liusq Date:20210126 for:登录成功,删除Redis

中的验证码

redisUtil.del(realKey);

中的验证码

LoginUser loginUser = new LoginUser;

BeanUtils.copyProperties(sysUser, loginUser);

baseCommonService.addLog("用户名: " + username + ",登录成功!",

CommonConstant.LOG_TYPE_1, null,loginUser);

//update-end--Author:wangshuai Date:20200714 for:登录日志没有记

录人员 return result;

}

调用sysUserService的checkUserIsEffective方法,代码如下:

/**

* 校验用户是否有效

* @param sysUser

* @return

*/

@Override

public Result checkUserIsEffective(SysUser sysUser) {

Result result = new Result;

//情况1:根据用户信息查询,该用户不存在

if (sysUser == null) {

result.error500("该用户不存在,请注册");

baseCommonService.addLog("用户登录失败,用户不存在!",

CommonConstant.

LOG_TYPE_1, null);

return result;

}

//情况2:根据用户信息查询,该用户已注销

//update-begin---author:王帅 if条件永远为false

if (CommonConstant.DEL_FLAG_1.equals(sysUser.getDelFlag)) {

//update-end---author:王帅 if条件永远为false

baseCommonService.addLog("用户登录失败,用户名:" +

sysUser.getUsername

+ "已注销!", CommonConstant.LOG_TYPE_1, null);

result.error500("该用户已注销");

return result;

}

//情况3:根据用户信息查询,该用户已冻结

if (CommonConstant.USER_FREEZE.equals(sysUser.getStatus)) {

+ "已冻结!", CommonConstant.LOG_TYPE_1, null);

result.error500("该用户已冻结");

return result; }

return result;

}

Controller的login方法用于判断用户登录是否成功,其执行逻辑如下:

(1)判断验证码是否正确。

(2)调用sysUserService的checkUserIsEffective方法查询当前用户是否存在,用户状态是否正常,确认用户没有注销和被冻结。

(3)校验用户名和密码是否正确。

(4)完善登录成功的用户信息。

(5)记录日志。

(6)返回登录成功的结果。

在网页上可以快速进行菜单的创建和查看,在“系统管理”|“菜单管理”菜单下,可以看到系统的所有菜单,并且可以新建一个菜单。新建菜单的页面如图8.5所示,系统菜单的控制器为SysPermissionController,数据库对应的表为sys_permission。

SysPermissionController类的部分方法如下:

/**

*

* 菜单权限表的前端控制器

*

*

* @Author scott

* @since 2018-12-21

*/

@Slf4j

@RestController

@RequestMapping("/sys/permission")

public class SysPermissionController { @Autowired

private ISysPermissionService sysPermissionService;

/**

* 加载数据节点

*

* @return

*/

@RequestMapping(value = "/list", method = RequestMethod.GET)

public Result> list {

long start = System.currentTimeMillis;

Result> result = new Result;

try {

LambdaQueryWrapperquery = new

LambdaQueryWrapper;

query.eq(SysPermission::getDelFlag,

CommonConstant.DEL_FLAG_0);

query.orderByAsc(SysPermission::getSortNo);

Listlist = sysPermissionService.list(query);treeList = new ArrayList;

getTreeList(treeList, list, null);

result.setResult(treeList);

result.setSuccess(true);

log.info("======获取全部菜单数据=====耗时:" +

(System.currentTimeMillis - start) + "毫秒");

} catch (Exception e) {

log.error(e.getMessage, e);

}

return result;

}

/**

* 添加菜单

* @param permission

* @return

*/

//@RequiresRoles({ "admin" })

@RequestMapping(value = "/add", method = RequestMethod.POST)

public Resultadd(@RequestBody SysPermissionresult = new Result;

try {

permission =

PermissionDataUtil.intelligentProcessData(permission);

sysPermissionService.addPermission(permission);

result.success("添加成功!");

} catch (Exception e) {

log.error(e.getMessage, e);

result.error500("操作失败");

}

return result;

}

}

以上为菜单列表和新建菜单的Controller,其对应新增菜单的service代码如下:

@Override

@CacheEvict(value =

CacheConstant.SYS_DATA_PERMISSIONS_CACHE,allEntries=

true)

public void addPermission(SysPermission sysPermission) throws

JeecgBoot

Exception {

//

-

//判断是否是一级菜单,如果是,则清空父菜单

if(CommonConstant.MENU_TYPE_0.equals(sysPermission.getMenuType))

{

sysPermission.setParentId(null);

}

-

String pid = sysPermission.getParentId;

if(oConvertUtils.isNotEmpty(pid)) {

//设置父节点不为子节点

this.sysPermissionMapper.setMenuLeaf(pid, 0); }

sysPermission.setCreateTime(new Date);

sysPermission.setDelFlag(0);

sysPermission.setLeaf(true);

this.save(sysPermission);

}

对应Dao方法的代码如下:

/**

* 修改菜单状态字段: 是否子节点

*/

@Update("update sys_permission set is_leaf=#{leaf} where id = #{id}")

public int setMenuLeaf(@Param("id") String id,@Param("leaf") int

leaf);

保存一个新的菜单时应判断是否是一级菜单,如果是,则清空父菜单,否则直接将菜单拼接到父菜单之下。

角色是系统权限管理的一部分,用来管理一部分权限的合集。JeecgBoot的角色管理功能是在“系统管理”|“角色管理”菜单下。创建一个新的角色,如图8.6所示,这里创建了一个临时工的角色。在角色管理中还可以查看有多少用户拥有该角色。

角色管理的入口控制器是SysRoleController,其部分源码如下:

/**

*

* 角色表的前端控制器

*

*

* @Author scott

* @since 2018-12-19

*/

@RestController

@RequestMapping("/sys/role")

@Slf4j

public class SysRoleController {

@Autowired

private ISysRoleService sysRoleService;

/**

* 分页列表查询

* @param role

* @param pageNo

* @param pageSize

* @param req

* @return */

@RequestMapping(value = "/list", method = RequestMethod.GET)

public Result> queryPageList(SysRole role,

@RequestParam(name="pageNo", defaultValue="1")

Integer pageNo,

@RequestParam(name="pageSize", defaultValue="10")

Integer

pageSize,

HttpServletRequest req) {

ResultQueryWrapperqueryWrapper = QueryGenerator.initQuery

Wrapper(role, req.getParameterMap);

Pagepage = new PageIPagepageList = sysRoleService.page(page,

queryWrapper);

result.setSuccess(true);

result.setResult(pageList);

return result;

}

/**

* 添加

* @param role

* @return

*/

@RequestMapping(value = "/add", method = RequestMethod.POST)

//@RequiresRoles({"admin"})

public Resultadd(@RequestBody SysRole role) {Result;

try {

role.setCreateTime(new Date);

sysRoleService.save(role);

result.success("添加成功!");

} catch (Exception e) {

log.error(e.getMessage, e);

result.error500("操作失败");

}

return result;

}

}

角色的列表查询和新建都使用MyBatisPlus的接口方法实现,在开发中不需要再次实现,从而更加快捷地完成功能开发。

当前登录的用户是管理员,系统还内置了其他账户。在sys_user表中,使用“系统管理”|“用户管理”命令可以查看所有的用户。下面新建一个账号为cc的新用户,然后登录。新建用户的设置页面如图8.7所示。

退出当前用户账号,使用cc登录,登录成功后的页面如图8.8所示。可以看到,已经登录成功,但是因为当前用户未分配任何权限,所以是空白页。

用户管理的入口控制器Controller是SysUserController,其部分源码如下:

/**

*

* 用户表的前端控制器

*

*/

@Slf4j

@RestController

@RequestMapping("/sys/user")

public class SysUserController {

@Autowired

private ISysBaseAPI sysBaseAPI;

@Autowired

private ISysUserService sysUserService;

/**

* 获取用户列表数据

* @param user

* @param pageNo

* @param pageSize

* @param req

* @return

*/

@PermissionData(pageComponent = "system/UserList") @RequestMapping(value = "/list", method = RequestMethod.GET)

public Result> queryPageList(SysUser

user,@RequestParam

(name="pageNo", defaultValue="1") Integer pageNo,

@RequestParam(name="pageSize", defaultValue="10") Integer

pageSize,

HttpServletRequest req) {

ResultQueryWrapperqueryWrapper =

QueryGenerator.initQueryWrapper

(user, req.getParameterMap);

// 外部模拟登录临时账号,不显示列表

queryWrapper.ne("username","_reserve_user_external");

Pagepage = new PageIPagepageList = sysUserService.page(page,

queryWrapper);

//批量查询用户的所属部门

//步骤1:先获取全部的userIds

//步骤2:通过userIds一次性查询用户所属部门的名称

List

pageList.getRecords.stream.map(SysUser::

getId).collect(Collectors.toList);

if(userIds!=null && userIds.size>0){

MapuseDepNames =

sysUserService.getDepNamesByUserIds(userIds);

pageList.getRecords.forEach(item->{

item.setOrgCodeTxt(useDepNames.get(item.getId));

});

}

result.setSuccess(true);

result.setResult(pageList);

log.info(pageList.toString);

return result;

}

//@RequiresRoles({"admin"})

//@RequiresPermissions("user:add")

@RequestMapping(value = "/add", method = RequestMethod.POST)

public Resultadd(@RequestBody JSONObject jsonObject) {Result; String selectedRoles = jsonObject.getString("selectedroles");

String selectedDeparts = jsonObject.getString("selecteddeparts");

try {

SysUser user = JSON.parseObject(jsonObject.toJSONString,

SysUser.class);

user.setCreateTime(new Date); //设置创建时间

String salt = oConvertUtils.randomGen(8);

user.setSalt(salt);

String passwordEncode =

PasswordUtil.encrypt(user.getUsername,user.getPassword, salt);

user.setPassword(passwordEncode);

user.setStatus(1);

user.setDelFlag(CommonConstant.DEL_FLAG_0);

// 在一个方法中使用事务保存用户信息

sysUserService.saveUser(user, selectedRoles, selectedDeparts);

result.success("添加成功!");

} catch (Exception e) {

log.error(e.getMessage, e);

result.error500("操作失败");

}

return result;

}

}

这里节选了用户列表和新增用户入口的方法。查询用户列表的service实现代码节选如下:

@Override

public Map

this.baseMapper.getDepNamesByUserIds(userIds);

Map;

list.forEach(item -> {

if (res.get(item.getUserId) == null) {

res.put(item.getUserId, item.getDepartName); } else {

res.put(item.getUserId, res.get(item.getUserId) +

"," +

item.getDepartName);

}

}

);

return res;

}

其中,调用Dao的代码如下:

/**

* 根据 userIds查询,查询用户所属部门的名称(多个部门名称用逗号隔开)

* @param

* @return

*/

public MapgetDepNamesByUserIds(List

XML中的SQL语句如下:

新增用户的service代码如下:

@Override

@Transactional(rollbackFor = Exception.class)

public void saveUser(SysUser user, String selectedRoles, String

selected

Departs) {

//步骤1 保存用户

this.save(user);

//步骤2 保存角色

if(oConvertUtils.isNotEmpty(selectedRoles)) {

String arr = selectedRoles.split(",");

for (String roleId : arr) {

SysUserRole userRole = new SysUserRole(user.getId, roleId);

sysUserRoleMapper.insert(userRole);

}

}

//步骤3 保存所属部门

if(oConvertUtils.isNotEmpty(selectedDeparts)) {

String arr = selectedDeparts.split(",");

for (String deaprtId : arr) {

SysUserDepart userDeaprt = new SysUserDepart(user.getId,

deaprtId);

sysUserDepartMapper.insert(userDeaprt);

}

}

}

保存用户时附带保存用户角色和用户所属部门的数据。在方法中增加了事务,以确保数据保存的完整性。

package org.jeecg.common.exception;

/**

* 异常处理器

*

* @Author scott

* @Date 2019

*/

@RestControllerAdvice

@SLF4J

public class JeecgBootExceptionHandler {

/**

* 处理自定义异常

*/

@ExceptionHandler(JeecgBootException.class)

public Result handleRRException(JeecgBootException e){

log.error(e.getMessage, e);

return Result.error(e.getMessage);

}

@ExceptionHandler(NoHandlerFoundException.class)

public Result handlerNoFoundException(Exception e) {

log.error(e.getMessage, e);

return Result.error(404, "路径不存在,请检查路径是否正确");

}

@ExceptionHandler({UnauthorizedException.class,

AuthorizationException.class})

public Result handleAuthorizationException(AuthorizationException

e){

log.error(e.getMessage, e);

return Result.noauth("没有权限,请联系管理员授权");

}

@ExceptionHandler(Exception.class)

public Result handleException(Exception e){ log.error(e.getMessage, e);

return Result.error("操作失败,"+e.getMessage);

}

/**

* Spring默认上传文件的大小为10MB,若超出则捕获异常MaxUploadSizeExceeded

Exception

*/

@ExceptionHandler(MaxUploadSizeExceededException.class)

public Result handleMaxUploadSizeExceededException(MaxUploadSize

ExceededException e) {

log.error(e.getMessage, e);

return Result.error("文件超出10MB的限制,请压缩或降低文件质量! ");

}

@ExceptionHandler(PoolException.class)

public Result handlePoolException(PoolException e) {

log.error(e.getMessage, e);

return Result.error("Redis 连接异常!");

}

}

以上列举的只是Jeecg Boot的一部分功能,还有很多功能读者可以自行去挖掘。常见的功能如下:

统计报表功能:使用该功能后,现有的报表就不再需要重新构建报表页面,而只需要后端返回响应的数据就能看到与结果相符的报表。

在线开发功能:使用该功能可以非常快速地开发在线表单,并且可以设置参数校验的规则,从而对系统的数据源进行管理。

还有动态切换数据源和代码生成等功能读者可以自行演示。

说明:如果现有功能不能满足用户的业务需要,就需要用户自己完成开发。

如果在使用的过程中发现系统Bug,则可以向Jeecg Boot团队反馈,也可以提供建议,这样也为开源做出了自己的贡献。