XX后台管理系统

Springboot + vue + dm8 的前后端分离项目,后端项目

https://spring.io
https://start.aliyun.com

1. 创建项目

初始化项目,导入坐标

web、lombok、devtools
<!-- web start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- web end --> <!-- devtools start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- devtools end --> <!-- lombok start -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- lombok end -->

2.整合Swagger3

2.1 导入swagger3坐标

<swagger3.version>3.0.0</swagger3.version>
<!-- swagger3 start  -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${swagger3.version}</version>
</dependency>
<!-- swagger3 end -->

2.2 编写swagger3配置类

package org.cn.common.config;

import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set; @Configuration
@EnableOpenApi
@EnableWebMvc
public class Swagger3Config { /**
* 创建API
* http:localhost:9999/swagger-ui/index.html 原生地址
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.OAS_30).pathMapping("/")
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
/*.enable(enable)*/
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有有注解的api,用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解
//.apis(RequestHandlerSelectors.basePackage("com.cn"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.regex("(?!/ApiError.*).*"))
.paths(PathSelectors.any())
.build()
// 支持的通讯协议集合
.protocols(newHashSet("https", "http"))
.securitySchemes(securitySchemes())
.securityContexts(securityContexts()); } /**
* 支持的通讯协议集合
*
* @param type1
* @param type2
* @return
*/
private Set<String> newHashSet(String type1, String type2) {
Set<String> set = new HashSet<>();
set.add(type1);
set.add(type2);
return set;
} /**
* 认证的安全上下文
*/
private List<SecurityScheme> securitySchemes() {
List<SecurityScheme> securitySchemes = new ArrayList<>();
securitySchemes.add(new ApiKey("Authorization", "Authorization", "header"));
return securitySchemes;
} /**
* 授权信息全局应用
*/
private List<SecurityContext> securityContexts() {
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.any()).build());
return securityContexts;
} private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
} /**
* 添加摘要信息
* @return 返回ApiInfo对象
*/
private ApiInfo apiInfo() {
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("接口文档")
// 服务条款
.termsOfServiceUrl("NO terms of service")
// 描述
.description("权限模型管理系统-接口文档")
// 作者信息
.contact(new Contact("LM", "https://www.cnblogs.com/longronglang/", "lumin@gmail.com"))
// 版本
.version("版本号:V1.0")
//协议
.license("The Apache License")
// 协议url
.licenseUrl("https://www.apache.org/licenses/LICENSE-2.0.html")
.build();
}
}

2.3 swagger3注解测试

新增测试类、测试方法等,加上Swagger3注解进行测试

http:localhost:9999/swagger-ui/index.html

@Api(tags = "用户管理")
@ApiOperation("用户登录")
http://localhost:9999/swagger-ui/index.html

3. 代码自动生成

3.1 引入MP&DB坐标

mybatis-plus-boot-starter、mybatis-plus-generator、freemarker、dameng

注意:mybatis-plus 坐标引入的是mybatis-plus启动坐标和代码生成坐标

<dm.version>8.1.2.192</dm.version>
<mybatis-plus-boot.version>3.5.3.1</mybatis-plus-boot.version>
<mybatis-plus-generator.version>3.5.3.1</mybatis-plus-generator.version>
<!-- mybatis-plus-boot start -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot.version}</version>
</dependency>
<!-- mybatis-plus-boot end --> <!-- mybatis-plus-generator start -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus-generator.version}</version>
</dependency>
<!-- mybatis-plus-generator end --> <!-- freemarker start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- freemarker end --> <!-- dameng start -->
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>${dm.version}</version>
</dependency>
<!-- dameng end -->

3.2 代码生成工具类编写

3.2.1 代码生成工具类
package generator;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections; public class CodeGenerator {
static String url ="jdbc:dm://LOCALHOST:5236";
static String username ="SYSDBA";
static String password ="SYSDBA001";
static String moduleName ="system";
static String javaLocation ="F:\\install\\workspace\\sty\\x-dm-server\\src\\main\\java";
static String mapperLocation ="F:\\install\\workspace\\sty\\x-dm-server\\src\\main\\resources\\mapper\\";
static String tables = "x_user,x_role,x_menu,x_user_role,x_role_menu"; public static void main(String[] args) { FastAutoGenerator.create(url,username,password)
.globalConfig(builder -> builder
.author("LuMin")
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir(javaLocation)
.commentDate("yyyy-MM-dd")
)
.packageConfig(builder -> builder
.parent("org.cn") // 设置父包名
.moduleName(moduleName) // 设置父包模块名
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.pathInfo(Collections.singletonMap(OutputFile.xml, mapperLocation))
)
.strategyConfig(builder -> builder
.addInclude(tables) // 设置需要生成的表名
.addTablePrefix("x_") // 设置过滤表前缀
.entityBuilder()
.enableLombok()
)
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
} public static class Test {
static Connection con = null;
static String cname = "dm.jdbc.driver.DmDriver";
static String url = "jdbc:dm://192.168.106.137:30236";
static String userid = "SYSDBA";
static String pwd = "SYSDBA001";
public static void main(String[] args) {
try {
Class.forName(cname);
con = DriverManager.getConnection(url, userid, pwd);
con.setAutoCommit(true);
System.out.println("[SUCCESS]conn database");
} catch (Exception e){
System.out.println("[FAIL]conn database:" + e.getMessage());
}
} public void disConn(Connection con) throws SQLException {
if (con != null) {
con.close();
}
}
}
}
3.2.2 启动异常解决
# 注意生成代码之后,直接启动项目会报错,需要对项目信息进行修改
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'menuServiceImpl': Unsatisfied dependency expressed through field 'baseMapper'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.cn.system.mapper.MenuMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
# ①修改启动类扫描的包
@SpringBootApplication(scanBasePackages = {"org.cn"})
@MapperScan(value = {"org.cn.*.mapper"})
# ②在Mapper接口上增加@Mapper类注解,启动还是会报错
initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'menuServiceImpl': Unsatisfied dependency expressed through field 'baseMapper'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'menuMapper' defined in file
# ③删除创建项目默认生成的demos包,重新启动

3.3 application.yml配置

# server
server:
port: 9999 # datasource
spring:
datasource:
url: jdbc:dm://LOCALHOST:5236/XGIS
username: SYSDBA
password: SYSDBA001
driver-class-name: dm.jdbc.driver.DmDriver
jpa:
database-platform: org.hibernate.dialect.DMDialect
hibernate:
ddl-auto: update # 或者 create, create-drop, validate, none
show-sql: true # 显示执行的 SQL 语句,便于调试
properties:
hibernate:
format_sql: true # 格式化 SQL 语句输出
redis:
port: 6379
password: Admin@123
host: localhost # mybatis-plus
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
logic-delete-field: deleted
logic-not-delete-value: 0
logic-delete-value: 1
mapper-locations: classpath*:mapper/*.xml
type-aliases-package: org.cn # logging
logging:
charset:
console: utf-8
level:
org.cn: debug

3.4 测试生成代码生成可用性

利用SpringBootJunit测试工具进行测试,在XDmServerApplicationTests类中新建testUserMapper()方法

package org.cn;

import org.cn.system.entity.User;
import org.cn.system.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import java.util.List; @SpringBootTest
class XDmServerApplicationTests { @Autowired
private UserMapper userMapper; @Test
void contextLoads() {
} @Test
void testUserMapper () {
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}

foreach循环时没有打印出预期的内容(例如用户对象的某些属性),而是打印出了类似org.cn.system.entity.User@1ad1c363这样的内容,这通常意味着你没有正确覆写User类的toString方法。

org.cn.system.entity.User@76d0ecd7
org.cn.system.entity.User@57c69937
org.cn.system.entity.User@1ad1c363
org.cn.system.entity.User@446b64b3
org.cn.system.entity.User@35ac9ebd
org.cn.system.entity.User@56c0a61e

一是在User实体类中增加 toString()方法

二是在实体类上添加@Data/@NoArgsConstructor/@AllArgsConstructor注解

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", status='" + status + '\'' +
", phone=" + phone +
", avatar='" + avatar + '\'' +
", deleted=" + deleted +
'}';
}

4. 统一结果返回封装

4.1 封装公共响应类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
/**
* 返回编码
*/
private Integer code;
/**
* 返回信息
*/
private String message;
/**
* 返回数据
*/
private T data; /**
* 返回成功
* @return
* @param <T>
*/
public static <T> Result<T> success(){
return new Result<>(20000, "success",null);
} public static <T> Result<T> success(T data){
return new Result<>(20000, "success",data);
}
public static <T> Result<T> success(T data,String message){
return new Result<>(20000, message,data);
} public static <T> Result<T> success(String message){
return new Result<>(20000, message,null);
} /**
* 返回失败
* @return
* @param <T>
*/
public static <T> Result<T> fail(){
return new Result<>(20001, "fail",null);
}
public static <T> Result<T> fail(Integer code){
return new Result<>(20001, "fail",null);
}
public static <T> Result<T> fail(Integer code,String message){
return new Result<>(20001, message,null);
}
public static <T> Result<T> fail(String message){
return new Result<>(20001, message,null);
} }

4.2 测试公共响应类

修改控制器,测试公共响应类是否返回自定义数据

4.2.1 修改注解
修改UserController控制器的@Controller为@RestController
4.2.2 新增方法测试

在控制类中注入IUserService接口服务类,新增getUserList()测试方法进行测试

@Resource
private IUserService userService; @GetMapping("/getUserList")
public Result<List<User>> getUserList() {
List<User> userList = userService.list();
return Result.success(userList, "查询成功");
}

5.实体类Lombok改造

5.1 删除注解

@Getter/@Setter是mybatis-plus-generator生成实体时自动添加的注解

@Getter
@Setter

5.2 新增注解

@Data/@NoArgsConstructor/@AllArgsConstructor是lombok中的注解

@Data
@NoArgsConstructor
@AllArgsConstructor

6.JWT登录实现

6.1 JWT 整合

6.1.1 导入Jwt坐标
spring-security-core、jjwt
<fastjson.version>2.0.1</fastjson.version>
<!-- spring-security-core start -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<!-- spring-security-core end --> <!-- jjwt start -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<!-- jjwt end -->
6.1.2 编写Jwt工具类

①在application.yml配置JWT令牌和有效期

# JWT 配置
jwt:
secret-key: 123456
token-validity-in-seconds: 86400000

②JwtUtil工具类

6.1.3 Jwt验证拦截器
6.1.3.1 定义Jwt拦截器
@Component
@Slf4j
public class JwtValidateInterceptor implements HandlerInterceptor { @Resource
private JwtUtil jwtUtil; @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// String token = request.getHeader("Authorization");
/*request.getHeader()方法与集成Swagger中ApiKey并无多大关系
private List<SecurityScheme> securitySchemes() {
List<SecurityScheme> securitySchemes = new ArrayList<>();
securitySchemes.add(new ApiKey("Authorization", "Authorization", "header"));
return securitySchemes;
}*/
String token = request.getHeader("X-Token");
log.debug(request.getRequestURI()+"需要验证"+token);
if (token != null) {
try {
jwtUtil.parseToken(token);
log.debug(request.getRequestURI()+"验证通过");
return true;
} catch (Exception e) {
throw new Exception(e);
}
}
log.debug(request.getRequestURI()+"验证失败,禁止访问");
response.setContentType("application/json;charset=utf-8");
Result<Object> fail = Result.fail(20003, "jwt验证失败,请重新登录");
response.getWriter().write(JSON.toJSONString(fail));
return false; //拦截
}
}
6.1.3.2 注册Jwt拦截器
@Configuration
public class JwtInterceptorConfig implements WebMvcConfigurer { @Resource
private JwtValidateInterceptor jwtValidateInterceptor; @Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(jwtValidateInterceptor);
registration.addPathPatterns("/**")
.excludePathPatterns(
"/system/user/login",
"/system/user/info",
"/system/user/logout",
"/swagger-ui/**",
"/swagger-resources/**",
"/v3/**",
"/error",
"/webjars/**",
"/v2/**",
"/public/**",
"/static/**",
"*.html",
"doc.html",
"/favicon.ico"
);
}
}
6.1.3.3 Jwt拦截器测试

①在 XAdminServerApplicationTests 测试类中注入

@Autowired
private JwtUtil jwtUtil;
在 XAdminServerApplicationTests 测试

②测试Jwt创建

/**
* Jwt创建
*/
@Test
void testCreateJwt(){
User user = new User();
user.setUsername("zhangsan");
user.setPhone(18722820382L);
String token = jwtUtil.createToken(user);
System.out.println(token);
}

③测试Jwt解析

/**
* Jwt解析
*/
@Test
void testParseJwt(){
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJkZWRiNTRiMC1kOTllLTRhZGEtOGFjMi1iZTA2ODRlNzVhZmYiLCJzdWIiOiJ7XCJ0ZWxlcGhvbmVcIjoxODcyMjgyMDM4MixcInVzZXJuYW1lXCI6XCJ6aGFuZ3NhblwifSIsImlzcyI6InN5c3RlbSIsImlhdCI6MTcwNjExMzU3MCwiZXhwIjoxNzA2MTE1MzcwfQ.Pcj8kjcAC8FW-PER65wiYYJ1bSptOG8mViIHyeIY3EI";
Claims claims = jwtUtil.parseToken(token);
System.out.println("claims = " + claims);
}

6.2 登录逻辑

认证过程可以参考此篇博客,写得非常详细

https://blog.csdn.net/liuerchong/article/details/108606650

6.2.1 新增用户登录接口

在IUserService服务类中新增用户登录业务逻辑接口

 /**
* 用户登录业务逻辑接口
* @param user
* @return
*/
Map<String, Object> login(User user);
6.2.2 实现用户登录接口

第一步根据用户名和密码查询用户信息;

第二步判断查询结果是否为空,为空抛出异常信息,不为空则将封装的登录信息传入JwtUtil中生成token

6.2.2.1 用户信息查询

根据前端传入User对象,LambdaQueryWrapper构造wrapper查询对象参数,再将wrapper参数传入后端baseMapper查询接口,查询用户信息是否存在。

// 1. 根据用户名查询用户信息
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername,user.getUsername());
User loginUser = this.baseMapper.selectOne(wrapper);
6.2.2.2 用户token生成

第一步:判断用户信息是否为空

判断用户信息不为空,使用spring-security-core中PasswordEncoder进行加密并使用matches方法匹配后端查询出来的密码是否一致,这里需要注意一下,安全考虑,密码在传输过程中需要进行置空,不要放到JWT生成token中,PasswordEncoder需要配置成一个公用组件,不然会报错。PasswordEncoder组件配置如下:

@Configuration
public class PasswordEncoderConfig {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}

第二步:不为空,则创建token

①定义一个字符串token,用于接收jwtUtil中生成的token值(实质是JwtBuilder中将AES加密令牌密钥和过期时间压缩的字符串);

②将封装的loginUser登录信息传入JwtUtil工具类中createToken方法中生成token;

③定义一个Map集合,将jwtUtil生成的token存储在Map集合中,返回出去

// 2. 结果不为空,则生成 token
if (loginUser != null && passwordEncoder.matches(user.getPassword(),loginUser.getPassword())) {
String key = "user" + UUID.randomUUID();
loginUser.setPassword(null);
String token = jwtUtil.createToken(loginUser);
Map<String,Object> data = new HashMap<>();
data.put("token",token);
return data;
}

第三步:JwtUtil工具生成token步骤

①在application.yml配置JWT令牌和有效期

# JWT 配置
jwt:
secret-key: 123456
token-validity-in-seconds: 86400

②创建JWT工具类,通对令牌密钥进行Base64编码,再将编码后的令牌密码进行AES加密,并设置令牌密码过期时间,过期时间等于当前时间加上application.yml中设置的过期时间,最后将加密后的令牌密钥和过期时间压缩成紧凑字符串返回出去。

/*
* @Description: JwtUtils工具类
**/
@Component
public class JwtUtil {
// 有效期
@Value("${jwt.token-validity-in-seconds}")
private Long JWT_EXPIRE;
// 令牌密钥
@Value("${jwt.secret-key}")
private String JWT_KEY;
public String createToken(Object data){
// 当前时间
Long currentTime = System.currentTimeMillis();
// 过期时间
Long expTime = currentTime+JWT_EXPIRE;
//jwt
JwtBuilder builder = Jwts.builder()
.setId(UUID.randomUUID()+"")
.setSubject(JSON.toJSONString(data))
.setIssuer("acme") // 设置JWT的发布者信息
.setIssuedAt(new Date(currentTime))
// 签名方法
.signWith(SignatureAlgorithm.HS256,encodeSecret(JWT_KEY))
.setExpiration(new Date(expTime));
return builder.compact();
} private SecretKey encodeSecret(String key) {
byte[] encode = Base64.getEncoder().encode(key.getBytes());
SecretKeySpec aes = new SecretKeySpec(encode,0,encode.length,"AES");
return aes;
} public Claims parseToken (String token) {
Claims body = Jwts.parser()
.setSigningKey(encodeSecret(JWT_KEY))
.parseClaimsJws(token)
.getBody();
return body;
} public <T> T parseToken (String token,Class<T> clazz) {
Claims body = Jwts.parser()
.setSigningKey(encodeSecret(JWT_KEY))
.parseClaimsJws(token)
.getBody();
return JSON.parseObject(body.getSubject(),clazz);
} }
6.2.3 测试用户登录接口

7. 获取当前用户信息

7.1 新增获取用户信息接口

在IUserService新增获取当前用户信息业务逻辑接口

/**
* 获取当前用户信息业务逻辑接口
* @param token
* @return
*/
Map<String, Object> getCurrentUserInfo(String token);

7.2 实现获取用户信息接口

在UserServiceImpl业务逻辑接口实现类中实现获取当前用户信息接口

@Override
public Map<String, Object> getCurrentUserInfo(String token) {
// 1. 根据 token 获取用户信息
User loginUser = null;
try {
loginUser = jwtUtil.parseToken(token,User.class);
} catch (Exception e) {
e.printStackTrace();
}
if (loginUser != null) {
Map<String, Object> data = new HashMap<>();
// 用户信息
data.put("username", loginUser.getUsername());
data.put("avatar", loginUser.getAvatar()); // 角色 // 权限列表 // 返回用户信息数据
return data;
}
return null;
}

7.3 调用获取用户信息接口

在UserController控制器新增获取用户信息方法,调用获取用户信息业务逻辑实现接口实现方法

/**
* 获取当前用户信息
* @param token
* @return
*/
@ApiOperation("当前用户信息")
@GetMapping("/info")
public Result<Map<String,Object>> getCurrentUserInfo(@RequestParam("token") String token){
// 根据 token 获取用户信息
Map<String,Object> data = userService.getCurrentUserInfo(token);
if (data != null) {
return Result.success(data);
}
return Result.fail(20003,"用户登录无效,请重新登录");
}

7.4 测试获取用户信息接口

8. 注销登录

8.1 新增注销登录接口

在IUserService新增用户登录业务逻辑接口

/**
* 注销登录
* @param token
* @return
*/
@PostMapping("logout")
public Result<?> logout(@RequestHeader("token") String token){
userService.logout(token);
return Result.success();
}

8.2 实现注销登录接口

在UserServiceImpl业务逻辑接口实现类中实现注销登录接口

@Override
public void logout(String token) {
}

8.3 调用注销登录接口

在UserController控制器新增注销登录方法,调用注销登录业务逻辑实现接口实现方法

/**
* 注销登录
* @param token
* @return
*/
@PostMapping("logout")
public Result<?> logout(@RequestHeader("token") String token){
userService.logout(token);
return Result.success();
}

9.前后端对接

解决前后端跨越问题

Access to XMLHttpRequest at 'http://localhost:9999/system/user/login' from origin
'http://localhost:9528' has been blocked by CORS policy: Response to preflight request
doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

9.1 后端跨域拦截器配置

9.1.1 在application.yml配置
# CorsUrl
corsUrl:
url: http://localhost:9528
9.1.2 跨域后端拦截器配置
package org.cn.common.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter; /*
* @Description: 跨域后端拦截器配置类
**/
@Configuration
public class CorsConfig {
// 允许前端服务访问后端接口
@Value("${corsUrl.url}")
private String CorsUrl; @Bean
public CorsFilter corsFilter() {
CorsConfiguration configuration = new CorsConfiguration(); // 允许什么网址来异步访问
configuration.addAllowedOrigin(CorsUrl);
// 获取cookie
configuration.setAllowCredentials(true);
// 允许什么方法? POST、GET,此处为* 意味全部允许
configuration.addAllowedMethod("*");
// 允许所有的请求头
configuration.addAllowedHeader("*"); // 设置资源过滤器,过滤什么资源
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",configuration); return new CorsFilter(urlBasedCorsConfigurationSource); }
}
9.1.3 Jwt拦截器放行配置
package org.cn.common.config;

import org.cn.common.interceptor.JwtValidateInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.annotation.Resource; @Configuration
public class JwtInterceptorConfig implements WebMvcConfigurer { @Resource
private JwtValidateInterceptor jwtValidateInterceptor; @Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(jwtValidateInterceptor);
registration.addPathPatterns("/**")
.excludePathPatterns(
"/system/user/login",
"/system/user/info",
"/system/user/logout",
"/swagger-ui/**",
"/swagger-resources/**",
"/v3/**",
"/error",
"/webjars/**",
"/v2/**",
"/public/**",
"/static/**",
"*.html",
"doc.html",
"/favicon.ico"
);
}
}

9.2 前后端联通测试

前端登录,查看页面控制台是否还报跨越的问题

10.系统管理

10.1 分页插件

实现分页查询,需要Mybatis Plus中分页插件配置成分页拦截器组件

package org.cn.common.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /*
* @Description: MybatisPlusConfig分页拦截器
**/
@Configuration
public class MybatisPlusPageConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.DM));//如果配置多个插件,切记分页最后添加
//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
return interceptor;
}
}

10.2 用户管理

10.2.1 用户列表
10.2.1.1 处理前端请求

①在UserController中接收前端发送 /system/user/list 请求

/**
* 分页查询
* @param username
* @param phone
* @param pageNo
* @param pageSize
* @return
*/
@ApiOperation("分页查询")
@GetMapping("/list")
public Result<Map<String,Object>> getUserList(
@RequestParam(value = "username",required = false) String username,
@RequestParam(value = "phone",required = false) String phone,
@RequestParam(value = "pageNo") Long pageNo,
@RequestParam(value = "pageSize") Long pageSize){
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StringUtils.hasLength(username),User::getUsername,username);
// 疑惑:为什么请求参数中phone改成Long就查不出数据,请求参数中的类型是String才能查出数据
wrapper.eq(StringUtils.hasLength(phone),User::getPhone,phone);
wrapper.orderByDesc(User::getId); Page<User> page = new Page<>(pageNo, pageSize);
userService.page(page,wrapper); Map<String,Object> data = new HashMap<>();
data.put("total",page.getTotal());
data.put("rows",page.getRecords()); return Result.success(data);
}
10.2.1.2 测试接口服务

②使用Postman/Apifox/Swagger等接口测试工具,获取token,传入参数,进行获取用户列表接口服务测试

注意:特别注意,测试工具的使用中,参数传入异常,折磨了两个小时,pageNo、pageSize参数未传入

Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'pageNo' for method parameter type Long is not present]

pageNo、pageSize参数正确传入

③前端获取数据进行展示

10.2.2 新增用户
10.2.2.1 处理前端请求

①在UserController中注入PasswordEncoder并调用后端服务接口

@Resource
private PasswordEncoder passwordEncoder;
// 新增用户
@ApiOperation("添加用户")
@PostMapping
public Result<?> addUser(@RequestBody User user){
user.setPassword(passwordEncoder.encode(user.getPassword()));
userService.addUser(user);
return Result.success("添加成功");
}
10.2.1.2 新增服务接口

②在IUserService中新增用户接口

/**
* 新增用户
* @param user
*/
void addUser(User user);
10.2.1.3 实现接口服务

③在UserServiceImpl实现新增用户接口

/**
* 新增用户
* @param user
*/
@Override
@Transactional
public void addUser(User user) {
// 写入用户表
this.baseMapper.insert(user);
// 写入用户角色表
}
10.2.1.4 测试接口服务

④使用Postman/Apifox/Swagger等接口测试工具,进行添加用户接口服务测试,出现了一个异常

SQL: INSERT INTO XGIS.X_USER  ( id, username, password, email, status, phone )  VALUES  ( ?, ?, ?, ?, ?, ? )
### Cause: dm.jdbc.driver.DMException: 仅当指定列列表,且SET IDENTITY_INSERT为ON时,才能对自增列赋值
SET IDENTITY_INSERT XGIS.X_USER ON; // 开启自增

SET IDENTITY_INSERT XGIS.X_USER OFF; // 关闭自增

ALTER TABLE XGIS.X_USER DROP IDENTITY; // 删除自增

10.2.3 修改用户
10.2.3.1 处理前端请求

在UserController控制器获取用户ID的方法

/**
* 根据前端传入用户id查询到单条用户信息,用于回显修改弹出框
* @param id
* @return
*/
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable("id") Integer id){
User user = userService.getById(id);
return Result.success(user);
}

在UserController控制器添加修改用户方法

/**
* 修改用户
* @param user
* @return
*/
@PutMapping
public Result<?> updateUser(@RequestBody User user){
user.setPassword(null);
userService.updateById(user);
return Result.success("修改成功");
}
10.2.3.2 测试接口服务

使用Postman/Apifox/Swagger等接口测试工具,进行修改用户接口服务测试。

10.2.4 删除用户
10.2.4.1 处理前端请求

在UserController控制器添加删除用户方法

/**
* 根据用户id删除用户
* @param id
* @return
*/
@DeleteMapping("/{id}")
public Result<User> deleteUserById(@PathVariable("id") Integer id){
userService.removeById(id);
return Result.success("删除成功");
}

在application.yml中配置逻辑删除

mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
logic-delete-field: deleted
logic-not-delete-value: 0
logic-delete-value: 1
mapper-locations: classpath*:mapper/*.xml
type-aliases-package: org.cn
10.2.4.2 测试接口服务

使用Postman/Apifox/Swagger等接口测试工具,进行删除用户接口服务测试。

10.3 角色管理

10.2.1 角色列表
10.2.1.1 处理前端请求

单表增删改查

package org.cn.system.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.cn.common.utils.Result;
import org.cn.system.entity.Role;
import org.cn.system.service.IRoleService;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller; import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map; /**
* <p>
* 角色表 前端控制器
* </p>
*
* @author LuMin
* @since 2024-11-26
*/
@Api(tags = "角色管理")
@RestController
@RequestMapping("/system/role")
public class RoleController { @Resource
private IRoleService roleService;
/**
* 角色列表
* @param roleName
* @param roleDesc
* @param pageNo
* @param pageSize
* @return
*/
@ApiOperation("角色列表")
@GetMapping("/list")
public Result<Map<String,Object>> getRoleList(
@RequestParam(value = "roleName",required = false) String roleName,
@RequestParam(value = "roleDesc",required = false) String roleDesc,
@RequestParam(value = "pageNo") Long pageNo,
@RequestParam(value = "pageSize") Long pageSize){
LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StringUtils.hasLength(roleName),Role::getRoleName,roleName);
wrapper.eq(StringUtils.hasLength(roleDesc),Role::getRoleDesc,roleDesc);
wrapper.orderByDesc(Role::getID); Page<Role> page = new Page<>(pageNo, pageSize);
roleService.page(page,wrapper); Map<String,Object> data = new HashMap<>();
data.put("total",page.getTotal());
data.put("rows",page.getRecords()); return Result.success(data);
} /**
* 添加角色
* @param role
* @return
*/
@ApiOperation("添加角色")
@PostMapping("/add")
public Result<?> addRole(@RequestBody Role role){
roleService.save(role);
return Result.success("添加成功");
} /**
* 根据页面点解获取角色id查询到单条角色信息,用于回显修改弹出框
* @param id
* @return
*/
@ApiOperation("单个角色")
@GetMapping("/{id}")
public Result<Role> getRoleById(@PathVariable("id") Integer id){
Role role = roleService.getById(id);
return Result.success(role);
} /**
* 修改角色
* @param role
* @return
*/
@ApiOperation("修改角色")
@PutMapping("/update")
public Result<?> updateRole(@RequestBody Role role){
roleService.updateById(role);
return Result.success("修改成功");
} /**
* 根据角色id删除角色信息
* @param id
* @return
*/
@ApiOperation("删除角色")
@DeleteMapping("/{id}")
public Result<Role> deleteRoleById(@PathVariable("id") Integer id){
roleService.removeById(id);
return Result.success("删除成功");
}
}
10.2.1.2 测试接口服务

Postman工具测试

10.4 角色菜单

10.4.1 菜单列表
10.4.1.1 处理前端请求

①在Menu实体中加上构建菜单属性

@ApiModelProperty("子菜单")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@TableField(exist = false)
private List<Menu> children; @TableField(exist = false)
@ApiModelProperty("构建meta对象")
private Map<String,Object> meta; public Map<String, Object> getMeta() {
meta = new HashMap<>();
meta.put("title",title);
meta.put("icon",icon);
return meta;
}

②在MenuController控制器种添加拦截请求

注意:将@Controller 改为@RestController,否则会报异常

异常:javax.servlet.ServletException: Could not resolve view with name 'system/menu/list' in servlet with name 'dispatcherServlet

@Resource
private IMenuService menuService; @ApiOperation("菜单列表")
@GetMapping("/list")
public Result<List<Menu>> getMenuList () {
List<Menu> menuList = menuService.getMenuList();
return Result.success(menuList);
}

③在IMenuService服务类种构建getMenuList()接口

/**
* 菜单列表
* @return
*/
List<Menu> getMenuList();

④在MenuServiceImpl实现类中实现getMenuList()接口

@Override
public List<Menu> getMenuList() {
// 一级菜单
LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Menu::getParentId,0);
List<Menu> menuList = this.list(wrapper); // 填充子菜单
setMenuChildren(menuList);
return menuList;
} private void setMenuChildren(List<Menu> menuList) {
if (menuList != null) {
for (Menu menu : menuList) {
LambdaQueryWrapper<Menu> subWrapper = new LambdaQueryWrapper<>();
subWrapper.eq(Menu::getParentId,menu.getId());
List<Menu> subMenuList = this.list(subWrapper);
menu.setChildren(subMenuList);
// 递归
setMenuChildren(subMenuList);
}
}
}
10.4.1.2 测试接口服务

⑤在Postman中测试getMenuList()接口

10.4.2 单条角色菜单
10.4.2.1 处理前端请求

①在Role实体中加入菜单属性

@ApiModelProperty("菜单列表")
@TableField(exist = false)
private List<Integer> menuIdList;

②在RoleController控制器中改造获取单个角色的方法

/**
* 根据角色id查询某个角色所拥有的菜单权限
* @param id
* @return
*/
@ApiOperation("单条角色菜单")
@GetMapping("/{id}")
public Result<Role> getRoleById(@PathVariable("id") Integer id){
Role role = roleService.getRoleById(id);
return Result.success(role);
}

③在IRoleService服务类中构建getRoleById()接口

/**
* 根据角色id查询某个角色所拥有的菜单权限
* @param id
* @return
*/
Role getRoleById(Integer id);

④在RoleServiceImpl实现类中实现getRoleById()接口

@Override
public Role getRoleById(Integer id) {
Role role = this.baseMapper.selectById(id);
List<Integer> menuIdList = roleMenuMapper.getMenuIdListByRoleId(id);
role.setMenuIdList(menuIdList);
return role;
}

⑤在RoleMenuMapper中构建查询数据库的getMenuIdListByRoleId()接口

/**
* 根据角色菜单关联表,关联查询出某个角色拥有多少菜单权限
* @param id
* @return
*/
List<Integer> getMenuIdListByRoleId(Integer id);

⑥在RoleMenuMapper.xml构建自定义查询菜单SQL

<select id="getMenuIdListByRoleId" parameterType="Integer" resultType="Integer">
SELECT XRM.MENU_ID
FROM XGIS.X_ROLE_MENU AS XRM,
XGIS.X_MENU AS XM
WHERE XRM.MENU_ID = XM.ID
AND XM.IS_LEAF = 'Y'
AND XRM.ROLE_ID = #{ID}
</select>
10.4.2.2 测试接口服务

⑦在Postman中测试getMenuList()接口

10.4.2 添加角色菜单
10.4.2.1 处理前端请求

①在RoleController控制器中添加addRole()方法

/**
* 添加角色菜单
* @param role
* @return
*/
@ApiOperation("添加角色菜单")
@PostMapping("/add")
public Result<?> addRole(@RequestBody Role role){
roleService.addRole(role);
return Result.success("添加成功");
}

②在IRoleService服务类中添加addRole()服务接口

/**
* 添加角色菜单
* @param role
*/
void addRole(Role role);

③在IRoleServiceImpl中实现addRole()服务接口

注意:使用roleMenuMapper.insert()时,需要在RoleMenu实体中添加构造方法,否则会报错

@Override
@Transactional
public void addRole(Role role) {
// 写入角色表
this.baseMapper.insert(role); // 写入菜单菜单关联表
if (null != role.getMenuIdList()) {
for (Integer menuId : role.getMenuIdList()){
roleMenuMapper.insert(new RoleMenu(null,role.getId(),menuId));
}
}
}

④在ReloMenu实体中添加构造方法

public RoleMenu(Object o, Integer roleId, Integer menuId) {
this.roleId = roleId;
this.menuId = menuId;
}
10.4.2.2 测试接口服务

⑦在Postman中测试addRole()接口

10.4.3 修改角色菜单
10.4.3.1 处理前端请求

①在RoleController控制器中添加updateRole()方法

/**
* 修改角色菜单:逻辑(根据角色Id把原先角色信息删除掉,再重新新增角色信息即可)
* @param role
* @return
*/
@ApiOperation("修改角色菜单")
@PutMapping("/update")
public Result<?> updateRole(@RequestBody Role role){
roleService.updateRole(role);
return Result.success("修改成功");
}

②在IRoleService服务类中添加 updateRole()服务接口

/**
* 修改角色菜单
* @param role
*/
void updateRole(Role role);

③在IRoleServiceImpl中实现addRole()服务接口

注意:使用roleMenuMapper.insert()时,需要在RoleMenu实体中添加构造方法,否则会报错

@Override
@Transactional
public void updateRole(Role role) {
// 修改角色表
this.baseMapper.updateById(role);
// 删除原有权限
LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RoleMenu::getRoleId,role.getId());
roleMenuMapper.delete(wrapper);
// 新增权限
if (null != role.getMenuIdList()) {
for (Integer menuId : role.getMenuIdList()){
roleMenuMapper.insert(new RoleMenu(null,role.getId(),menuId));
}
}
}

④在ReloMenu实体中添加构造方法

public RoleMenu(Object o, Integer roleId, Integer menuId) {
this.roleId = roleId;
this.menuId = menuId;
}
10.4.3.2 测试接口服务

⑦在Postman中测试updateRole()接口

10.4.4 删除角色菜单
10.4.4.1 处理前端请求

①在RoleController控制器中添加deleteRoleById()方法

/**
* 删除角色菜单
* @param roleId
* @return
*/
@DeleteMapping("/{roleId}")
public Result<Role> deleteRoleById(@PathVariable("roleId") Integer roleId){
roleService.deleteRoleById(roleId);
return Result.success("删除成功");
}

②在IRoleService服务类中添加 deleteRoleById()服务接口

/**
* 删除角色菜单
* @param roleId
*/
void deleteRoleById(Integer roleId);

③在IRoleServiceImpl中实现deleteRoleById()服务接口

@Override
@Transactional
public void deleteRoleById(Integer id) {
// 删除角色
this.baseMapper.deleteById(id);
// 删除权限
LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RoleMenu::getRoleId,id);
roleMenuMapper.delete(wrapper); }
10.4.4.2 测试接口服务

⑦在Postman中测试deleteRoleById()接口

10.5 用户角色

10.5.1 单条角色菜单
10.5.2.1 处理前端请求

①在User实体中加入角色属性

/**
* 角色列表
*/
@TableField(exist = false)
private List<Integer> roleIdList;

②在UserController控制器中改造获取单个用户的方法

/**
* 根据前端传入用户id查询到单条用户和角色信息,用于回显修改弹出框
* @param id
* @return
*/
@ApiOperation("单条用户角色")
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable("id") Integer id){
User user = userService.getUserById(id);
return Result.success(user);
}

③在IUserService服务类中构建getRoleById()接口

/**
* 获取单条用户角色
* @param id
* @return
*/
User getUserById(Integer id);

④在UserServiceImpl实现类中实现getUserById()接口

/**
* 单条用户角色
* @param id
* @return
*/
@Override
public User getUserById(Integer id) {
User user = this.baseMapper.selectById(id);
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserRole::getUserId,id);
List<UserRole> userRoleList = userRoleMapper.selectList(wrapper); List<Integer> roleIdList = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList()); user.setRoleIdList(roleIdList);
return user;
}
10.5.2.2 测试接口服务

⑤在Postman中测试getMenuList()接口

10.5.2 添加用户角色
10.5.2.1 处理前端请求

①在UserController控制器中添加addUser()方法

/**
* 添加用户角色
* @param user
* @return
*/
@ApiOperation("添加用户角色")
@PostMapping("/add")
public Result<?> addUser(@RequestBody User user){
user.setPassword(passwordEncoder.encode(user.getPassword()));
userService.addUser(user);
return Result.success("添加成功");
}

②在IUserService服务类中添加addUser()服务接口

/**
* 添加用户角色
* @param user
*/
void addUser(User user);

③在IUsererviceImpl中实现addUser()服务接口

注意:使用userRoleMapper.insert()时,需要在UserRole实体中添加构造方法,否则会报错

/**
* 添加用户角色
* @param user
*/
@Override
@Transactional
public void addUser(User user) {
// 写入用户表
this.baseMapper.insert(user);
// 写入用户角色表
List<Integer> roleIdList = user.getRoleIdList();
if (roleIdList != null) {
for (Integer roleId : roleIdList){
userRoleMapper.insert(new UserRole(null,user.getId(),roleId));
}
}
}

④在UserRole实体中添加构造方法

public UserRole(Object o, Integer id, Integer roleId) {
this.userId = id;
this.roleId = roleId;
}
10.4.5.2 测试接口服务

⑦在Postman中测试addUser()接口

10.5.3 修改用户角色
10.5.3.1 处理前端请求

①在UserController控制器中添加updateUser()方法

/**
* 修改用户角色
* @param user
* @return
*/
@ApiOperation("修改用户角色")
@PutMapping("/update")
public Result<?> updateUser(@RequestBody User user){
user.setPassword(null);
userService.updateUser(user);
return Result.success("修改成功");
}

②在IUserService服务类中添加updateUser()服务接口

/**
* 修改用户角色
* @param user
*/
void updateUser(User user);

③在IUsererviceImpl中实现updateUser()服务接口

注意:使用userRoleMapper.insert()时,需要在UserRole实体中添加构造方法,否则会报错

/**
* 修改用户角色
* @param user
*/
@Override
@Transactional
public void updateUser(User user) {
// 更新用户表
this.baseMapper.updateById(user);
// 删除原有角色
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserRole::getUserId,user.getId());
userRoleMapper.delete(wrapper);
// 设置新的角色
List<Integer> roleIdList = user.getRoleIdList();
if (roleIdList != null) {
for (Integer roleId : roleIdList){
userRoleMapper.insert(new UserRole(null,user.getId(),roleId));
}
}
}

④在UserRole实体中添加构造方法

public UserRole(Object o, Integer id, Integer roleId) {
this.userId = id;
this.roleId = roleId;
}
10.4.3.2 测试接口服务

⑦在Postman中测试updateUser()接口

10.5.4 删除用户角色
10.5.4.1 处理前端请求

①在UserController控制器中添加deleteUserById()方法

/**
* 删除用户角色
* @param id
* @return
*/
@ApiOperation("删除用户角色")
@DeleteMapping("/{id}")
public Result<User> deleteUserById(@PathVariable("id") Integer id){
userService.deleteUserById(id);
return Result.success("删除成功");
}

②在IUserService服务类中添加deleteUserById()服务接口

/**
* 删除用户角色
* @param id
*/
void deleteUserById(Integer id);

③在IUsererviceImpl中实现deleteUserById()服务接口

/**
* 删除用户角色
* @param id
*/
@Override
public void deleteUserById(Integer id) {
// 删除用户
this.baseMapper.deleteById(id);
// 删除角色
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserRole::getUserId,id);
userRoleMapper.delete(wrapper);
}
10.4.3.2 测试接口服务

⑦在Postman中测试deleteById()接口

11 动态路由

11.1 菜单列表

11.1.1 自定义查询SQL

在 MenuMapper.xml 中菜单列表关联查询SQL

<select id="getMenuListByUserId" resultType="org.cn.system.entity.Menu">
SELECT DISTINCT XM.*
FROM XGIS.X_USER_ROLE AS XUR,
XGIS.X_ROLE_MENU AS XRM,
XGIS.X_MENU AS XM,
WHERE XM.ID = XRM.MENU_ID
AND XRM.ROLE_ID = XUR.ROLE_ID
AND XUR.USER_ID = #{userId}
AND XM.PARENT_ID = #{parentId}
</select>
11.2 构建菜单列表Mapper接口

在 MenuMapper 中构建获取菜单列表接口:getMenuListByUserId()

/**
* 根据前端传入的userId查询到所属角色信息,根据用户角色关联表获取角色ID,
* 再根据角色ID查询所属菜单,并且通过传入参数中parentId是否存在判断是否有子菜单,用于动态路由
* @param userId
* @param parentId
* @return
*/
public List<Menu> getMenuListByUserId(@Param("userId") Integer userId, @Param("parentId") Integer parentId);
11.3 构建菜单列表Service接口

在 IMenuService 中构建获取菜单列表服务接口:getMenuListByUserId()

/**
* 根据前端传入的userId查询到所属角色信息,根据用户角色关联表获取角色ID,
* 再根据角色ID查询所属菜单,并且通过传入参数中parentId是否存在判断是否有子菜单,用于动态路由
* @param userId
* @return
*/
List<Menu> getMenuListByUserId(Integer userId);
11.4 实现菜单列表Service接口

在 MenuServiceImpl 中实现获取菜单列表服务接口:getMenuListByUserId()

/**
* 根据前端传入的userId查询到所属角色信息,根据用户角色关联表获取角色ID,
* 再根据角色ID查询所属菜单,并且通过传入参数中parentId是否存在判断是否有子菜单,用于动态路由
* @param userId
* @return
*/
@Override
public List<Menu> getMenuListByUserId(Integer userId) {
// 一级菜单
List<Menu> menuList = this.baseMapper.getMenuListByUserId(userId, 0);
// 子菜单
setMenuListByUserId(userId, menuList);
return menuList;
} private void setMenuListByUserId(Integer userId, List<Menu> menuList) {
if (menuList != null){
for (Menu menu : menuList) {
List<Menu> subMenuList = this.baseMapper.getMenuListByUserId(userId,menu.getId());
menu.setChildren(subMenuList);
// 递归
setMenuListByUserId(userId,subMenuList);
}
}
}

11.2 角色列表

11.2.1 自定义查询SQL

在 UserMapper.xml 中菜单列表关联查询SQL

<select id="getRoleNameByUserId" resultType="java.lang.String">
SELECT XR.ROLE_DESC
FROM XGIS.X_USER_ROLE AS XUR,
XGIS.X_ROLE AS XR
WHERE XUR.ROLE_ID = XR.ID
AND XUR.USER_ID = #{userId}
</select>
11.2.2 构建角色名称Mapper接口

在 UserMapper 中构建获取菜单列表接口:getRoleNameByUserId()

/**
* 根据用户ID获取角色名称
* @param id
* @return
*/
List<String> getRoleNameByUserId(Integer id);
11.3 获取所有角色信息列表
/**
* 角色列表
* @return
*/
@ApiOperation("角色列表")
@GetMapping("/all")
public Result<List<Role>> getAllRoleList(){
List<Role> roleList = roleService.list();
return Result.success(roleList);
}
11.4 测试用户角色接口服务

在UserController控制器中测试获取当前用户信息接口是否能够获取到用户对应的角色信息、菜单信息

调用顺序:getCurrentUserInfo() —>getRoleNameByUserId()—>getMenuListByUserId()

/**
* 获取当前用户信息:包括用户所有角色信息,用户所有菜单信息
* @param token
* @return
*/
@ApiOperation("当前用户信息")
@GetMapping("/info")
public Result<Map<String,Object>> getCurrentUserInfo(@RequestParam("token") String token){
// 根据 token 获取用户信息
Map<String,Object> data = userService.getCurrentUserInfo(token);
if (data != null) {
return Result.success(data);
}
return Result.fail(20003,"用户登录无效,请重新登录");
}

(二)Springboot + vue + 达梦数据库构建RBAC权限模型前后端分离脚手架保姆级教程(后端项目)的更多相关文章

  1. springboot+mybatis+达梦数据库

    准备工作: 首先,安装达梦6数据库.安装完之后如下建表 然后,很重要的一点(写法一定要这样写,否则无限报错) 达梦数据库查表方式: select  *  from    "库名". ...

  2. 国产达梦数据库的结合Enterprise Library的应用开发

    在上篇<基于Enterprise Library的Winform开发框架实现支持国产达梦数据库的扩展操作>介绍了如何在Enterprise Library的数据访问层上支持这种神秘的国产数 ...

  3. DB 查询分析器 6.03 如何灵活、快捷地操作国产达梦数据库

    DB 查询分析器 6.03 如何灵活.快捷地操作国产达梦数据库 马根峰 (广东联合电子服务股份有限公司, 广州 510300) 摘要       本文详细地介绍了"万能数据库查询分析器&qu ...

  4. Linux平台达梦数据库V7单实例安装方式之图形方式

    一 前言 我们在学习任何一个应用时,了解它的最初步骤通常是学会如何进行安装配置,后序才去关心如何使用,学习达梦数据库也是如此,而达梦数据库的安装提供了多种方式,接下来会一一介绍每种安装方式,达梦数据库 ...

  5. 达梦数据库学习(一、linux操作系统安装及数据库安装)

    达梦数据库学习(一.linux操作系统安装及数据库安装) 环境介绍: 使用VM12+中标麒麟V7.0操作系统+达梦8数据库 一.linux系统搭建 本部分没有需要着重介绍,注意安装时基本环境选择&qu ...

  6. 达梦数据库产品支持技术学习分享_Week2

    本周主要从以下几个方面进行本人对达梦数据库学习的分享,学习进度和学习情况因人而异,仅供参考. 一.文本命令行工具使用的方法(Disql和dmfldr) 二.数据库备份 三.定时作业功能 四.系统表和动 ...

  7. 达梦数据库产品支持技术学习分享_Week1

    本周主要从以下几个方面进行本人对达梦数据库学习的分享,学习进度和学习情况因人而异,仅供参考. 一.达梦数据库的体系架构 二.达梦数据库的安装 三.达梦数据库的数据类型 四.达梦数据库的DDL.DML. ...

  8. 基于Enterprise Library的Winform开发框架实现支持国产达梦数据库的扩展操作

    由于一个客户朋友的需求,需要我的Winform开发框架支持国产达梦数据库的操作,这个数据库很早就听过,但是真正一般项目用的很少,一般在一些特殊的项目可能需要用到.由于我的Winform开发框架,是基于 ...

  9. 达梦数据库DM7小结

    除了很多主流的数据库,我们很熟悉之外,越来越多的国产数据库也涌现出来. 这次就小结一些有关武汉的达梦数据库7这个开发版数据库的有别或者需要注意的地方进行一个简单备注吧. 1.第一件大事就是下载.数据库 ...

  10. Shell脚本使用汇总整理——达梦数据库备份脚本

    Shell脚本使用汇总整理——达梦数据库备份脚本 Shell脚本使用的基本知识点汇总详情见连接: https://www.cnblogs.com/lsy-blogs/p/9223477.html 脚本 ...

随机推荐

  1. Hydra(海德拉)工具使用从0到1,爆破服务器密码,2024最新版

    Hydra(海德拉)工具使用从0到1,爆破服务器密码,2024最新版 Hydra简介 Hydra又叫九头蛇,是一款由著名的黑客组织THC开发的开源暴力破解工具,支持大部分协议的在线密码破解,是网络安全 ...

  2. linux操作系统和文件系统,命令(上)

    Linux是一个类似于windows的操作系统 Linux操作系统的一种主要使用方式是通过终端软件:终端软件里只能使用键盘不能使用鼠标,在终端软件里通过输入命令完成各种任务 clear命令可以删除终端 ...

  3. Autodesk Maya无法打开 refrence file 解决办法

    删除预设 预设的位置在: 我的文档/maya/version/ prefs 备注: 我的文档/maya/version/ 无法确定是不是该路径下的所有文件夹都能删除,所以只剪切了 prefs 文件夹出 ...

  4. manim边学边做--图形间集合关系

    几何图形间的集合关系,是数学和几何学中的一个基本概念, 通过计算不同形状(如圆形.矩形.三角形等)的交集和并集等关系,可以实现复杂的图形处理和视觉效果. manim中提供了4种计算几何形状间集合关系的 ...

  5. NIO实现聊天室之:一切都要从网络编程的基础开始聊起!

    一.写在开头 大家好,Build哥回来啦!停更了大概2个月之久,之前有段时间去写小说去了,后来又因为公司活太多,牛马干的太投入,就拉下了博客的更新,国庆节期间,难得的闲下来,准备回归老本行啦. 大致的 ...

  6. WebBrowser中打开新页面

    要求新打开的网页受控于WebBrowser 解决办法很简单,分两情况,一是在当前WebBrowser中打开新页面,二是在新Form中的WebBrowser中打开新页面: public Form1() ...

  7. ZRAM的Swap功能和 SWAP分区有什么区别

    ZRAM(压缩内存块设备)和传统的SWAP分区都是Linux系统中用来增加可用内存的方法,但它们的工作原理和实现方式有很大的区别: ZRAM 压缩内存:ZRAM使用压缩算法将数据存储在内存中.这样,当 ...

  8. webgl和canvas的区别

    webgl和canvas的区别 WebGL和Canvas的主要区别在于它们的渲染方式.功能复杂性.以及编程难度.12 渲染方式:Canvas使用2D渲染上下文来绘制图形和图像,基于像素的绘图系统, ...

  9. kotlin类与对象——>数据类、密封类、泛型

    数据类,用来保存数据的类,使用data对class进行标记 data class User(val name: String, val age: Int) //编译器自动从主构造函数中声明的所有属性导 ...

  10. python数据结构学习第一章——栈

    在这片文章中,我们使用python3.8自制一个具有基本功能的栈结构,它的功能只有push,pop,peek这三个功能 ` #!/usr/bin/env python # * coding: utf- ...