feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.destiny</groupId>
|
||||
<artifactId>everything-is-suitable-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>everything-is-suitable-sys</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Everything Is Suitable Sys</name>
|
||||
<description>System entities and utilities for Everything Is Suitable API</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.destiny</groupId>
|
||||
<artifactId>everything-is-suitable-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javers</groupId>
|
||||
<artifactId>javers-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package io.destiny.sys.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OperationLog {
|
||||
|
||||
String moduleName() default "";
|
||||
|
||||
String operationDesc() default "";
|
||||
|
||||
boolean recordRequest() default true;
|
||||
|
||||
boolean recordResponse() default true;
|
||||
|
||||
boolean maskSensitive() default true;
|
||||
|
||||
boolean recordDiff() default false;
|
||||
|
||||
String diffParamIndex() default "";
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package io.destiny.sys.aspect;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.destiny.sys.core.domain.OperationLog;
|
||||
import io.destiny.sys.core.service.IDataMaskingService;
|
||||
import io.destiny.sys.core.service.IJaversAuditService;
|
||||
import io.destiny.sys.core.service.IOperationLogService;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
public class OperationLogAspect {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OperationLogAspect.class);
|
||||
|
||||
private final IOperationLogService logService;
|
||||
|
||||
private final IDataMaskingService maskingService;
|
||||
|
||||
private final IJaversAuditService javersAuditService;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public OperationLogAspect(IOperationLogService logService, IDataMaskingService maskingService,
|
||||
IJaversAuditService javersAuditService, ObjectMapper objectMapper) {
|
||||
this.logService = logService;
|
||||
this.maskingService = maskingService;
|
||||
this.javersAuditService = javersAuditService;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Around("@annotation(operationLog)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, io.destiny.sys.annotation.OperationLog operationLog)
|
||||
throws Throwable {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
OperationLog operationLogEntity = new OperationLogEntity();
|
||||
operationLogEntity.setOperationTime(LocalDateTime.now());
|
||||
operationLogEntity.setModuleName(operationLog.moduleName());
|
||||
operationLogEntity.setOperationDesc(operationLog.operationDesc());
|
||||
|
||||
Object[] args = joinPoint.getArgs();
|
||||
for (Object arg : args) {
|
||||
if (arg instanceof ServerWebExchange) {
|
||||
ServerWebExchange exchange = (ServerWebExchange) arg;
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
operationLogEntity.setRequestPath(request.getPath().value());
|
||||
operationLogEntity.setRequestMethod(request.getMethod().name());
|
||||
operationLogEntity.setIpAddress(
|
||||
request.getRemoteAddress() != null ? request.getRemoteAddress().getAddress().getHostAddress()
|
||||
: "unknown");
|
||||
}
|
||||
}
|
||||
|
||||
if (operationLog.recordRequest()) {
|
||||
String requestParams = toJson(args);
|
||||
if (operationLog.maskSensitive()) {
|
||||
requestParams = maskingService.maskSensitiveData(requestParams).block();
|
||||
}
|
||||
operationLogEntity.setRequestParams(requestParams);
|
||||
}
|
||||
|
||||
Object oldObject = null;
|
||||
Object newObject = null;
|
||||
|
||||
if (operationLog.recordDiff() && !operationLog.diffParamIndex().isEmpty()) {
|
||||
try {
|
||||
String[] indices = operationLog.diffParamIndex().split(",");
|
||||
if (indices.length == 2) {
|
||||
int oldIndex = Integer.parseInt(indices[0].trim());
|
||||
int newIndex = Integer.parseInt(indices[1].trim());
|
||||
if (oldIndex < args.length && newIndex < args.length) {
|
||||
oldObject = args[oldIndex];
|
||||
newObject = args[newIndex];
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析diffParamIndex失败: {}", operationLog.diffParamIndex(), e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Object result = joinPoint.proceed();
|
||||
long endTime = System.currentTimeMillis();
|
||||
|
||||
operationLogEntity.setStatus("0");
|
||||
operationLogEntity.setExecutionTime(endTime - startTime);
|
||||
|
||||
if (operationLog.recordResponse()) {
|
||||
String responseResult = toJson(result);
|
||||
if (operationLog.maskSensitive()) {
|
||||
responseResult = maskingService.maskSensitiveData(responseResult).block();
|
||||
}
|
||||
operationLogEntity.setResponseResult(responseResult);
|
||||
}
|
||||
|
||||
if (operationLog.recordDiff() && oldObject != null && newObject != null) {
|
||||
javersAuditService.compare(oldObject, newObject)
|
||||
.doOnNext(diffJson -> operationLogEntity.setDiffJson(diffJson))
|
||||
.doOnError(error -> log.warn("生成对象差异失败", error))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
long endTime = System.currentTimeMillis();
|
||||
|
||||
operationLogEntity.setStatus("1");
|
||||
operationLogEntity.setExecutionTime(endTime - startTime);
|
||||
operationLogEntity.setExceptionMessage(ExceptionUtils.getStackTrace(e));
|
||||
|
||||
throw e;
|
||||
} finally {
|
||||
logService.save(operationLogEntity).subscribe(
|
||||
saved -> log.debug("操作日志保存成功"),
|
||||
error -> log.warn("操作日志保存失败,但不影响主业务流程", error));
|
||||
}
|
||||
}
|
||||
|
||||
private String toJson(Object obj) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(obj);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Failed to serialize object to JSON", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class OperationLogEntity extends OperationLog {
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package io.destiny.sys.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class SysCorsConfig {
|
||||
|
||||
private List<String> allowedOrigins = new ArrayList<>();
|
||||
|
||||
private List<String> allowedMethods = new ArrayList<>();
|
||||
|
||||
private List<String> allowedHeaders = new ArrayList<>();
|
||||
|
||||
private boolean allowCredentials = true;
|
||||
|
||||
private int maxAge = 3600;
|
||||
|
||||
public List<String> getAllowedOrigins() {
|
||||
return allowedOrigins;
|
||||
}
|
||||
|
||||
public void setAllowedOrigins(List<String> allowedOrigins) {
|
||||
this.allowedOrigins = allowedOrigins;
|
||||
}
|
||||
|
||||
public List<String> getAllowedMethods() {
|
||||
return allowedMethods;
|
||||
}
|
||||
|
||||
public void setAllowedMethods(List<String> allowedMethods) {
|
||||
this.allowedMethods = allowedMethods;
|
||||
}
|
||||
|
||||
public List<String> getAllowedHeaders() {
|
||||
return allowedHeaders;
|
||||
}
|
||||
|
||||
public void setAllowedHeaders(List<String> allowedHeaders) {
|
||||
this.allowedHeaders = allowedHeaders;
|
||||
}
|
||||
|
||||
public boolean isAllowCredentials() {
|
||||
return allowCredentials;
|
||||
}
|
||||
|
||||
public void setAllowCredentials(boolean allowCredentials) {
|
||||
this.allowCredentials = allowCredentials;
|
||||
}
|
||||
|
||||
public int getMaxAge() {
|
||||
return maxAge;
|
||||
}
|
||||
|
||||
public void setMaxAge(int maxAge) {
|
||||
this.maxAge = maxAge;
|
||||
}
|
||||
}
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
package io.destiny.sys.config;
|
||||
|
||||
import io.destiny.sys.handler.SysAuthHandler;
|
||||
import io.destiny.sys.handler.SysMenuHandler;
|
||||
import io.destiny.sys.handler.SysRoleHandler;
|
||||
import io.destiny.sys.handler.SysUserHandler;
|
||||
import io.destiny.sys.handler.OperationLogHandler;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springdoc.core.annotations.RouterOperation;
|
||||
import org.springdoc.core.annotations.RouterOperations;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
|
||||
|
||||
@Configuration
|
||||
@Tag(name = "系统管理", description = "系统管理相关接口")
|
||||
public class SysRouter {
|
||||
|
||||
private final SysUserHandler userHandler;
|
||||
private final SysRoleHandler roleHandler;
|
||||
private final SysMenuHandler menuHandler;
|
||||
private final SysAuthHandler authHandler;
|
||||
private final OperationLogHandler operationLogHandler;
|
||||
|
||||
public SysRouter(SysUserHandler userHandler, SysRoleHandler roleHandler, SysMenuHandler menuHandler, SysAuthHandler authHandler, OperationLogHandler operationLogHandler) {
|
||||
this.userHandler = userHandler;
|
||||
this.roleHandler = roleHandler;
|
||||
this.menuHandler = menuHandler;
|
||||
this.authHandler = authHandler;
|
||||
this.operationLogHandler = operationLogHandler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@RouterOperations({
|
||||
@RouterOperation(
|
||||
path = "/sys/auth/register",
|
||||
method = RequestMethod.POST,
|
||||
beanClass = SysAuthHandler.class,
|
||||
beanMethod = "register"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/auth/login",
|
||||
method = RequestMethod.POST,
|
||||
beanClass = SysAuthHandler.class,
|
||||
beanMethod = "login"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/auth/refresh/{token}",
|
||||
method = RequestMethod.POST,
|
||||
beanClass = SysAuthHandler.class,
|
||||
beanMethod = "refreshToken"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/auth/logout",
|
||||
method = RequestMethod.POST,
|
||||
beanClass = SysAuthHandler.class,
|
||||
beanMethod = "logout"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/user/{id}",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysUserHandler.class,
|
||||
beanMethod = "getUserById"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/user/username/{username}",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysUserHandler.class,
|
||||
beanMethod = "getUserByUsername"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/user",
|
||||
method = RequestMethod.POST,
|
||||
beanClass = SysUserHandler.class,
|
||||
beanMethod = "createUser"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/user",
|
||||
method = RequestMethod.PUT,
|
||||
beanClass = SysUserHandler.class,
|
||||
beanMethod = "updateUser"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/user/{id}",
|
||||
method = RequestMethod.DELETE,
|
||||
beanClass = SysUserHandler.class,
|
||||
beanMethod = "deleteUser"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/user/{id}/ban",
|
||||
method = RequestMethod.PUT,
|
||||
beanClass = SysUserHandler.class,
|
||||
beanMethod = "banUser"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/user/{id}/unban",
|
||||
method = RequestMethod.PUT,
|
||||
beanClass = SysUserHandler.class,
|
||||
beanMethod = "unbanUser"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/user",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysUserHandler.class,
|
||||
beanMethod = "getAllUsers"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/user/page",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysUserHandler.class,
|
||||
beanMethod = "queryUsersWithPagination"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/role/{id}",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysRoleHandler.class,
|
||||
beanMethod = "getRoleById"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/role/roleKey/{roleKey}",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysRoleHandler.class,
|
||||
beanMethod = "getRoleByRoleKey"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/role/user/{userId}",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysRoleHandler.class,
|
||||
beanMethod = "getRolesByUserId"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/role",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysRoleHandler.class,
|
||||
beanMethod = "getAllRoles"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/role",
|
||||
method = RequestMethod.POST,
|
||||
beanClass = SysRoleHandler.class,
|
||||
beanMethod = "createRole"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/role",
|
||||
method = RequestMethod.PUT,
|
||||
beanClass = SysRoleHandler.class,
|
||||
beanMethod = "updateRole"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/role/{id}",
|
||||
method = RequestMethod.DELETE,
|
||||
beanClass = SysRoleHandler.class,
|
||||
beanMethod = "deleteRole"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/role/assign",
|
||||
method = RequestMethod.POST,
|
||||
beanClass = SysRoleHandler.class,
|
||||
beanMethod = "assignRoleToUser"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/role/user/{userId}/role/{roleId}",
|
||||
method = RequestMethod.DELETE,
|
||||
beanClass = SysRoleHandler.class,
|
||||
beanMethod = "removeRoleFromUser"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/menu/{id}",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysMenuHandler.class,
|
||||
beanMethod = "getMenuById"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/menu/role/{roleId}",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysMenuHandler.class,
|
||||
beanMethod = "getMenusByRoleId"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/menu/user/{userId}",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysMenuHandler.class,
|
||||
beanMethod = "getMenusByUserId"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/menu",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = SysMenuHandler.class,
|
||||
beanMethod = "getAllMenus"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/menu",
|
||||
method = RequestMethod.POST,
|
||||
beanClass = SysMenuHandler.class,
|
||||
beanMethod = "createMenu"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/menu",
|
||||
method = RequestMethod.PUT,
|
||||
beanClass = SysMenuHandler.class,
|
||||
beanMethod = "updateMenu"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/menu/{id}",
|
||||
method = RequestMethod.DELETE,
|
||||
beanClass = SysMenuHandler.class,
|
||||
beanMethod = "deleteMenu"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/menu/assign",
|
||||
method = RequestMethod.POST,
|
||||
beanClass = SysMenuHandler.class,
|
||||
beanMethod = "assignMenuToRole"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/menu/role/{roleId}/menu/{menuId}",
|
||||
method = RequestMethod.DELETE,
|
||||
beanClass = SysMenuHandler.class,
|
||||
beanMethod = "removeMenuFromRole"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/operationLog/{id}",
|
||||
method = RequestMethod.GET,
|
||||
beanClass = OperationLogHandler.class,
|
||||
beanMethod = "getLogById"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/operationLog/query",
|
||||
method = RequestMethod.POST,
|
||||
beanClass = OperationLogHandler.class,
|
||||
beanMethod = "queryLogs"
|
||||
),
|
||||
@RouterOperation(
|
||||
path = "/sys/operationLog/{id}",
|
||||
method = RequestMethod.DELETE,
|
||||
beanClass = OperationLogHandler.class,
|
||||
beanMethod = "deleteLog"
|
||||
)
|
||||
})
|
||||
@Operation(summary = "系统管理路由配置")
|
||||
public RouterFunction<ServerResponse> sysRoutes() {
|
||||
return RouterFunctions.route()
|
||||
.path("/sys", builder -> builder
|
||||
.path("/auth", authBuilder -> authBuilder
|
||||
.POST("/register", accept(MediaType.APPLICATION_JSON), authHandler::register)
|
||||
.POST("/login", accept(MediaType.APPLICATION_JSON), authHandler::login)
|
||||
.POST("/refresh/{token}", accept(MediaType.APPLICATION_JSON), authHandler::refreshToken)
|
||||
.POST("/logout", accept(MediaType.APPLICATION_JSON), authHandler::logout))
|
||||
.path("/user", userBuilder -> userBuilder
|
||||
.GET("/{id}", accept(MediaType.APPLICATION_JSON), userHandler::getUserById)
|
||||
.GET("/username/{username}", accept(MediaType.APPLICATION_JSON), userHandler::getUserByUsername)
|
||||
.GET("/page", accept(MediaType.APPLICATION_JSON), userHandler::queryUsersWithPagination)
|
||||
.GET("", accept(MediaType.APPLICATION_JSON), userHandler::getAllUsers)
|
||||
.POST("", accept(MediaType.APPLICATION_JSON), userHandler::createUser)
|
||||
.PUT("", accept(MediaType.APPLICATION_JSON), userHandler::updateUser)
|
||||
.DELETE("/{id}", accept(MediaType.APPLICATION_JSON), userHandler::deleteUser)
|
||||
.PUT("/{id}/ban", accept(MediaType.APPLICATION_JSON), userHandler::banUser)
|
||||
.PUT("/{id}/unban", accept(MediaType.APPLICATION_JSON), userHandler::unbanUser))
|
||||
.path("/role", roleBuilder -> roleBuilder
|
||||
.GET("/{id}", accept(MediaType.APPLICATION_JSON), roleHandler::getRoleById)
|
||||
.GET("/roleKey/{roleKey}", accept(MediaType.APPLICATION_JSON), roleHandler::getRoleByRoleKey)
|
||||
.GET("/user/{userId}", accept(MediaType.APPLICATION_JSON), roleHandler::getRolesByUserId)
|
||||
.GET("", accept(MediaType.APPLICATION_JSON), roleHandler::getAllRoles)
|
||||
.POST("", accept(MediaType.APPLICATION_JSON), roleHandler::createRole)
|
||||
.PUT("", accept(MediaType.APPLICATION_JSON), roleHandler::updateRole)
|
||||
.DELETE("/{id}", accept(MediaType.APPLICATION_JSON), roleHandler::deleteRole)
|
||||
.POST("/assign", accept(MediaType.APPLICATION_JSON), roleHandler::assignRoleToUser)
|
||||
.DELETE("/user/{userId}/role/{roleId}", accept(MediaType.APPLICATION_JSON), roleHandler::removeRoleFromUser))
|
||||
.path("/menu", menuBuilder -> menuBuilder
|
||||
.GET("/{id}", accept(MediaType.APPLICATION_JSON), menuHandler::getMenuById)
|
||||
.GET("/role/{roleId}", accept(MediaType.APPLICATION_JSON), menuHandler::getMenusByRoleId)
|
||||
.GET("/user/{userId}", accept(MediaType.APPLICATION_JSON), menuHandler::getMenusByUserId)
|
||||
.GET("", accept(MediaType.APPLICATION_JSON), menuHandler::getAllMenus)
|
||||
.POST("", accept(MediaType.APPLICATION_JSON), menuHandler::createMenu)
|
||||
.PUT("", accept(MediaType.APPLICATION_JSON), menuHandler::updateMenu)
|
||||
.DELETE("/{id}", accept(MediaType.APPLICATION_JSON), menuHandler::deleteMenu)
|
||||
.POST("/assign", accept(MediaType.APPLICATION_JSON), menuHandler::assignMenuToRole)
|
||||
.DELETE("/role/{roleId}/menu/{menuId}", accept(MediaType.APPLICATION_JSON), menuHandler::removeMenuFromRole))
|
||||
.path("/operationLog", logBuilder -> logBuilder
|
||||
.GET("/{id}", accept(MediaType.APPLICATION_JSON), operationLogHandler::getLogById)
|
||||
.POST("/query", accept(MediaType.APPLICATION_JSON), operationLogHandler::queryLogs)
|
||||
.DELETE("/{id}", accept(MediaType.APPLICATION_JSON), operationLogHandler::deleteLog)))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package io.destiny.sys.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
public class SysSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
package io.destiny.sys.core.domain;
|
||||
|
||||
import io.destiny.common.utils.SnowflakeId;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class OperationLog {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String operator;
|
||||
|
||||
private LocalDateTime operationTime;
|
||||
|
||||
private String requestPath;
|
||||
|
||||
private String requestMethod;
|
||||
|
||||
private String requestParams;
|
||||
|
||||
private String responseResult;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
private Long executionTime;
|
||||
|
||||
private String status;
|
||||
|
||||
private String exceptionMessage;
|
||||
|
||||
private String moduleName;
|
||||
|
||||
private String operationDesc;
|
||||
|
||||
private String diffJson;
|
||||
|
||||
private String createBy;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public void setOperator(String operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
public LocalDateTime getOperationTime() {
|
||||
return operationTime;
|
||||
}
|
||||
|
||||
public void setOperationTime(LocalDateTime operationTime) {
|
||||
this.operationTime = operationTime;
|
||||
}
|
||||
|
||||
public String getRequestPath() {
|
||||
return requestPath;
|
||||
}
|
||||
|
||||
public void setRequestPath(String requestPath) {
|
||||
this.requestPath = requestPath;
|
||||
}
|
||||
|
||||
public String getRequestMethod() {
|
||||
return requestMethod;
|
||||
}
|
||||
|
||||
public void setRequestMethod(String requestMethod) {
|
||||
this.requestMethod = requestMethod;
|
||||
}
|
||||
|
||||
public String getRequestParams() {
|
||||
return requestParams;
|
||||
}
|
||||
|
||||
public void setRequestParams(String requestParams) {
|
||||
this.requestParams = requestParams;
|
||||
}
|
||||
|
||||
public String getResponseResult() {
|
||||
return responseResult;
|
||||
}
|
||||
|
||||
public void setResponseResult(String responseResult) {
|
||||
this.responseResult = responseResult;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public Long getExecutionTime() {
|
||||
return executionTime;
|
||||
}
|
||||
|
||||
public void setExecutionTime(Long executionTime) {
|
||||
this.executionTime = executionTime;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getExceptionMessage() {
|
||||
return exceptionMessage;
|
||||
}
|
||||
|
||||
public void setExceptionMessage(String exceptionMessage) {
|
||||
this.exceptionMessage = exceptionMessage;
|
||||
}
|
||||
|
||||
public String getModuleName() {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
public void setModuleName(String moduleName) {
|
||||
this.moduleName = moduleName;
|
||||
}
|
||||
|
||||
public String getOperationDesc() {
|
||||
return operationDesc;
|
||||
}
|
||||
|
||||
public void setOperationDesc(String operationDesc) {
|
||||
this.operationDesc = operationDesc;
|
||||
}
|
||||
|
||||
public String getDiffJson() {
|
||||
return diffJson;
|
||||
}
|
||||
|
||||
public void setDiffJson(String diffJson) {
|
||||
this.diffJson = diffJson;
|
||||
}
|
||||
|
||||
public String getCreateBy() {
|
||||
return createBy;
|
||||
}
|
||||
|
||||
public void setCreateBy(String createBy) {
|
||||
this.createBy = createBy;
|
||||
}
|
||||
|
||||
public String getUpdateBy() {
|
||||
return updateBy;
|
||||
}
|
||||
|
||||
public void setUpdateBy(String updateBy) {
|
||||
this.updateBy = updateBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
public Long generateId() {
|
||||
this.id = SnowflakeId.nextId();
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deletedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
package io.destiny.sys.core.domain;
|
||||
|
||||
import io.destiny.common.utils.SnowflakeId;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SysMenu {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String menuName;
|
||||
|
||||
private Long parentId;
|
||||
|
||||
private Integer orderNum;
|
||||
|
||||
private String menuType;
|
||||
|
||||
private String perms;
|
||||
|
||||
private String component;
|
||||
|
||||
private String status;
|
||||
|
||||
private String createBy;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getMenuName() {
|
||||
return menuName;
|
||||
}
|
||||
|
||||
public void setMenuName(String menuName) {
|
||||
this.menuName = menuName;
|
||||
}
|
||||
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public Integer getOrderNum() {
|
||||
return orderNum;
|
||||
}
|
||||
|
||||
public void setOrderNum(Integer orderNum) {
|
||||
this.orderNum = orderNum;
|
||||
}
|
||||
|
||||
public String getMenuType() {
|
||||
return menuType;
|
||||
}
|
||||
|
||||
public void setMenuType(String menuType) {
|
||||
this.menuType = menuType;
|
||||
}
|
||||
|
||||
public String getPerms() {
|
||||
return perms;
|
||||
}
|
||||
|
||||
public void setPerms(String perms) {
|
||||
this.perms = perms;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return perms;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.perms = code;
|
||||
}
|
||||
|
||||
public String getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
public void setComponent(String component) {
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getCreateBy() {
|
||||
return createBy;
|
||||
}
|
||||
|
||||
public void setCreateBy(String createBy) {
|
||||
this.createBy = createBy;
|
||||
}
|
||||
|
||||
public String getUpdateBy() {
|
||||
return updateBy;
|
||||
}
|
||||
|
||||
public void setUpdateBy(String updateBy) {
|
||||
this.updateBy = updateBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
public Long generateId() {
|
||||
this.id = SnowflakeId.nextId();
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deletedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package io.destiny.sys.core.domain;
|
||||
|
||||
import io.destiny.common.utils.SnowflakeId;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SysRole {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String roleName;
|
||||
|
||||
private String roleKey;
|
||||
|
||||
private Integer roleSort;
|
||||
|
||||
private String status;
|
||||
|
||||
private String createBy;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getRoleName() {
|
||||
return roleName;
|
||||
}
|
||||
|
||||
public void setRoleName(String roleName) {
|
||||
this.roleName = roleName;
|
||||
}
|
||||
|
||||
public String getRoleKey() {
|
||||
return roleKey;
|
||||
}
|
||||
|
||||
public void setRoleKey(String roleKey) {
|
||||
this.roleKey = roleKey;
|
||||
}
|
||||
|
||||
public Integer getRoleSort() {
|
||||
return roleSort;
|
||||
}
|
||||
|
||||
public void setRoleSort(Integer roleSort) {
|
||||
this.roleSort = roleSort;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getCreateBy() {
|
||||
return createBy;
|
||||
}
|
||||
|
||||
public void setCreateBy(String createBy) {
|
||||
this.createBy = createBy;
|
||||
}
|
||||
|
||||
public String getUpdateBy() {
|
||||
return updateBy;
|
||||
}
|
||||
|
||||
public void setUpdateBy(String updateBy) {
|
||||
this.updateBy = updateBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
public Long generateId() {
|
||||
this.id = SnowflakeId.nextId();
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deletedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.destiny.sys.core.domain;
|
||||
|
||||
public class SysRoleMenu {
|
||||
|
||||
private Long roleId;
|
||||
|
||||
private Long menuId;
|
||||
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public Long getMenuId() {
|
||||
return menuId;
|
||||
}
|
||||
|
||||
public void setMenuId(Long menuId) {
|
||||
this.menuId = menuId;
|
||||
}
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
package io.destiny.sys.core.domain;
|
||||
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
import io.destiny.common.primitive.EmailAddress;
|
||||
import io.destiny.common.utils.SnowflakeId;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SysUser {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private EmailAddress email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private PhoneNumber phone;
|
||||
|
||||
/**
|
||||
* 状态 0-正常 1-禁用
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 删除时间
|
||||
*/
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public EmailAddress getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(EmailAddress email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public PhoneNumber getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(PhoneNumber phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getCreateBy() {
|
||||
return createBy;
|
||||
}
|
||||
|
||||
public void setCreateBy(String createBy) {
|
||||
this.createBy = createBy;
|
||||
}
|
||||
|
||||
public String getUpdateBy() {
|
||||
return updateBy;
|
||||
}
|
||||
|
||||
public void setUpdateBy(String updateBy) {
|
||||
this.updateBy = updateBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成主键ID
|
||||
*
|
||||
* @return 主键ID
|
||||
*/
|
||||
public Long generateId() {
|
||||
this.id = SnowflakeId.nextId();
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
public void delete() {
|
||||
this.deletedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.destiny.sys.core.domain;
|
||||
|
||||
public class SysUserRole {
|
||||
|
||||
private Long userId;
|
||||
|
||||
private Long roleId;
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package io.destiny.sys.core.domain.query;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class OperationLogQuery {
|
||||
|
||||
private String operator;
|
||||
|
||||
private LocalDateTime startTime;
|
||||
|
||||
private LocalDateTime endTime;
|
||||
|
||||
private String moduleName;
|
||||
|
||||
private String status;
|
||||
|
||||
private String requestPath;
|
||||
|
||||
private Integer pageNum;
|
||||
|
||||
private Integer pageSize;
|
||||
|
||||
public String getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public void setOperator(String operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
public LocalDateTime getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(LocalDateTime startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setEndTime(LocalDateTime endTime) {
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public String getModuleName() {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
public void setModuleName(String moduleName) {
|
||||
this.moduleName = moduleName;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getRequestPath() {
|
||||
return requestPath;
|
||||
}
|
||||
|
||||
public void setRequestPath(String requestPath) {
|
||||
this.requestPath = requestPath;
|
||||
}
|
||||
|
||||
public Integer getPageNum() {
|
||||
return pageNum;
|
||||
}
|
||||
|
||||
public void setPageNum(Integer pageNum) {
|
||||
this.pageNum = pageNum;
|
||||
}
|
||||
|
||||
public Integer getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize(Integer pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public Integer getOffset() {
|
||||
if (pageNum == null || pageSize == null) {
|
||||
return null;
|
||||
}
|
||||
return (pageNum - 1) * pageSize;
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package io.destiny.sys.core.domain.query;
|
||||
|
||||
import io.destiny.common.primitive.EmailAddress;
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
|
||||
public class SysUserQuery {
|
||||
|
||||
private String username;
|
||||
|
||||
private EmailAddress email;
|
||||
|
||||
private PhoneNumber phone;
|
||||
|
||||
private String status;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public EmailAddress getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(EmailAddress email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public PhoneNumber getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(PhoneNumber phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.destiny.sys.core.repository;
|
||||
|
||||
import io.destiny.sys.core.domain.OperationLog;
|
||||
import io.destiny.sys.core.domain.query.OperationLogQuery;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface IOperationLogRepository {
|
||||
|
||||
Mono<OperationLog> findById(Long id);
|
||||
|
||||
Mono<OperationLog> save(OperationLog operationLog);
|
||||
|
||||
Flux<OperationLog> findByQuery(OperationLogQuery query);
|
||||
|
||||
Mono<Long> countByQuery(OperationLogQuery query);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.destiny.sys.core.repository;
|
||||
|
||||
import io.destiny.sys.core.domain.SysMenu;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysMenuRepository {
|
||||
|
||||
Mono<SysMenu> findById(Long id);
|
||||
|
||||
Flux<SysMenu> findByRoleId(Long roleId);
|
||||
|
||||
Flux<SysMenu> findByUserId(Long userId);
|
||||
|
||||
Flux<SysMenu> findAll();
|
||||
|
||||
Mono<SysMenu> save(SysMenu sysMenu);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package io.destiny.sys.core.repository;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysRoleMenuRepository {
|
||||
|
||||
Flux<Long> findMenuIdsByRoleId(Long roleId);
|
||||
|
||||
Mono<Void> assignMenuToRole(Long roleId, Long menuId);
|
||||
|
||||
Mono<Void> removeMenuFromRole(Long roleId, Long menuId);
|
||||
|
||||
Mono<Void> removeAllMenusFromRole(Long roleId);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.destiny.sys.core.repository;
|
||||
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysRoleRepository {
|
||||
|
||||
Mono<SysRole> findById(Long id);
|
||||
|
||||
Mono<SysRole> findByRoleKey(String roleKey);
|
||||
|
||||
Flux<SysRole> findByUserId(Long userId);
|
||||
|
||||
Flux<SysRole> findAll();
|
||||
|
||||
Mono<SysRole> save(SysRole sysRole);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package io.destiny.sys.core.repository;
|
||||
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.domain.query.SysUserQuery;
|
||||
import io.destiny.common.request.PageQuery;
|
||||
import io.destiny.common.response.PageModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysUserRepository {
|
||||
|
||||
Mono<SysUser> findByUsername(String username);
|
||||
|
||||
Mono<SysUser> findById(Long id);
|
||||
|
||||
Mono<SysUser> save(SysUser sysUser);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
|
||||
Flux<SysUser> findAll();
|
||||
|
||||
Mono<PageModel<SysUser>> findByQueryWithPagination(SysUserQuery query, PageQuery pageQuery);
|
||||
|
||||
Mono<Void> removeAllRolesFromUser(Long userId);
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package io.destiny.sys.core.repository;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysUserRoleRepository {
|
||||
|
||||
Flux<Long> findRoleIdsByUserId(Long userId);
|
||||
|
||||
Mono<Void> assignRoleToUser(Long userId, Long roleId);
|
||||
|
||||
Mono<Void> removeRoleFromUser(Long userId, Long roleId);
|
||||
|
||||
Mono<Void> removeAllUsersFromRole(Long roleId);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface IDataMaskingService {
|
||||
|
||||
Mono<String> maskPassword(String password);
|
||||
|
||||
Mono<String> maskPhone(String phone);
|
||||
|
||||
Mono<String> maskEmail(String email);
|
||||
|
||||
Mono<String> maskIdCard(String idCard);
|
||||
|
||||
Mono<String> maskBankCard(String bankCard);
|
||||
|
||||
Mono<String> maskSensitiveData(String data);
|
||||
|
||||
Mono<String> mask(String data, String type);
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IJaversAuditService {
|
||||
|
||||
Mono<String> compare(Object oldObject, Object newObject);
|
||||
|
||||
Mono<List<String>> getChangedFields(Object oldObject, Object newObject);
|
||||
|
||||
Mono<String> toJson(Object object);
|
||||
|
||||
Mono<String> commit(Object object);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import io.destiny.sys.core.domain.OperationLog;
|
||||
import io.destiny.sys.core.domain.query.OperationLogQuery;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface IOperationLogService {
|
||||
|
||||
Mono<OperationLog> findById(Long id);
|
||||
|
||||
Mono<OperationLog> save(OperationLog operationLog);
|
||||
|
||||
Flux<OperationLog> findByQuery(OperationLogQuery query);
|
||||
|
||||
Mono<Long> countByQuery(OperationLogQuery query);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import io.destiny.sys.dto.request.SysUserLoginRequest;
|
||||
import io.destiny.sys.dto.response.SysLoginResponse;
|
||||
import io.destiny.sys.dto.response.SysUserRegisterRequest;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysAuthService {
|
||||
|
||||
Mono<Long> register(SysUserRegisterRequest request);
|
||||
|
||||
Mono<SysLoginResponse> login(SysUserLoginRequest request);
|
||||
|
||||
Mono<SysLoginResponse> refreshToken(String refreshToken);
|
||||
|
||||
Mono<Void> logout(String token);
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import io.destiny.sys.core.domain.SysMenu;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysMenuService {
|
||||
|
||||
Mono<SysMenu> findById(Long id);
|
||||
|
||||
Flux<SysMenu> findByRoleId(Long roleId);
|
||||
|
||||
Flux<SysMenu> findByUserId(Long userId);
|
||||
|
||||
Flux<SysMenu> findAll();
|
||||
|
||||
Mono<SysMenu> create(SysMenu sysMenu);
|
||||
|
||||
Mono<SysMenu> update(SysMenu sysMenu);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
|
||||
Mono<Void> assignMenuToRole(Long roleId, Long menuId);
|
||||
|
||||
Mono<Void> removeMenuFromRole(Long roleId, Long menuId);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysPermissionInitService {
|
||||
|
||||
Mono<Void> initializeAdminPermissions(Long userId);
|
||||
|
||||
Mono<Void> initializeUserPermissions(Long userId);
|
||||
|
||||
Mono<Void> ensureDashboardMenuExists();
|
||||
|
||||
Mono<Void> ensureUserRoleExists();
|
||||
|
||||
Mono<Void> assignDashboardToUserRole();
|
||||
|
||||
Mono<Boolean> isUserAdmin(Long userId);
|
||||
|
||||
Mono<Boolean> isUserRegularUser(Long userId);
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysPermissionService {
|
||||
|
||||
Mono<Set<String>> getUserPermissions(Long userId);
|
||||
|
||||
boolean hasPermission(Long userId, String permission);
|
||||
|
||||
boolean hasAnyPermission(Long userId, Set<String> permissions);
|
||||
|
||||
boolean hasRole(Long userId, String roleKey);
|
||||
|
||||
boolean hasAnyRole(Long userId, Set<String> roleKeys);
|
||||
|
||||
List<String> getUserRoles(Long userId);
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysRoleService {
|
||||
|
||||
Mono<SysRole> findById(Long id);
|
||||
|
||||
Mono<SysRole> findByRoleKey(String roleKey);
|
||||
|
||||
Flux<SysRole> findByUserId(Long userId);
|
||||
|
||||
Flux<SysRole> findAll();
|
||||
|
||||
Mono<SysRole> create(SysRole sysRole);
|
||||
|
||||
Mono<SysRole> update(SysRole sysRole);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
|
||||
Mono<Void> assignRoleToUser(Long userId, Long roleId);
|
||||
|
||||
Mono<Void> removeRoleFromUser(Long userId, Long roleId);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.domain.query.SysUserQuery;
|
||||
import io.destiny.common.request.PageQuery;
|
||||
import io.destiny.common.response.PageModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysUserService {
|
||||
|
||||
Mono<SysUser> findByUsername(String username);
|
||||
|
||||
Mono<SysUser> findById(Long id);
|
||||
|
||||
Mono<SysUser> create(SysUser sysUser);
|
||||
|
||||
Mono<SysUser> update(SysUser sysUser);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
|
||||
Flux<SysUser> findAll();
|
||||
|
||||
Mono<PageModel<SysUser>> findByQueryWithPagination(SysUserQuery query, PageQuery pageQuery);
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package io.destiny.sys.core.service.impl;
|
||||
|
||||
import io.destiny.sys.core.service.IDataMaskingService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Service
|
||||
public class DataMaskingServiceImpl implements IDataMaskingService {
|
||||
|
||||
private static final String PASSWORD_MASK = "******";
|
||||
|
||||
private static final String PHONE_MASK = "****";
|
||||
|
||||
private static final String EMAIL_MASK = "***";
|
||||
|
||||
private static final String IDCARD_MASK = "********";
|
||||
|
||||
private static final String BANKCARD_MASK = "***********";
|
||||
|
||||
public Mono<String> maskPassword(String password) {
|
||||
if (password == null || password.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return Mono.just(PASSWORD_MASK);
|
||||
}
|
||||
|
||||
public Mono<String> maskPhone(String phone) {
|
||||
if (phone == null || phone.length() < 7) {
|
||||
return Mono.empty();
|
||||
}
|
||||
String masked = phone.substring(0, 3) + PHONE_MASK + phone.substring(7);
|
||||
return Mono.just(masked);
|
||||
}
|
||||
|
||||
public Mono<String> maskEmail(String email) {
|
||||
if (email == null || !email.contains("@")) {
|
||||
return Mono.empty();
|
||||
}
|
||||
String[] parts = email.split("@");
|
||||
if (parts[0].length() <= 2) {
|
||||
return Mono.empty();
|
||||
}
|
||||
String masked = parts[0].substring(0, 2) + EMAIL_MASK + "@" + parts[1];
|
||||
return Mono.just(masked);
|
||||
}
|
||||
|
||||
public Mono<String> maskIdCard(String idCard) {
|
||||
if (idCard == null || idCard.length() < 10) {
|
||||
return Mono.empty();
|
||||
}
|
||||
String masked = idCard.substring(0, 6) + IDCARD_MASK + idCard.substring(idCard.length() - 4);
|
||||
return Mono.just(masked);
|
||||
}
|
||||
|
||||
public Mono<String> maskBankCard(String bankCard) {
|
||||
if (bankCard == null || bankCard.length() < 10) {
|
||||
return Mono.empty();
|
||||
}
|
||||
String masked = bankCard.substring(0, 4) + BANKCARD_MASK + bankCard.substring(bankCard.length() - 4);
|
||||
return Mono.just(masked);
|
||||
}
|
||||
|
||||
public Mono<String> maskSensitiveData(String data) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
String masked = data;
|
||||
masked = masked.replaceAll("\"password\"\\s*:\\s*\"[^\"]*\"", "\"password\":\"" + PASSWORD_MASK + "\"");
|
||||
masked = masked.replaceAll("\"phone\"\\s*:\\s*\"[^\"]*\"", "\"phone\":\"" + PHONE_MASK + "\"");
|
||||
masked = masked.replaceAll("\"email\"\\s*:\\s*\"[^\"]*\"", "\"email\":\"" + EMAIL_MASK + "\"");
|
||||
masked = masked.replaceAll("\"idCard\"\\s*:\\s*\"[^\"]*\"", "\"idCard\":\"" + IDCARD_MASK + "\"");
|
||||
masked = masked.replaceAll("\"bankCard\"\\s*:\\s*\"[^\"]*\"", "\"bankCard\":\"" + BANKCARD_MASK + "\"");
|
||||
|
||||
return Mono.just(masked);
|
||||
}
|
||||
|
||||
public Mono<String> mask(String data, String type) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case "password":
|
||||
return maskPassword(data);
|
||||
case "phone":
|
||||
return maskPhone(data);
|
||||
case "email":
|
||||
return maskEmail(data);
|
||||
case "idCard":
|
||||
return maskIdCard(data);
|
||||
case "bankCard":
|
||||
return maskBankCard(data);
|
||||
case "sensitive":
|
||||
return maskSensitiveData(data);
|
||||
default:
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package io.destiny.sys.core.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.destiny.sys.core.service.IJaversAuditService;
|
||||
import org.javers.core.Javers;
|
||||
import org.javers.core.JaversBuilder;
|
||||
import org.javers.core.diff.Diff;
|
||||
import org.javers.core.diff.changetype.ValueChange;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class JaversAuditServiceImpl implements IJaversAuditService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(JaversAuditServiceImpl.class);
|
||||
|
||||
private final Javers javers;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public JaversAuditServiceImpl(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.javers = JaversBuilder.javers().build();
|
||||
}
|
||||
|
||||
public Mono<String> compare(Object oldObject, Object newObject) {
|
||||
return Mono.fromCallable(() -> {
|
||||
Diff diff = javers.compare(oldObject, newObject);
|
||||
return diff.prettyPrint();
|
||||
})
|
||||
.doOnSuccess(result -> log.debug("对象比较成功"))
|
||||
.doOnError(error -> log.error("对象比较失败", error));
|
||||
}
|
||||
|
||||
public Mono<List<String>> getChangedFields(Object oldObject, Object newObject) {
|
||||
return Mono.fromCallable(() -> {
|
||||
Diff diff = javers.compare(oldObject, newObject);
|
||||
List<String> changedFields = new ArrayList<>();
|
||||
|
||||
for (ValueChange change : diff.getChangesByType(ValueChange.class)) {
|
||||
String fieldName = change.getPropertyName();
|
||||
Object oldValue = change.getLeft();
|
||||
Object newValue = change.getRight();
|
||||
changedFields.add(String.format("%s: %s -> %s", fieldName, oldValue, newValue));
|
||||
}
|
||||
|
||||
return changedFields;
|
||||
})
|
||||
.doOnSuccess(result -> log.debug("获取变更字段成功,数量: {}", result.size()))
|
||||
.doOnError(error -> log.error("获取变更字段失败", error));
|
||||
}
|
||||
|
||||
public Mono<String> toJson(Object object) {
|
||||
return Mono.fromCallable(() -> {
|
||||
JsonNode jsonNode = objectMapper.valueToTree(object);
|
||||
return jsonNode.toString();
|
||||
})
|
||||
.doOnSuccess(result -> log.debug("对象转JSON成功"))
|
||||
.doOnError(error -> log.error("对象转JSON失败", error));
|
||||
}
|
||||
|
||||
public Mono<String> commit(Object object) {
|
||||
return Mono.fromCallable(() -> {
|
||||
return javers.commit("system", object);
|
||||
})
|
||||
.flatMap(commit -> Mono.just(commit.getId().toString()))
|
||||
.doOnSuccess(result -> log.debug("提交对象到JaVers成功,ID: {}", result))
|
||||
.doOnError(error -> log.error("提交对象到JaVers失败", error));
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package io.destiny.sys.core.service.impl;
|
||||
|
||||
import io.destiny.sys.core.domain.OperationLog;
|
||||
import io.destiny.sys.core.domain.query.OperationLogQuery;
|
||||
import io.destiny.sys.core.repository.IOperationLogRepository;
|
||||
import io.destiny.sys.core.service.IOperationLogService;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Service
|
||||
public class OperationLogServiceImpl implements IOperationLogService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OperationLogServiceImpl.class);
|
||||
|
||||
private final IOperationLogRepository operationLogRepository;
|
||||
|
||||
public OperationLogServiceImpl(IOperationLogRepository operationLogRepository) {
|
||||
this.operationLogRepository = operationLogRepository;
|
||||
}
|
||||
|
||||
public Mono<OperationLog> findById(Long id) {
|
||||
return operationLogRepository.findById(id)
|
||||
.doOnSuccess(result -> log.debug("查询操作日志成功,ID: {}", id))
|
||||
.doOnError(error -> log.error("查询操作日志失败,ID: {}", id, error));
|
||||
}
|
||||
|
||||
public Mono<OperationLog> save(OperationLog operationLog) {
|
||||
return Mono.fromCallable(operationLog::generateId)
|
||||
.flatMap(id -> operationLogRepository.save(operationLog))
|
||||
.doOnSuccess(result -> log.debug("保存操作日志成功,ID: {}", result.getId()))
|
||||
.doOnError(error -> log.error("保存操作日志失败", error))
|
||||
.onErrorResume(error -> {
|
||||
log.warn("保存操作日志失败,但不影响主业务流程", error);
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
|
||||
public Flux<OperationLog> findByQuery(OperationLogQuery query) {
|
||||
return operationLogRepository.findByQuery(query)
|
||||
.doOnComplete(() -> log.debug("查询操作日志列表成功"))
|
||||
.doOnError(error -> log.error("查询操作日志列表失败", error));
|
||||
}
|
||||
|
||||
public Mono<Long> countByQuery(OperationLogQuery query) {
|
||||
return operationLogRepository.countByQuery(query)
|
||||
.doOnSuccess(count -> log.debug("统计操作日志数量成功,数量: {}", count))
|
||||
.doOnError(error -> log.error("统计操作日志数量失败", error));
|
||||
}
|
||||
|
||||
public Mono<Void> deleteById(Long id) {
|
||||
return operationLogRepository.deleteById(id)
|
||||
.doOnSuccess(result -> log.debug("删除操作日志成功,ID: {}", id))
|
||||
.doOnError(error -> log.error("删除操作日志失败,ID: {}", id, error));
|
||||
}
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package io.destiny.sys.core.service.impl;
|
||||
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.service.ISysAuthService;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import io.destiny.sys.core.service.ISysPermissionService;
|
||||
import io.destiny.sys.core.service.ISysPermissionInitService;
|
||||
import io.destiny.sys.dto.request.SysUserLoginRequest;
|
||||
import io.destiny.sys.dto.response.SysLoginResponse;
|
||||
import io.destiny.sys.dto.response.SysUserRegisterRequest;
|
||||
import io.destiny.sys.security.SysJwtTokenProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Service
|
||||
public class SysAuthServiceImpl implements ISysAuthService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SysAuthServiceImpl.class);
|
||||
|
||||
private final ISysUserService userService;
|
||||
|
||||
private final SysJwtTokenProvider jwtTokenProvider;
|
||||
|
||||
private final BCryptPasswordEncoder passwordEncoder;
|
||||
|
||||
private final ISysPermissionService permissionService;
|
||||
|
||||
private final ISysPermissionInitService permissionInitService;
|
||||
|
||||
public SysAuthServiceImpl(ISysUserService userService, SysJwtTokenProvider jwtTokenProvider, ISysPermissionService permissionService, ISysPermissionInitService permissionInitService) {
|
||||
this.userService = userService;
|
||||
this.jwtTokenProvider = jwtTokenProvider;
|
||||
this.passwordEncoder = new BCryptPasswordEncoder();
|
||||
this.permissionService = permissionService;
|
||||
this.permissionInitService = permissionInitService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> register(SysUserRegisterRequest request) {
|
||||
logger.info("User registration attempt: {}", request.getUsername());
|
||||
|
||||
return userService.findByUsername(request.getUsername())
|
||||
.flatMap(existingUser -> Mono.<Long>error(new RuntimeException("用户名已存在")))
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
SysUser user = request.toDomain();
|
||||
user.setPassword(passwordEncoder.encode(user.getPassword()));
|
||||
return userService.create(user)
|
||||
.flatMap(savedUser -> {
|
||||
if ("admin".equalsIgnoreCase(savedUser.getUsername())) {
|
||||
return permissionInitService.initializeAdminPermissions(savedUser.getId())
|
||||
.thenReturn(savedUser.getId());
|
||||
} else {
|
||||
return permissionInitService.initializeUserPermissions(savedUser.getId())
|
||||
.thenReturn(savedUser.getId());
|
||||
}
|
||||
});
|
||||
}))
|
||||
.doOnSuccess(userId -> logger.info("User registered successfully with permissions: {}", userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysLoginResponse> login(SysUserLoginRequest request) {
|
||||
logger.info("User login attempt: {}", request.getUsername());
|
||||
|
||||
return userService.findByUsername(request.getUsername())
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("用户名或密码错误")))
|
||||
.flatMap(user -> {
|
||||
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
|
||||
return Mono.error(new RuntimeException("用户名或密码错误"));
|
||||
}
|
||||
|
||||
if (!"0".equals(user.getStatus())) {
|
||||
return Mono.error(new RuntimeException("用户已被禁用"));
|
||||
}
|
||||
|
||||
String accessToken = jwtTokenProvider.generateAccessToken(user.getId(), user.getUsername());
|
||||
String refreshToken = jwtTokenProvider.generateRefreshToken(user.getId(), user.getUsername());
|
||||
|
||||
logger.info("About to call permissionService.getUserPermissions for userId: {}", user.getId());
|
||||
|
||||
// 使用 reactive 方式获取权限
|
||||
return permissionService.getUserPermissions(user.getId())
|
||||
.flatMap(permissions -> {
|
||||
logger.info("User permissions loaded: userId={}, permissions.size={}", user.getId(), permissions != null ? permissions.size() : 0);
|
||||
logger.info("User logged in successfully: {}", user.getId());
|
||||
return Mono.just(SysLoginResponse.from(accessToken, refreshToken, user, permissions));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysLoginResponse> refreshToken(String refreshToken) {
|
||||
logger.info("Token refresh attempt");
|
||||
|
||||
if (!jwtTokenProvider.validateToken(refreshToken)) {
|
||||
return Mono.error(new RuntimeException("无效的刷新令牌"));
|
||||
}
|
||||
|
||||
if (jwtTokenProvider.isTokenExpired(refreshToken)) {
|
||||
return Mono.error(new RuntimeException("刷新令牌已过期"));
|
||||
}
|
||||
|
||||
String tokenType = jwtTokenProvider.getTokenType(refreshToken);
|
||||
if (!"refresh".equals(tokenType)) {
|
||||
return Mono.error(new RuntimeException("令牌类型错误"));
|
||||
}
|
||||
|
||||
Long userId = jwtTokenProvider.getUserIdFromToken(refreshToken);
|
||||
|
||||
return userService.findById(userId)
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("用户不存在")))
|
||||
.flatMap(user -> {
|
||||
if (!"0".equals(user.getStatus())) {
|
||||
return Mono.error(new RuntimeException("用户已被禁用"));
|
||||
}
|
||||
|
||||
String newAccessToken = jwtTokenProvider.generateAccessToken(user.getId(), user.getUsername());
|
||||
String newRefreshToken = jwtTokenProvider.generateRefreshToken(user.getId(), user.getUsername());
|
||||
|
||||
return permissionService.getUserPermissions(user.getId())
|
||||
.flatMap(permissions -> {
|
||||
logger.info("Token refreshed successfully for user: {}", userId);
|
||||
return Mono.just(SysLoginResponse.from(newAccessToken, newRefreshToken, user, permissions));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> logout(String token) {
|
||||
logger.info("User logout attempt");
|
||||
|
||||
if (token == null || !jwtTokenProvider.validateToken(token)) {
|
||||
logger.warn("Invalid token during logout");
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
Long userId = jwtTokenProvider.getUserIdFromToken(token);
|
||||
logger.info("User logged out: {}", userId);
|
||||
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package io.destiny.sys.core.service.impl;
|
||||
|
||||
import io.destiny.sys.core.domain.SysMenu;
|
||||
import io.destiny.sys.core.repository.ISysMenuRepository;
|
||||
import io.destiny.sys.core.repository.ISysRoleMenuRepository;
|
||||
import io.destiny.sys.core.service.ISysMenuService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Service
|
||||
public class SysMenuServiceImpl implements ISysMenuService {
|
||||
|
||||
private final ISysMenuRepository sysMenuRepository;
|
||||
private final ISysRoleMenuRepository sysRoleMenuRepository;
|
||||
|
||||
public SysMenuServiceImpl(ISysMenuRepository sysMenuRepository, ISysRoleMenuRepository sysRoleMenuRepository) {
|
||||
this.sysMenuRepository = sysMenuRepository;
|
||||
this.sysRoleMenuRepository = sysRoleMenuRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysMenu> findById(Long id) {
|
||||
return sysMenuRepository.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysMenu> findByRoleId(Long roleId) {
|
||||
return sysMenuRepository.findByRoleId(roleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysMenu> findByUserId(Long userId) {
|
||||
return sysMenuRepository.findByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysMenu> findAll() {
|
||||
return sysMenuRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<SysMenu> create(SysMenu sysMenu) {
|
||||
return sysMenuRepository.save(sysMenu);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<SysMenu> update(SysMenu sysMenu) {
|
||||
return sysMenuRepository.findById(sysMenu.getId())
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("菜单不存在")))
|
||||
.flatMap(existingMenu -> {
|
||||
sysMenu.setCreatedAt(existingMenu.getCreatedAt());
|
||||
return sysMenuRepository.save(sysMenu);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> deleteById(Long id) {
|
||||
return sysMenuRepository.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> assignMenuToRole(Long roleId, Long menuId) {
|
||||
return sysRoleMenuRepository.assignMenuToRole(roleId, menuId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> removeMenuFromRole(Long roleId, Long menuId) {
|
||||
return sysRoleMenuRepository.removeMenuFromRole(roleId, menuId);
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package io.destiny.sys.core.service.impl;
|
||||
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import io.destiny.sys.core.domain.SysMenu;
|
||||
import io.destiny.sys.core.repository.ISysRoleRepository;
|
||||
import io.destiny.sys.core.repository.ISysMenuRepository;
|
||||
import io.destiny.sys.core.repository.ISysUserRoleRepository;
|
||||
import io.destiny.sys.core.repository.ISysRoleMenuRepository;
|
||||
import io.destiny.sys.core.service.ISysPermissionInitService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Service
|
||||
public class SysPermissionInitServiceImpl implements ISysPermissionInitService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SysPermissionInitServiceImpl.class);
|
||||
|
||||
private static final Long ADMIN_ROLE_ID = 1L;
|
||||
private static final Long USER_ROLE_ID = 2L;
|
||||
private static final Long DASHBOARD_MENU_ID = 2L;
|
||||
|
||||
private final ISysRoleRepository roleRepository;
|
||||
private final ISysMenuRepository menuRepository;
|
||||
private final ISysUserRoleRepository userRoleRepository;
|
||||
private final ISysRoleMenuRepository roleMenuRepository;
|
||||
|
||||
public SysPermissionInitServiceImpl(
|
||||
ISysRoleRepository roleRepository,
|
||||
ISysMenuRepository menuRepository,
|
||||
ISysUserRoleRepository userRoleRepository,
|
||||
ISysRoleMenuRepository roleMenuRepository) {
|
||||
this.roleRepository = roleRepository;
|
||||
this.menuRepository = menuRepository;
|
||||
this.userRoleRepository = userRoleRepository;
|
||||
this.roleMenuRepository = roleMenuRepository;
|
||||
}
|
||||
|
||||
public Mono<Void> initializeAdminPermissions(Long userId) {
|
||||
logger.info("Initializing admin permissions for user: {}", userId);
|
||||
|
||||
return roleRepository.findById(ADMIN_ROLE_ID)
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("超级管理员角色不存在")))
|
||||
.flatMap(adminRole -> {
|
||||
logger.info("Assigning admin role {} to user {}", adminRole.getRoleKey(), userId);
|
||||
return userRoleRepository.assignRoleToUser(userId, ADMIN_ROLE_ID);
|
||||
})
|
||||
.doOnSuccess(v -> logger.info("Admin permissions initialized successfully for user: {}", userId))
|
||||
.doOnError(e -> logger.error("Failed to initialize admin permissions for user: {}", userId, e));
|
||||
}
|
||||
|
||||
public Mono<Void> initializeUserPermissions(Long userId) {
|
||||
logger.info("Initializing user permissions for user: {}", userId);
|
||||
|
||||
return roleRepository.findById(USER_ROLE_ID)
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("普通用户角色不存在")))
|
||||
.flatMap(userRole -> {
|
||||
logger.info("Assigning user role {} to user {}", userRole.getRoleKey(), userId);
|
||||
return userRoleRepository.assignRoleToUser(userId, USER_ROLE_ID);
|
||||
})
|
||||
.doOnSuccess(v -> logger.info("User permissions initialized successfully for user: {}", userId))
|
||||
.doOnError(e -> logger.error("Failed to initialize user permissions for user: {}", userId, e));
|
||||
}
|
||||
|
||||
public Mono<Void> ensureDashboardMenuExists() {
|
||||
logger.info("Ensuring dashboard menu exists");
|
||||
|
||||
return menuRepository.findById(DASHBOARD_MENU_ID)
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
logger.info("Dashboard menu not found, creating default dashboard menu");
|
||||
SysMenu dashboardMenu = new SysMenu();
|
||||
dashboardMenu.setId(DASHBOARD_MENU_ID);
|
||||
dashboardMenu.setMenuName("仪表盘");
|
||||
dashboardMenu.setParentId(0L);
|
||||
dashboardMenu.setOrderNum(0);
|
||||
dashboardMenu.setMenuType("C");
|
||||
dashboardMenu.setPerms("sys:dashboard:view");
|
||||
dashboardMenu.setComponent("dashboard/index");
|
||||
dashboardMenu.setStatus("0");
|
||||
dashboardMenu.setCreateBy("system");
|
||||
return menuRepository.save(dashboardMenu);
|
||||
}))
|
||||
.then()
|
||||
.doOnSuccess(v -> logger.info("Dashboard menu ensured"))
|
||||
.doOnError(e -> logger.error("Failed to ensure dashboard menu", e));
|
||||
}
|
||||
|
||||
public Mono<Void> ensureUserRoleExists() {
|
||||
logger.info("Ensuring user role exists");
|
||||
|
||||
return roleRepository.findById(USER_ROLE_ID)
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
logger.info("User role not found, creating default user role");
|
||||
SysRole userRole = new SysRole();
|
||||
userRole.setId(USER_ROLE_ID);
|
||||
userRole.setRoleName("普通用户");
|
||||
userRole.setRoleKey("user");
|
||||
userRole.setRoleSort(2);
|
||||
userRole.setStatus("0");
|
||||
userRole.setCreateBy("system");
|
||||
return roleRepository.save(userRole);
|
||||
}))
|
||||
.then()
|
||||
.doOnSuccess(v -> logger.info("User role ensured"))
|
||||
.doOnError(e -> logger.error("Failed to ensure user role", e));
|
||||
}
|
||||
|
||||
public Mono<Void> assignDashboardToUserRole() {
|
||||
logger.info("Assigning dashboard menu to user role");
|
||||
|
||||
return roleMenuRepository.assignMenuToRole(USER_ROLE_ID, DASHBOARD_MENU_ID)
|
||||
.then()
|
||||
.doOnSuccess(v -> logger.info("Dashboard menu assigned to user role"))
|
||||
.doOnError(e -> logger.error("Failed to assign dashboard to user role", e));
|
||||
}
|
||||
|
||||
public Mono<Boolean> isUserAdmin(Long userId) {
|
||||
return userRoleRepository.findRoleIdsByUserId(userId)
|
||||
.any(roleId -> ADMIN_ROLE_ID.equals(roleId));
|
||||
}
|
||||
|
||||
public Mono<Boolean> isUserRegularUser(Long userId) {
|
||||
return userRoleRepository.findRoleIdsByUserId(userId)
|
||||
.any(roleId -> USER_ROLE_ID.equals(roleId));
|
||||
}
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
package io.destiny.sys.core.service.impl;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import io.destiny.sys.core.domain.SysMenu;
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import io.destiny.sys.core.repository.ISysMenuRepository;
|
||||
import io.destiny.sys.core.repository.ISysRoleRepository;
|
||||
import io.destiny.sys.core.service.ISysPermissionService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class SysPermissionServiceImpl implements ISysPermissionService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SysPermissionServiceImpl.class);
|
||||
|
||||
private final ISysRoleRepository sysRoleRepository;
|
||||
private final ISysMenuRepository sysMenuRepository;
|
||||
|
||||
private final Cache<@Nullable Long, @Nullable Set<String>> userPermissionCache;
|
||||
private final Cache<@Nullable Long, @Nullable Set<String>> userRoleCache;
|
||||
|
||||
public SysPermissionServiceImpl(ISysRoleRepository sysRoleRepository, ISysMenuRepository sysMenuRepository) {
|
||||
this.sysRoleRepository = sysRoleRepository;
|
||||
this.sysMenuRepository = sysMenuRepository;
|
||||
|
||||
this.userPermissionCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(30, TimeUnit.MINUTES)
|
||||
.maximumSize(1000)
|
||||
.build();
|
||||
|
||||
this.userRoleCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(30, TimeUnit.MINUTES)
|
||||
.maximumSize(1000)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Set<String>> getUserPermissions(Long userId) {
|
||||
logger.info("getUserPermissions called with userId: {}", userId);
|
||||
if (userId == null) {
|
||||
logger.info("userId is null, returning empty set");
|
||||
return Mono.just(new HashSet<>());
|
||||
}
|
||||
|
||||
Set<String> cachedPermissions = userPermissionCache.getIfPresent(userId);
|
||||
logger.info("Cached permissions: {}", cachedPermissions);
|
||||
|
||||
// 如果缓存中有权限且不为空,直接返回
|
||||
if (cachedPermissions != null && !cachedPermissions.isEmpty()) {
|
||||
logger.info("Returning cached permissions: {}", cachedPermissions);
|
||||
return Mono.just(cachedPermissions);
|
||||
}
|
||||
|
||||
// 从数据库加载权限
|
||||
logger.info("Loading permissions from database");
|
||||
return loadUserPermissions(userId)
|
||||
.doOnNext(permissions -> {
|
||||
if (permissions != null && !permissions.isEmpty()) {
|
||||
userPermissionCache.put(userId, permissions);
|
||||
logger.info("Permissions loaded and cached: {}", permissions);
|
||||
} else {
|
||||
logger.info("No permissions found in database");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Long userId, String permission) {
|
||||
// 同步方法,使用 block() 在 boundedElastic 调度器上执行
|
||||
try {
|
||||
Set<String> permissions = getUserPermissions(userId)
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.block();
|
||||
return permissions != null && permissions.contains(permission);
|
||||
} catch (Exception e) {
|
||||
logger.error("检查权限失败", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(Long userId, Set<String> permissions) {
|
||||
try {
|
||||
Set<String> userPermissions = getUserPermissions(userId)
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.block();
|
||||
return userPermissions != null && userPermissions.stream().anyMatch(permissions::contains);
|
||||
} catch (Exception e) {
|
||||
logger.error("检查权限失败", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(Long userId, String roleKey) {
|
||||
List<String> roles = getUserRoles(userId);
|
||||
return roles.contains(roleKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(Long userId, Set<String> roleKeys) {
|
||||
List<String> roles = getUserRoles(userId);
|
||||
return roles.stream().anyMatch(roleKeys::contains);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUserRoles(Long userId) {
|
||||
if (userId == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Set<String> cachedRoles = userRoleCache.getIfPresent(userId);
|
||||
if (cachedRoles != null) {
|
||||
return new ArrayList<>(cachedRoles);
|
||||
}
|
||||
try {
|
||||
Set<String> roles = loadUserRoles(userId)
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.block();
|
||||
if (roles != null) {
|
||||
userRoleCache.put(userId, roles);
|
||||
}
|
||||
return roles != null ? new ArrayList<>(roles) : new ArrayList<>();
|
||||
} catch (Exception e) {
|
||||
logger.error("加载用户角色失败", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearUserPermissionCache(Long userId) {
|
||||
if (userId != null) {
|
||||
userPermissionCache.invalidate(userId);
|
||||
userRoleCache.invalidate(userId);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearAllCache() {
|
||||
userPermissionCache.invalidateAll();
|
||||
userRoleCache.invalidateAll();
|
||||
}
|
||||
|
||||
private Mono<Set<String>> loadUserPermissions(Long userId) {
|
||||
logger.info("开始加载用户权限,用户ID: {}", userId);
|
||||
|
||||
return sysMenuRepository.findByUserId(userId)
|
||||
.collectList()
|
||||
.flatMap(menus -> {
|
||||
logger.info("用户菜单数量: {}", menus != null ? menus.size() : 0);
|
||||
|
||||
if (menus == null || menus.isEmpty()) {
|
||||
logger.info("用户没有菜单,返回空权限集合");
|
||||
return Mono.just(new HashSet<String>());
|
||||
}
|
||||
|
||||
// 打印所有菜单的权限信息
|
||||
menus.forEach(menu -> {
|
||||
logger.info("菜单: id={}, name={}, perms={}", menu.getId(), menu.getMenuName(), menu.getPerms());
|
||||
});
|
||||
|
||||
// 从已获取的菜单中过滤出有权限的
|
||||
Set<String> permissions = menus.stream()
|
||||
.filter(menu -> menu.getPerms() != null && !menu.getPerms().isEmpty())
|
||||
.map(SysMenu::getPerms)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
logger.info("用户权限数量: {}", permissions.size());
|
||||
return Mono.just(permissions);
|
||||
})
|
||||
.onErrorResume(e -> {
|
||||
logger.error("加载用户权限失败,用户ID: {}", userId, e);
|
||||
return Mono.just(new HashSet<String>());
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<Set<String>> loadUserRoles(Long userId) {
|
||||
return sysRoleRepository.findByUserId(userId)
|
||||
.filter(role -> role.getRoleKey() != null && !role.getRoleKey().isEmpty())
|
||||
.map(SysRole::getRoleKey)
|
||||
.collectList()
|
||||
.map(HashSet::new);
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package io.destiny.sys.core.service.impl;
|
||||
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import io.destiny.sys.core.repository.ISysRoleRepository;
|
||||
import io.destiny.sys.core.repository.ISysUserRoleRepository;
|
||||
import io.destiny.sys.core.repository.ISysRoleMenuRepository;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Service
|
||||
public class SysRoleServiceImpl implements ISysRoleService {
|
||||
|
||||
private final ISysRoleRepository sysRoleRepository;
|
||||
private final ISysUserRoleRepository sysUserRoleRepository;
|
||||
private final ISysRoleMenuRepository sysRoleMenuRepository;
|
||||
|
||||
public SysRoleServiceImpl(ISysRoleRepository sysRoleRepository, ISysUserRoleRepository sysUserRoleRepository, ISysRoleMenuRepository sysRoleMenuRepository) {
|
||||
this.sysRoleRepository = sysRoleRepository;
|
||||
this.sysUserRoleRepository = sysUserRoleRepository;
|
||||
this.sysRoleMenuRepository = sysRoleMenuRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysRole> findById(Long id) {
|
||||
return sysRoleRepository.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysRole> findByRoleKey(String roleKey) {
|
||||
return sysRoleRepository.findByRoleKey(roleKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysRole> findByUserId(Long userId) {
|
||||
return sysRoleRepository.findByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysRole> findAll() {
|
||||
return sysRoleRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<SysRole> create(SysRole sysRole) {
|
||||
return sysRoleRepository.save(sysRole);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<SysRole> update(SysRole sysRole) {
|
||||
return sysRoleRepository.findById(sysRole.getId())
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("角色不存在")))
|
||||
.flatMap(existingRole -> {
|
||||
sysRole.setCreatedAt(existingRole.getCreatedAt());
|
||||
return sysRoleRepository.save(sysRole);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> deleteById(Long id) {
|
||||
return sysRoleRepository.findById(id)
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("角色不存在")))
|
||||
.flatMap(role -> {
|
||||
return sysUserRoleRepository.removeAllUsersFromRole(id)
|
||||
.then(sysRoleMenuRepository.removeAllMenusFromRole(id))
|
||||
.then(sysRoleRepository.deleteById(id));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> assignRoleToUser(Long userId, Long roleId) {
|
||||
return sysUserRoleRepository.assignRoleToUser(userId, roleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> removeRoleFromUser(Long userId, Long roleId) {
|
||||
return sysUserRoleRepository.removeRoleFromUser(userId, roleId);
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package io.destiny.sys.core.service.impl;
|
||||
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.domain.query.SysUserQuery;
|
||||
import io.destiny.sys.core.repository.ISysUserRepository;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import io.destiny.sys.core.service.ISysPermissionInitService;
|
||||
import io.destiny.common.request.PageQuery;
|
||||
import io.destiny.common.response.PageModel;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Service
|
||||
public class SysUserServiceImpl implements ISysUserService {
|
||||
|
||||
private final ISysUserRepository sysUserRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final ISysPermissionInitService permissionInitService;
|
||||
|
||||
public SysUserServiceImpl(ISysUserRepository sysUserRepository, PasswordEncoder passwordEncoder, ISysPermissionInitService permissionInitService) {
|
||||
this.sysUserRepository = sysUserRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.permissionInitService = permissionInitService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysUser> findByUsername(String username) {
|
||||
return sysUserRepository.findByUsername(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysUser> findById(Long id) {
|
||||
return sysUserRepository.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysUser> create(SysUser sysUser) {
|
||||
return findByUsername(sysUser.getUsername())
|
||||
.flatMap(existingUser -> Mono.<SysUser>error(new RuntimeException("用户名已存在")))
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
return Mono.fromCallable(() -> passwordEncoder.encode(sysUser.getPassword()))
|
||||
.map(encodedPassword -> {
|
||||
SysUser newUser = new SysUser();
|
||||
newUser.setUsername(sysUser.getUsername());
|
||||
newUser.setPassword(encodedPassword);
|
||||
newUser.setEmail(sysUser.getEmail());
|
||||
newUser.setPhone(sysUser.getPhone());
|
||||
newUser.setStatus(sysUser.getStatus());
|
||||
return newUser;
|
||||
})
|
||||
.flatMap(sysUserRepository::save)
|
||||
.flatMap(savedUser -> {
|
||||
if ("admin".equalsIgnoreCase(savedUser.getUsername())) {
|
||||
return permissionInitService.initializeAdminPermissions(savedUser.getId())
|
||||
.thenReturn(savedUser);
|
||||
} else {
|
||||
return permissionInitService.initializeUserPermissions(savedUser.getId())
|
||||
.thenReturn(savedUser);
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysUser> update(SysUser sysUser) {
|
||||
return sysUserRepository.findById(sysUser.getId())
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("用户不存在")))
|
||||
.flatMap(existingUser -> {
|
||||
sysUser.setCreatedAt(existingUser.getCreatedAt());
|
||||
return sysUserRepository.save(sysUser);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteById(Long id) {
|
||||
return sysUserRepository.findById(id)
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("用户不存在")))
|
||||
.flatMap(user -> {
|
||||
return sysUserRepository.removeAllRolesFromUser(id)
|
||||
.then(sysUserRepository.deleteById(id));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysUser> findAll() {
|
||||
return sysUserRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<PageModel<SysUser>> findByQueryWithPagination(SysUserQuery query, PageQuery pageQuery) {
|
||||
return sysUserRepository.findByQueryWithPagination(query, pageQuery);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package io.destiny.sys.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "分配菜单到角色请求DTO")
|
||||
public class AssignMenuToRoleRequest {
|
||||
|
||||
@Schema(description = "角色ID", example = "1234567890123456789", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long roleId;
|
||||
|
||||
@Schema(description = "菜单ID", example = "1234567890123456789", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long menuId;
|
||||
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public Long getMenuId() {
|
||||
return menuId;
|
||||
}
|
||||
|
||||
public void setMenuId(Long menuId) {
|
||||
this.menuId = menuId;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Long roleId;
|
||||
private Long menuId;
|
||||
|
||||
public Builder roleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder menuId(Long menuId) {
|
||||
this.menuId = menuId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssignMenuToRoleRequest build() {
|
||||
AssignMenuToRoleRequest request = new AssignMenuToRoleRequest();
|
||||
request.setRoleId(roleId);
|
||||
request.setMenuId(menuId);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package io.destiny.sys.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "分配角色到用户请求DTO")
|
||||
public class AssignRoleToUserRequest {
|
||||
|
||||
@Schema(description = "用户ID", example = "1234567890123456789", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "角色ID", example = "1234567890123456789", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long roleId;
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Long userId;
|
||||
private Long roleId;
|
||||
|
||||
public Builder userId(Long userId) {
|
||||
this.userId = userId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder roleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssignRoleToUserRequest build() {
|
||||
AssignRoleToUserRequest request = new AssignRoleToUserRequest();
|
||||
request.setUserId(userId);
|
||||
request.setRoleId(roleId);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
package io.destiny.sys.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "菜单请求DTO")
|
||||
public class SysMenuRequest {
|
||||
|
||||
@Schema(description = "菜单ID", example = "1234567890123456789")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "菜单名称", example = "用户管理", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String name;
|
||||
|
||||
@Schema(description = "菜单编码", example = "user")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "路由路径", example = "/system/user")
|
||||
private String path;
|
||||
|
||||
@Schema(description = "菜单图标", example = "user")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "父菜单ID", example = "0")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "显示顺序", example = "1")
|
||||
private Integer sortOrder;
|
||||
|
||||
@Schema(description = "菜单状态 0-正常 1-停用", example = "0")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "组件路径", example = "system/user/index")
|
||||
private String component;
|
||||
|
||||
@Schema(description = "重定向路径", example = "/system/user/index")
|
||||
private String redirect;
|
||||
|
||||
@Schema(description = "菜单描述", example = "用户管理模块")
|
||||
private String description;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public Integer getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
public void setSortOrder(Integer sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
public void setComponent(String component) {
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
public String getRedirect() {
|
||||
return redirect;
|
||||
}
|
||||
|
||||
public void setRedirect(String redirect) {
|
||||
this.redirect = redirect;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private String path;
|
||||
private String icon;
|
||||
private Long parentId;
|
||||
private Integer sortOrder;
|
||||
private String status;
|
||||
private String component;
|
||||
private String redirect;
|
||||
private String description;
|
||||
|
||||
public Builder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder code(String code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder path(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder icon(String icon) {
|
||||
this.icon = icon;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder parentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder sortOrder(Integer sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder status(String status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder component(String component) {
|
||||
this.component = component;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder redirect(String redirect) {
|
||||
this.redirect = redirect;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SysMenuRequest build() {
|
||||
SysMenuRequest request = new SysMenuRequest();
|
||||
request.setId(id);
|
||||
request.setName(name);
|
||||
request.setCode(code);
|
||||
request.setPath(path);
|
||||
request.setIcon(icon);
|
||||
request.setParentId(parentId);
|
||||
request.setSortOrder(sortOrder);
|
||||
request.setStatus(status);
|
||||
request.setComponent(component);
|
||||
request.setRedirect(redirect);
|
||||
request.setDescription(description);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package io.destiny.sys.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
@Schema(description = "角色请求DTO")
|
||||
public class SysRoleRequest {
|
||||
|
||||
@Schema(description = "角色ID", example = "1234567890123456789")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "角色名称", example = "管理员", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "角色名称不能为空")
|
||||
@Size(max = 30, message = "角色名称长度不能超过30个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "角色权限字符串", example = "admin", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "角色权限字符串不能为空")
|
||||
@Size(max = 100, message = "角色权限字符串长度不能超过100个字符")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_\\-]+$", message = "角色权限字符串只能包含字母、数字、下划线和连字符")
|
||||
private String roleKey;
|
||||
|
||||
@Schema(description = "显示顺序", example = "1")
|
||||
private Integer sortOrder;
|
||||
|
||||
@Schema(description = "角色状态 0-正常 1-停用", example = "0")
|
||||
@Pattern(regexp = "^[01]$", message = "角色状态只能是0或1")
|
||||
private String status;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getRoleKey() {
|
||||
return roleKey;
|
||||
}
|
||||
|
||||
public void setRoleKey(String roleKey) {
|
||||
this.roleKey = roleKey;
|
||||
}
|
||||
|
||||
public Integer getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
public void setSortOrder(Integer sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String roleKey;
|
||||
private Integer sortOrder;
|
||||
private String status;
|
||||
|
||||
public Builder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder roleKey(String roleKey) {
|
||||
this.roleKey = roleKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder sortOrder(Integer sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder status(String status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SysRoleRequest build() {
|
||||
SysRoleRequest request = new SysRoleRequest();
|
||||
request.setId(id);
|
||||
request.setName(name);
|
||||
request.setRoleKey(roleKey);
|
||||
request.setSortOrder(sortOrder);
|
||||
request.setStatus(status);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package io.destiny.sys.dto.request;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class SysUserLoginRequest {
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package io.destiny.sys.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
@Schema(description = "用户请求DTO")
|
||||
public class SysUserRequest {
|
||||
|
||||
@Schema(description = "用户ID", example = "1234567890123456789")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户名", example = "zhangsan", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码", example = "password123")
|
||||
@Size(min = 6, max = 100, message = "密码长度必须在6-100个字符之间")
|
||||
@Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*\\d).+$", message = "密码必须包含字母和数字")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "邮箱", example = "zhangsan@example.com")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "手机号", example = "13800138000")
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "状态 0-正常 1-禁用", example = "0")
|
||||
@Pattern(regexp = "^[01]$", message = "状态必须是0或1")
|
||||
private String status;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String password;
|
||||
private String email;
|
||||
private String phone;
|
||||
private String status;
|
||||
|
||||
public Builder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder username(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder password(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder email(String email) {
|
||||
this.email = email;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder phone(String phone) {
|
||||
this.phone = phone;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder status(String status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SysUserRequest build() {
|
||||
SysUserRequest request = new SysUserRequest();
|
||||
request.setId(id);
|
||||
request.setUsername(username);
|
||||
request.setPassword(password);
|
||||
request.setEmail(email);
|
||||
request.setPhone(phone);
|
||||
request.setStatus(status);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package io.destiny.sys.dto.response;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class SysLoginResponse {
|
||||
|
||||
@JsonProperty("token")
|
||||
private String token;
|
||||
|
||||
@JsonProperty("refreshToken")
|
||||
private String refreshToken;
|
||||
|
||||
@JsonProperty("user")
|
||||
private SysUserResponse user;
|
||||
|
||||
@JsonProperty("permissions")
|
||||
private Set<String> permissions;
|
||||
|
||||
public SysLoginResponse() {
|
||||
}
|
||||
|
||||
public SysLoginResponse(String token, String refreshToken, SysUser user, Set<String> permissions) {
|
||||
this.token = token;
|
||||
this.refreshToken = refreshToken;
|
||||
this.user = SysUserResponse.fromSysUser(user);
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public static SysLoginResponse from(String token, String refreshToken, SysUser user) {
|
||||
return new SysLoginResponse(token, refreshToken, user, null);
|
||||
}
|
||||
|
||||
public static SysLoginResponse from(String token, String refreshToken, SysUser user, Set<String> permissions) {
|
||||
return new SysLoginResponse(token, refreshToken, user, permissions);
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
public void setRefreshToken(String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
public SysUserResponse getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(SysUserResponse user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Set<String> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public void setPermissions(Set<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
}
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
package io.destiny.sys.dto.response;
|
||||
|
||||
import io.destiny.sys.core.domain.SysMenu;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "菜单响应DTO")
|
||||
public class SysMenuResponse {
|
||||
|
||||
@Schema(description = "菜单ID", example = "1234567890123456789")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "菜单名称", example = "用户管理")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "菜单编码", example = "user")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "路由路径", example = "/system/user")
|
||||
private String path;
|
||||
|
||||
@Schema(description = "菜单图标", example = "user")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "父菜单ID", example = "0")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "显示顺序", example = "1")
|
||||
private Integer sortOrder;
|
||||
|
||||
@Schema(description = "菜单状态 0-正常 1-停用", example = "0")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "组件路径", example = "system/user/index")
|
||||
private String component;
|
||||
|
||||
@Schema(description = "重定向路径", example = "/system/user/index")
|
||||
private String redirect;
|
||||
|
||||
@Schema(description = "菜单描述", example = "用户管理模块")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "子菜单列表")
|
||||
private List<SysMenuResponse> children;
|
||||
|
||||
@Schema(description = "创建时间", example = "2024-01-01T00:00:00")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间", example = "2024-01-01T00:00:00")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public SysMenuResponse() {
|
||||
}
|
||||
|
||||
public SysMenuResponse(Long id, String name, String code, String path, String icon, Long parentId, Integer sortOrder,
|
||||
String status, String component, String redirect, String description, List<SysMenuResponse> children,
|
||||
LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.code = code;
|
||||
this.path = path;
|
||||
this.icon = icon;
|
||||
this.parentId = parentId;
|
||||
this.sortOrder = sortOrder;
|
||||
this.status = status;
|
||||
this.component = component;
|
||||
this.redirect = redirect;
|
||||
this.description = description;
|
||||
this.children = children;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public Integer getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
public void setSortOrder(Integer sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
public void setComponent(String component) {
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
public String getRedirect() {
|
||||
return redirect;
|
||||
}
|
||||
|
||||
public void setRedirect(String redirect) {
|
||||
this.redirect = redirect;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public List<SysMenuResponse> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<SysMenuResponse> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public static SysMenuResponse fromSysMenu(SysMenu sysMenu) {
|
||||
if (sysMenu == null) {
|
||||
return null;
|
||||
}
|
||||
return new SysMenuResponse(
|
||||
sysMenu.getId(),
|
||||
sysMenu.getMenuName(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
sysMenu.getParentId(),
|
||||
sysMenu.getOrderNum(),
|
||||
sysMenu.getStatus(),
|
||||
sysMenu.getComponent(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
sysMenu.getCreatedAt(),
|
||||
sysMenu.getUpdatedAt());
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private String path;
|
||||
private String icon;
|
||||
private Long parentId;
|
||||
private Integer sortOrder;
|
||||
private String status;
|
||||
private String component;
|
||||
private String redirect;
|
||||
private String description;
|
||||
private List<SysMenuResponse> children;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Builder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder code(String code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder path(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder icon(String icon) {
|
||||
this.icon = icon;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder parentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder sortOrder(Integer sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder status(String status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder component(String component) {
|
||||
this.component = component;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder redirect(String redirect) {
|
||||
this.redirect = redirect;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder children(List<SysMenuResponse> children) {
|
||||
this.children = children;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SysMenuResponse build() {
|
||||
return new SysMenuResponse(id, name, code, path, icon, parentId, sortOrder, status, component, redirect,
|
||||
description, children, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
package io.destiny.sys.dto.response;
|
||||
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "角色响应DTO")
|
||||
public class SysRoleResponse {
|
||||
|
||||
@Schema(description = "角色ID", example = "1234567890123456789")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "角色名称", example = "管理员")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "角色权限字符串", example = "admin")
|
||||
private String roleKey;
|
||||
|
||||
@Schema(description = "角色描述", example = "系统管理员角色")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "角色状态 0-正常 1-停用", example = "0")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "显示顺序", example = "1")
|
||||
private Integer sortOrder;
|
||||
|
||||
@Schema(description = "备注", example = "管理员角色")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "菜单ID列表")
|
||||
private List<Long> menuIds;
|
||||
|
||||
@Schema(description = "创建时间", example = "2024-01-01T00:00:00")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间", example = "2024-01-01T00:00:00")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public SysRoleResponse() {
|
||||
}
|
||||
|
||||
public SysRoleResponse(Long id, String name, String roleKey, String description, String status, Integer sortOrder,
|
||||
String remark, List<Long> menuIds, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.roleKey = roleKey;
|
||||
this.description = description;
|
||||
this.status = status;
|
||||
this.sortOrder = sortOrder;
|
||||
this.remark = remark;
|
||||
this.menuIds = menuIds;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getRoleKey() {
|
||||
return roleKey;
|
||||
}
|
||||
|
||||
public void setRoleKey(String roleKey) {
|
||||
this.roleKey = roleKey;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Integer getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
public void setSortOrder(Integer sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public List<Long> getMenuIds() {
|
||||
return menuIds;
|
||||
}
|
||||
|
||||
public void setMenuIds(List<Long> menuIds) {
|
||||
this.menuIds = menuIds;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public static SysRoleResponse fromSysRole(SysRole sysRole) {
|
||||
if (sysRole == null) {
|
||||
return null;
|
||||
}
|
||||
return new SysRoleResponse(
|
||||
sysRole.getId(),
|
||||
sysRole.getRoleName(),
|
||||
sysRole.getRoleKey(),
|
||||
null,
|
||||
sysRole.getStatus(),
|
||||
sysRole.getRoleSort(),
|
||||
null,
|
||||
null,
|
||||
sysRole.getCreatedAt(),
|
||||
sysRole.getUpdatedAt());
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String roleKey;
|
||||
private String description;
|
||||
private String status;
|
||||
private Integer sortOrder;
|
||||
private String remark;
|
||||
private List<Long> menuIds;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Builder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder roleKey(String roleKey) {
|
||||
this.roleKey = roleKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder status(String status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder sortOrder(Integer sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder remark(String remark) {
|
||||
this.remark = remark;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder menuIds(List<Long> menuIds) {
|
||||
this.menuIds = menuIds;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SysRoleResponse build() {
|
||||
return new SysRoleResponse(id, name, roleKey, description, status, sortOrder, remark, menuIds, createdAt,
|
||||
updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package io.destiny.sys.dto.response;
|
||||
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class SysUserRegisterRequest {
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 100, message = "密码长度必须在6-100个字符之间")
|
||||
@Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*\\d).+$", message = "密码必须包含字母和数字")
|
||||
private String password;
|
||||
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
private String phone;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public SysUser toDomain() {
|
||||
SysUser user = new SysUser();
|
||||
user.setUsername(this.username);
|
||||
user.setPassword(this.password);
|
||||
user.setEmail(this.email != null ? io.destiny.common.primitive.EmailAddress.of(this.email) : null);
|
||||
user.setPhone(this.phone != null ? io.destiny.common.primitive.PhoneNumber.of(this.phone) : null);
|
||||
user.setStatus("0");
|
||||
return user;
|
||||
}
|
||||
}
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
package io.destiny.sys.dto.response;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import io.destiny.common.serializer.SensitiveDataJsonSerializer;
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Schema(description = "用户响应DTO")
|
||||
public class SysUserResponse {
|
||||
|
||||
@Schema(description = "用户ID", example = "1234567890123456789")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户名", example = "zhangsan")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "邮箱", example = "zhangsan@example.com")
|
||||
@JsonSerialize(using = SensitiveDataJsonSerializer.class)
|
||||
private String email;
|
||||
|
||||
@Schema(description = "手机号", example = "13800138000")
|
||||
@JsonSerialize(using = SensitiveDataJsonSerializer.class)
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "昵称", example = "张三")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "状态 0-正常 1-禁用", example = "0")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "备注", example = "系统管理员")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "权限列表")
|
||||
private Set<String> permissions;
|
||||
|
||||
@Schema(description = "角色列表")
|
||||
private List<SysRoleResponse> roles;
|
||||
|
||||
@Schema(description = "创建人", example = "admin")
|
||||
private String createBy;
|
||||
|
||||
@Schema(description = "更新人", example = "admin")
|
||||
private String updateBy;
|
||||
|
||||
@Schema(description = "创建时间", example = "2024-01-01T00:00:00")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间", example = "2024-01-01T00:00:00")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public SysUserResponse() {
|
||||
}
|
||||
|
||||
public SysUserResponse(Long id, String username, String email, String phone, String nickname, String status,
|
||||
String remark, Set<String> permissions, List<SysRoleResponse> roles, String createBy, String updateBy,
|
||||
LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.email = email;
|
||||
this.phone = phone;
|
||||
this.nickname = nickname;
|
||||
this.status = status;
|
||||
this.remark = remark;
|
||||
this.permissions = permissions;
|
||||
this.roles = roles;
|
||||
this.createBy = createBy;
|
||||
this.updateBy = updateBy;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Set<String> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public void setPermissions(Set<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public List<SysRoleResponse> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(List<SysRoleResponse> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public String getCreateBy() {
|
||||
return createBy;
|
||||
}
|
||||
|
||||
public void setCreateBy(String createBy) {
|
||||
this.createBy = createBy;
|
||||
}
|
||||
|
||||
public String getUpdateBy() {
|
||||
return updateBy;
|
||||
}
|
||||
|
||||
public void setUpdateBy(String updateBy) {
|
||||
this.updateBy = updateBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public static SysUserResponse fromSysUser(SysUser sysUser) {
|
||||
if (sysUser == null) {
|
||||
return null;
|
||||
}
|
||||
return new SysUserResponse(
|
||||
sysUser.getId(),
|
||||
sysUser.getUsername(),
|
||||
sysUser.getEmail() != null ? sysUser.getEmail().getValue() : null,
|
||||
sysUser.getPhone() != null ? sysUser.getPhone().getValue() : null,
|
||||
null,
|
||||
sysUser.getStatus(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
sysUser.getCreateBy(),
|
||||
sysUser.getUpdateBy(),
|
||||
sysUser.getCreatedAt(),
|
||||
sysUser.getUpdatedAt());
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String email;
|
||||
private String phone;
|
||||
private String nickname;
|
||||
private String status;
|
||||
private String remark;
|
||||
private Set<String> permissions;
|
||||
private List<SysRoleResponse> roles;
|
||||
private String createBy;
|
||||
private String updateBy;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Builder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder username(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder email(String email) {
|
||||
this.email = email;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder phone(String phone) {
|
||||
this.phone = phone;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder nickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder status(String status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder remark(String remark) {
|
||||
this.remark = remark;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder permissions(Set<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder roles(List<SysRoleResponse> roles) {
|
||||
this.roles = roles;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder createBy(String createBy) {
|
||||
this.createBy = createBy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder updateBy(String updateBy) {
|
||||
this.updateBy = updateBy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SysUserResponse build() {
|
||||
return new SysUserResponse(id, username, email, phone, nickname, status, remark, permissions, roles, createBy,
|
||||
updateBy, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package io.destiny.sys.handler;
|
||||
|
||||
import io.destiny.common.response.Result;
|
||||
import io.destiny.sys.core.domain.query.OperationLogQuery;
|
||||
import io.destiny.sys.core.service.IOperationLogService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@Tag(name = "操作日志", description = "操作日志相关接口")
|
||||
public class OperationLogHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OperationLogHandler.class);
|
||||
|
||||
private final IOperationLogService logService;
|
||||
|
||||
public OperationLogHandler(IOperationLogService logService) {
|
||||
this.logService = logService;
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID查询操作日志", description = "根据日志ID查询操作日志详细信息")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getLogById(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return logService.findById(id)
|
||||
.flatMap(log -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Result.success(log)))
|
||||
.switchIfEmpty(ServerResponse.badRequest().bodyValue(Result.error("400", "操作日志不存在")))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询操作日志失败,ID: {}", id, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("500", "查询操作日志失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询操作日志", description = "根据条件分页查询操作日志列表")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> queryLogs(ServerRequest request) {
|
||||
return request.bodyToMono(OperationLogQuery.class)
|
||||
.flatMap(query -> {
|
||||
if (query.getPageNum() == null) {
|
||||
query.setPageNum(1);
|
||||
}
|
||||
if (query.getPageSize() == null) {
|
||||
query.setPageSize(10);
|
||||
}
|
||||
|
||||
return logService.findByQuery(query)
|
||||
.collectList()
|
||||
.zipWith(logService.countByQuery(query))
|
||||
.map(tuple -> {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("list", tuple.getT1());
|
||||
result.put("total", tuple.getT2());
|
||||
result.put("pageNum", query.getPageNum());
|
||||
result.put("pageSize", query.getPageSize());
|
||||
return Result.success(result);
|
||||
})
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result));
|
||||
})
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询操作日志列表失败", e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询操作日志列表失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "删除操作日志", description = "根据ID删除操作日志")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "删除成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> deleteLog(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return logService.findById(id)
|
||||
.hasElement()
|
||||
.flatMap(exists -> {
|
||||
if (!exists) {
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("400", "操作日志不存在"));
|
||||
}
|
||||
return logService.deleteById(id)
|
||||
.then(Mono.fromCallable(() -> Result.success(null)))
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result));
|
||||
})
|
||||
.onErrorResume(e -> {
|
||||
log.error("删除操作日志失败,ID: {}", id, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("500", "删除操作日志失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package io.destiny.sys.handler;
|
||||
|
||||
import io.destiny.common.response.Result;
|
||||
import io.destiny.sys.core.service.ISysAuthService;
|
||||
import io.destiny.sys.dto.request.SysUserLoginRequest;
|
||||
import io.destiny.sys.dto.response.SysLoginResponse;
|
||||
import io.destiny.sys.dto.response.SysUserRegisterRequest;
|
||||
import io.destiny.sys.util.ValidationUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
@Tag(name = "认证管理", description = "用户认证相关接口")
|
||||
public class SysAuthHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SysAuthHandler.class);
|
||||
|
||||
private final ISysAuthService authService;
|
||||
|
||||
public SysAuthHandler(ISysAuthService authService) {
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@Operation(summary = "用户注册", description = "新用户注册,需要提供用户名、密码、邮箱和手机号")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "注册成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> register(ServerRequest request) {
|
||||
return request.bodyToMono(SysUserRegisterRequest.class)
|
||||
.flatMap(ValidationUtil::validate)
|
||||
.flatMap(authService::register)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("用户注册失败", e);
|
||||
return ServerResponse.badRequest().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Result.error("400", e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "用户登录", description = "用户登录获取访问令牌和刷新令牌")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "登录成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "401", description = "用户名或密码错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> login(ServerRequest request) {
|
||||
return request.bodyToMono(SysUserLoginRequest.class)
|
||||
.flatMap(ValidationUtil::validate)
|
||||
.flatMap(authService::login)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("用户登录失败", e);
|
||||
return ServerResponse.status(401).contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Result.error("401", e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "刷新token", description = "使用刷新令牌获取新的访问令牌")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "刷新成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "401", description = "刷新令牌无效或已过期"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> refreshToken(ServerRequest request) {
|
||||
String refreshToken = request.pathVariable("token");
|
||||
return authService.refreshToken(refreshToken)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("刷新令牌失败", e);
|
||||
return ServerResponse.status(401).contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Result.error("401", e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "用户登出", description = "用户登出,使当前令牌失效")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "登出成功"),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> logout(ServerRequest request) {
|
||||
String token = request.headers().firstHeader("Authorization");
|
||||
if (token != null && token.startsWith("Bearer ")) {
|
||||
token = token.substring(7);
|
||||
}
|
||||
return authService.logout(token)
|
||||
.then(Mono.fromCallable(() -> Result.success("登出成功")))
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("用户登出失败", e);
|
||||
return ServerResponse.badRequest().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Result.error("400", e.getMessage()));
|
||||
});
|
||||
}
|
||||
}
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
package io.destiny.sys.handler;
|
||||
|
||||
import io.destiny.common.response.Result;
|
||||
import io.destiny.sys.core.domain.SysMenu;
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import io.destiny.sys.core.service.ISysMenuService;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import io.destiny.sys.dto.request.SysMenuRequest;
|
||||
import io.destiny.sys.dto.response.SysMenuResponse;
|
||||
import io.destiny.sys.security.RequirePermission;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
@Tag(name = "菜单管理", description = "菜单管理相关接口")
|
||||
public class SysMenuHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SysMenuHandler.class);
|
||||
|
||||
private final ISysMenuService menuService;
|
||||
private final ISysRoleService roleService;
|
||||
|
||||
public SysMenuHandler(ISysMenuService menuService, ISysRoleService roleService) {
|
||||
this.menuService = menuService;
|
||||
this.roleService = roleService;
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID查询菜单", description = "根据菜单ID查询菜单详细信息")
|
||||
@RequirePermission("sys:menu:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getMenuById(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return menuService.findById(id)
|
||||
.map(SysMenuResponse::fromSysMenu)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result))
|
||||
.switchIfEmpty(Mono.defer(() -> ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("菜单不存在"))))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询菜单失败,ID: {}", id, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询菜单失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "根据角色ID查询菜单列表", description = "根据角色ID查询该角色拥有的所有菜单")
|
||||
@RequirePermission("sys:menu:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getMenusByRoleId(ServerRequest request) {
|
||||
Long roleId = Long.parseLong(request.pathVariable("roleId"));
|
||||
return menuService.findByRoleId(roleId)
|
||||
.map(SysMenuResponse::fromSysMenu)
|
||||
.collectList()
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询角色菜单失败,角色ID: {}", roleId, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询角色菜单失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "根据用户ID查询菜单列表", description = "根据用户ID查询该用户拥有的所有菜单")
|
||||
@RequirePermission("sys:menu:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getMenusByUserId(ServerRequest request) {
|
||||
Long userId = Long.parseLong(request.pathVariable("userId"));
|
||||
return menuService.findByUserId(userId)
|
||||
.map(SysMenuResponse::fromSysMenu)
|
||||
.collectList()
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询用户菜单失败,用户ID: {}", userId, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询用户菜单失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "查询所有菜单", description = "查询系统中的所有菜单")
|
||||
@RequirePermission("sys:menu:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getAllMenus(ServerRequest request) {
|
||||
return menuService.findAll()
|
||||
.map(SysMenuResponse::fromSysMenu)
|
||||
.collectList()
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询所有菜单失败", e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询所有菜单失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "创建菜单", description = "创建新菜单")
|
||||
@RequirePermission("sys:menu:create")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "创建成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> createMenu(ServerRequest request) {
|
||||
return request.bodyToMono(SysMenuRequest.class)
|
||||
.flatMap(menuRequest -> {
|
||||
SysMenu sysMenu = new SysMenu();
|
||||
sysMenu.setMenuName(menuRequest.getName());
|
||||
sysMenu.setParentId(menuRequest.getParentId());
|
||||
sysMenu.setOrderNum(menuRequest.getSortOrder());
|
||||
sysMenu.setMenuType(menuRequest.getCode());
|
||||
sysMenu.setPerms(menuRequest.getCode());
|
||||
sysMenu.setComponent(menuRequest.getComponent());
|
||||
sysMenu.setStatus(menuRequest.getStatus());
|
||||
sysMenu.generateId();
|
||||
return menuService.create(sysMenu);
|
||||
})
|
||||
.map(SysMenuResponse::fromSysMenu)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("创建菜单失败", e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("创建菜单失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "更新菜单", description = "更新菜单信息")
|
||||
@RequirePermission("sys:menu:update")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "更新成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> updateMenu(ServerRequest request) {
|
||||
return request.bodyToMono(SysMenuRequest.class)
|
||||
.flatMap(menuRequest -> {
|
||||
return menuService.findById(menuRequest.getId())
|
||||
.flatMap(sysMenu -> {
|
||||
sysMenu.setMenuName(menuRequest.getName());
|
||||
sysMenu.setParentId(menuRequest.getParentId());
|
||||
sysMenu.setOrderNum(menuRequest.getSortOrder());
|
||||
sysMenu.setMenuType(menuRequest.getCode());
|
||||
sysMenu.setPerms(menuRequest.getCode());
|
||||
sysMenu.setComponent(menuRequest.getComponent());
|
||||
sysMenu.setStatus(menuRequest.getStatus());
|
||||
return menuService.update(sysMenu);
|
||||
})
|
||||
.map(SysMenuResponse::fromSysMenu)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result));
|
||||
})
|
||||
.switchIfEmpty(Mono.defer(() -> ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("菜单不存在"))))
|
||||
.onErrorResume(e -> {
|
||||
log.error("更新菜单失败", e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("更新菜单失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "删除菜单", description = "根据ID删除菜单")
|
||||
@RequirePermission("sys:menu:delete")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "删除成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> deleteMenu(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return menuService.deleteById(id)
|
||||
.then(Mono.fromCallable(() -> Result.success(null)))
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("删除菜单失败,ID: {}", id, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("删除菜单失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "分配菜单到角色", description = "将菜单分配给角色")
|
||||
@RequirePermission("sys:menu:assign")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "分配成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> assignMenuToRole(ServerRequest request) {
|
||||
return request.bodyToMono(io.destiny.sys.dto.request.AssignMenuToRoleRequest.class)
|
||||
.flatMap(assignRequest -> {
|
||||
Long roleId = assignRequest.getRoleId();
|
||||
Long menuId = assignRequest.getMenuId();
|
||||
|
||||
return roleService.findById(roleId)
|
||||
.switchIfEmpty(Mono.error(new IllegalArgumentException("角色不存在")))
|
||||
.flatMap(role -> {
|
||||
return menuService.findById(menuId)
|
||||
.switchIfEmpty(Mono.error(new IllegalArgumentException("菜单不存在")))
|
||||
.flatMap(menu -> {
|
||||
return menuService.findByRoleId(roleId)
|
||||
.filter(roleMenu -> roleMenu.getId().equals(menuId))
|
||||
.hasElements()
|
||||
.flatMap(hasMenu -> {
|
||||
if (hasMenu) {
|
||||
return Mono.error(new IllegalArgumentException("角色已拥有该菜单权限"));
|
||||
}
|
||||
return menuService.assignMenuToRole(roleId, menuId);
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(Mono.fromCallable(() -> Result.success(null)))
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("分配菜单到角色失败", e);
|
||||
String errorMessage = e.getMessage();
|
||||
if (errorMessage != null && errorMessage.contains("角色不存在")) {
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("角色不存在"));
|
||||
}
|
||||
if (errorMessage != null && errorMessage.contains("菜单不存在")) {
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("菜单不存在"));
|
||||
}
|
||||
if (errorMessage != null && errorMessage.contains("角色已拥有该菜单权限")) {
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("角色已拥有该菜单权限"));
|
||||
}
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("分配菜单到角色失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "移除角色的菜单", description = "从角色中移除菜单")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "移除成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> removeMenuFromRole(ServerRequest request) {
|
||||
Long roleId = Long.parseLong(request.pathVariable("roleId"));
|
||||
Long menuId = Long.parseLong(request.pathVariable("menuId"));
|
||||
return menuService.removeMenuFromRole(roleId, menuId)
|
||||
.then(Mono.fromCallable(() -> Result.success(null)))
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("移除角色菜单失败,角色ID: {}, 菜单ID: {}", roleId, menuId, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("移除角色菜单失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
}
|
||||
+282
@@ -0,0 +1,282 @@
|
||||
package io.destiny.sys.handler;
|
||||
|
||||
import io.destiny.common.response.Result;
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import io.destiny.sys.dto.request.SysRoleRequest;
|
||||
import io.destiny.sys.dto.response.SysRoleResponse;
|
||||
import io.destiny.sys.security.RequirePermission;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
@Tag(name = "角色管理", description = "角色管理相关接口")
|
||||
public class SysRoleHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SysRoleHandler.class);
|
||||
|
||||
private final ISysRoleService roleService;
|
||||
|
||||
public SysRoleHandler(ISysRoleService roleService) {
|
||||
this.roleService = roleService;
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID查询角色", description = "根据角色ID查询角色详细信息")
|
||||
@RequirePermission("sys:role:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getRoleById(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return roleService.findById(id)
|
||||
.map(SysRoleResponse::fromSysRole)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.switchIfEmpty(Mono.defer(() -> ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("角色不存在"))))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询角色失败,ID: {}", id, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询角色失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "根据角色权限字符串查询角色", description = "根据角色权限字符串查询角色详细信息")
|
||||
@RequirePermission("sys:role:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getRoleByRoleKey(ServerRequest request) {
|
||||
String roleKey = request.pathVariable("roleKey");
|
||||
return roleService.findByRoleKey(roleKey)
|
||||
.map(SysRoleResponse::fromSysRole)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.switchIfEmpty(Mono.defer(() -> ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("角色不存在"))))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询角色失败,角色权限字符串: {}", roleKey, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询角色失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "根据用户ID查询角色列表", description = "根据用户ID查询该用户拥有的所有角色")
|
||||
@RequirePermission("sys:role:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getRolesByUserId(ServerRequest request) {
|
||||
Long userId = Long.parseLong(request.pathVariable("userId"));
|
||||
return roleService.findByUserId(userId)
|
||||
.map(SysRoleResponse::fromSysRole)
|
||||
.collectList()
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询用户角色失败,用户ID: {}", userId, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询用户角色失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "查询所有角色", description = "查询系统中的所有角色")
|
||||
@RequirePermission("sys:role:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getAllRoles(ServerRequest request) {
|
||||
return roleService.findAll()
|
||||
.map(SysRoleResponse::fromSysRole)
|
||||
.collectList()
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询所有角色失败", e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询所有角色失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "创建角色", description = "创建新角色")
|
||||
@RequirePermission("sys:role:create")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "创建成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> createRole(ServerRequest request) {
|
||||
return request.bodyToMono(SysRoleRequest.class)
|
||||
.flatMap(roleRequest -> {
|
||||
return roleService.findByRoleKey(roleRequest.getRoleKey())
|
||||
.flatMap(existingRole -> {
|
||||
return Mono.error(new IllegalArgumentException("角色权限字符串已存在"));
|
||||
})
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
SysRole sysRole = new SysRole();
|
||||
sysRole.setRoleName(roleRequest.getName());
|
||||
sysRole.setRoleKey(roleRequest.getRoleKey());
|
||||
sysRole.setRoleSort(roleRequest.getSortOrder());
|
||||
sysRole.setStatus(roleRequest.getStatus());
|
||||
sysRole.generateId();
|
||||
return roleService.create(sysRole);
|
||||
}));
|
||||
})
|
||||
.cast(SysRole.class)
|
||||
.map(SysRoleResponse::fromSysRole)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("创建角色失败", e);
|
||||
String errorMessage = e.getMessage();
|
||||
if (errorMessage != null && errorMessage.contains("角色权限字符串已存在")) {
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("角色权限字符串已存在"));
|
||||
}
|
||||
if (errorMessage != null && errorMessage.contains("角色名称不能为空")) {
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("角色名称不能为空"));
|
||||
}
|
||||
if (errorMessage != null && errorMessage.contains("角色权限字符串不能为空")) {
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("角色权限字符串不能为空"));
|
||||
}
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("创建角色失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "更新角色", description = "更新角色信息")
|
||||
@RequirePermission("sys:role:update")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "更新成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> updateRole(ServerRequest request) {
|
||||
return request.bodyToMono(SysRoleRequest.class)
|
||||
.flatMap(roleRequest -> {
|
||||
return roleService.findById(roleRequest.getId())
|
||||
.flatMap(sysRole -> {
|
||||
sysRole.setRoleName(roleRequest.getName());
|
||||
sysRole.setRoleKey(roleRequest.getRoleKey());
|
||||
sysRole.setRoleSort(roleRequest.getSortOrder());
|
||||
sysRole.setStatus(roleRequest.getStatus());
|
||||
return roleService.update(sysRole);
|
||||
})
|
||||
.map(SysRoleResponse::fromSysRole)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result));
|
||||
})
|
||||
.switchIfEmpty(Mono.defer(() -> ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("角色不存在"))))
|
||||
.onErrorResume(e -> {
|
||||
log.error("更新角色失败", e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("更新角色失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "删除角色", description = "根据ID删除角色")
|
||||
@RequirePermission("sys:role:delete")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "删除成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> deleteRole(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return roleService.deleteById(id)
|
||||
.then(Mono.fromCallable(() -> Result.success(null)))
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("删除角色失败,ID: {}", id, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("删除角色失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "分配角色到用户", description = "将角色分配给用户")
|
||||
@RequirePermission("sys:role:assign")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "分配成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> assignRoleToUser(ServerRequest request) {
|
||||
return request.bodyToMono(io.destiny.sys.dto.request.AssignRoleToUserRequest.class)
|
||||
.flatMap(assignRequest -> {
|
||||
Long userId = assignRequest.getUserId();
|
||||
Long roleId = assignRequest.getRoleId();
|
||||
|
||||
return roleService.findById(roleId)
|
||||
.switchIfEmpty(Mono.error(new IllegalArgumentException("角色不存在")))
|
||||
.flatMap(role -> {
|
||||
return roleService.findByUserId(userId)
|
||||
.filter(userRole -> userRole.getId().equals(roleId))
|
||||
.hasElements()
|
||||
.flatMap(hasRole -> {
|
||||
if (hasRole) {
|
||||
return Mono.error(new IllegalArgumentException("用户已拥有该角色"));
|
||||
}
|
||||
return roleService.assignRoleToUser(userId, roleId);
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(Mono.fromCallable(() -> Result.success(null)))
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("分配角色到用户失败", e);
|
||||
String errorMessage = e.getMessage();
|
||||
if (errorMessage != null && errorMessage.contains("角色不存在")) {
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("角色不存在"));
|
||||
}
|
||||
if (errorMessage != null && errorMessage.contains("用户已拥有该角色")) {
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("用户已拥有该角色"));
|
||||
}
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("分配角色到用户失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "移除用户的角色", description = "从用户中移除角色")
|
||||
@RequirePermission("sys:role:assign")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "移除成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> removeRoleFromUser(ServerRequest request) {
|
||||
Long userId = Long.parseLong(request.pathVariable("userId"));
|
||||
Long roleId = Long.parseLong(request.pathVariable("roleId"));
|
||||
return roleService.removeRoleFromUser(userId, roleId)
|
||||
.then(Mono.fromCallable(() -> Result.success(null)))
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("移除用户角色失败,用户ID: {}, 角色ID: {}", userId, roleId, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("移除用户角色失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
}
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
package io.destiny.sys.handler;
|
||||
|
||||
import io.destiny.common.response.Result;
|
||||
import io.destiny.common.utils.XssUtils;
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import io.destiny.sys.dto.request.SysUserRequest;
|
||||
import io.destiny.sys.dto.response.SysUserResponse;
|
||||
import io.destiny.sys.security.RequirePermission;
|
||||
import io.destiny.sys.util.ValidationUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
@Tag(name = "用户管理", description = "用户管理相关接口")
|
||||
public class SysUserHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SysUserHandler.class);
|
||||
|
||||
private final ISysUserService userService;
|
||||
|
||||
public SysUserHandler(ISysUserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID查询用户", description = "根据用户ID查询用户详细信息")
|
||||
@RequirePermission("sys:user:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getUserById(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return userService.findById(id)
|
||||
.map(SysUserResponse::fromSysUser)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.switchIfEmpty(Mono.defer(() -> ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("用户不存在"))))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询用户失败,ID: {}", id, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询用户失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "根据用户名查询用户", description = "根据用户名查询用户详细信息")
|
||||
@RequirePermission("sys:user:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getUserByUsername(ServerRequest request) {
|
||||
String username = request.pathVariable("username");
|
||||
return userService.findByUsername(username)
|
||||
.map(SysUserResponse::fromSysUser)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.switchIfEmpty(Mono.defer(() -> ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("用户不存在"))))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询用户失败,用户名: {}", username, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询用户失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "创建用户", description = "创建新用户")
|
||||
@RequirePermission("sys:user:create")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "创建成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> createUser(ServerRequest request) {
|
||||
return request.bodyToMono(SysUserRequest.class)
|
||||
.flatMap(ValidationUtil::validate)
|
||||
.map(userRequest -> {
|
||||
SysUserRequest cleanedRequest = new SysUserRequest();
|
||||
cleanedRequest.setId(userRequest.getId());
|
||||
cleanedRequest.setUsername(XssUtils.clean(userRequest.getUsername()));
|
||||
cleanedRequest.setPassword(userRequest.getPassword());
|
||||
cleanedRequest.setEmail(XssUtils.clean(userRequest.getEmail()));
|
||||
cleanedRequest.setPhone(XssUtils.clean(userRequest.getPhone()));
|
||||
cleanedRequest.setStatus(userRequest.getStatus());
|
||||
return cleanedRequest;
|
||||
})
|
||||
.flatMap(cleanedRequest -> {
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUsername(cleanedRequest.getUsername());
|
||||
sysUser.setPassword(cleanedRequest.getPassword());
|
||||
sysUser.setEmail(io.destiny.common.primitive.EmailAddress.of(cleanedRequest.getEmail()));
|
||||
sysUser.setPhone(io.destiny.common.primitive.PhoneNumber.of(cleanedRequest.getPhone()));
|
||||
sysUser.setStatus(cleanedRequest.getStatus());
|
||||
sysUser.generateId();
|
||||
return userService.create(sysUser);
|
||||
})
|
||||
.map(SysUserResponse::fromSysUser)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("创建用户失败", e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("创建用户失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "更新用户", description = "更新用户信息")
|
||||
@RequirePermission("sys:user:update")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "更新成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> updateUser(ServerRequest request) {
|
||||
return request.bodyToMono(SysUserRequest.class)
|
||||
.flatMap(userRequest -> {
|
||||
return userService.findById(userRequest.getId())
|
||||
.flatMap(sysUser -> {
|
||||
sysUser.setUsername(userRequest.getUsername());
|
||||
if (userRequest.getPassword() != null) {
|
||||
sysUser.setPassword(userRequest.getPassword());
|
||||
}
|
||||
if (userRequest.getEmail() != null) {
|
||||
sysUser.setEmail(
|
||||
io.destiny.common.primitive.EmailAddress.of(userRequest.getEmail()));
|
||||
}
|
||||
if (userRequest.getPhone() != null) {
|
||||
sysUser.setPhone(
|
||||
io.destiny.common.primitive.PhoneNumber.of(userRequest.getPhone()));
|
||||
}
|
||||
if (userRequest.getStatus() != null) {
|
||||
sysUser.setStatus(userRequest.getStatus());
|
||||
}
|
||||
return userService.update(sysUser);
|
||||
})
|
||||
.map(SysUserResponse::fromSysUser)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result));
|
||||
})
|
||||
.switchIfEmpty(Mono.defer(() -> ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("用户不存在"))))
|
||||
.onErrorResume(e -> {
|
||||
log.error("更新用户失败", e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("更新用户失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "删除用户", description = "根据ID删除用户")
|
||||
@RequirePermission("sys:user:delete")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "删除成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> deleteUser(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return userService.deleteById(id)
|
||||
.then(Mono.fromCallable(() -> Result.success(null)))
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("删除用户失败,ID: {}", id, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("删除用户失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "封禁用户", description = "封禁指定用户")
|
||||
@RequirePermission("sys:user:edit")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "封禁成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> banUser(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return userService.findById(id)
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("用户不存在")))
|
||||
.flatMap(user -> {
|
||||
user.setStatus("1");
|
||||
return userService.update(user);
|
||||
})
|
||||
.map(SysUserResponse::fromSysUser)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("封禁用户失败,ID: {}", id, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("封禁用户失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "查询所有用户", description = "查询系统中的所有用户")
|
||||
@RequirePermission("sys:user:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> getAllUsers(ServerRequest request) {
|
||||
return userService.findAll()
|
||||
.map(SysUserResponse::fromSysUser)
|
||||
.collectList()
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("查询所有用户失败", e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("查询所有用户失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询用户", description = "根据条件分页查询用户列表")
|
||||
@RequirePermission("sys:user:query")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "查询成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> queryUsersWithPagination(ServerRequest request) {
|
||||
return Mono.fromCallable(() -> {
|
||||
io.destiny.common.request.PageQuery pageQuery = new io.destiny.common.request.PageQuery();
|
||||
request.queryParam("pageNum").ifPresent(p -> pageQuery.setPageNum(Integer.parseInt(p)));
|
||||
request.queryParam("pageSize").ifPresent(p -> pageQuery.setPageSize(Integer.parseInt(p)));
|
||||
request.queryParam("sortField").ifPresent(pageQuery::setSortField);
|
||||
request.queryParam("sortOrder").ifPresent(pageQuery::setSortOrder);
|
||||
return pageQuery;
|
||||
})
|
||||
.flatMap(pageQuery -> {
|
||||
io.destiny.sys.core.domain.query.SysUserQuery query = new io.destiny.sys.core.domain.query.SysUserQuery();
|
||||
request.queryParam("username").ifPresent(query::setUsername);
|
||||
request.queryParam("status").ifPresent(query::setStatus);
|
||||
request.queryParam("email")
|
||||
.ifPresent(e -> query.setEmail(io.destiny.common.primitive.EmailAddress.of(e)));
|
||||
request.queryParam("phone")
|
||||
.ifPresent(p -> query.setPhone(io.destiny.common.primitive.PhoneNumber.of(p)));
|
||||
return userService.findByQueryWithPagination(query, pageQuery);
|
||||
})
|
||||
.map(pageModel -> {
|
||||
java.util.List<SysUserResponse> dataList = pageModel.getData().stream()
|
||||
.map(SysUserResponse::fromSysUser)
|
||||
.toList();
|
||||
io.destiny.common.response.PageModel<SysUserResponse> response = new io.destiny.common.response.PageModel<>(
|
||||
pageModel.getCount(), dataList);
|
||||
return Result.success(response);
|
||||
})
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("分页查询用户失败", e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("分页查询用户失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "解封用户", description = "解封指定用户")
|
||||
@RequirePermission("sys:user:edit")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "解封成功", content = @Content(schema = @Schema(implementation = Result.class))),
|
||||
@ApiResponse(responseCode = "400", description = "请求参数错误"),
|
||||
@ApiResponse(responseCode = "500", description = "服务器内部错误")
|
||||
})
|
||||
public Mono<ServerResponse> unbanUser(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return userService.findById(id)
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("用户不存在")))
|
||||
.flatMap(user -> {
|
||||
user.setStatus("0");
|
||||
return userService.update(user);
|
||||
})
|
||||
.map(SysUserResponse::fromSysUser)
|
||||
.map(Result::success)
|
||||
.flatMap(result -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(result))
|
||||
.onErrorResume(e -> {
|
||||
log.error("解封用户失败,ID: {}", id, e);
|
||||
return ServerResponse.badRequest()
|
||||
.bodyValue(Result.error("解封用户失败:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package io.destiny.sys.security;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequirePermission {
|
||||
String value();
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package io.destiny.sys.security;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequireRole {
|
||||
String value();
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package io.destiny.sys.security;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class SysJwtTokenProvider {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SysJwtTokenProvider.class);
|
||||
|
||||
@Value("${jwt.secret:this-is-a-secure-jwt-secret-key-that-must-be-at-least-64-characters-long-for-hs512-algorithm}")
|
||||
private String secret;
|
||||
|
||||
@Value("${jwt.expiration:86400000}")
|
||||
private Long expiration;
|
||||
|
||||
@Value("${jwt.refresh-expiration:604800000}")
|
||||
private Long refreshExpiration;
|
||||
|
||||
private SecretKey getSigningKey() {
|
||||
byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
|
||||
return Keys.hmacShaKeyFor(keyBytes);
|
||||
}
|
||||
|
||||
public String generateAccessToken(Long userId, String username) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("userId", userId);
|
||||
claims.put("username", username);
|
||||
claims.put("type", "access");
|
||||
return createToken(claims, userId.toString(), expiration);
|
||||
}
|
||||
|
||||
public String generateRefreshToken(Long userId, String username) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("userId", userId);
|
||||
claims.put("username", username);
|
||||
claims.put("type", "refresh");
|
||||
return createToken(claims, userId.toString(), refreshExpiration);
|
||||
}
|
||||
|
||||
private String createToken(Map<String, Object> claims, String subject, Long expirationTime) {
|
||||
Date now = new Date();
|
||||
Date expiryDate = new Date(now.getTime() + expirationTime);
|
||||
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setSubject(subject)
|
||||
.setIssuedAt(now)
|
||||
.setExpiration(expiryDate)
|
||||
.signWith(getSigningKey())
|
||||
.compact();
|
||||
}
|
||||
|
||||
public Long getUserIdFromToken(String token) {
|
||||
try {
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
return claims.get("userId", Long.class);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to get user id from token", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getUsernameFromToken(String token) {
|
||||
try {
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
return claims.get("username", String.class);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to get username from token", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getTokenType(String token) {
|
||||
try {
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
return claims.get("type", String.class);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to get token type", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean validateToken(String token) {
|
||||
try {
|
||||
Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
.build()
|
||||
.parseClaimsJws(token);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.error("Invalid JWT token", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean isTokenExpired(String token) {
|
||||
try {
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
Date expiration = claims.getExpiration();
|
||||
return expiration.before(new Date());
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to check token expiration", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
package io.destiny.sys.security;
|
||||
|
||||
import io.destiny.sys.core.service.ISysPermissionService;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Order(4)
|
||||
public class SysPermissionFilter implements WebFilter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SysPermissionFilter.class);
|
||||
|
||||
private static final String USER_ID_HEADER = "X-User-Id";
|
||||
private static final String USERNAME_HEADER = "X-Username";
|
||||
|
||||
private final ISysUserService userService;
|
||||
private final ISysPermissionService permissionService;
|
||||
|
||||
private final List<String> publicPaths;
|
||||
private final List<PermissionRule> permissionRules;
|
||||
|
||||
public SysPermissionFilter(ISysUserService userService, ISysPermissionService permissionService) {
|
||||
logger.debug("SysPermissionFilter constructor called");
|
||||
this.userService = userService;
|
||||
this.permissionService = permissionService;
|
||||
|
||||
this.publicPaths = List.of(
|
||||
"/api/sys/auth/register",
|
||||
"/api/sys/auth/login",
|
||||
"/api/sys/auth/refresh",
|
||||
"/api/sys/auth/logout",
|
||||
"/api/client/auth/register",
|
||||
"/api/client/auth/login",
|
||||
"/sys/auth/register",
|
||||
"/sys/auth/login",
|
||||
"/sys/auth/refresh",
|
||||
"/sys/auth/logout",
|
||||
"/api/almanac",
|
||||
"/api/almanac/**",
|
||||
"/almanac",
|
||||
"/almanac/**",
|
||||
"/api/health",
|
||||
"/health",
|
||||
"/swagger-ui.html",
|
||||
"/swagger-ui",
|
||||
"/swagger-ui/**",
|
||||
"/v3/api-docs",
|
||||
"/v3/api-docs/**",
|
||||
"/webjars/**",
|
||||
"/api/swagger-ui.html",
|
||||
"/api/swagger-ui",
|
||||
"/api/swagger-ui/**",
|
||||
"/api/v3/api-docs",
|
||||
"/api/v3/api-docs/**",
|
||||
"/api/webjars/**",
|
||||
"/api/actuator/**",
|
||||
"/actuator/**"
|
||||
);
|
||||
|
||||
logger.info("SysPermissionFilter initialized with {} public paths", this.publicPaths.size());
|
||||
|
||||
this.permissionRules = List.of(
|
||||
new PermissionRule("GET", "/sys/user", "sys:user:list"),
|
||||
new PermissionRule("POST", "/sys/user", "sys:user:add"),
|
||||
new PermissionRule("PUT", "/sys/user", "sys:user:edit"),
|
||||
new PermissionRule("DELETE", "/sys/user", "sys:user:remove"),
|
||||
new PermissionRule("GET", "/sys/role", "sys:role:list"),
|
||||
new PermissionRule("POST", "/sys/role", "sys:role:add"),
|
||||
new PermissionRule("PUT", "/sys/role", "sys:role:edit"),
|
||||
new PermissionRule("DELETE", "/sys/role", "sys:role:remove"),
|
||||
new PermissionRule("GET", "/sys/menu", "sys:menu:list"),
|
||||
new PermissionRule("POST", "/sys/menu", "sys:menu:add"),
|
||||
new PermissionRule("PUT", "/sys/menu", "sys:menu:edit"),
|
||||
new PermissionRule("DELETE", "/sys/menu", "sys:menu:remove")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
String path = exchange.getRequest().getPath().value();
|
||||
String method = exchange.getRequest().getMethod().name();
|
||||
|
||||
logger.debug("SysPermissionFilter processing: {} {}", method, path);
|
||||
|
||||
// Skip OPTIONS requests for CORS preflight
|
||||
if ("OPTIONS".equals(method)) {
|
||||
logger.debug("OPTIONS request, skipping permission check: {} {}", method, path);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// Special handling for Swagger paths
|
||||
if (path.startsWith("/swagger-ui") || path.startsWith("/v3/api-docs") || path.startsWith("/webjars")) {
|
||||
logger.debug("Swagger path detected, skipping permission check: {} {}", method, path);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// Special handling for /actuator/** paths
|
||||
if (path.startsWith("/actuator/")) {
|
||||
logger.debug("Path '{}' is an actuator path, skipping permission check", path);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
if (isPublicPath(path)) {
|
||||
logger.debug("Public path, skipping permission check: {} {}", method, path);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
String userIdHeader = exchange.getRequest().getHeaders().getFirst(USER_ID_HEADER);
|
||||
String usernameHeader = exchange.getRequest().getHeaders().getFirst(USERNAME_HEADER);
|
||||
|
||||
if (userIdHeader == null || usernameHeader == null) {
|
||||
logger.warn("Missing user headers for path: {} {}", method, path);
|
||||
return forbidden(exchange, "Missing user authentication information");
|
||||
}
|
||||
|
||||
try {
|
||||
Long userId = Long.parseLong(userIdHeader);
|
||||
|
||||
return userService.findById(userId)
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("User not found")))
|
||||
.flatMap(user -> {
|
||||
if (!"0".equals(user.getStatus())) {
|
||||
logger.warn("User is disabled: {} ({})", user.getUsername(), userId);
|
||||
return forbidden(exchange, "User account is disabled");
|
||||
}
|
||||
|
||||
PermissionRule rule = findPermissionRule(method, path);
|
||||
if (rule == null) {
|
||||
logger.debug("No permission rule defined for {} {}, allowing access", method, path);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
return checkPermission(userId, rule.permission)
|
||||
.flatMap(hasPermission -> {
|
||||
if (hasPermission) {
|
||||
logger.debug("Permission granted for user {} to access {} {}", user.getUsername(), method, path);
|
||||
return chain.filter(exchange);
|
||||
} else {
|
||||
logger.warn("Permission denied for user {} to access {} {} (required: {})",
|
||||
user.getUsername(), method, path, rule.permission);
|
||||
return forbidden(exchange, "Permission denied");
|
||||
}
|
||||
});
|
||||
})
|
||||
.onErrorResume(e -> {
|
||||
logger.error("Error during permission check for {} {}", method, path, e);
|
||||
if (e.getMessage().contains("User not found")) {
|
||||
return forbidden(exchange, "User not found");
|
||||
}
|
||||
return forbidden(exchange, "Permission check failed");
|
||||
});
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
logger.error("Invalid user ID format: {}", userIdHeader);
|
||||
return forbidden(exchange, "Invalid user ID format");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPublicPath(String path) {
|
||||
// Special handling for Swagger paths
|
||||
if (path.startsWith("/swagger-ui") || path.startsWith("/v3/api-docs") || path.startsWith("/webjars")) {
|
||||
logger.debug("Swagger path detected, marking as public: {}", path);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Special handling for /actuator/** paths
|
||||
if (path.startsWith("/actuator/")) {
|
||||
logger.debug("Path '{}' is an actuator path, marking as public", path);
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isPublic = publicPaths.stream().anyMatch(publicPath -> {
|
||||
boolean match = false;
|
||||
if (publicPath.endsWith("/**")) {
|
||||
String prefix = publicPath.substring(0, publicPath.length() - 3);
|
||||
match = path.startsWith(prefix);
|
||||
} else if (publicPath.endsWith("/*")) {
|
||||
String prefix = publicPath.substring(0, publicPath.length() - 2);
|
||||
match = path.startsWith(prefix) && path.substring(prefix.length()).indexOf('/') == -1;
|
||||
} else {
|
||||
boolean exactMatch = path.equals(publicPath);
|
||||
boolean prefixMatch = path.startsWith(publicPath);
|
||||
match = exactMatch || prefixMatch;
|
||||
}
|
||||
logger.debug("Checking path '{}' against public path '{}': {}", path, publicPath, match);
|
||||
return match;
|
||||
});
|
||||
logger.debug("Checking path '{}' against public paths: {}", path, isPublic);
|
||||
return isPublic;
|
||||
}
|
||||
|
||||
private PermissionRule findPermissionRule(String method, String path) {
|
||||
for (PermissionRule rule : permissionRules) {
|
||||
if (rule.method.equals(method) && path.startsWith(rule.path)) {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Mono<Boolean> checkPermission(Long userId, String permission) {
|
||||
return Mono.fromCallable(() -> permissionService.hasPermission(userId, permission));
|
||||
}
|
||||
|
||||
private Mono<Void> forbidden(ServerWebExchange exchange, String message) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
|
||||
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
String body = String.format("{\"code\":\"FORBIDDEN\",\"message\":\"%s\"}", message);
|
||||
|
||||
return exchange.getResponse().writeWith(
|
||||
reactor.core.publisher.Flux.just(
|
||||
exchange.getResponse().bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8))));
|
||||
}
|
||||
|
||||
private static class PermissionRule {
|
||||
final String method;
|
||||
final String path;
|
||||
final String permission;
|
||||
|
||||
PermissionRule(String method, String path, String permission) {
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
this.permission = permission;
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package io.destiny.sys.util;
|
||||
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.ValidatorFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ValidationUtil {
|
||||
|
||||
private static final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
|
||||
private static final Validator validator = factory.getValidator();
|
||||
|
||||
public static <T> Mono<T> validate(T object) {
|
||||
Set<ConstraintViolation<T>> violations = validator.validate(object);
|
||||
if (violations.isEmpty()) {
|
||||
return Mono.just(object);
|
||||
}
|
||||
String errorMessage = violations.stream()
|
||||
.map(ConstraintViolation::getMessage)
|
||||
.collect(Collectors.joining("; "));
|
||||
return Mono.error(new IllegalArgumentException(errorMessage));
|
||||
}
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
package io.destiny.sys.api;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.destiny.common.primitive.EmailAddress;
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
import io.destiny.sys.dto.response.SysUserResponse;
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.handler.SysAuthHandler;
|
||||
import io.destiny.sys.handler.SysUserHandler;
|
||||
import io.destiny.sys.handler.SysRoleHandler;
|
||||
import io.destiny.sys.handler.SysMenuHandler;
|
||||
import io.destiny.sys.handler.OperationLogHandler;
|
||||
import io.destiny.sys.config.SysRouter;
|
||||
import io.destiny.sys.core.service.ISysAuthService;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import io.destiny.sys.core.service.ISysPermissionService;
|
||||
import io.destiny.sys.core.service.ISysMenuService;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import io.destiny.sys.security.SysJwtTokenProvider;
|
||||
import io.destiny.sys.dto.response.SysLoginResponse;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@DisplayName("敏感数据脱敏集成测试")
|
||||
class SensitiveDataIntegrationTest {
|
||||
|
||||
@Mock
|
||||
private ISysAuthService authService;
|
||||
|
||||
@Mock
|
||||
private ISysUserService userService;
|
||||
|
||||
@Mock
|
||||
private ISysPermissionService permissionService;
|
||||
|
||||
@Mock
|
||||
private ISysMenuService menuService;
|
||||
|
||||
@Mock
|
||||
private ISysRoleService roleService;
|
||||
|
||||
@Mock
|
||||
private SysJwtTokenProvider jwtTokenProvider;
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
objectMapper = new ObjectMapper();
|
||||
|
||||
Set<String> permissions = new HashSet<>();
|
||||
permissions.add("user:read");
|
||||
permissions.add("user:write");
|
||||
|
||||
io.destiny.sys.core.domain.SysUser testUser = new io.destiny.sys.core.domain.SysUser();
|
||||
testUser.setId(1L);
|
||||
testUser.setUsername("admin");
|
||||
testUser.setEmail(EmailAddress.of("admin@example.com"));
|
||||
testUser.setPhone(PhoneNumber.of("13800138000"));
|
||||
testUser.setPassword(new BCryptPasswordEncoder().encode("admin123"));
|
||||
testUser.setStatus("0");
|
||||
|
||||
when(userService.findByUsername(anyString())).thenReturn(Mono.just(testUser));
|
||||
when(jwtTokenProvider.generateAccessToken(any(), anyString())).thenReturn("test-access-token");
|
||||
when(jwtTokenProvider.generateRefreshToken(any(), anyString())).thenReturn("test-refresh-token");
|
||||
when(permissionService.getUserPermissions(any())).thenReturn(Mono.just(permissions));
|
||||
|
||||
SysLoginResponse loginResponse = SysLoginResponse.from("test-access-token", "test-refresh-token", testUser, permissions);
|
||||
|
||||
when(authService.login(any())).thenReturn(Mono.just(loginResponse));
|
||||
|
||||
SysAuthHandler authHandler = new SysAuthHandler(authService);
|
||||
SysUserHandler userHandler = new SysUserHandler(null);
|
||||
SysRoleHandler roleHandler = new SysRoleHandler(null);
|
||||
SysMenuHandler menuHandler = new SysMenuHandler(menuService, roleService);
|
||||
OperationLogHandler logHandler = new OperationLogHandler(null);
|
||||
|
||||
SysRouter router = new SysRouter(userHandler, roleHandler, menuHandler, authHandler, logHandler);
|
||||
webTestClient = WebTestClient.bindToRouterFunction(router.sysRoutes())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("登录响应中的用户信息应该脱敏")
|
||||
void testLoginResponseShouldMaskSensitiveData() {
|
||||
var response = webTestClient.post()
|
||||
.uri("/sys/auth/login")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue("{\"username\":\"admin\",\"password\":\"admin123\"}")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.returnResult()
|
||||
.getResponseBody();
|
||||
|
||||
assertNotNull(response, "登录响应不应为空");
|
||||
|
||||
String responseString = new String(response);
|
||||
System.out.println("登录响应: " + responseString);
|
||||
|
||||
assertTrue(responseString.contains("\"token\""), "响应应该包含token");
|
||||
assertTrue(responseString.contains("\"refreshToken\""), "响应应该包含refreshToken");
|
||||
assertTrue(responseString.contains("\"user\""), "响应应该包含user信息");
|
||||
|
||||
if (responseString.contains("\"email\"")) {
|
||||
String email = extractField(responseString, "email");
|
||||
System.out.println("邮箱脱敏结果: " + email);
|
||||
assertNotNull(email, "邮箱不应为null");
|
||||
assertTrue(email.contains("***") || email.length() < 10, "邮箱应该被脱敏");
|
||||
}
|
||||
|
||||
if (responseString.contains("\"phone\"")) {
|
||||
String phone = extractField(responseString, "phone");
|
||||
System.out.println("手机号脱敏结果: " + phone);
|
||||
assertNotNull(phone, "手机号不应为null");
|
||||
assertTrue(phone.contains("****") || phone.length() < 11, "手机号应该被脱敏");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("SysUserResponse序列化应该脱敏敏感数据")
|
||||
void testSysUserResponseSerialization() throws Exception {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setEmail(EmailAddress.of("testuser@example.com"));
|
||||
user.setPhone(PhoneNumber.of("13800138000"));
|
||||
user.setStatus("0");
|
||||
|
||||
SysUserResponse response = SysUserResponse.fromSysUser(user);
|
||||
|
||||
String json = objectMapper.writeValueAsString(response);
|
||||
System.out.println("SysUserResponse JSON: " + json);
|
||||
|
||||
assertTrue(json.contains("testuser"), "JSON应该包含用户名");
|
||||
|
||||
if (json.contains("\"email\"")) {
|
||||
String email = extractField(json, "email");
|
||||
System.out.println("邮箱脱敏结果: " + email);
|
||||
assertTrue(email.contains("***") || email.length() < 10, "邮箱应该被脱敏");
|
||||
}
|
||||
|
||||
if (json.contains("\"phone\"")) {
|
||||
String phone = extractField(json, "phone");
|
||||
System.out.println("手机号脱敏结果: " + phone);
|
||||
assertTrue(phone.contains("****") || phone.length() < 11, "手机号应该被脱敏");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("应该正确处理空值")
|
||||
void testHandleNullValues() throws Exception {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setEmail(null);
|
||||
user.setPhone(null);
|
||||
user.setStatus("0");
|
||||
|
||||
SysUserResponse response = SysUserResponse.fromSysUser(user);
|
||||
|
||||
String json = objectMapper.writeValueAsString(response);
|
||||
System.out.println("空值处理 JSON: " + json);
|
||||
|
||||
assertTrue(json.contains("testuser"), "JSON应该包含用户名");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("应该正确处理空字符串")
|
||||
void testHandleEmptyStrings() throws Exception {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setEmail(EmailAddress.of(""));
|
||||
user.setPhone(PhoneNumber.of(""));
|
||||
user.setStatus("0");
|
||||
|
||||
SysUserResponse response = SysUserResponse.fromSysUser(user);
|
||||
|
||||
String json = objectMapper.writeValueAsString(response);
|
||||
System.out.println("空字符串处理 JSON: " + json);
|
||||
|
||||
assertTrue(json.contains("testuser"), "JSON应该包含用户名");
|
||||
}
|
||||
|
||||
private String extractField(String json, String fieldName) {
|
||||
String pattern = "\"" + fieldName + "\":\"([^\"]+)\"";
|
||||
java.util.regex.Pattern p = java.util.regex.Pattern.compile(pattern);
|
||||
java.util.regex.Matcher m = p.matcher(json);
|
||||
if (m.find()) {
|
||||
return m.group(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import io.destiny.sys.core.service.impl.DataMaskingServiceImpl;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("数据脱敏服务测试")
|
||||
class DataMaskingServiceTest {
|
||||
|
||||
@InjectMocks
|
||||
private DataMaskingServiceImpl dataMaskingService;
|
||||
|
||||
@Test
|
||||
@DisplayName("测试手机号脱敏 - 完整手机号")
|
||||
void testMaskPhoneNumber_Full() {
|
||||
String phoneNumber = "13800138000";
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskPhone(phoneNumber))
|
||||
.expectNext("138****8000")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试手机号脱敏 - 空值")
|
||||
void testMaskPhoneNumber_Null() {
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskPhone(null))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试手机号脱敏 - 空字符串")
|
||||
void testMaskPhoneNumber_Empty() {
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskPhone(""))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试邮箱脱敏 - 完整邮箱")
|
||||
void testMaskEmail_Full() {
|
||||
String email = "test@example.com";
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskEmail(email))
|
||||
.expectNext("te***@example.com")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试邮箱脱敏 - 空值")
|
||||
void testMaskEmail_Null() {
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskEmail(null))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试邮箱脱敏 - 短邮箱")
|
||||
void testMaskEmail_Short() {
|
||||
String email = "a@b.com";
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskEmail(email))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试身份证号脱敏 - 完整身份证号")
|
||||
void testMaskIdCard_Full() {
|
||||
String idCard = "110101199001011234";
|
||||
|
||||
StepVerifier.create(dataMaskingService.mask(idCard, "idCard"))
|
||||
.expectNext("110101********1234")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试身份证号脱敏 - 空值")
|
||||
void testMaskIdCard_Null() {
|
||||
|
||||
StepVerifier.create(dataMaskingService.mask(null, "idCard"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试银行卡号脱敏 - 完整银行卡号")
|
||||
void testMaskBankCard_Full() {
|
||||
String bankCard = "6222021234567890123";
|
||||
|
||||
StepVerifier.create(dataMaskingService.mask(bankCard, "bankCard"))
|
||||
.expectNext("6222***********0123")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试银行卡号脱敏 - 空值")
|
||||
void testMaskBankCard_Null() {
|
||||
|
||||
StepVerifier.create(dataMaskingService.mask(null, "bankCard"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试密码脱敏 - 完整密码")
|
||||
void testMaskPassword_Full() {
|
||||
String password = "password123";
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskPassword(password))
|
||||
.expectNext("******")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试密码脱敏 - 空值")
|
||||
void testMaskPassword_Null() {
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskPassword(null))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试通用脱敏 - 完整字符串")
|
||||
void testMask_Full() {
|
||||
String data = "sensitiveData";
|
||||
|
||||
StepVerifier.create(dataMaskingService.mask(data, "sensitive"))
|
||||
.expectNext("sensitiveData")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试通用脱敏 - 空值")
|
||||
void testMask_Null() {
|
||||
|
||||
StepVerifier.create(dataMaskingService.mask(null, "sensitive"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试通用脱敏 - 短字符串")
|
||||
void testMask_Short() {
|
||||
String data = "abc";
|
||||
|
||||
StepVerifier.create(dataMaskingService.mask(data, "sensitive"))
|
||||
.expectNext("abc")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试JSON数据脱敏 - 包含敏感字段")
|
||||
void testMaskJson_WithSensitiveFields() {
|
||||
String json = "{\"username\":\"test\",\"phone\":\"13800138000\",\"email\":\"test@example.com\"}";
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskSensitiveData(json))
|
||||
.expectNextMatches(
|
||||
result -> result.contains("\"phone\":\"****\"") && result.contains("\"email\":\"***\""))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试JSON数据脱敏 - 空值")
|
||||
void testMaskJson_Null() {
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskSensitiveData(null))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试JSON数据脱敏 - 无效JSON")
|
||||
void testMaskJson_Invalid() {
|
||||
String json = "invalid json";
|
||||
|
||||
StepVerifier.create(dataMaskingService.maskSensitiveData(json))
|
||||
.expectNext("invalid json")
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.destiny.sys.core.service.impl.JaversAuditServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("JaVers审计服务测试")
|
||||
class JaversAuditServiceTest {
|
||||
|
||||
@Spy
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private JaversAuditServiceImpl javersAuditService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
javersAuditService = new JaversAuditServiceImpl(objectMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试对象比较 - 成功")
|
||||
void testCompare_Success() {
|
||||
@SuppressWarnings("unused")
|
||||
class TestEntity {
|
||||
private String name;
|
||||
|
||||
public TestEntity(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
TestEntity oldObject = new TestEntity("oldName");
|
||||
TestEntity newObject = new TestEntity("newName");
|
||||
|
||||
StepVerifier.create(javersAuditService.compare(oldObject, newObject))
|
||||
.expectNextMatches(result -> result != null)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试对象比较 - 异常")
|
||||
void testCompare_Exception() {
|
||||
@SuppressWarnings("unused")
|
||||
class TestEntity {
|
||||
private String name;
|
||||
|
||||
public TestEntity(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
TestEntity oldObject = new TestEntity("oldName");
|
||||
|
||||
StepVerifier.create(javersAuditService.compare(oldObject, oldObject))
|
||||
.expectNextMatches(result -> result != null)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试获取变更字段 - 成功")
|
||||
void testGetChangedFields_Success() {
|
||||
@SuppressWarnings("unused")
|
||||
class TestEntity {
|
||||
private String name;
|
||||
|
||||
public TestEntity(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
TestEntity oldObject = new TestEntity("oldName");
|
||||
TestEntity newObject = new TestEntity("newName");
|
||||
|
||||
StepVerifier.create(javersAuditService.getChangedFields(oldObject, newObject))
|
||||
.expectNextMatches(fields -> fields != null)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试获取变更字段 - 无变更")
|
||||
void testGetChangedFields_NoChanges() {
|
||||
@SuppressWarnings("unused")
|
||||
class TestEntity {
|
||||
private String name;
|
||||
|
||||
public TestEntity(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
TestEntity oldObject = new TestEntity("name");
|
||||
TestEntity newObject = new TestEntity("name");
|
||||
|
||||
StepVerifier.create(javersAuditService.getChangedFields(oldObject, newObject))
|
||||
.expectNextMatches(List -> List != null)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试获取变更字段 - 异常")
|
||||
void testGetChangedFields_Exception() {
|
||||
@SuppressWarnings("unused")
|
||||
class TestEntity {
|
||||
private String name;
|
||||
|
||||
public TestEntity(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
TestEntity oldObject = new TestEntity("oldName");
|
||||
|
||||
StepVerifier.create(javersAuditService.getChangedFields(oldObject, oldObject))
|
||||
.expectNextMatches(fields -> fields != null)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试对象序列化为JSON - 成功")
|
||||
void testToJson_Success() {
|
||||
String testStr = "test";
|
||||
|
||||
StepVerifier.create(javersAuditService.toJson(testStr))
|
||||
.expectNextMatches(json -> json != null && json.contains("test"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试对象序列化为JSON - 空对象")
|
||||
void testToJson_Null() {
|
||||
StepVerifier.create(javersAuditService.toJson(null))
|
||||
.expectNextMatches(json -> json != null && json.equals("null"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试对象序列化为JSON - 异常")
|
||||
void testToJson_Exception() {
|
||||
doThrow(new RuntimeException("序列化失败"))
|
||||
.when(objectMapper).valueToTree(any());
|
||||
|
||||
StepVerifier.create(javersAuditService.toJson("test"))
|
||||
.expectErrorMatches(throwable -> throwable instanceof RuntimeException)
|
||||
.verify();
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
package io.destiny.sys.core.service;
|
||||
|
||||
import io.destiny.sys.core.domain.OperationLog;
|
||||
import io.destiny.sys.core.domain.query.OperationLogQuery;
|
||||
import io.destiny.sys.core.repository.IOperationLogRepository;
|
||||
import io.destiny.sys.core.service.impl.OperationLogServiceImpl;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("操作日志服务测试")
|
||||
class OperationLogServiceTest {
|
||||
|
||||
@Mock
|
||||
private IOperationLogRepository repository;
|
||||
|
||||
@InjectMocks
|
||||
private OperationLogServiceImpl operationLogService;
|
||||
|
||||
private OperationLog testLog;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testLog = new OperationLog();
|
||||
testLog.setId(1L);
|
||||
testLog.setOperationTime(LocalDateTime.now());
|
||||
testLog.setModuleName("用户管理");
|
||||
testLog.setOperationDesc("创建用户");
|
||||
testLog.setOperator("admin");
|
||||
testLog.setRequestMethod("POST");
|
||||
testLog.setRequestPath("/sys/user");
|
||||
testLog.setRequestParams("{\"username\":\"test\"}");
|
||||
testLog.setResponseResult("{\"id\":1}");
|
||||
testLog.setIpAddress("127.0.0.1");
|
||||
testLog.setExecutionTime(100L);
|
||||
testLog.setStatus("SUCCESS");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询操作日志 - 成功")
|
||||
void testFindById_Success() {
|
||||
when(repository.findById(1L)).thenReturn(Mono.just(testLog));
|
||||
|
||||
StepVerifier.create(operationLogService.findById(1L))
|
||||
.expectNext(testLog)
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).findById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询操作日志 - 不存在")
|
||||
void testFindById_NotFound() {
|
||||
when(repository.findById(999L)).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(operationLogService.findById(999L))
|
||||
.expectNextCount(0)
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).findById(999L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试保存操作日志 - 成功")
|
||||
void testSave_Success() {
|
||||
OperationLog newLog = new OperationLog();
|
||||
newLog.setModuleName("测试模块");
|
||||
newLog.setOperationDesc("测试操作");
|
||||
newLog.setOperator("testuser");
|
||||
|
||||
when(repository.save(any(OperationLog.class))).thenReturn(Mono.just(testLog));
|
||||
|
||||
StepVerifier.create(operationLogService.save(newLog))
|
||||
.expectNext(testLog)
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).save(any(OperationLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试保存操作日志 - 异常")
|
||||
void testSave_Exception() {
|
||||
OperationLog newLog = new OperationLog();
|
||||
when(repository.save(any(OperationLog.class)))
|
||||
.thenReturn(Mono.error(new RuntimeException("数据库连接失败")));
|
||||
|
||||
StepVerifier.create(operationLogService.save(newLog))
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).save(any(OperationLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据条件查询操作日志 - 成功")
|
||||
void testFindByQuery_Success() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setOperator("admin");
|
||||
query.setModuleName("用户管理");
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
List<OperationLog> logs = Arrays.asList(testLog);
|
||||
when(repository.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.fromIterable(logs));
|
||||
|
||||
StepVerifier.create(operationLogService.findByQuery(query))
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据条件查询操作日志 - 空结果")
|
||||
void testFindByQuery_Empty() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setOperator("nonexistent");
|
||||
|
||||
when(repository.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.empty());
|
||||
|
||||
StepVerifier.create(operationLogService.findByQuery(query))
|
||||
.expectNextCount(0)
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试统计操作日志数量 - 成功")
|
||||
void testCountByQuery_Success() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setOperator("admin");
|
||||
|
||||
when(repository.countByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Mono.just(10L));
|
||||
|
||||
StepVerifier.create(operationLogService.countByQuery(query))
|
||||
.expectNext(10L)
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).countByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试统计操作日志数量 - 零")
|
||||
void testCountByQuery_Zero() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setOperator("nonexistent");
|
||||
|
||||
when(repository.countByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Mono.just(0L));
|
||||
|
||||
StepVerifier.create(operationLogService.countByQuery(query))
|
||||
.expectNext(0L)
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).countByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除操作日志 - 成功")
|
||||
void testDeleteById_Success() {
|
||||
when(repository.deleteById(1L)).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(operationLogService.deleteById(1L))
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).deleteById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除操作日志 - 异常")
|
||||
void testDeleteById_Exception() {
|
||||
when(repository.deleteById(1L))
|
||||
.thenReturn(Mono.error(new RuntimeException("删除失败")));
|
||||
|
||||
StepVerifier.create(operationLogService.deleteById(1L))
|
||||
.expectErrorMatches(throwable -> throwable instanceof RuntimeException &&
|
||||
throwable.getMessage().equals("删除失败"))
|
||||
.verify();
|
||||
|
||||
verify(repository, times(1)).deleteById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据时间范围查询 - 成功")
|
||||
void testFindByTimeRange_Success() {
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(7);
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setStartTime(startTime);
|
||||
query.setEndTime(endTime);
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
List<OperationLog> logs = Arrays.asList(testLog);
|
||||
when(repository.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.fromIterable(logs));
|
||||
|
||||
StepVerifier.create(operationLogService.findByQuery(query))
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据状态查询 - 成功")
|
||||
void testFindByStatus_Success() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setStatus("SUCCESS");
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
List<OperationLog> logs = Arrays.asList(testLog);
|
||||
when(repository.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.fromIterable(logs));
|
||||
|
||||
StepVerifier.create(operationLogService.findByQuery(query))
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
|
||||
verify(repository, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
}
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
package io.destiny.sys.handler;
|
||||
|
||||
import io.destiny.sys.core.domain.OperationLog;
|
||||
import io.destiny.sys.core.domain.query.OperationLogQuery;
|
||||
import io.destiny.sys.core.service.IOperationLogService;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@DisplayName("操作日志处理器测试")
|
||||
class OperationLogHandlerTest {
|
||||
|
||||
private OperationLogHandler operationLogHandler;
|
||||
private IOperationLogService logService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
logService = mock(IOperationLogService.class);
|
||||
operationLogHandler = new OperationLogHandler(logService);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询操作日志 - 成功")
|
||||
void testGetLogById_Success() {
|
||||
Long logId = 1L;
|
||||
OperationLog log = new OperationLog();
|
||||
log.setId(logId);
|
||||
log.setModuleName("用户管理");
|
||||
log.setOperationDesc("创建用户");
|
||||
log.setOperator("admin");
|
||||
log.setOperationTime(LocalDateTime.now());
|
||||
|
||||
when(logService.findById(logId)).thenReturn(Mono.just(log));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", logId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = operationLogHandler.getLogById(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, times(1)).findById(logId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询操作日志 - 不存在")
|
||||
void testGetLogById_NotFound() {
|
||||
Long logId = 999L;
|
||||
when(logService.findById(logId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", logId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = operationLogHandler.getLogById(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, times(1)).findById(logId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询操作日志 - 异常")
|
||||
void testGetLogById_Exception() {
|
||||
Long logId = 1L;
|
||||
when(logService.findById(logId))
|
||||
.thenReturn(Mono.error(new RuntimeException("数据库连接失败")));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", logId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = operationLogHandler.getLogById(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, times(1)).findById(logId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 成功")
|
||||
void testQueryLogs_Success() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setOperator("admin");
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
OperationLog log1 = new OperationLog();
|
||||
log1.setId(1L);
|
||||
log1.setModuleName("用户管理");
|
||||
log1.setOperationDesc("创建用户");
|
||||
log1.setOperator("admin");
|
||||
|
||||
OperationLog log2 = new OperationLog();
|
||||
log2.setId(2L);
|
||||
log2.setModuleName("角色管理");
|
||||
log2.setOperationDesc("创建角色");
|
||||
log2.setOperator("admin");
|
||||
|
||||
List<OperationLog> logs = Arrays.asList(log1, log2);
|
||||
|
||||
when(logService.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.fromIterable(logs));
|
||||
when(logService.countByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Mono.just(2L));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
|
||||
.body(Mono.just(query));
|
||||
|
||||
Mono<ServerResponse> responseMono = operationLogHandler.queryLogs(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
verify(logService, times(1)).countByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 空结果")
|
||||
void testQueryLogs_Empty() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setOperator("nonexistent");
|
||||
|
||||
when(logService.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.empty());
|
||||
when(logService.countByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Mono.just(0L));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
|
||||
.body(Mono.just(query));
|
||||
|
||||
Mono<ServerResponse> responseMono = operationLogHandler.queryLogs(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
verify(logService, times(1)).countByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 默认分页参数")
|
||||
void testQueryLogs_DefaultPagination() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setOperator("admin");
|
||||
|
||||
when(logService.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.empty());
|
||||
when(logService.countByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Mono.just(0L));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
|
||||
.body(Mono.just(query));
|
||||
|
||||
Mono<ServerResponse> responseMono = operationLogHandler.queryLogs(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 异常")
|
||||
void testQueryLogs_Exception() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
|
||||
when(logService.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.error(new RuntimeException("查询失败")));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
|
||||
.body(Mono.just(query));
|
||||
|
||||
Mono<ServerResponse> responseMono = operationLogHandler.queryLogs(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除操作日志 - 成功")
|
||||
void testDeleteLog_Success() {
|
||||
Long logId = 1L;
|
||||
OperationLog log = new OperationLog();
|
||||
log.setId(logId);
|
||||
when(logService.findById(logId)).thenReturn(Mono.just(log));
|
||||
when(logService.deleteById(logId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", logId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = operationLogHandler.deleteLog(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, times(1)).findById(logId);
|
||||
verify(logService, times(1)).deleteById(logId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除操作日志 - 异常")
|
||||
void testDeleteLog_Exception() {
|
||||
Long logId = 1L;
|
||||
when(logService.findById(logId)).thenReturn(Mono.just(new OperationLog()));
|
||||
when(logService.deleteById(logId))
|
||||
.thenReturn(Mono.error(new RuntimeException("删除失败")));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", logId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = operationLogHandler.deleteLog(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, times(1)).findById(logId);
|
||||
verify(logService, times(1)).deleteById(logId);
|
||||
}
|
||||
}
|
||||
+425
@@ -0,0 +1,425 @@
|
||||
package io.destiny.sys.handler;
|
||||
|
||||
import io.destiny.sys.core.domain.SysMenu;
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import io.destiny.sys.core.service.ISysMenuService;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import io.destiny.sys.dto.request.AssignMenuToRoleRequest;
|
||||
import io.destiny.sys.dto.request.SysMenuRequest;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@DisplayName("菜单处理器测试")
|
||||
class SysMenuHandlerTest {
|
||||
|
||||
private SysMenuHandler menuHandler;
|
||||
private ISysMenuService menuService;
|
||||
private ISysRoleService roleService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
menuService = mock(ISysMenuService.class);
|
||||
roleService = mock(ISysRoleService.class);
|
||||
menuHandler = new SysMenuHandler(menuService, roleService);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询菜单 - 成功")
|
||||
void testGetMenuById_Success() {
|
||||
Long menuId = 1L;
|
||||
SysMenu menu = new SysMenu();
|
||||
menu.setId(menuId);
|
||||
menu.setMenuName("系统管理");
|
||||
menu.setParentId(0L);
|
||||
menu.setOrderNum(1);
|
||||
menu.setMenuType("M");
|
||||
menu.setPerms("system");
|
||||
menu.setComponent("system/index");
|
||||
menu.setStatus("0");
|
||||
menu.setCreatedAt(LocalDateTime.now());
|
||||
menu.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
when(menuService.findById(menuId)).thenReturn(Mono.just(menu));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", menuId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.getMenuById(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(menuService, times(1)).findById(menuId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询菜单 - 菜单不存在")
|
||||
void testGetMenuById_NotFound() {
|
||||
Long menuId = 1L;
|
||||
when(menuService.findById(menuId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", menuId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.getMenuById(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(menuService, times(1)).findById(menuId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据角色ID查询菜单列表 - 成功")
|
||||
void testGetMenusByRoleId_Success() {
|
||||
Long roleId = 1L;
|
||||
SysMenu menu1 = new SysMenu();
|
||||
menu1.setId(1L);
|
||||
menu1.setMenuName("系统管理");
|
||||
menu1.setParentId(0L);
|
||||
menu1.setOrderNum(1);
|
||||
menu1.setMenuType("M");
|
||||
menu1.setPerms("system");
|
||||
menu1.setComponent("system/index");
|
||||
menu1.setStatus("0");
|
||||
|
||||
SysMenu menu2 = new SysMenu();
|
||||
menu2.setId(2L);
|
||||
menu2.setMenuName("用户管理");
|
||||
menu2.setParentId(1L);
|
||||
menu2.setOrderNum(1);
|
||||
menu2.setMenuType("C");
|
||||
menu2.setPerms("system:user:list");
|
||||
menu2.setComponent("system/user/index");
|
||||
menu2.setStatus("0");
|
||||
|
||||
when(menuService.findByRoleId(roleId)).thenReturn(Flux.just(menu1, menu2));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("roleId", roleId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.getMenusByRoleId(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(menuService, times(1)).findByRoleId(roleId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据用户ID查询菜单列表 - 成功")
|
||||
void testGetMenusByUserId_Success() {
|
||||
Long userId = 1L;
|
||||
SysMenu menu1 = new SysMenu();
|
||||
menu1.setId(1L);
|
||||
menu1.setMenuName("系统管理");
|
||||
menu1.setParentId(0L);
|
||||
menu1.setOrderNum(1);
|
||||
menu1.setMenuType("M");
|
||||
menu1.setPerms("system");
|
||||
menu1.setComponent("system/index");
|
||||
menu1.setStatus("0");
|
||||
|
||||
SysMenu menu2 = new SysMenu();
|
||||
menu2.setId(2L);
|
||||
menu2.setMenuName("用户管理");
|
||||
menu2.setParentId(1L);
|
||||
menu2.setOrderNum(1);
|
||||
menu2.setMenuType("C");
|
||||
menu2.setPerms("system:user:list");
|
||||
menu2.setComponent("system/user/index");
|
||||
menu2.setStatus("0");
|
||||
|
||||
when(menuService.findByUserId(userId)).thenReturn(Flux.just(menu1, menu2));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("userId", userId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.getMenusByUserId(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(menuService, times(1)).findByUserId(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试查询所有菜单 - 成功")
|
||||
void testGetAllMenus_Success() {
|
||||
SysMenu menu1 = new SysMenu();
|
||||
menu1.setId(1L);
|
||||
menu1.setMenuName("系统管理");
|
||||
menu1.setParentId(0L);
|
||||
menu1.setOrderNum(1);
|
||||
menu1.setMenuType("M");
|
||||
menu1.setPerms("system");
|
||||
menu1.setComponent("system/index");
|
||||
menu1.setStatus("0");
|
||||
|
||||
SysMenu menu2 = new SysMenu();
|
||||
menu2.setId(2L);
|
||||
menu2.setMenuName("用户管理");
|
||||
menu2.setParentId(1L);
|
||||
menu2.setOrderNum(1);
|
||||
menu2.setMenuType("C");
|
||||
menu2.setPerms("system:user:list");
|
||||
menu2.setComponent("system/user/index");
|
||||
menu2.setStatus("0");
|
||||
|
||||
when(menuService.findAll()).thenReturn(Flux.just(menu1, menu2));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.getAllMenus(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(menuService, times(1)).findAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建菜单 - 成功")
|
||||
void testCreateMenu_Success() {
|
||||
SysMenuRequest menuRequest = new SysMenuRequest();
|
||||
menuRequest.setName("测试菜单");
|
||||
menuRequest.setParentId(0L);
|
||||
menuRequest.setSortOrder(3);
|
||||
menuRequest.setComponent("test/index");
|
||||
menuRequest.setStatus("0");
|
||||
|
||||
SysMenu createdMenu = new SysMenu();
|
||||
createdMenu.setId(3L);
|
||||
createdMenu.setMenuName("测试菜单");
|
||||
createdMenu.setParentId(0L);
|
||||
createdMenu.setOrderNum(3);
|
||||
createdMenu.setMenuType("C");
|
||||
createdMenu.setPerms("test");
|
||||
createdMenu.setComponent("test/index");
|
||||
createdMenu.setStatus("0");
|
||||
createdMenu.setCreatedAt(LocalDateTime.now());
|
||||
createdMenu.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
when(menuService.create(any(SysMenu.class))).thenReturn(Mono.just(createdMenu));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(menuRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.createMenu(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(menuService, times(1)).create(any(SysMenu.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新菜单 - 成功")
|
||||
void testUpdateMenu_Success() {
|
||||
SysMenuRequest menuRequest = new SysMenuRequest();
|
||||
menuRequest.setId(1L);
|
||||
menuRequest.setName("系统管理");
|
||||
menuRequest.setParentId(0L);
|
||||
menuRequest.setSortOrder(1);
|
||||
menuRequest.setComponent("system/index");
|
||||
menuRequest.setStatus("0");
|
||||
|
||||
SysMenu existingMenu = new SysMenu();
|
||||
existingMenu.setId(1L);
|
||||
existingMenu.setMenuName("系统管理");
|
||||
existingMenu.setParentId(0L);
|
||||
existingMenu.setOrderNum(1);
|
||||
existingMenu.setMenuType("M");
|
||||
existingMenu.setPerms("system");
|
||||
existingMenu.setComponent("system/index");
|
||||
existingMenu.setStatus("0");
|
||||
existingMenu.setCreatedAt(LocalDateTime.now());
|
||||
existingMenu.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
SysMenu updatedMenu = new SysMenu();
|
||||
updatedMenu.setId(1L);
|
||||
updatedMenu.setMenuName("系统管理");
|
||||
updatedMenu.setParentId(0L);
|
||||
updatedMenu.setOrderNum(1);
|
||||
updatedMenu.setMenuType("M");
|
||||
updatedMenu.setPerms("system");
|
||||
updatedMenu.setComponent("system/index");
|
||||
updatedMenu.setStatus("0");
|
||||
updatedMenu.setCreatedAt(LocalDateTime.now());
|
||||
updatedMenu.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
when(menuService.findById(1L)).thenReturn(Mono.just(existingMenu));
|
||||
when(menuService.update(any(SysMenu.class))).thenReturn(Mono.just(updatedMenu));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(menuRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.updateMenu(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(menuService, times(1)).findById(1L);
|
||||
verify(menuService, times(1)).update(any(SysMenu.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新菜单 - 菜单不存在")
|
||||
void testUpdateMenu_NotFound() {
|
||||
SysMenuRequest menuRequest = new SysMenuRequest();
|
||||
menuRequest.setId(1L);
|
||||
menuRequest.setName("系统管理");
|
||||
menuRequest.setParentId(0L);
|
||||
menuRequest.setSortOrder(1);
|
||||
menuRequest.setComponent("system/index");
|
||||
menuRequest.setStatus("0");
|
||||
|
||||
when(menuService.findById(1L)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(menuRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.updateMenu(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(menuService, times(1)).findById(1L);
|
||||
verify(menuService, never()).update(any(SysMenu.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除菜单 - 成功")
|
||||
void testDeleteMenu_Success() {
|
||||
Long menuId = 1L;
|
||||
when(menuService.deleteById(menuId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", menuId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.deleteMenu(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(menuService, times(1)).deleteById(menuId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分配菜单到角色 - 成功")
|
||||
void testAssignMenuToRole_Success() {
|
||||
AssignMenuToRoleRequest assignRequest = new AssignMenuToRoleRequest();
|
||||
assignRequest.setRoleId(1L);
|
||||
assignRequest.setMenuId(1L);
|
||||
|
||||
SysRole role = new SysRole();
|
||||
role.setId(1L);
|
||||
role.setRoleName("管理员");
|
||||
role.setRoleKey("admin");
|
||||
role.setRoleSort(1);
|
||||
role.setStatus("0");
|
||||
role.setCreatedAt(LocalDateTime.now());
|
||||
role.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
SysMenu menu = new SysMenu();
|
||||
menu.setId(1L);
|
||||
menu.setMenuName("系统管理");
|
||||
menu.setMenuType("M");
|
||||
menu.setOrderNum(1);
|
||||
menu.setStatus("0");
|
||||
menu.setCreatedAt(LocalDateTime.now());
|
||||
menu.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
when(roleService.findById(1L)).thenReturn(Mono.just(role));
|
||||
when(menuService.findById(1L)).thenReturn(Mono.just(menu));
|
||||
when(menuService.findByRoleId(1L)).thenReturn(Flux.empty());
|
||||
when(menuService.assignMenuToRole(1L, 1L)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(assignRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.assignMenuToRole(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findById(1L);
|
||||
verify(menuService, times(1)).findById(1L);
|
||||
verify(menuService, times(1)).findByRoleId(1L);
|
||||
verify(menuService, times(1)).assignMenuToRole(1L, 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试移除角色的菜单 - 成功")
|
||||
void testRemoveMenuFromRole_Success() {
|
||||
Long roleId = 1L;
|
||||
Long menuId = 1L;
|
||||
|
||||
when(menuService.removeMenuFromRole(roleId, menuId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("roleId", roleId.toString())
|
||||
.pathVariable("menuId", menuId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = menuHandler.removeMenuFromRole(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(menuService, times(1)).removeMenuFromRole(roleId, menuId);
|
||||
}
|
||||
}
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
package io.destiny.sys.handler;
|
||||
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import io.destiny.sys.dto.request.AssignRoleToUserRequest;
|
||||
import io.destiny.sys.dto.request.SysRoleRequest;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@DisplayName("角色处理器测试")
|
||||
class SysRoleHandlerTest {
|
||||
|
||||
private SysRoleHandler roleHandler;
|
||||
private ISysRoleService roleService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
roleService = mock(ISysRoleService.class);
|
||||
roleHandler = new SysRoleHandler(roleService);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询角色 - 成功")
|
||||
void testGetRoleById_Success() {
|
||||
Long roleId = 1L;
|
||||
SysRole role = new SysRole();
|
||||
role.setId(roleId);
|
||||
role.setRoleName("管理员");
|
||||
role.setRoleKey("admin");
|
||||
role.setRoleSort(1);
|
||||
role.setStatus("0");
|
||||
role.setCreatedAt(LocalDateTime.now());
|
||||
role.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
when(roleService.findById(roleId)).thenReturn(Mono.just(role));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", roleId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.getRoleById(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findById(roleId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询角色 - 角色不存在")
|
||||
void testGetRoleById_NotFound() {
|
||||
Long roleId = 1L;
|
||||
when(roleService.findById(roleId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", roleId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.getRoleById(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findById(roleId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据角色权限字符串查询角色 - 成功")
|
||||
void testGetRoleByRoleKey_Success() {
|
||||
String roleKey = "admin";
|
||||
SysRole role = new SysRole();
|
||||
role.setId(1L);
|
||||
role.setRoleName("管理员");
|
||||
role.setRoleKey(roleKey);
|
||||
role.setRoleSort(1);
|
||||
role.setStatus("0");
|
||||
role.setCreatedAt(LocalDateTime.now());
|
||||
role.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
when(roleService.findByRoleKey(roleKey)).thenReturn(Mono.just(role));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("roleKey", roleKey)
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.getRoleByRoleKey(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findByRoleKey(roleKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据角色权限字符串查询角色 - 角色不存在")
|
||||
void testGetRoleByRoleKey_NotFound() {
|
||||
String roleKey = "nonexistent";
|
||||
when(roleService.findByRoleKey(roleKey)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("roleKey", roleKey)
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.getRoleByRoleKey(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findByRoleKey(roleKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据用户ID查询角色列表 - 成功")
|
||||
void testGetRolesByUserId_Success() {
|
||||
Long userId = 1L;
|
||||
SysRole role1 = new SysRole();
|
||||
role1.setId(1L);
|
||||
role1.setRoleName("管理员");
|
||||
role1.setRoleKey("admin");
|
||||
role1.setRoleSort(1);
|
||||
role1.setStatus("0");
|
||||
|
||||
SysRole role2 = new SysRole();
|
||||
role2.setId(2L);
|
||||
role2.setRoleName("用户");
|
||||
role2.setRoleKey("user");
|
||||
role2.setRoleSort(2);
|
||||
role2.setStatus("0");
|
||||
|
||||
when(roleService.findByUserId(userId)).thenReturn(Flux.just(role1, role2));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("userId", userId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.getRolesByUserId(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findByUserId(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试查询所有角色 - 成功")
|
||||
void testGetAllRoles_Success() {
|
||||
SysRole role1 = new SysRole();
|
||||
role1.setId(1L);
|
||||
role1.setRoleName("管理员");
|
||||
role1.setRoleKey("admin");
|
||||
role1.setRoleSort(1);
|
||||
role1.setStatus("0");
|
||||
|
||||
SysRole role2 = new SysRole();
|
||||
role2.setId(2L);
|
||||
role2.setRoleName("用户");
|
||||
role2.setRoleKey("user");
|
||||
role2.setRoleSort(2);
|
||||
role2.setStatus("0");
|
||||
|
||||
when(roleService.findAll()).thenReturn(Flux.just(role1, role2));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.getAllRoles(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建角色 - 成功")
|
||||
void testCreateRole_Success() {
|
||||
SysRoleRequest roleRequest = new SysRoleRequest();
|
||||
roleRequest.setName("测试角色");
|
||||
roleRequest.setRoleKey("test");
|
||||
roleRequest.setSortOrder(3);
|
||||
roleRequest.setStatus("0");
|
||||
|
||||
SysRole createdRole = new SysRole();
|
||||
createdRole.setId(3L);
|
||||
createdRole.setRoleName("测试角色");
|
||||
createdRole.setRoleKey("test");
|
||||
createdRole.setRoleSort(3);
|
||||
createdRole.setStatus("0");
|
||||
createdRole.setCreatedAt(LocalDateTime.now());
|
||||
createdRole.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
when(roleService.findByRoleKey("test")).thenReturn(Mono.empty());
|
||||
when(roleService.create(any(SysRole.class))).thenReturn(Mono.just(createdRole));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(roleRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.createRole(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findByRoleKey("test");
|
||||
verify(roleService, times(1)).create(any(SysRole.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新角色 - 成功")
|
||||
void testUpdateRole_Success() {
|
||||
SysRoleRequest roleRequest = new SysRoleRequest();
|
||||
roleRequest.setId(1L);
|
||||
roleRequest.setName("管理员");
|
||||
roleRequest.setRoleKey("admin");
|
||||
roleRequest.setSortOrder(1);
|
||||
roleRequest.setStatus("0");
|
||||
|
||||
SysRole existingRole = new SysRole();
|
||||
existingRole.setId(1L);
|
||||
existingRole.setRoleName("管理员");
|
||||
existingRole.setRoleKey("admin");
|
||||
existingRole.setRoleSort(1);
|
||||
existingRole.setStatus("0");
|
||||
existingRole.setCreatedAt(LocalDateTime.now());
|
||||
existingRole.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
SysRole updatedRole = new SysRole();
|
||||
updatedRole.setId(1L);
|
||||
updatedRole.setRoleName("管理员");
|
||||
updatedRole.setRoleKey("admin");
|
||||
updatedRole.setRoleSort(1);
|
||||
updatedRole.setStatus("0");
|
||||
updatedRole.setCreatedAt(LocalDateTime.now());
|
||||
updatedRole.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
when(roleService.findById(1L)).thenReturn(Mono.just(existingRole));
|
||||
when(roleService.update(any(SysRole.class))).thenReturn(Mono.just(updatedRole));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(roleRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.updateRole(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findById(1L);
|
||||
verify(roleService, times(1)).update(any(SysRole.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新角色 - 角色不存在")
|
||||
void testUpdateRole_NotFound() {
|
||||
SysRoleRequest roleRequest = new SysRoleRequest();
|
||||
roleRequest.setId(1L);
|
||||
roleRequest.setName("管理员");
|
||||
roleRequest.setRoleKey("admin");
|
||||
roleRequest.setSortOrder(1);
|
||||
roleRequest.setStatus("0");
|
||||
|
||||
when(roleService.findById(1L)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(roleRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.updateRole(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findById(1L);
|
||||
verify(roleService, never()).update(any(SysRole.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除角色 - 成功")
|
||||
void testDeleteRole_Success() {
|
||||
Long roleId = 1L;
|
||||
when(roleService.deleteById(roleId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", roleId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.deleteRole(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).deleteById(roleId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分配角色到用户 - 成功")
|
||||
void testAssignRoleToUser_Success() {
|
||||
AssignRoleToUserRequest assignRequest = new AssignRoleToUserRequest();
|
||||
assignRequest.setUserId(1L);
|
||||
assignRequest.setRoleId(1L);
|
||||
|
||||
SysRole role = new SysRole();
|
||||
role.setId(1L);
|
||||
role.setRoleName("管理员");
|
||||
role.setRoleKey("admin");
|
||||
role.setRoleSort(1);
|
||||
role.setStatus("0");
|
||||
role.setCreatedAt(LocalDateTime.now());
|
||||
role.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
when(roleService.findById(1L)).thenReturn(Mono.just(role));
|
||||
when(roleService.findByUserId(1L)).thenReturn(Flux.empty());
|
||||
when(roleService.assignRoleToUser(1L, 1L)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(assignRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.assignRoleToUser(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).findById(1L);
|
||||
verify(roleService, times(1)).findByUserId(1L);
|
||||
verify(roleService, times(1)).assignRoleToUser(1L, 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试移除用户的角色 - 成功")
|
||||
void testRemoveRoleFromUser_Success() {
|
||||
Long userId = 1L;
|
||||
Long roleId = 1L;
|
||||
|
||||
when(roleService.removeRoleFromUser(userId, roleId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("userId", userId.toString())
|
||||
.pathVariable("roleId", roleId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = roleHandler.removeRoleFromUser(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(roleService, times(1)).removeRoleFromUser(userId, roleId);
|
||||
}
|
||||
}
|
||||
+404
@@ -0,0 +1,404 @@
|
||||
package io.destiny.sys.handler;
|
||||
|
||||
import io.destiny.common.primitive.EmailAddress;
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import io.destiny.sys.dto.request.SysUserRequest;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@DisplayName("用户处理器测试")
|
||||
class SysUserHandlerTest {
|
||||
|
||||
private SysUserHandler userHandler;
|
||||
private ISysUserService userService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
userService = mock(ISysUserService.class);
|
||||
userHandler = new SysUserHandler(userService);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询用户 - 正常情况")
|
||||
void testGetUserById_Success() {
|
||||
Long userId = 1L;
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setId(userId);
|
||||
sysUser.setUsername("testuser");
|
||||
sysUser.setEmail(EmailAddress.of("test@example.com"));
|
||||
sysUser.setPhone(PhoneNumber.of("13800138000"));
|
||||
sysUser.setStatus("0");
|
||||
sysUser.setCreatedAt(LocalDateTime.now());
|
||||
|
||||
when(userService.findById(userId)).thenReturn(Mono.just(sysUser));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", userId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.getUserById(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).findById(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询用户 - 用户不存在")
|
||||
void testGetUserById_NotFound() {
|
||||
Long userId = 1L;
|
||||
when(userService.findById(userId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", userId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.getUserById(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).findById(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询用户 - 服务异常")
|
||||
void testGetUserById_ServiceException() {
|
||||
Long userId = 1L;
|
||||
when(userService.findById(userId)).thenReturn(Mono.error(new RuntimeException("数据库连接失败")));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", userId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.getUserById(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).findById(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据用户名查询用户 - 正常情况")
|
||||
void testGetUserByUsername_Success() {
|
||||
String username = "testuser";
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setId(1L);
|
||||
sysUser.setUsername(username);
|
||||
sysUser.setEmail(EmailAddress.of("test@example.com"));
|
||||
sysUser.setPhone(PhoneNumber.of("13800138000"));
|
||||
sysUser.setStatus("0");
|
||||
sysUser.setCreatedAt(LocalDateTime.now());
|
||||
|
||||
when(userService.findByUsername(username)).thenReturn(Mono.just(sysUser));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("username", username)
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.getUserByUsername(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).findByUsername(username);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据用户名查询用户 - 用户不存在")
|
||||
void testGetUserByUsername_NotFound() {
|
||||
String username = "nonexistent";
|
||||
when(userService.findByUsername(username)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("username", username)
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.getUserByUsername(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).findByUsername(username);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建用户 - 正常情况")
|
||||
void testCreateUser_Success() {
|
||||
SysUserRequest userRequest = new SysUserRequest();
|
||||
userRequest.setUsername("newuser");
|
||||
userRequest.setPassword("password123");
|
||||
userRequest.setEmail("newuser@example.com");
|
||||
userRequest.setPhone("13900139000");
|
||||
userRequest.setStatus("0");
|
||||
|
||||
SysUser createdUser = new SysUser();
|
||||
createdUser.setId(1L);
|
||||
createdUser.setUsername("newuser");
|
||||
createdUser.setEmail(EmailAddress.of("newuser@example.com"));
|
||||
createdUser.setPhone(PhoneNumber.of("13900139000"));
|
||||
createdUser.setStatus("0");
|
||||
createdUser.setCreatedAt(LocalDateTime.now());
|
||||
|
||||
when(userService.create(any(SysUser.class))).thenReturn(Mono.just(createdUser));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(userRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.createUser(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).create(any(SysUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建用户 - 服务异常")
|
||||
void testCreateUser_ServiceException() {
|
||||
SysUserRequest userRequest = new SysUserRequest();
|
||||
userRequest.setUsername("newuser");
|
||||
userRequest.setPassword("password123");
|
||||
userRequest.setEmail("newuser@example.com");
|
||||
userRequest.setPhone("13900139000");
|
||||
userRequest.setStatus("0");
|
||||
|
||||
when(userService.create(any(SysUser.class)))
|
||||
.thenReturn(Mono.error(new RuntimeException("用户名已存在")));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(userRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.createUser(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).create(any(SysUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新用户 - 正常情况")
|
||||
void testUpdateUser_Success() {
|
||||
Long userId = 1L;
|
||||
SysUserRequest userRequest = new SysUserRequest();
|
||||
userRequest.setId(userId);
|
||||
userRequest.setUsername("updateduser");
|
||||
userRequest.setEmail("updated@example.com");
|
||||
userRequest.setPhone("13800138000");
|
||||
userRequest.setStatus("0");
|
||||
|
||||
SysUser existingUser = new SysUser();
|
||||
existingUser.setId(userId);
|
||||
existingUser.setUsername("olduser");
|
||||
existingUser.setEmail(EmailAddress.of("old@example.com"));
|
||||
existingUser.setPhone(PhoneNumber.of("13800138000"));
|
||||
existingUser.setStatus("0");
|
||||
existingUser.setCreatedAt(LocalDateTime.now());
|
||||
|
||||
SysUser updatedUser = new SysUser();
|
||||
updatedUser.setId(userId);
|
||||
updatedUser.setUsername("updateduser");
|
||||
updatedUser.setEmail(EmailAddress.of("updated@example.com"));
|
||||
updatedUser.setPhone(PhoneNumber.of("13800138000"));
|
||||
updatedUser.setStatus("0");
|
||||
updatedUser.setCreatedAt(LocalDateTime.now());
|
||||
|
||||
when(userService.findById(userId)).thenReturn(Mono.just(existingUser));
|
||||
when(userService.update(any(SysUser.class))).thenReturn(Mono.just(updatedUser));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(userRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.updateUser(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).findById(userId);
|
||||
verify(userService, times(1)).update(any(SysUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新用户 - 用户不存在")
|
||||
void testUpdateUser_NotFound() {
|
||||
Long userId = 1L;
|
||||
SysUserRequest userRequest = new SysUserRequest();
|
||||
userRequest.setId(userId);
|
||||
userRequest.setUsername("updateduser");
|
||||
|
||||
when(userService.findById(userId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(userRequest));
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.updateUser(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).findById(userId);
|
||||
verify(userService, never()).update(any(SysUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除用户 - 正常情况")
|
||||
void testDeleteUser_Success() {
|
||||
Long userId = 1L;
|
||||
when(userService.deleteById(userId)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", userId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.deleteUser(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).deleteById(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除用户 - 服务异常")
|
||||
void testDeleteUser_ServiceException() {
|
||||
Long userId = 1L;
|
||||
when(userService.deleteById(userId))
|
||||
.thenReturn(Mono.error(new RuntimeException("删除失败")));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", userId.toString())
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.deleteUser(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).deleteById(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试查询所有用户 - 成功")
|
||||
void testGetAllUsers_Success() {
|
||||
SysUser user1 = new SysUser();
|
||||
user1.setId(1L);
|
||||
user1.setUsername("admin");
|
||||
user1.setEmail(EmailAddress.of("admin@example.com"));
|
||||
user1.setPhone(PhoneNumber.of("13800138000"));
|
||||
user1.setStatus("0");
|
||||
user1.setCreatedAt(LocalDateTime.now());
|
||||
|
||||
SysUser user2 = new SysUser();
|
||||
user2.setId(2L);
|
||||
user2.setUsername("testuser");
|
||||
user2.setEmail(EmailAddress.of("test@example.com"));
|
||||
user2.setPhone(PhoneNumber.of("13900139000"));
|
||||
user2.setStatus("0");
|
||||
user2.setCreatedAt(LocalDateTime.now());
|
||||
|
||||
when(userService.findAll()).thenReturn(Flux.just(user1, user2));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.getAllUsers(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).findAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试查询所有用户 - 空列表")
|
||||
void testGetAllUsers_EmptyList() {
|
||||
when(userService.findAll()).thenReturn(Flux.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.getAllUsers(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(200, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).findAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试查询所有用户 - 服务异常")
|
||||
void testGetAllUsers_ServiceException() {
|
||||
when(userService.findAll()).thenReturn(Flux.error(new RuntimeException("数据库连接失败")));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> responseMono = userHandler.getAllUsers(request);
|
||||
|
||||
StepVerifier.create(responseMono)
|
||||
.assertNext(response -> {
|
||||
assertEquals(400, response.statusCode().value());
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService, times(1)).findAll();
|
||||
}
|
||||
}
|
||||
+354
@@ -0,0 +1,354 @@
|
||||
package io.destiny.sys.integration;
|
||||
|
||||
import io.destiny.sys.core.domain.OperationLog;
|
||||
import io.destiny.sys.core.domain.query.OperationLogQuery;
|
||||
import io.destiny.sys.core.service.IOperationLogService;
|
||||
import io.destiny.sys.core.service.ISysMenuService;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import io.destiny.sys.core.service.impl.SysAuthServiceImpl;
|
||||
import io.destiny.sys.handler.OperationLogHandler;
|
||||
import io.destiny.sys.handler.SysUserHandler;
|
||||
import io.destiny.sys.handler.SysRoleHandler;
|
||||
import io.destiny.sys.handler.SysMenuHandler;
|
||||
import io.destiny.sys.handler.SysAuthHandler;
|
||||
import io.destiny.sys.config.SysRouter;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@DisplayName("操作日志API集成测试")
|
||||
class OperationLogApiIntegrationTest {
|
||||
|
||||
@Mock
|
||||
private IOperationLogService logService;
|
||||
|
||||
@Mock
|
||||
private ISysMenuService menuService;
|
||||
|
||||
@Mock
|
||||
private ISysRoleService roleService;
|
||||
|
||||
@InjectMocks
|
||||
private OperationLogHandler logHandler;
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
SysUserHandler userHandler = new SysUserHandler(null);
|
||||
SysRoleHandler roleHandler = new SysRoleHandler(null);
|
||||
SysMenuHandler menuHandler = new SysMenuHandler(menuService, roleService);
|
||||
SysAuthServiceImpl authService = new SysAuthServiceImpl(null, null, null, null);
|
||||
SysAuthHandler authHandler = new SysAuthHandler(authService);
|
||||
|
||||
SysRouter router = new SysRouter(userHandler, roleHandler, menuHandler, authHandler, logHandler);
|
||||
webTestClient = WebTestClient.bindToRouterFunction(router.sysRoutes())
|
||||
.build();
|
||||
|
||||
OperationLog defaultLog = createDefaultLog();
|
||||
when(logService.findById(anyLong()))
|
||||
.thenReturn(Mono.just(defaultLog));
|
||||
when(logService.findById(999L))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
when(logService.save(any(OperationLog.class)))
|
||||
.thenReturn(Mono.just(defaultLog));
|
||||
|
||||
OperationLog log1 = createLog(1L, "用户管理", "创建用户", "admin");
|
||||
OperationLog log2 = createLog(2L, "角色管理", "创建角色", "admin");
|
||||
OperationLog log3 = createLog(3L, "权限管理", "分配权限", "user");
|
||||
|
||||
when(logService.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.fromIterable(Arrays.asList(log1, log2, log3)));
|
||||
when(logService.countByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Mono.just(3L));
|
||||
|
||||
when(logService.deleteById(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
when(logService.deleteById(999L))
|
||||
.thenReturn(Mono.error(new RuntimeException("日志不存在")));
|
||||
}
|
||||
|
||||
private OperationLog createDefaultLog() {
|
||||
return createLog(1L, "用户管理", "创建用户", "admin");
|
||||
}
|
||||
|
||||
private OperationLog createLog(Long id, String moduleName, String operationDesc, String operator) {
|
||||
OperationLog log = new OperationLog();
|
||||
log.setId(id);
|
||||
log.setModuleName(moduleName);
|
||||
log.setOperationDesc(operationDesc);
|
||||
log.setOperator(operator);
|
||||
log.setRequestPath("/sys/user");
|
||||
log.setRequestMethod("POST");
|
||||
log.setRequestParams("{\"username\":\"test\"}");
|
||||
log.setResponseResult("{\"id\":1}");
|
||||
log.setIpAddress("127.0.0.1");
|
||||
log.setStatus("SUCCESS");
|
||||
log.setExecutionTime(100L);
|
||||
log.setCreatedAt(LocalDateTime.now());
|
||||
return log;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID获取操作日志 - 成功")
|
||||
void testGetLogById_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/operationLog/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.id").isEqualTo(1)
|
||||
.jsonPath("$.data.moduleName").isEqualTo("用户管理")
|
||||
.jsonPath("$.data.operationDesc").isEqualTo("创建用户")
|
||||
.jsonPath("$.data.operator").isEqualTo("admin")
|
||||
.jsonPath("$.data.status").isEqualTo("SUCCESS");
|
||||
|
||||
verify(logService, times(1)).findById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID获取操作日志 - 日志不存在")
|
||||
void testGetLogById_NotFound() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/operationLog/999")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(400)
|
||||
.jsonPath("$.message").exists();
|
||||
|
||||
verify(logService, times(1)).findById(999L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 成功")
|
||||
void testQueryLogs_Success() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setOperator("admin");
|
||||
query.setModuleName("用户管理");
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.total").isEqualTo(3)
|
||||
.jsonPath("$.data.pageNum").isEqualTo(1)
|
||||
.jsonPath("$.data.pageSize").isEqualTo(10)
|
||||
.jsonPath("$.data.list").isArray()
|
||||
.jsonPath("$.data.list[0].operator").isEqualTo("admin");
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
verify(logService, times(1)).countByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 按操作人查询")
|
||||
void testQueryLogs_ByOperator() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setOperator("admin");
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.data.total").isEqualTo(3);
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 按模块查询")
|
||||
void testQueryLogs_ByModule() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setModuleName("用户管理");
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.data.total").isEqualTo(3);
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 按状态查询")
|
||||
void testQueryLogs_ByStatus() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setStatus("SUCCESS");
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.data.total").isEqualTo(3);
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 按时间范围查询")
|
||||
void testQueryLogs_ByTimeRange() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setStartTime(LocalDateTime.now().minusDays(7));
|
||||
query.setEndTime(LocalDateTime.now());
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.data.total").isEqualTo(3);
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 空查询条件")
|
||||
void testQueryLogs_EmptyQuery() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.data.total").isEqualTo(3);
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除操作日志 - 成功")
|
||||
void testDeleteLog_Success() {
|
||||
webTestClient.delete()
|
||||
.uri("/sys/operationLog/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success");
|
||||
|
||||
verify(logService, times(1)).deleteById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除操作日志 - 日志不存在")
|
||||
void testDeleteLog_NotFound() {
|
||||
webTestClient.delete()
|
||||
.uri("/sys/operationLog/999")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(400);
|
||||
|
||||
verify(logService, times(1)).findById(999L);
|
||||
verify(logService, never()).deleteById(999L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 分页参数验证")
|
||||
void testQueryLogs_PaginationValidation() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setPageNum(0);
|
||||
query.setPageSize(10);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分页查询操作日志 - 每页大小验证")
|
||||
void testQueryLogs_PageSizeValidation() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(100);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试API响应格式一致性")
|
||||
void testApiResponseFormatConsistency() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/operationLog/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").exists()
|
||||
.jsonPath("$.message").exists()
|
||||
.jsonPath("$.data").exists();
|
||||
}
|
||||
}
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
package io.destiny.sys.integration;
|
||||
|
||||
import io.destiny.sys.aspect.OperationLogAspect;
|
||||
import io.destiny.sys.core.domain.OperationLog;
|
||||
import io.destiny.sys.core.domain.query.OperationLogQuery;
|
||||
import io.destiny.sys.core.service.IDataMaskingService;
|
||||
import io.destiny.sys.core.service.IJaversAuditService;
|
||||
import io.destiny.sys.core.service.IOperationLogService;
|
||||
import io.destiny.sys.core.service.ISysMenuService;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import io.destiny.sys.handler.OperationLogHandler;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@DisplayName("操作日志端到端集成测试")
|
||||
class OperationLogEndToEndIntegrationTest {
|
||||
|
||||
@Mock
|
||||
private IOperationLogService logService;
|
||||
|
||||
@Mock
|
||||
private IDataMaskingService dataMaskingService;
|
||||
|
||||
@Mock
|
||||
private IJaversAuditService javersAuditService;
|
||||
|
||||
@Mock
|
||||
private ISysMenuService menuService;
|
||||
|
||||
@Mock
|
||||
private ISysRoleService roleService;
|
||||
|
||||
@InjectMocks
|
||||
private OperationLogAspect operationLogAspect;
|
||||
|
||||
@InjectMocks
|
||||
private OperationLogHandler logHandler;
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
io.destiny.sys.handler.SysUserHandler userHandler = new io.destiny.sys.handler.SysUserHandler(null);
|
||||
io.destiny.sys.handler.SysRoleHandler roleHandler = new io.destiny.sys.handler.SysRoleHandler(null);
|
||||
io.destiny.sys.handler.SysMenuHandler menuHandler = new io.destiny.sys.handler.SysMenuHandler(menuService, roleService);
|
||||
io.destiny.sys.core.service.impl.SysAuthServiceImpl authService = new io.destiny.sys.core.service.impl.SysAuthServiceImpl(
|
||||
null,
|
||||
null, null, null);
|
||||
io.destiny.sys.handler.SysAuthHandler authHandler = new io.destiny.sys.handler.SysAuthHandler(
|
||||
authService);
|
||||
|
||||
io.destiny.sys.config.SysRouter router = new io.destiny.sys.config.SysRouter(userHandler, roleHandler,
|
||||
menuHandler, authHandler, logHandler);
|
||||
webTestClient = WebTestClient.bindToRouterFunction(router.sysRoutes())
|
||||
.build();
|
||||
|
||||
setupDefaultMockBehaviors();
|
||||
}
|
||||
|
||||
private void setupDefaultMockBehaviors() {
|
||||
OperationLog defaultLog = createDefaultLog();
|
||||
when(logService.findById(anyLong()))
|
||||
.thenReturn(Mono.just(defaultLog));
|
||||
when(logService.findById(999L))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
when(logService.save(any(OperationLog.class)))
|
||||
.thenReturn(Mono.just(defaultLog));
|
||||
|
||||
OperationLog log1 = createLog(1L, "用户管理", "创建用户", "admin");
|
||||
OperationLog log2 = createLog(2L, "角色管理", "创建角色", "admin");
|
||||
OperationLog log3 = createLog(3L, "权限管理", "分配权限", "user");
|
||||
|
||||
when(logService.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.fromIterable(Arrays.asList(log1, log2, log3)));
|
||||
when(logService.countByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Mono.just(3L));
|
||||
|
||||
when(logService.deleteById(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
when(logService.deleteById(999L))
|
||||
.thenReturn(Mono.error(new RuntimeException("日志不存在")));
|
||||
|
||||
when(dataMaskingService.maskSensitiveData(anyString()))
|
||||
.thenAnswer(invocation -> Mono.just(invocation.getArgument(0)));
|
||||
|
||||
List<String> changedFields = new ArrayList<>();
|
||||
changedFields.add("变更摘要");
|
||||
when(javersAuditService.getChangedFields(any(), any()))
|
||||
.thenReturn(Mono.just(changedFields));
|
||||
}
|
||||
|
||||
private OperationLog createDefaultLog() {
|
||||
return createLog(1L, "用户管理", "创建用户", "admin");
|
||||
}
|
||||
|
||||
private OperationLog createLog(Long id, String moduleName, String operationDesc, String operator) {
|
||||
OperationLog log = new OperationLog();
|
||||
log.setId(id);
|
||||
log.setModuleName(moduleName);
|
||||
log.setOperationDesc(operationDesc);
|
||||
log.setOperator(operator);
|
||||
log.setRequestPath("/sys/user");
|
||||
log.setRequestMethod("POST");
|
||||
log.setRequestParams("{\"username\":\"test\"}");
|
||||
log.setResponseResult("{\"id\":1}");
|
||||
log.setIpAddress("127.0.0.1");
|
||||
log.setStatus("SUCCESS");
|
||||
log.setExecutionTime(100L);
|
||||
log.setCreatedAt(LocalDateTime.now());
|
||||
return log;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试完整日志记录流程 - 成功场景")
|
||||
void testCompleteLogRecordingFlow_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/operationLog/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.data.id").isEqualTo(1);
|
||||
|
||||
verify(logService, times(1)).findById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志记录流程 - 敏感数据脱敏")
|
||||
void testLogRecordingFlow_WithSensitiveData() {
|
||||
String maskedParams = "{\"username\":\"test\",\"password\":\"******\",\"phone\":\"138****8000\"}";
|
||||
|
||||
OperationLog logWithSensitiveData = createDefaultLog();
|
||||
logWithSensitiveData.setRequestParams(maskedParams);
|
||||
|
||||
when(logService.findById(1L))
|
||||
.thenReturn(Mono.just(logWithSensitiveData));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/sys/operationLog/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.data.requestParams").isEqualTo(maskedParams);
|
||||
|
||||
verify(logService, times(1)).findById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志记录流程 - 对象变更追踪")
|
||||
void testLogRecordingFlow_WithObjectChangeTracking() {
|
||||
Map<String, Object> oldObject = new HashMap<>();
|
||||
oldObject.put("username", "oldUser");
|
||||
oldObject.put("email", "old@example.com");
|
||||
|
||||
Map<String, Object> newObject = new HashMap<>();
|
||||
newObject.put("username", "newUser");
|
||||
newObject.put("email", "new@example.com");
|
||||
|
||||
List<String> changedFields = new ArrayList<>();
|
||||
changedFields.add("username: oldUser -> newUser");
|
||||
changedFields.add("email: old@example.com -> new@example.com");
|
||||
|
||||
OperationLog logWithChanges = createDefaultLog();
|
||||
logWithChanges.setResponseResult(String.join("\n", changedFields));
|
||||
|
||||
when(logService.findById(1L))
|
||||
.thenReturn(Mono.just(logWithChanges));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/sys/operationLog/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.data.responseResult").exists();
|
||||
|
||||
verify(logService, times(1)).findById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志记录流程 - 异常处理")
|
||||
void testLogRecordingFlow_ExceptionHandling() {
|
||||
when(logService.save(any(OperationLog.class)))
|
||||
.thenReturn(Mono.error(new RuntimeException("数据库连接失败")));
|
||||
|
||||
OperationLog logToSave = createDefaultLog();
|
||||
logToSave.setId(null);
|
||||
|
||||
Mono<Void> result = logService.save(logToSave)
|
||||
.then()
|
||||
.onErrorResume(e -> Mono.empty());
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, times(1)).save(any(OperationLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志查询流程 - 多条件组合查询")
|
||||
void testLogQueryFlow_CombinedConditions() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setOperator("admin");
|
||||
query.setModuleName("用户管理");
|
||||
query.setStatus("SUCCESS");
|
||||
query.setStartTime(LocalDateTime.now().minusDays(7));
|
||||
query.setEndTime(LocalDateTime.now());
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.data.total").isEqualTo(3)
|
||||
.jsonPath("$.data.list").isArray();
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
verify(logService, times(1)).countByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志查询流程 - 分页功能")
|
||||
void testLogQueryFlow_Pagination() {
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.data.pageNum").isEqualTo(1)
|
||||
.jsonPath("$.data.pageSize").isEqualTo(10)
|
||||
.jsonPath("$.data.total").isEqualTo(3);
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志删除流程 - 成功删除")
|
||||
void testLogDeleteFlow_Success() {
|
||||
when(logService.findById(1L))
|
||||
.thenReturn(Mono.just(createDefaultLog()));
|
||||
|
||||
webTestClient.delete()
|
||||
.uri("/sys/operationLog/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success");
|
||||
|
||||
verify(logService, times(1)).deleteById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志删除流程 - 删除不存在的日志")
|
||||
void testLogDeleteFlow_LogNotFound() {
|
||||
when(logService.findById(999L))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
webTestClient.delete()
|
||||
.uri("/sys/operationLog/999")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
|
||||
verify(logService, times(1)).findById(999L);
|
||||
verify(logService, never()).deleteById(999L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志记录流程 - 性能指标记录")
|
||||
void testLogRecordingFlow_PerformanceMetrics() {
|
||||
OperationLog logWithPerformance = createDefaultLog();
|
||||
logWithPerformance.setExecutionTime(150L);
|
||||
|
||||
when(logService.findById(1L))
|
||||
.thenReturn(Mono.just(logWithPerformance));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/sys/operationLog/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.data.executionTime").isEqualTo(150L);
|
||||
|
||||
verify(logService, times(1)).findById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志记录流程 - 状态记录")
|
||||
void testLogRecordingFlow_StatusRecording() {
|
||||
OperationLog successLog = createLog(1L, "用户管理", "创建用户", "admin");
|
||||
successLog.setStatus("SUCCESS");
|
||||
|
||||
OperationLog failureLog = createLog(2L, "用户管理", "创建用户", "admin");
|
||||
failureLog.setStatus("FAILURE");
|
||||
failureLog.setExceptionMessage("用户名已存在");
|
||||
|
||||
when(logService.findByQuery(any(OperationLogQuery.class)))
|
||||
.thenReturn(Flux.fromIterable(Arrays.asList(successLog, failureLog)));
|
||||
|
||||
OperationLogQuery query = new OperationLogQuery();
|
||||
query.setPageNum(1);
|
||||
query.setPageSize(10);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/operationLog/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(query)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.data.list[0].status").isEqualTo("SUCCESS")
|
||||
.jsonPath("$.data.list[1].status").isEqualTo("FAILURE")
|
||||
.jsonPath("$.data.list[1].exceptionMessage").isEqualTo("用户名已存在");
|
||||
|
||||
verify(logService, times(1)).findByQuery(any(OperationLogQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志记录流程 - 操作IP记录")
|
||||
void testLogRecordingFlow_IpRecording() {
|
||||
OperationLog logWithIp = createDefaultLog();
|
||||
logWithIp.setIpAddress("192.168.1.100");
|
||||
|
||||
when(logService.findById(1L))
|
||||
.thenReturn(Mono.just(logWithIp));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/sys/operationLog/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.data.ipAddress").isEqualTo("192.168.1.100");
|
||||
|
||||
verify(logService, times(1)).findById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试日志记录流程 - 时间戳记录")
|
||||
void testLogRecordingFlow_TimestampRecording() {
|
||||
LocalDateTime testTime = LocalDateTime.of(2026, 1, 9, 10, 30, 0);
|
||||
OperationLog logWithTimestamp = createDefaultLog();
|
||||
logWithTimestamp.setCreatedAt(testTime);
|
||||
|
||||
when(logService.findById(1L))
|
||||
.thenReturn(Mono.just(logWithTimestamp));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/sys/operationLog/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.data.createdAt").exists();
|
||||
|
||||
verify(logService, times(1)).findById(1L);
|
||||
}
|
||||
}
|
||||
+349
@@ -0,0 +1,349 @@
|
||||
package io.destiny.sys.integration;
|
||||
|
||||
import io.destiny.common.primitive.EmailAddress;
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import io.destiny.sys.core.service.ISysPermissionService;
|
||||
import io.destiny.sys.core.service.ISysPermissionInitService;
|
||||
import io.destiny.sys.core.service.ISysMenuService;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import io.destiny.sys.core.service.impl.SysAuthServiceImpl;
|
||||
import io.destiny.sys.dto.request.SysUserLoginRequest;
|
||||
import io.destiny.sys.dto.response.SysUserRegisterRequest;
|
||||
import io.destiny.sys.handler.SysAuthHandler;
|
||||
import io.destiny.sys.handler.SysUserHandler;
|
||||
import io.destiny.sys.handler.SysRoleHandler;
|
||||
import io.destiny.sys.handler.SysMenuHandler;
|
||||
import io.destiny.sys.handler.OperationLogHandler;
|
||||
import io.destiny.sys.security.SysJwtTokenProvider;
|
||||
import io.destiny.sys.config.SysRouter;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@DisplayName("认证管理API集成测试")
|
||||
class SysAuthApiIntegrationTest {
|
||||
|
||||
@Mock
|
||||
private ISysUserService userService;
|
||||
|
||||
@Mock
|
||||
private SysJwtTokenProvider jwtTokenProvider;
|
||||
|
||||
@Mock
|
||||
private ISysPermissionService permissionService;
|
||||
|
||||
@Mock
|
||||
private ISysPermissionInitService permissionInitService;
|
||||
|
||||
@Mock
|
||||
private ISysMenuService menuService;
|
||||
|
||||
@Mock
|
||||
private ISysRoleService roleService;
|
||||
|
||||
private SysAuthServiceImpl authService;
|
||||
|
||||
private SysAuthHandler authHandler;
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
authService = new SysAuthServiceImpl(userService, jwtTokenProvider, permissionService, permissionInitService);
|
||||
authHandler = new SysAuthHandler(authService);
|
||||
|
||||
SysUserHandler userHandler = new SysUserHandler(null);
|
||||
SysRoleHandler roleHandler = new SysRoleHandler(null);
|
||||
SysMenuHandler menuHandler = new SysMenuHandler(menuService, roleService);
|
||||
OperationLogHandler logHandler = new OperationLogHandler(null);
|
||||
|
||||
SysRouter router = new SysRouter(userHandler, roleHandler,
|
||||
menuHandler, authHandler, logHandler);
|
||||
webTestClient = WebTestClient.bindToRouterFunction(router.sysRoutes())
|
||||
.build();
|
||||
|
||||
SysUser defaultUser = createDefaultUser();
|
||||
|
||||
when(userService.findByUsername(anyString()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
when(userService.create(any(SysUser.class)))
|
||||
.thenReturn(Mono.just(defaultUser));
|
||||
|
||||
when(userService.findById(anyLong()))
|
||||
.thenReturn(Mono.just(defaultUser));
|
||||
|
||||
when(jwtTokenProvider.generateAccessToken(anyLong(), anyString()))
|
||||
.thenReturn("mock-jwt-token");
|
||||
|
||||
when(jwtTokenProvider.generateRefreshToken(anyLong(), anyString()))
|
||||
.thenReturn("mock-refresh-token");
|
||||
|
||||
when(jwtTokenProvider.validateToken(anyString()))
|
||||
.thenReturn(true);
|
||||
|
||||
when(jwtTokenProvider.getUsernameFromToken(anyString()))
|
||||
.thenReturn("testuser");
|
||||
|
||||
when(jwtTokenProvider.getUserIdFromToken(anyString()))
|
||||
.thenReturn(1L);
|
||||
|
||||
when(jwtTokenProvider.isTokenExpired(anyString()))
|
||||
.thenReturn(false);
|
||||
|
||||
when(jwtTokenProvider.getTokenType(anyString()))
|
||||
.thenReturn("refresh");
|
||||
|
||||
when(permissionInitService.initializeUserPermissions(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
when(permissionService.getUserPermissions(anyLong()))
|
||||
.thenReturn(Mono.just(java.util.Set.of("sys:user:query")));
|
||||
}
|
||||
|
||||
private SysUser createDefaultUser() {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
user.setPassword(encoder.encode("password123"));
|
||||
user.setEmail(EmailAddress.of("test@example.com"));
|
||||
user.setPhone(PhoneNumber.mobileOf("13800138000"));
|
||||
user.setStatus("0");
|
||||
user.setCreateBy("admin");
|
||||
user.setUpdateBy("admin");
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
user.setUpdatedAt(LocalDateTime.now());
|
||||
return user;
|
||||
}
|
||||
|
||||
private SysUserLoginRequest createLoginRequest() {
|
||||
SysUserLoginRequest request = new SysUserLoginRequest();
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("password123");
|
||||
request.setIpAddress("127.0.0.1");
|
||||
return request;
|
||||
}
|
||||
|
||||
private SysUserRegisterRequest createRegisterRequest() {
|
||||
SysUserRegisterRequest request = new SysUserRegisterRequest();
|
||||
request.setUsername("newuser");
|
||||
request.setPassword("password123");
|
||||
request.setEmail("newuser@example.com");
|
||||
request.setPhone("13900139000");
|
||||
return request;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户注册 - 成功场景")
|
||||
void testRegister_Success() {
|
||||
SysUserRegisterRequest request = createRegisterRequest();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.data").isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户注册 - 用户名已存在")
|
||||
void testRegister_UsernameExists() {
|
||||
when(userService.findByUsername(anyString()))
|
||||
.thenReturn(Mono.just(createDefaultUser()));
|
||||
|
||||
SysUserRegisterRequest request = createRegisterRequest();
|
||||
request.setUsername("testuser");
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("400")
|
||||
.jsonPath("$.message").isEqualTo("用户名已存在");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户登录 - 成功场景")
|
||||
void testLogin_Success() {
|
||||
when(userService.findByUsername(anyString()))
|
||||
.thenReturn(Mono.just(createDefaultUser()));
|
||||
|
||||
SysUserLoginRequest request = createLoginRequest();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/login")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.data.token").isEqualTo("mock-jwt-token")
|
||||
.jsonPath("$.data.user.id").isEqualTo(1)
|
||||
.jsonPath("$.data.user.username").isEqualTo("testuser");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户登录 - 用户名不存在")
|
||||
void testLogin_UserNotFound() {
|
||||
when(userService.findByUsername(anyString()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
SysUserLoginRequest request = createLoginRequest();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/login")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isUnauthorized()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("401")
|
||||
.jsonPath("$.message").isEqualTo("用户名或密码错误");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户登录 - 密码错误")
|
||||
void testLogin_InvalidPassword() {
|
||||
SysUser user = createDefaultUser();
|
||||
user.setPassword("wrong-password");
|
||||
when(userService.findByUsername(anyString()))
|
||||
.thenReturn(Mono.just(user));
|
||||
|
||||
SysUserLoginRequest request = createLoginRequest();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/login")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isUnauthorized()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("401")
|
||||
.jsonPath("$.message").isEqualTo("用户名或密码错误");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户登录 - 用户已禁用")
|
||||
void testLogin_UserDisabled() {
|
||||
SysUser disabledUser = new SysUser();
|
||||
disabledUser.setId(1L);
|
||||
disabledUser.setUsername("testuser");
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
disabledUser.setPassword(encoder.encode("password123"));
|
||||
disabledUser.setStatus("1");
|
||||
|
||||
when(userService.findByUsername(anyString()))
|
||||
.thenReturn(Mono.just(disabledUser));
|
||||
|
||||
SysUserLoginRequest request = createLoginRequest();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/login")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isUnauthorized()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("401")
|
||||
.jsonPath("$.message").isEqualTo("用户已被禁用");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试刷新令牌 - 成功场景")
|
||||
void testRefreshToken_Success() {
|
||||
when(userService.findByUsername(anyString()))
|
||||
.thenReturn(Mono.just(createDefaultUser()));
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/refresh/mock-refresh-token")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.data.token").isEqualTo("mock-jwt-token")
|
||||
.jsonPath("$.data.user.id").isEqualTo(1)
|
||||
.jsonPath("$.data.user.username").isEqualTo("testuser");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试刷新令牌 - 令牌无效")
|
||||
void testRefreshToken_InvalidToken() {
|
||||
when(jwtTokenProvider.validateToken(anyString()))
|
||||
.thenReturn(false);
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/refresh/invalid-token")
|
||||
.exchange()
|
||||
.expectStatus().isUnauthorized()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("401")
|
||||
.jsonPath("$.message").isEqualTo("无效的刷新令牌");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户登出 - 成功场景")
|
||||
void testLogout_Success() {
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/logout")
|
||||
.header("Authorization", "Bearer mock-jwt-token")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.data").isEqualTo("登出成功");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户登出 - 无令牌")
|
||||
void testLogout_NoToken() {
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/logout")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.data").isEqualTo("登出成功");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户登出 - 令牌格式错误")
|
||||
void testLogout_InvalidTokenFormat() {
|
||||
webTestClient.post()
|
||||
.uri("/sys/auth/logout")
|
||||
.header("Authorization", "InvalidFormat mock-jwt-token")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.data").isEqualTo("登出成功");
|
||||
}
|
||||
}
|
||||
+384
@@ -0,0 +1,384 @@
|
||||
package io.destiny.sys.integration;
|
||||
|
||||
import io.destiny.sys.core.domain.SysMenu;
|
||||
import io.destiny.sys.core.service.ISysMenuService;
|
||||
import io.destiny.sys.core.service.impl.SysAuthServiceImpl;
|
||||
import io.destiny.sys.dto.request.AssignMenuToRoleRequest;
|
||||
import io.destiny.sys.dto.request.SysMenuRequest;
|
||||
import io.destiny.sys.handler.SysMenuHandler;
|
||||
import io.destiny.sys.handler.SysUserHandler;
|
||||
import io.destiny.sys.handler.SysRoleHandler;
|
||||
import io.destiny.sys.handler.SysAuthHandler;
|
||||
import io.destiny.sys.handler.OperationLogHandler;
|
||||
import io.destiny.sys.config.SysRouter;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@DisplayName("菜单管理API集成测试")
|
||||
class SysMenuApiIntegrationTest {
|
||||
|
||||
@Mock
|
||||
private ISysMenuService menuService;
|
||||
|
||||
@InjectMocks
|
||||
private SysMenuHandler menuHandler;
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
SysUserHandler userHandler = new SysUserHandler(null);
|
||||
SysRoleHandler roleHandler = new SysRoleHandler(null);
|
||||
SysAuthServiceImpl authService = new SysAuthServiceImpl(null, null, null, null);
|
||||
SysAuthHandler authHandler = new SysAuthHandler(authService);
|
||||
OperationLogHandler logHandler = new OperationLogHandler(null);
|
||||
|
||||
SysRouter router = new SysRouter(userHandler, roleHandler, menuHandler, authHandler, logHandler);
|
||||
webTestClient = WebTestClient.bindToRouterFunction(router.sysRoutes())
|
||||
.build();
|
||||
|
||||
when(menuService.findById(anyLong()))
|
||||
.thenReturn(Mono.just(createDefaultMenu()));
|
||||
|
||||
when(menuService.findByRoleId(anyLong()))
|
||||
.thenReturn(Flux.fromIterable(createMenuList()));
|
||||
|
||||
when(menuService.findByUserId(anyLong()))
|
||||
.thenReturn(Flux.fromIterable(createMenuList()));
|
||||
|
||||
when(menuService.findAll())
|
||||
.thenReturn(Flux.fromIterable(createMenuList()));
|
||||
|
||||
when(menuService.create(any(SysMenu.class)))
|
||||
.thenReturn(Mono.just(createDefaultMenu()));
|
||||
|
||||
when(menuService.update(any(SysMenu.class)))
|
||||
.thenReturn(Mono.just(createDefaultMenu()));
|
||||
|
||||
when(menuService.deleteById(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
when(menuService.assignMenuToRole(anyLong(), anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
when(menuService.removeMenuFromRole(anyLong(), anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
}
|
||||
|
||||
private SysMenu createDefaultMenu() {
|
||||
SysMenu menu = new SysMenu();
|
||||
menu.setId(1L);
|
||||
menu.setMenuName("用户管理");
|
||||
menu.setParentId(0L);
|
||||
menu.setOrderNum(1);
|
||||
menu.setMenuType("C");
|
||||
menu.setPerms("system:user:list");
|
||||
menu.setComponent("system/user/index");
|
||||
menu.setStatus("0");
|
||||
menu.setCreateBy("admin");
|
||||
menu.setUpdateBy("admin");
|
||||
menu.setCreatedAt(LocalDateTime.now());
|
||||
menu.setUpdatedAt(LocalDateTime.now());
|
||||
return menu;
|
||||
}
|
||||
|
||||
private SysMenuRequest createDefaultMenuRequest() {
|
||||
return SysMenuRequest.builder()
|
||||
.name("用户管理")
|
||||
.parentId(0L)
|
||||
.sortOrder(1)
|
||||
.code("C")
|
||||
.component("system/user/index")
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
private SysMenuRequest createUpdateMenuRequest() {
|
||||
return SysMenuRequest.builder()
|
||||
.id(1L)
|
||||
.name("用户管理")
|
||||
.parentId(0L)
|
||||
.sortOrder(1)
|
||||
.code("C")
|
||||
.component("system/user/index")
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<SysMenu> createMenuList() {
|
||||
SysMenu menu1 = new SysMenu();
|
||||
menu1.setId(1L);
|
||||
menu1.setMenuName("用户管理");
|
||||
menu1.setParentId(0L);
|
||||
menu1.setOrderNum(1);
|
||||
menu1.setMenuType("C");
|
||||
menu1.setPerms("system:user:list");
|
||||
menu1.setComponent("system/user/index");
|
||||
menu1.setStatus("0");
|
||||
menu1.setCreateBy("admin");
|
||||
menu1.setUpdateBy("admin");
|
||||
menu1.setCreatedAt(LocalDateTime.now());
|
||||
menu1.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
SysMenu menu2 = new SysMenu();
|
||||
menu2.setId(2L);
|
||||
menu2.setMenuName("角色管理");
|
||||
menu2.setParentId(0L);
|
||||
menu2.setOrderNum(2);
|
||||
menu2.setMenuType("C");
|
||||
menu2.setPerms("system:role:list");
|
||||
menu2.setComponent("system/role/index");
|
||||
menu2.setStatus("0");
|
||||
menu2.setCreateBy("admin");
|
||||
menu2.setUpdateBy("admin");
|
||||
menu2.setCreatedAt(LocalDateTime.now());
|
||||
menu2.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
return Arrays.asList(menu1, menu2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询菜单 - 成功场景")
|
||||
void testGetMenuById_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/menu/1")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.id").isEqualTo(1)
|
||||
.jsonPath("$.data.name").isEqualTo("用户管理");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID查询菜单 - 菜单不存在")
|
||||
void testGetMenuById_MenuNotFound() {
|
||||
when(menuService.findById(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/sys/menu/999")
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("500")
|
||||
.jsonPath("$.message").isEqualTo("菜单不存在");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据角色ID查询菜单列表 - 成功场景")
|
||||
void testGetMenusByRoleId_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/menu/role/1")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data").isArray()
|
||||
.jsonPath("$.data.length()").isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据用户ID查询菜单列表 - 成功场景")
|
||||
void testGetMenusByUserId_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/menu/user/1")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data").isArray()
|
||||
.jsonPath("$.data.length()").isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试查询所有菜单 - 成功场景")
|
||||
void testGetAllMenus_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/menu")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data").isArray()
|
||||
.jsonPath("$.data.length()").isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建菜单 - 成功场景")
|
||||
void testCreateMenu_Success() {
|
||||
SysMenuRequest request = createDefaultMenuRequest();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/menu")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.name").isEqualTo("用户管理");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建菜单 - 必填字段验证")
|
||||
void testCreateMenu_RequiredFieldsValidation() {
|
||||
SysMenuRequest request = SysMenuRequest.builder()
|
||||
.build();
|
||||
|
||||
when(menuService.create(any(SysMenu.class)))
|
||||
.thenReturn(Mono.error(new RuntimeException("菜单名称不能为空")));
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/menu")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建菜单 - 菜单类型验证")
|
||||
void testCreateMenu_MenuTypeValidation() {
|
||||
SysMenuRequest request = SysMenuRequest.builder()
|
||||
.name("测试菜单")
|
||||
.code("X")
|
||||
.build();
|
||||
|
||||
when(menuService.create(any(SysMenu.class)))
|
||||
.thenReturn(Mono.error(new RuntimeException("菜单类型不正确")));
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/menu")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新菜单 - 成功场景")
|
||||
void testUpdateMenu_Success() {
|
||||
SysMenuRequest request = createUpdateMenuRequest();
|
||||
|
||||
webTestClient.put()
|
||||
.uri("/sys/menu")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.name").isEqualTo("用户管理");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新菜单 - 菜单不存在")
|
||||
void testUpdateMenu_MenuNotFound() {
|
||||
SysMenuRequest request = createUpdateMenuRequest();
|
||||
|
||||
when(menuService.findById(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
webTestClient.put()
|
||||
.uri("/sys/menu")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("500")
|
||||
.jsonPath("$.message").isEqualTo("菜单不存在");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除菜单 - 成功场景")
|
||||
void testDeleteMenu_Success() {
|
||||
webTestClient.delete()
|
||||
.uri("/sys/menu/1")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.message").isEqualTo("Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分配菜单到角色 - 成功场景")
|
||||
void testAssignMenuToRole_Success() {
|
||||
AssignMenuToRoleRequest request = AssignMenuToRoleRequest.builder()
|
||||
.roleId(1L)
|
||||
.menuId(1L)
|
||||
.build();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/menu/assign")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.message").isEqualTo("Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试移除角色的菜单 - 成功场景")
|
||||
void testRemoveMenuFromRole_Success() {
|
||||
webTestClient.delete()
|
||||
.uri("/sys/menu/role/1/menu/1")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.message").isEqualTo("Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试菜单树形结构 - 父子关系验证")
|
||||
void testMenuTreeStructure_ParentChildRelationship() {
|
||||
List<SysMenu> menuTree = Arrays.asList(
|
||||
createDefaultMenu(),
|
||||
createDefaultMenu()
|
||||
);
|
||||
|
||||
when(menuService.findAll())
|
||||
.thenReturn(Flux.fromIterable(menuTree));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/sys/menu")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("200")
|
||||
.jsonPath("$.data").isArray()
|
||||
.jsonPath("$.data[0].parentId").isEqualTo(0);
|
||||
}
|
||||
}
|
||||
+447
@@ -0,0 +1,447 @@
|
||||
package io.destiny.sys.integration;
|
||||
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.service.ISysPermissionService;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import io.destiny.sys.security.SysPermissionFilter;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@DisplayName("权限验证集成测试")
|
||||
class SysPermissionIntegrationTest {
|
||||
|
||||
@Mock
|
||||
private ISysUserService userService;
|
||||
|
||||
@Mock
|
||||
private ISysPermissionService permissionService;
|
||||
|
||||
@Mock
|
||||
private WebFilterChain filterChain;
|
||||
|
||||
private SysPermissionFilter permissionFilter;
|
||||
|
||||
private SysUser activeUser;
|
||||
private SysUser disabledUser;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
permissionFilter = new SysPermissionFilter(userService, permissionService);
|
||||
|
||||
activeUser = new SysUser();
|
||||
activeUser.setId(1L);
|
||||
activeUser.setUsername("admin");
|
||||
activeUser.setStatus("0");
|
||||
|
||||
disabledUser = new SysUser();
|
||||
disabledUser.setId(2L);
|
||||
disabledUser.setUsername("disabled_user");
|
||||
disabledUser.setStatus("1");
|
||||
|
||||
when(userService.findById(1L)).thenReturn(Mono.just(activeUser));
|
||||
when(userService.findById(2L)).thenReturn(Mono.just(disabledUser));
|
||||
when(filterChain.filter(any())).thenAnswer(invocation -> {
|
||||
ServerWebExchange exchange = invocation.getArgument(0);
|
||||
exchange.getResponse().setStatusCode(HttpStatus.OK);
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试公开路径 - 不需要权限验证")
|
||||
void testPublicPath_NoPermissionRequired() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest
|
||||
.get("/api/sys/auth/login")
|
||||
.accept(MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(userService, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试注册路径 - 不需要权限验证")
|
||||
void testRegisterPath_NoPermissionRequired() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest
|
||||
.post("/api/sys/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body("{\"username\":\"test\",\"password\":\"123456\"}"));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(userService, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试需要权限的路径 - 有权限的用户可以访问")
|
||||
void testProtectedPath_WithPermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:user:list")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.get("/sys/user")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:user:list");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试需要权限的路径 - 没有权限的用户被拒绝")
|
||||
void testProtectedPath_WithoutPermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:user:list")).thenReturn(false);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.get("/sys/user")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.FORBIDDEN, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, never()).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:user:list");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试需要权限的路径 - 缺少用户ID头部")
|
||||
void testProtectedPath_MissingUserIdHeader() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.get("/sys/user")
|
||||
.header("X-Username", "admin")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.FORBIDDEN, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, never()).filter(any());
|
||||
verify(userService, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试需要权限的路径 - 缺少用户名头部")
|
||||
void testProtectedPath_MissingUsernameHeader() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.get("/sys/user")
|
||||
.header("X-User-Id", "1")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.FORBIDDEN, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, never()).filter(any());
|
||||
verify(userService, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试需要权限的路径 - 用户被禁用")
|
||||
void testProtectedPath_UserDisabled() {
|
||||
when(permissionService.hasPermission(2L, "sys:user:list")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.get("/sys/user")
|
||||
.header("X-User-Id", "2")
|
||||
.header("X-Username", "disabled_user")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.FORBIDDEN, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, never()).filter(any());
|
||||
verify(permissionService, never()).hasPermission(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户管理 - 添加用户权限")
|
||||
void testUserManagement_AddPermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:user:add")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.post("/sys/user")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.body("{\"username\":\"newuser\",\"password\":\"123456\"}"));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:user:add");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户管理 - 编辑用户权限")
|
||||
void testUserManagement_EditPermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:user:edit")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.put("/sys/user/1")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.body("{\"username\":\"updateduser\"}"));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:user:edit");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户管理 - 删除用户权限")
|
||||
void testUserManagement_DeletePermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:user:remove")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.delete("/sys/user/1")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:user:remove");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试角色管理 - 列表权限")
|
||||
void testRoleManagement_ListPermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:role:list")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.get("/sys/role")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:role:list");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试角色管理 - 添加角色权限")
|
||||
void testRoleManagement_AddPermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:role:add")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.post("/sys/role")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.body("{\"roleName\":\"test_role\",\"roleKey\":\"test\"}"));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:role:add");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试角色管理 - 编辑角色权限")
|
||||
void testRoleManagement_EditPermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:role:edit")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.put("/sys/role/1")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.body("{\"roleName\":\"updated_role\"}"));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:role:edit");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试角色管理 - 删除角色权限")
|
||||
void testRoleManagement_DeletePermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:role:remove")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.delete("/sys/role/1")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:role:remove");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试菜单管理 - 列表权限")
|
||||
void testMenuManagement_ListPermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:menu:list")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.get("/sys/menu")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:menu:list");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试菜单管理 - 添加菜单权限")
|
||||
void testMenuManagement_AddPermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:menu:add")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.post("/sys/menu")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.body("{\"menuName\":\"test_menu\",\"menuType\":\"M\"}"));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:menu:add");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试菜单管理 - 编辑菜单权限")
|
||||
void testMenuManagement_EditPermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:menu:edit")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.put("/sys/menu/1")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.body("{\"menuName\":\"updated_menu\"}"));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:menu:edit");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试菜单管理 - 删除菜单权限")
|
||||
void testMenuManagement_DeletePermission() {
|
||||
when(permissionService.hasPermission(1L, "sys:menu:remove")).thenReturn(true);
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.delete("/sys/menu/1")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, times(1)).filter(any());
|
||||
verify(permissionService, times(1)).hasPermission(1L, "sys:menu:remove");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试无效的用户ID格式")
|
||||
void testInvalidUserIdFormat() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.get("/sys/user")
|
||||
.header("X-User-Id", "invalid")
|
||||
.header("X-Username", "admin")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.FORBIDDEN, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, never()).filter(any());
|
||||
verify(userService, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试用户不存在")
|
||||
void testUserNotFound() {
|
||||
when(userService.findById(999L)).thenReturn(Mono.empty());
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.get("/sys/user")
|
||||
.header("X-User-Id", "999")
|
||||
.header("X-Username", "nonexistent")
|
||||
.accept(org.springframework.http.MediaType.APPLICATION_JSON));
|
||||
|
||||
permissionFilter.filter(exchange, filterChain).block();
|
||||
|
||||
assertEquals(HttpStatus.FORBIDDEN, exchange.getResponse().getStatusCode());
|
||||
verify(filterChain, never()).filter(any());
|
||||
verify(userService, times(1)).findById(999L);
|
||||
}
|
||||
}
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
package io.destiny.sys.integration;
|
||||
|
||||
import io.destiny.sys.handler.SysRoleHandler;
|
||||
import io.destiny.sys.handler.SysUserHandler;
|
||||
import io.destiny.sys.handler.SysMenuHandler;
|
||||
import io.destiny.sys.handler.SysAuthHandler;
|
||||
import io.destiny.sys.handler.OperationLogHandler;
|
||||
import io.destiny.sys.config.SysRouter;
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import io.destiny.sys.core.service.ISysPermissionService;
|
||||
import io.destiny.sys.core.service.ISysPermissionInitService;
|
||||
import io.destiny.sys.core.service.impl.SysAuthServiceImpl;
|
||||
import io.destiny.sys.dto.request.AssignRoleToUserRequest;
|
||||
import io.destiny.sys.dto.request.SysRoleRequest;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@DisplayName("角色管理API集成测试")
|
||||
class SysRoleApiIntegrationTest {
|
||||
|
||||
@Mock
|
||||
private ISysRoleService roleService;
|
||||
|
||||
@Mock
|
||||
private ISysPermissionService permissionService;
|
||||
|
||||
@Mock
|
||||
private ISysPermissionInitService permissionInitService;
|
||||
|
||||
@InjectMocks
|
||||
private SysRoleHandler roleHandler;
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
SysUserHandler userHandler = new SysUserHandler(null);
|
||||
SysMenuHandler menuHandler = new SysMenuHandler(null, roleService);
|
||||
SysAuthServiceImpl authService = new SysAuthServiceImpl(null, null, permissionService, permissionInitService);
|
||||
SysAuthHandler authHandler = new SysAuthHandler(authService);
|
||||
OperationLogHandler logHandler = new OperationLogHandler(null);
|
||||
|
||||
SysRouter router = new SysRouter(userHandler, roleHandler, menuHandler, authHandler, logHandler);
|
||||
webTestClient = WebTestClient.bindToRouterFunction(router.sysRoutes())
|
||||
.build();
|
||||
|
||||
when(roleService.findById(anyLong()))
|
||||
.thenReturn(Mono.just(createDefaultRole()));
|
||||
|
||||
when(roleService.findByRoleKey(anyString()))
|
||||
.thenReturn(Mono.just(createDefaultRole()));
|
||||
|
||||
when(roleService.findByUserId(anyLong()))
|
||||
.thenReturn(Flux.just(createDefaultRole()));
|
||||
|
||||
when(roleService.findAll())
|
||||
.thenReturn(Flux.just(createDefaultRole(), createAdminRole()));
|
||||
|
||||
when(roleService.create(any(SysRole.class)))
|
||||
.thenReturn(Mono.just(createDefaultRole()));
|
||||
|
||||
when(roleService.update(any(SysRole.class)))
|
||||
.thenReturn(Mono.just(createDefaultRole()));
|
||||
|
||||
when(roleService.deleteById(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
when(roleService.assignRoleToUser(anyLong(), anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
when(roleService.removeRoleFromUser(anyLong(), anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
}
|
||||
|
||||
private SysRole createDefaultRole() {
|
||||
SysRole role = new SysRole();
|
||||
role.setId(1L);
|
||||
role.setRoleName("普通用户");
|
||||
role.setRoleKey("user");
|
||||
role.setRoleSort(1);
|
||||
role.setStatus("0");
|
||||
role.setCreateBy("admin");
|
||||
role.setUpdateBy("admin");
|
||||
role.setCreatedAt(LocalDateTime.now());
|
||||
role.setUpdatedAt(LocalDateTime.now());
|
||||
return role;
|
||||
}
|
||||
|
||||
private SysRole createAdminRole() {
|
||||
SysRole role = new SysRole();
|
||||
role.setId(2L);
|
||||
role.setRoleName("管理员");
|
||||
role.setRoleKey("admin");
|
||||
role.setRoleSort(0);
|
||||
role.setStatus("0");
|
||||
role.setCreateBy("system");
|
||||
role.setUpdateBy("system");
|
||||
role.setCreatedAt(LocalDateTime.now());
|
||||
role.setUpdatedAt(LocalDateTime.now());
|
||||
return role;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID获取角色信息 - 成功")
|
||||
void testGetRoleById_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/role/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.id").isEqualTo(1)
|
||||
.jsonPath("$.data.name").isEqualTo("普通用户")
|
||||
.jsonPath("$.data.roleKey").isEqualTo("user")
|
||||
.jsonPath("$.data.sortOrder").isEqualTo(1)
|
||||
.jsonPath("$.data.status").isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID获取角色信息 - 角色不存在")
|
||||
void testGetRoleById_NotFound() {
|
||||
when(roleService.findById(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/sys/role/999")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("500")
|
||||
.jsonPath("$.message").isEqualTo("角色不存在");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据角色权限字符串获取角色信息 - 成功")
|
||||
void testGetRoleByRoleKey_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/role/roleKey/user")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.id").isEqualTo(1)
|
||||
.jsonPath("$.data.name").isEqualTo("普通用户")
|
||||
.jsonPath("$.data.roleKey").isEqualTo("user");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据角色权限字符串获取角色信息 - 角色不存在")
|
||||
void testGetRoleByRoleKey_NotFound() {
|
||||
when(roleService.findByRoleKey(anyString()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/sys/role/roleKey/nonexistent")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("500")
|
||||
.jsonPath("$.message").isEqualTo("角色不存在");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据用户ID获取角色列表 - 成功")
|
||||
void testGetRolesByUserId_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/role/user/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data[0].id").isEqualTo(1)
|
||||
.jsonPath("$.data[0].name").isEqualTo("普通用户")
|
||||
.jsonPath("$.data[0].roleKey").isEqualTo("user");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试获取所有角色 - 成功")
|
||||
void testGetAllRoles_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/role")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.length()").isEqualTo(2)
|
||||
.jsonPath("$.data[0].name").isEqualTo("普通用户")
|
||||
.jsonPath("$.data[1].name").isEqualTo("管理员");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建角色 - 成功")
|
||||
void testCreateRole_Success() {
|
||||
SysRoleRequest request = createDefaultRoleRequest();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/role")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.name").isEqualTo("普通用户")
|
||||
.jsonPath("$.data.roleKey").isEqualTo("user")
|
||||
.jsonPath("$.data.sortOrder").isEqualTo(1)
|
||||
.jsonPath("$.data.status").isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建角色 - 角色权限字符串冲突")
|
||||
void testCreateRole_RoleKeyConflict() {
|
||||
when(roleService.create(any(SysRole.class)))
|
||||
.thenReturn(Mono.error(new RuntimeException("角色权限字符串已存在")));
|
||||
|
||||
SysRoleRequest request = createDefaultRoleRequest();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/role")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(500)
|
||||
.jsonPath("$.message").isEqualTo("创建角色失败:角色权限字符串已存在");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新角色 - 成功")
|
||||
void testUpdateRole_Success() {
|
||||
SysRoleRequest request = createUpdateRoleRequest();
|
||||
|
||||
webTestClient.put()
|
||||
.uri("/sys/role")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.name").isEqualTo("普通用户")
|
||||
.jsonPath("$.data.roleKey").isEqualTo("user");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新角色 - 角色不存在")
|
||||
void testUpdateRole_NotFound() {
|
||||
when(roleService.findById(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
SysRoleRequest request = createUpdateRoleRequest();
|
||||
|
||||
webTestClient.put()
|
||||
.uri("/sys/role")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("500")
|
||||
.jsonPath("$.message").isEqualTo("角色不存在");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除角色 - 成功")
|
||||
void testDeleteRole_Success() {
|
||||
webTestClient.delete()
|
||||
.uri("/sys/role/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除角色 - 角色不存在")
|
||||
void testDeleteRole_NotFound() {
|
||||
webTestClient.delete()
|
||||
.uri("/sys/role/999")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分配角色到用户 - 成功")
|
||||
void testAssignRoleToUser_Success() {
|
||||
AssignRoleToUserRequest request = AssignRoleToUserRequest.builder()
|
||||
.userId(1L)
|
||||
.roleId(1L)
|
||||
.build();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/role/assign")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试分配角色到用户 - 分配失败")
|
||||
void testAssignRoleToUser_Failure() {
|
||||
AssignRoleToUserRequest request = AssignRoleToUserRequest.builder()
|
||||
.userId(1L)
|
||||
.roleId(1L)
|
||||
.build();
|
||||
|
||||
when(roleService.assignRoleToUser(anyLong(), anyLong()))
|
||||
.thenReturn(Mono.error(new RuntimeException("分配角色失败")));
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/role/assign")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo("500")
|
||||
.jsonPath("$.message").exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试移除用户的角色 - 成功")
|
||||
void testRemoveRoleFromUser_Success() {
|
||||
webTestClient.delete()
|
||||
.uri("/sys/role/user/1/role/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试移除用户的角色 - 移除失败")
|
||||
void testRemoveRoleFromUser_Failure() {
|
||||
webTestClient.delete()
|
||||
.uri("/sys/role/user/1/role/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建角色 - 角色名称为空")
|
||||
void testCreateRole_EmptyRoleName() {
|
||||
SysRoleRequest request = SysRoleRequest.builder()
|
||||
.name("")
|
||||
.roleKey("test_role")
|
||||
.sortOrder(1)
|
||||
.status("0")
|
||||
.build();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/role")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建角色 - 角色权限字符串为空")
|
||||
void testCreateRole_EmptyRoleKey() {
|
||||
SysRoleRequest request = SysRoleRequest.builder()
|
||||
.name("测试角色")
|
||||
.roleKey("")
|
||||
.sortOrder(1)
|
||||
.status("0")
|
||||
.build();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/role")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建角色 - 状态值无效")
|
||||
void testCreateRole_InvalidStatus() {
|
||||
SysRoleRequest request = SysRoleRequest.builder()
|
||||
.name("测试角色")
|
||||
.roleKey("test_role")
|
||||
.sortOrder(1)
|
||||
.status("2")
|
||||
.build();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/role")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200);
|
||||
}
|
||||
|
||||
private SysRoleRequest createDefaultRoleRequest() {
|
||||
return SysRoleRequest.builder()
|
||||
.name("普通用户")
|
||||
.roleKey("user")
|
||||
.sortOrder(1)
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
private SysRoleRequest createUpdateRoleRequest() {
|
||||
return SysRoleRequest.builder()
|
||||
.id(1L)
|
||||
.name("更新角色")
|
||||
.roleKey("updated_role")
|
||||
.sortOrder(2)
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
package io.destiny.sys.integration;
|
||||
|
||||
import io.destiny.common.primitive.EmailAddress;
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import io.destiny.sys.core.service.ISysMenuService;
|
||||
import io.destiny.sys.core.service.ISysRoleService;
|
||||
import io.destiny.sys.core.service.impl.SysAuthServiceImpl;
|
||||
import io.destiny.sys.dto.request.SysUserRequest;
|
||||
import io.destiny.sys.handler.SysUserHandler;
|
||||
import io.destiny.sys.handler.SysRoleHandler;
|
||||
import io.destiny.sys.handler.SysMenuHandler;
|
||||
import io.destiny.sys.handler.SysAuthHandler;
|
||||
import io.destiny.sys.handler.OperationLogHandler;
|
||||
import io.destiny.sys.config.SysRouter;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@DisplayName("用户管理API集成测试")
|
||||
class SysUserApiIntegrationTest {
|
||||
|
||||
@Mock
|
||||
private ISysUserService userService;
|
||||
|
||||
@Mock
|
||||
private ISysMenuService menuService;
|
||||
|
||||
@Mock
|
||||
private ISysRoleService roleService;
|
||||
|
||||
@InjectMocks
|
||||
private SysUserHandler userHandler;
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
SysRoleHandler roleHandler = new SysRoleHandler(null);
|
||||
SysMenuHandler menuHandler = new SysMenuHandler(menuService, roleService);
|
||||
SysAuthServiceImpl authService = new SysAuthServiceImpl(null, null, null, null);
|
||||
SysAuthHandler authHandler = new SysAuthHandler(authService);
|
||||
OperationLogHandler logHandler = new OperationLogHandler(null);
|
||||
|
||||
SysRouter router = new SysRouter(userHandler, roleHandler, menuHandler, authHandler, logHandler);
|
||||
webTestClient = WebTestClient.bindToRouterFunction(router.sysRoutes())
|
||||
.build();
|
||||
|
||||
when(userService.findById(anyLong()))
|
||||
.thenReturn(Mono.just(createDefaultUser()));
|
||||
|
||||
when(userService.findByUsername(any()))
|
||||
.thenReturn(Mono.just(createDefaultUser()));
|
||||
|
||||
when(userService.create(any(SysUser.class)))
|
||||
.thenReturn(Mono.just(createDefaultUser()));
|
||||
|
||||
when(userService.update(any(SysUser.class)))
|
||||
.thenReturn(Mono.just(createDefaultUser()));
|
||||
|
||||
when(userService.deleteById(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
}
|
||||
|
||||
private SysUser createDefaultUser() {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setPassword("password123");
|
||||
user.setEmail(EmailAddress.of("test@example.com"));
|
||||
user.setPhone(PhoneNumber.mobileOf("13800138000"));
|
||||
user.setStatus("0");
|
||||
user.setCreateBy("admin");
|
||||
user.setUpdateBy("admin");
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
user.setUpdatedAt(LocalDateTime.now());
|
||||
return user;
|
||||
}
|
||||
|
||||
private SysUserRequest createDefaultUserRequest() {
|
||||
return SysUserRequest.builder()
|
||||
.username("testuser")
|
||||
.password("password123")
|
||||
.email("test@example.com")
|
||||
.phone("13800138000")
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
private SysUserRequest createUpdateUserRequest() {
|
||||
return SysUserRequest.builder()
|
||||
.id(1L)
|
||||
.username("testuser")
|
||||
.email("test@example.com")
|
||||
.phone("13800138000")
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据ID获取用户信息 - 成功")
|
||||
void testGetUserById_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/user/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.id").isEqualTo(1)
|
||||
.jsonPath("$.data.username").isEqualTo("testuser")
|
||||
.jsonPath("$.data.email").isEqualTo("****************")
|
||||
.jsonPath("$.data.phone").isEqualTo("***********");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试根据用户名获取用户信息 - 成功")
|
||||
void testGetUserByUsername_Success() {
|
||||
webTestClient.get()
|
||||
.uri("/sys/user/username/testuser")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.username").isEqualTo("testuser");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建用户 - 成功")
|
||||
void testCreateUser_Success() {
|
||||
SysUserRequest request = createDefaultUserRequest();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/user")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.username").isEqualTo("testuser");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建用户 - 用户名已存在")
|
||||
void testCreateUser_UsernameExists() {
|
||||
SysUserRequest request = createDefaultUserRequest();
|
||||
|
||||
when(userService.create(any(SysUser.class)))
|
||||
.thenReturn(Mono.error(new RuntimeException("用户名已存在")));
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/user")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新用户 - 成功")
|
||||
void testUpdateUser_Success() {
|
||||
SysUserRequest request = createUpdateUserRequest();
|
||||
|
||||
webTestClient.put()
|
||||
.uri("/sys/user")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success")
|
||||
.jsonPath("$.data.username").isEqualTo("testuser");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新用户 - 用户不存在")
|
||||
void testUpdateUser_UserNotFound() {
|
||||
SysUserRequest request = createUpdateUserRequest();
|
||||
|
||||
when(userService.findById(anyLong()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
webTestClient.put()
|
||||
.uri("/sys/user")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除用户 - 成功")
|
||||
void testDeleteUser_Success() {
|
||||
webTestClient.delete()
|
||||
.uri("/sys/user/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试删除用户 - 用户不存在")
|
||||
void testDeleteUser_UserNotFound() {
|
||||
when(userService.deleteById(anyLong()))
|
||||
.thenReturn(Mono.error(new RuntimeException("用户不存在")));
|
||||
|
||||
webTestClient.delete()
|
||||
.uri("/sys/user/999")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建用户 - 邮箱格式验证")
|
||||
void testCreateUser_EmailValidation() {
|
||||
SysUserRequest request = SysUserRequest.builder()
|
||||
.username("testuser")
|
||||
.email("invalid-email")
|
||||
.phone("13800138000")
|
||||
.status("0")
|
||||
.build();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/user")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建用户 - 手机号格式验证")
|
||||
void testCreateUser_PhoneValidation() {
|
||||
SysUserRequest request = SysUserRequest.builder()
|
||||
.username("testuser")
|
||||
.email("test@example.com")
|
||||
.phone("123")
|
||||
.status("0")
|
||||
.build();
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/user")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试创建用户 - 必填字段验证")
|
||||
void testCreateUser_RequiredFieldsValidation() {
|
||||
SysUserRequest request = SysUserRequest.builder()
|
||||
.build();
|
||||
|
||||
when(userService.create(any(SysUser.class)))
|
||||
.thenReturn(Mono.error(new RuntimeException("用户名不能为空")));
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/sys/user")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新用户 - 邮箱格式验证")
|
||||
void testUpdateUser_EmailValidation() {
|
||||
SysUserRequest request = SysUserRequest.builder()
|
||||
.id(1L)
|
||||
.username("testuser")
|
||||
.email("invalid-email")
|
||||
.status("0")
|
||||
.build();
|
||||
|
||||
webTestClient.put()
|
||||
.uri("/sys/user")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试更新用户 - 状态值验证")
|
||||
void testUpdateUser_StatusValidation() {
|
||||
SysUserRequest request = SysUserRequest.builder()
|
||||
.id(1L)
|
||||
.username("testuser")
|
||||
.email("test@example.com")
|
||||
.status("invalid")
|
||||
.build();
|
||||
|
||||
webTestClient.put()
|
||||
.uri("/sys/user")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(request)
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
}
|
||||
}
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
package io.destiny.sys.security;
|
||||
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.service.ISysPermissionService;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SysPermissionFilterTest {
|
||||
|
||||
@Mock
|
||||
private ISysUserService userService;
|
||||
|
||||
@Mock
|
||||
private ISysPermissionService permissionService;
|
||||
|
||||
@InjectMocks
|
||||
private SysPermissionFilter permissionFilter;
|
||||
|
||||
private SysUser testUser;
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testUser = new SysUser();
|
||||
testUser.setId(1L);
|
||||
testUser.setUsername("admin");
|
||||
testUser.setStatus("0");
|
||||
|
||||
exchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.get("/sys/user")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer test-token")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_PublicPath_ShouldAllowAccess() {
|
||||
ServerWebExchange publicExchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.post("/sys/auth/login")
|
||||
);
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(publicExchange, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService, never()).findById(anyLong());
|
||||
verify(permissionService, never()).hasPermission(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_MissingUserHeaders_ShouldReturnForbidden() {
|
||||
ServerWebExchange exchangeWithoutHeaders = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.get("/sys/user")
|
||||
);
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(exchangeWithoutHeaders, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_UserNotFound_ShouldReturnForbidden() {
|
||||
when(userService.findById(1L)).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(exchange, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService).findById(1L);
|
||||
verify(permissionService, never()).hasPermission(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_UserDisabled_ShouldReturnForbidden() {
|
||||
testUser.setStatus("1");
|
||||
when(userService.findById(1L)).thenReturn(Mono.just(testUser));
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(exchange, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService).findById(1L);
|
||||
verify(permissionService, never()).hasPermission(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_UserHasPermission_ShouldAllowAccess() {
|
||||
when(userService.findById(1L)).thenReturn(Mono.just(testUser));
|
||||
when(permissionService.hasPermission(1L, "sys:user:list")).thenReturn(true);
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(exchange, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService).findById(1L);
|
||||
verify(permissionService).hasPermission(1L, "sys:user:list");
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_UserNoPermission_ShouldReturnForbidden() {
|
||||
when(userService.findById(1L)).thenReturn(Mono.just(testUser));
|
||||
when(permissionService.hasPermission(1L, "sys:user:list")).thenReturn(false);
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(exchange, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService).findById(1L);
|
||||
verify(permissionService).hasPermission(1L, "sys:user:list");
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_NoPermissionRuleDefined_ShouldAllowAccess() {
|
||||
ServerWebExchange exchangeWithNoRule = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.get("/sys/unknown")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
);
|
||||
|
||||
when(userService.findById(1L)).thenReturn(Mono.just(testUser));
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(exchangeWithNoRule, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService).findById(1L);
|
||||
verify(permissionService, never()).hasPermission(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_InvalidUserIdFormat_ShouldReturnForbidden() {
|
||||
ServerWebExchange exchangeWithInvalidUserId = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.get("/sys/user")
|
||||
.header("X-User-Id", "invalid")
|
||||
.header("X-Username", "admin")
|
||||
);
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(exchangeWithInvalidUserId, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_PostUser_ShouldCheckAddPermission() {
|
||||
ServerWebExchange postExchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.post("/sys/user")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
);
|
||||
|
||||
when(userService.findById(1L)).thenReturn(Mono.just(testUser));
|
||||
when(permissionService.hasPermission(1L, "sys:user:add")).thenReturn(true);
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(postExchange, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService).findById(1L);
|
||||
verify(permissionService).hasPermission(1L, "sys:user:add");
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_DeleteUser_ShouldCheckRemovePermission() {
|
||||
ServerWebExchange deleteExchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.delete("/sys/user/1")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
);
|
||||
|
||||
when(userService.findById(1L)).thenReturn(Mono.just(testUser));
|
||||
when(permissionService.hasPermission(1L, "sys:user:remove")).thenReturn(true);
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(deleteExchange, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService).findById(1L);
|
||||
verify(permissionService).hasPermission(1L, "sys:user:remove");
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_RoleManagement_ShouldCheckRolePermissions() {
|
||||
ServerWebExchange roleExchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.get("/sys/role")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
);
|
||||
|
||||
when(userService.findById(1L)).thenReturn(Mono.just(testUser));
|
||||
when(permissionService.hasPermission(1L, "sys:role:list")).thenReturn(true);
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(roleExchange, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService).findById(1L);
|
||||
verify(permissionService).hasPermission(1L, "sys:role:list");
|
||||
}
|
||||
|
||||
@Test
|
||||
void filter_MenuManagement_ShouldCheckMenuPermissions() {
|
||||
ServerWebExchange menuExchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.get("/sys/menu")
|
||||
.header("X-User-Id", "1")
|
||||
.header("X-Username", "admin")
|
||||
);
|
||||
|
||||
when(userService.findById(1L)).thenReturn(Mono.just(testUser));
|
||||
when(permissionService.hasPermission(1L, "sys:menu:list")).thenReturn(true);
|
||||
|
||||
StepVerifier.create(permissionFilter.filter(menuExchange, mockChain()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
verify(userService).findById(1L);
|
||||
verify(permissionService).hasPermission(1L, "sys:menu:list");
|
||||
}
|
||||
|
||||
private org.springframework.web.server.WebFilterChain mockChain() {
|
||||
return exchange -> {
|
||||
exchange.getResponse().setStatusCode(org.springframework.http.HttpStatus.OK);
|
||||
return exchange.getResponse().writeWith(
|
||||
reactor.core.publisher.Flux.just(
|
||||
exchange.getResponse().bufferFactory().wrap("OK".getBytes())));
|
||||
};
|
||||
}
|
||||
}
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
package io.destiny.sys.service;
|
||||
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.service.ISysUserService;
|
||||
import io.destiny.sys.core.service.ISysPermissionService;
|
||||
import io.destiny.sys.core.service.ISysPermissionInitService;
|
||||
import io.destiny.sys.core.service.impl.SysAuthServiceImpl;
|
||||
import io.destiny.sys.dto.request.SysUserLoginRequest;
|
||||
import io.destiny.sys.dto.response.SysUserRegisterRequest;
|
||||
import io.destiny.sys.security.SysJwtTokenProvider;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SysAuthServiceTest {
|
||||
|
||||
@Mock
|
||||
private ISysUserService userService;
|
||||
|
||||
@Mock
|
||||
private SysJwtTokenProvider jwtTokenProvider;
|
||||
|
||||
@Mock
|
||||
private ISysPermissionService permissionService;
|
||||
|
||||
@Mock
|
||||
private ISysPermissionInitService permissionInitService;
|
||||
|
||||
@InjectMocks
|
||||
private SysAuthServiceImpl authService;
|
||||
|
||||
private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
|
||||
private SysUser testUser;
|
||||
private SysUserRegisterRequest registerRequest;
|
||||
private SysUserLoginRequest loginRequest;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testUser = new SysUser();
|
||||
testUser.setId(1L);
|
||||
testUser.setUsername("testuser");
|
||||
testUser.setPassword(passwordEncoder.encode("password123"));
|
||||
testUser.setStatus("0");
|
||||
|
||||
registerRequest = new SysUserRegisterRequest();
|
||||
registerRequest.setUsername("testuser");
|
||||
registerRequest.setPassword("password123");
|
||||
registerRequest.setEmail("test@example.com");
|
||||
|
||||
loginRequest = new SysUserLoginRequest();
|
||||
loginRequest.setUsername("testuser");
|
||||
loginRequest.setPassword("password123");
|
||||
}
|
||||
|
||||
@Test
|
||||
void register_Success() {
|
||||
when(userService.findByUsername(anyString())).thenReturn(Mono.empty());
|
||||
when(userService.create(any(SysUser.class))).thenReturn(Mono.just(testUser));
|
||||
when(permissionInitService.initializeUserPermissions(anyLong())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(authService.register(registerRequest))
|
||||
.expectNext(1L)
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService).findByUsername("testuser");
|
||||
verify(userService).create(any(SysUser.class));
|
||||
verify(permissionInitService).initializeUserPermissions(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void register_AdminUser() {
|
||||
SysUser adminUser = new SysUser();
|
||||
adminUser.setId(2L);
|
||||
adminUser.setUsername("admin");
|
||||
adminUser.setPassword(passwordEncoder.encode("admin123"));
|
||||
adminUser.setStatus("0");
|
||||
|
||||
SysUserRegisterRequest adminRegisterRequest = new SysUserRegisterRequest();
|
||||
adminRegisterRequest.setUsername("admin");
|
||||
adminRegisterRequest.setPassword("admin123");
|
||||
adminRegisterRequest.setEmail("admin@example.com");
|
||||
|
||||
when(userService.findByUsername(anyString())).thenReturn(Mono.empty());
|
||||
when(userService.create(any(SysUser.class))).thenReturn(Mono.just(adminUser));
|
||||
when(permissionInitService.initializeAdminPermissions(anyLong())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(authService.register(adminRegisterRequest))
|
||||
.expectNext(2L)
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService).findByUsername("admin");
|
||||
verify(userService).create(any(SysUser.class));
|
||||
verify(permissionInitService).initializeAdminPermissions(2L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void register_UserExists() {
|
||||
when(userService.findByUsername(anyString())).thenReturn(Mono.just(testUser));
|
||||
|
||||
StepVerifier.create(authService.register(registerRequest))
|
||||
.expectErrorMatches(throwable -> throwable instanceof RuntimeException &&
|
||||
throwable.getMessage().equals("用户名已存在"))
|
||||
.verify();
|
||||
|
||||
verify(userService).findByUsername("testuser");
|
||||
verify(userService, never()).create(any(SysUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_Success() {
|
||||
Set<String> permissions = new HashSet<>();
|
||||
permissions.add("user:read");
|
||||
permissions.add("user:write");
|
||||
|
||||
when(userService.findByUsername(anyString())).thenReturn(Mono.just(testUser));
|
||||
when(jwtTokenProvider.generateAccessToken(anyLong(), anyString())).thenReturn("access-token");
|
||||
when(permissionService.getUserPermissions(anyLong())).thenReturn(Mono.just(permissions));
|
||||
|
||||
StepVerifier.create(authService.login(loginRequest))
|
||||
.expectNextMatches(response -> response.getToken().equals("access-token") &&
|
||||
response.getUser().getUsername().equals("testuser"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService).findByUsername("testuser");
|
||||
verify(jwtTokenProvider).generateAccessToken(1L, "testuser");
|
||||
verify(permissionService).getUserPermissions(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_UserNotFound() {
|
||||
when(userService.findByUsername(anyString())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(authService.login(loginRequest))
|
||||
.expectErrorMatches(throwable -> throwable instanceof RuntimeException &&
|
||||
throwable.getMessage().equals("用户名或密码错误"))
|
||||
.verify();
|
||||
|
||||
verify(userService).findByUsername("testuser");
|
||||
verify(jwtTokenProvider, never()).generateAccessToken(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_WrongPassword() {
|
||||
testUser.setPassword(passwordEncoder.encode("correctpassword"));
|
||||
when(userService.findByUsername(anyString())).thenReturn(Mono.just(testUser));
|
||||
|
||||
loginRequest.setPassword("wrongpassword");
|
||||
|
||||
StepVerifier.create(authService.login(loginRequest))
|
||||
.expectErrorMatches(throwable -> throwable instanceof RuntimeException &&
|
||||
throwable.getMessage().equals("用户名或密码错误"))
|
||||
.verify();
|
||||
|
||||
verify(userService).findByUsername("testuser");
|
||||
verify(jwtTokenProvider, never()).generateAccessToken(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_UserDisabled() {
|
||||
testUser.setStatus("1");
|
||||
when(userService.findByUsername(anyString())).thenReturn(Mono.just(testUser));
|
||||
|
||||
StepVerifier.create(authService.login(loginRequest))
|
||||
.expectErrorMatches(throwable -> throwable instanceof RuntimeException &&
|
||||
throwable.getMessage().equals("用户已被禁用"))
|
||||
.verify();
|
||||
|
||||
verify(userService).findByUsername("testuser");
|
||||
verify(jwtTokenProvider, never()).generateAccessToken(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshToken_Success() {
|
||||
Set<String> permissions = new HashSet<>();
|
||||
permissions.add("user:read");
|
||||
permissions.add("user:write");
|
||||
|
||||
String refreshToken = "valid-refresh-token";
|
||||
when(jwtTokenProvider.validateToken(anyString())).thenReturn(true);
|
||||
when(jwtTokenProvider.isTokenExpired(anyString())).thenReturn(false);
|
||||
when(jwtTokenProvider.getTokenType(anyString())).thenReturn("refresh");
|
||||
when(jwtTokenProvider.getUserIdFromToken(anyString())).thenReturn(1L);
|
||||
when(userService.findById(anyLong())).thenReturn(Mono.just(testUser));
|
||||
when(jwtTokenProvider.generateAccessToken(anyLong(), anyString())).thenReturn("new-access-token");
|
||||
when(permissionService.getUserPermissions(anyLong())).thenReturn(Mono.just(permissions));
|
||||
|
||||
StepVerifier.create(authService.refreshToken(refreshToken))
|
||||
.expectNextMatches(response -> response.getToken().equals("new-access-token") &&
|
||||
response.getUser().getUsername().equals("testuser"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(jwtTokenProvider).validateToken(refreshToken);
|
||||
verify(userService).findById(1L);
|
||||
verify(jwtTokenProvider).generateAccessToken(1L, "testuser");
|
||||
verify(permissionService).getUserPermissions(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshToken_InvalidToken() {
|
||||
String refreshToken = "invalid-token";
|
||||
when(jwtTokenProvider.validateToken(anyString())).thenReturn(false);
|
||||
|
||||
StepVerifier.create(authService.refreshToken(refreshToken))
|
||||
.expectErrorMatches(throwable -> throwable instanceof RuntimeException &&
|
||||
throwable.getMessage().equals("无效的刷新令牌"))
|
||||
.verify();
|
||||
|
||||
verify(jwtTokenProvider).validateToken(refreshToken);
|
||||
verify(userService, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshToken_ExpiredToken() {
|
||||
String refreshToken = "expired-token";
|
||||
when(jwtTokenProvider.validateToken(anyString())).thenReturn(true);
|
||||
when(jwtTokenProvider.isTokenExpired(anyString())).thenReturn(true);
|
||||
|
||||
StepVerifier.create(authService.refreshToken(refreshToken))
|
||||
.expectErrorMatches(throwable -> throwable instanceof RuntimeException &&
|
||||
throwable.getMessage().equals("刷新令牌已过期"))
|
||||
.verify();
|
||||
|
||||
verify(jwtTokenProvider).validateToken(refreshToken);
|
||||
verify(jwtTokenProvider).isTokenExpired(refreshToken);
|
||||
verify(userService, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshToken_WrongTokenType() {
|
||||
String refreshToken = "access-token";
|
||||
when(jwtTokenProvider.validateToken(anyString())).thenReturn(true);
|
||||
when(jwtTokenProvider.isTokenExpired(anyString())).thenReturn(false);
|
||||
when(jwtTokenProvider.getTokenType(anyString())).thenReturn("access");
|
||||
|
||||
StepVerifier.create(authService.refreshToken(refreshToken))
|
||||
.expectErrorMatches(throwable -> throwable instanceof RuntimeException &&
|
||||
throwable.getMessage().equals("令牌类型错误"))
|
||||
.verify();
|
||||
|
||||
verify(jwtTokenProvider).validateToken(refreshToken);
|
||||
verify(jwtTokenProvider).getTokenType(refreshToken);
|
||||
verify(userService, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void logout_Success() {
|
||||
String token = "valid-token";
|
||||
when(jwtTokenProvider.validateToken(anyString())).thenReturn(true);
|
||||
when(jwtTokenProvider.getUserIdFromToken(anyString())).thenReturn(1L);
|
||||
|
||||
StepVerifier.create(authService.logout(token))
|
||||
.verifyComplete();
|
||||
|
||||
verify(jwtTokenProvider).validateToken(token);
|
||||
verify(jwtTokenProvider).getUserIdFromToken(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
void logout_InvalidToken() {
|
||||
String token = "invalid-token";
|
||||
when(jwtTokenProvider.validateToken(anyString())).thenReturn(false);
|
||||
|
||||
StepVerifier.create(authService.logout(token))
|
||||
.verifyComplete();
|
||||
|
||||
verify(jwtTokenProvider).validateToken(token);
|
||||
verify(jwtTokenProvider, never()).getUserIdFromToken(anyString());
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package io.destiny.sys.util;
|
||||
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
public class BCryptGenerator {
|
||||
public static void main(String[] args) {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
|
||||
String[] passwords = {
|
||||
"admin123456",
|
||||
"user123",
|
||||
"test123"
|
||||
};
|
||||
|
||||
for (String password : passwords) {
|
||||
String encoded = encoder.encode(password);
|
||||
System.out.println("Password: " + password);
|
||||
System.out.println("BCrypt: " + encoded);
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package io.destiny.sys.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
public class BCryptGeneratorTest {
|
||||
|
||||
@Test
|
||||
public void generateBCryptPasswords() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
|
||||
String[] passwords = {
|
||||
"admin123456",
|
||||
"user123",
|
||||
"test123"
|
||||
};
|
||||
|
||||
for (String password : passwords) {
|
||||
String encoded = encoder.encode(password);
|
||||
System.out.println("Password: " + password);
|
||||
System.out.println("BCrypt: " + encoded);
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
package io.destiny.sys.util;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.util.UriBuilder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SysApiTestHelper {
|
||||
|
||||
public static Function<UriBuilder, URI> buildUserListUri(Integer page, Integer size, String username, Integer status) {
|
||||
return uriBuilder -> {
|
||||
UriBuilder builder = uriBuilder.path("/sys/user/list");
|
||||
if (page != null) {
|
||||
builder.queryParam("page", page);
|
||||
}
|
||||
if (size != null) {
|
||||
builder.queryParam("size", size);
|
||||
}
|
||||
if (username != null && !username.isEmpty()) {
|
||||
builder.queryParam("username", username);
|
||||
}
|
||||
if (status != null) {
|
||||
builder.queryParam("status", status);
|
||||
}
|
||||
return builder.build();
|
||||
};
|
||||
}
|
||||
|
||||
public static Function<UriBuilder, URI> buildUserDetailUri(Long userId) {
|
||||
return uriBuilder -> uriBuilder.path("/sys/user/" + userId).build();
|
||||
}
|
||||
|
||||
public static Function<UriBuilder, URI> buildRoleListUri(Integer page, Integer size, String roleName, Integer status) {
|
||||
return uriBuilder -> {
|
||||
UriBuilder builder = uriBuilder.path("/sys/role/list");
|
||||
if (page != null) {
|
||||
builder.queryParam("page", page);
|
||||
}
|
||||
if (size != null) {
|
||||
builder.queryParam("size", size);
|
||||
}
|
||||
if (roleName != null && !roleName.isEmpty()) {
|
||||
builder.queryParam("roleName", roleName);
|
||||
}
|
||||
if (status != null) {
|
||||
builder.queryParam("status", status);
|
||||
}
|
||||
return builder.build();
|
||||
};
|
||||
}
|
||||
|
||||
public static Function<UriBuilder, URI> buildRoleDetailUri(Long roleId) {
|
||||
return uriBuilder -> uriBuilder.path("/sys/role/" + roleId).build();
|
||||
}
|
||||
|
||||
public static Function<UriBuilder, URI> buildMenuListUri(Integer status) {
|
||||
return uriBuilder -> {
|
||||
UriBuilder builder = uriBuilder.path("/sys/menu/list");
|
||||
if (status != null) {
|
||||
builder.queryParam("status", status);
|
||||
}
|
||||
return builder.build();
|
||||
};
|
||||
}
|
||||
|
||||
public static Function<UriBuilder, URI> buildMenuTreeUri() {
|
||||
return uriBuilder -> uriBuilder.path("/sys/menu/tree").build();
|
||||
}
|
||||
|
||||
public static Function<UriBuilder, URI> buildMenuDetailUri(Long menuId) {
|
||||
return uriBuilder -> uriBuilder.path("/sys/menu/" + menuId).build();
|
||||
}
|
||||
|
||||
public static Function<UriBuilder, URI> buildAuthRegisterUri() {
|
||||
return uriBuilder -> uriBuilder.path("/sys/auth/register").build();
|
||||
}
|
||||
|
||||
public static Function<UriBuilder, URI> buildAuthLoginUri() {
|
||||
return uriBuilder -> uriBuilder.path("/sys/auth/login").build();
|
||||
}
|
||||
|
||||
public static Function<UriBuilder, URI> buildAuthRefreshUri() {
|
||||
return uriBuilder -> uriBuilder.path("/sys/auth/refresh").build();
|
||||
}
|
||||
|
||||
public static Function<UriBuilder, URI> buildAuthLogoutUri() {
|
||||
return uriBuilder -> uriBuilder.path("/sys/auth/logout").build();
|
||||
}
|
||||
|
||||
public static WebTestClient.ResponseSpec performGetRequest(
|
||||
WebTestClient webTestClient,
|
||||
Function<UriBuilder, URI> uriFunction) {
|
||||
return webTestClient.get()
|
||||
.uri(uriFunction)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange();
|
||||
}
|
||||
|
||||
public static WebTestClient.ResponseSpec performPostRequest(
|
||||
WebTestClient webTestClient,
|
||||
Function<UriBuilder, URI> uriFunction,
|
||||
Object body) {
|
||||
return webTestClient.post()
|
||||
.uri(uriFunction)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(body)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange();
|
||||
}
|
||||
|
||||
public static WebTestClient.ResponseSpec performPutRequest(
|
||||
WebTestClient webTestClient,
|
||||
Function<UriBuilder, URI> uriFunction,
|
||||
Object body) {
|
||||
return webTestClient.put()
|
||||
.uri(uriFunction)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(body)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange();
|
||||
}
|
||||
|
||||
public static WebTestClient.ResponseSpec performDeleteRequest(
|
||||
WebTestClient webTestClient,
|
||||
Function<UriBuilder, URI> uriFunction) {
|
||||
return webTestClient.delete()
|
||||
.uri(uriFunction)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange();
|
||||
}
|
||||
|
||||
public static void assertSuccessResponse(WebTestClient.BodyContentSpec bodySpec) {
|
||||
bodySpec
|
||||
.jsonPath("$.code").isEqualTo(200)
|
||||
.jsonPath("$.message").isEqualTo("Success");
|
||||
}
|
||||
|
||||
public static void assertErrorResponse(WebTestClient.BodyContentSpec bodySpec, int expectedCode) {
|
||||
bodySpec
|
||||
.jsonPath("$.code").isEqualTo(expectedCode);
|
||||
}
|
||||
|
||||
public static void assertDataNotEmpty(WebTestClient.BodyContentSpec bodySpec) {
|
||||
bodySpec.jsonPath("$.data").isNotEmpty();
|
||||
}
|
||||
|
||||
public static void assertDataIsArray(WebTestClient.BodyContentSpec bodySpec) {
|
||||
bodySpec.jsonPath("$.data").isArray();
|
||||
}
|
||||
|
||||
public static void assertArrayLength(WebTestClient.BodyContentSpec bodySpec, int expectedLength) {
|
||||
bodySpec.jsonPath("$.data.length()").isEqualTo(expectedLength);
|
||||
}
|
||||
|
||||
public static void assertPaginationData(WebTestClient.BodyContentSpec bodySpec) {
|
||||
bodySpec
|
||||
.jsonPath("$.data.records").isArray()
|
||||
.jsonPath("$.data.total").exists()
|
||||
.jsonPath("$.data.page").exists()
|
||||
.jsonPath("$.data.size").exists();
|
||||
}
|
||||
|
||||
public static void assertPaginationTotal(WebTestClient.BodyContentSpec bodySpec, long expectedTotal) {
|
||||
bodySpec.jsonPath("$.data.total").isEqualTo(expectedTotal);
|
||||
}
|
||||
|
||||
public static void assertPaginationPage(WebTestClient.BodyContentSpec bodySpec, int expectedPage) {
|
||||
bodySpec.jsonPath("$.data.page").isEqualTo(expectedPage);
|
||||
}
|
||||
|
||||
public static void assertPaginationSize(WebTestClient.BodyContentSpec bodySpec, int expectedSize) {
|
||||
bodySpec.jsonPath("$.data.size").isEqualTo(expectedSize);
|
||||
}
|
||||
|
||||
public static void assertUserDataFields(WebTestClient.BodyContentSpec bodySpec) {
|
||||
bodySpec
|
||||
.jsonPath("$.data.id").exists()
|
||||
.jsonPath("$.data.username").exists()
|
||||
.jsonPath("$.data.nickname").exists()
|
||||
.jsonPath("$.data.email").exists()
|
||||
.jsonPath("$.data.phone").exists()
|
||||
.jsonPath("$.data.status").exists()
|
||||
.jsonPath("$.data.createdAt").exists();
|
||||
}
|
||||
|
||||
public static void assertRoleDataFields(WebTestClient.BodyContentSpec bodySpec) {
|
||||
bodySpec
|
||||
.jsonPath("$.data.id").exists()
|
||||
.jsonPath("$.data.roleName").exists()
|
||||
.jsonPath("$.data.roleKey").exists()
|
||||
.jsonPath("$.data.roleSort").exists()
|
||||
.jsonPath("$.data.status").exists()
|
||||
.jsonPath("$.data.createdAt").exists();
|
||||
}
|
||||
|
||||
public static void assertMenuDataFields(WebTestClient.BodyContentSpec bodySpec) {
|
||||
bodySpec
|
||||
.jsonPath("$.data.id").exists()
|
||||
.jsonPath("$.data.menuName").exists()
|
||||
.jsonPath("$.data.parentId").exists()
|
||||
.jsonPath("$.data.menuType").exists()
|
||||
.jsonPath("$.data.perms").exists()
|
||||
.jsonPath("$.data.path").exists()
|
||||
.jsonPath("$.data.status").exists();
|
||||
}
|
||||
|
||||
public static void assertLoginResponseFields(WebTestClient.BodyContentSpec bodySpec) {
|
||||
bodySpec
|
||||
.jsonPath("$.data.accessToken").exists()
|
||||
.jsonPath("$.data.refreshToken").exists()
|
||||
.jsonPath("$.data.tokenType").exists()
|
||||
.jsonPath("$.data.expiresIn").exists();
|
||||
}
|
||||
|
||||
public static void assertUserId(WebTestClient.BodyContentSpec bodySpec, Long expectedUserId) {
|
||||
bodySpec.jsonPath("$.data.id").isEqualTo(expectedUserId);
|
||||
}
|
||||
|
||||
public static void assertUsername(WebTestClient.BodyContentSpec bodySpec, String expectedUsername) {
|
||||
bodySpec.jsonPath("$.data.username").isEqualTo(expectedUsername);
|
||||
}
|
||||
|
||||
public static void assertStatus(WebTestClient.BodyContentSpec bodySpec, Integer expectedStatus) {
|
||||
bodySpec.jsonPath("$.data.status").isEqualTo(expectedStatus);
|
||||
}
|
||||
|
||||
public static void assertTokenExists(WebTestClient.BodyContentSpec bodySpec) {
|
||||
bodySpec.jsonPath("$.data.accessToken").isNotEmpty()
|
||||
.jsonPath("$.data.refreshToken").isNotEmpty();
|
||||
}
|
||||
|
||||
public static void assertContentType(WebTestClient.ResponseSpec responseSpec) {
|
||||
responseSpec.expectHeader().contentType("application/json");
|
||||
}
|
||||
|
||||
public static void assertStatusCode(WebTestClient.ResponseSpec responseSpec, int expectedStatus) {
|
||||
responseSpec.expectStatus().isEqualTo(expectedStatus);
|
||||
}
|
||||
}
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
package io.destiny.sys.util;
|
||||
|
||||
import io.destiny.sys.core.domain.SysUser;
|
||||
import io.destiny.sys.core.domain.SysRole;
|
||||
import io.destiny.sys.core.domain.SysMenu;
|
||||
import io.destiny.sys.dto.request.SysMenuRequest;
|
||||
import io.destiny.sys.dto.request.SysRoleRequest;
|
||||
import io.destiny.sys.dto.request.SysUserLoginRequest;
|
||||
import io.destiny.sys.dto.request.SysUserRequest;
|
||||
import io.destiny.sys.dto.response.SysLoginResponse;
|
||||
import io.destiny.sys.dto.response.SysMenuResponse;
|
||||
import io.destiny.sys.dto.response.SysRoleResponse;
|
||||
import io.destiny.sys.dto.response.SysUserRegisterRequest;
|
||||
import io.destiny.sys.dto.response.SysUserResponse;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class SysTestDataBuilder {
|
||||
|
||||
public static SysUser createDefaultUser() {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setPassword("$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi");
|
||||
user.setEmail(io.destiny.common.primitive.EmailAddress.of("test@example.com"));
|
||||
user.setPhone(io.destiny.common.primitive.PhoneNumber.of("13800138000"));
|
||||
user.setStatus("0");
|
||||
user.setCreateBy("admin");
|
||||
user.setUpdateBy("admin");
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
user.setUpdatedAt(LocalDateTime.now());
|
||||
return user;
|
||||
}
|
||||
|
||||
public static SysUser createAdminUser() {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("admin");
|
||||
user.setPassword("$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi");
|
||||
user.setEmail(io.destiny.common.primitive.EmailAddress.of("admin@example.com"));
|
||||
user.setPhone(io.destiny.common.primitive.PhoneNumber.of("13800138001"));
|
||||
user.setStatus("0");
|
||||
user.setCreateBy("system");
|
||||
user.setUpdateBy("system");
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
user.setUpdatedAt(LocalDateTime.now());
|
||||
return user;
|
||||
}
|
||||
|
||||
public static SysUserResponse createDefaultUserResponse() {
|
||||
return SysUserResponse.builder()
|
||||
.id(1L)
|
||||
.username("testuser")
|
||||
.email("test@example.com")
|
||||
.phone("13800138000")
|
||||
.status("0")
|
||||
.createBy("admin")
|
||||
.updateBy("admin")
|
||||
.createdAt(LocalDateTime.now())
|
||||
.updatedAt(LocalDateTime.now())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SysUserRegisterRequest createDefaultUserRegisterRequest() {
|
||||
SysUserRegisterRequest request = new SysUserRegisterRequest();
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("password123");
|
||||
request.setEmail("test@example.com");
|
||||
request.setPhone("13800138000");
|
||||
return request;
|
||||
}
|
||||
|
||||
public static SysUserLoginRequest createDefaultUserLoginRequest() {
|
||||
SysUserLoginRequest request = new SysUserLoginRequest();
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("password123");
|
||||
request.setIpAddress("127.0.0.1");
|
||||
return request;
|
||||
}
|
||||
|
||||
public static SysUserRequest createDefaultUserRequest() {
|
||||
return SysUserRequest.builder()
|
||||
.id(1L)
|
||||
.username("testuser")
|
||||
.email("test@example.com")
|
||||
.phone("13800138000")
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SysUserRequest createUpdateUserRequest() {
|
||||
return SysUserRequest.builder()
|
||||
.id(1L)
|
||||
.username("updateduser")
|
||||
.email("updated@example.com")
|
||||
.phone("13900139000")
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SysRole createDefaultRole() {
|
||||
SysRole role = new SysRole();
|
||||
role.setId(1L);
|
||||
role.setRoleName("普通用户");
|
||||
role.setRoleKey("user");
|
||||
role.setRoleSort(1);
|
||||
role.setStatus("0");
|
||||
role.setCreateBy("admin");
|
||||
role.setUpdateBy("admin");
|
||||
role.setCreatedAt(LocalDateTime.now());
|
||||
role.setUpdatedAt(LocalDateTime.now());
|
||||
return role;
|
||||
}
|
||||
|
||||
public static SysRole createAdminRole() {
|
||||
SysRole role = new SysRole();
|
||||
role.setId(1L);
|
||||
role.setRoleName("管理员");
|
||||
role.setRoleKey("admin");
|
||||
role.setRoleSort(0);
|
||||
role.setStatus("0");
|
||||
role.setCreateBy("system");
|
||||
role.setUpdateBy("system");
|
||||
role.setCreatedAt(LocalDateTime.now());
|
||||
role.setUpdatedAt(LocalDateTime.now());
|
||||
return role;
|
||||
}
|
||||
|
||||
public static SysRoleResponse createDefaultRoleResponse() {
|
||||
return SysRoleResponse.builder()
|
||||
.id(1L)
|
||||
.name("普通用户")
|
||||
.roleKey("user")
|
||||
.sortOrder(1)
|
||||
.status("0")
|
||||
.createdAt(LocalDateTime.now())
|
||||
.updatedAt(LocalDateTime.now())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SysRoleRequest createDefaultRoleRequest() {
|
||||
return SysRoleRequest.builder()
|
||||
.name("普通用户")
|
||||
.roleKey("user")
|
||||
.sortOrder(1)
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SysRoleRequest createUpdateRoleRequest() {
|
||||
return SysRoleRequest.builder()
|
||||
.id(1L)
|
||||
.name("更新角色")
|
||||
.roleKey("updated_role")
|
||||
.sortOrder(2)
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SysMenu createDefaultMenu() {
|
||||
SysMenu menu = new SysMenu();
|
||||
menu.setId(1L);
|
||||
menu.setMenuName("系统管理");
|
||||
menu.setParentId(0L);
|
||||
menu.setOrderNum(1);
|
||||
menu.setMenuType("M");
|
||||
menu.setPerms("");
|
||||
menu.setComponent("");
|
||||
menu.setStatus("0");
|
||||
menu.setCreateBy("admin");
|
||||
menu.setUpdateBy("admin");
|
||||
menu.setCreatedAt(LocalDateTime.now());
|
||||
menu.setUpdatedAt(LocalDateTime.now());
|
||||
return menu;
|
||||
}
|
||||
|
||||
public static SysMenu createSubMenu() {
|
||||
SysMenu menu = new SysMenu();
|
||||
menu.setId(2L);
|
||||
menu.setMenuName("用户管理");
|
||||
menu.setParentId(1L);
|
||||
menu.setOrderNum(1);
|
||||
menu.setMenuType("C");
|
||||
menu.setPerms("system:user:list");
|
||||
menu.setComponent("system/user/index");
|
||||
menu.setStatus("0");
|
||||
menu.setCreateBy("admin");
|
||||
menu.setUpdateBy("admin");
|
||||
menu.setCreatedAt(LocalDateTime.now());
|
||||
menu.setUpdatedAt(LocalDateTime.now());
|
||||
return menu;
|
||||
}
|
||||
|
||||
public static SysMenuResponse createDefaultMenuResponse() {
|
||||
return SysMenuResponse.builder()
|
||||
.id(1L)
|
||||
.name("系统管理")
|
||||
.parentId(0L)
|
||||
.sortOrder(1)
|
||||
.status("0")
|
||||
.createdAt(LocalDateTime.now())
|
||||
.updatedAt(LocalDateTime.now())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SysMenuRequest createDefaultMenuRequest() {
|
||||
return SysMenuRequest.builder()
|
||||
.name("系统管理")
|
||||
.parentId(0L)
|
||||
.sortOrder(1)
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SysMenuRequest createUpdateMenuRequest() {
|
||||
return SysMenuRequest.builder()
|
||||
.id(1L)
|
||||
.name("更新菜单")
|
||||
.parentId(0L)
|
||||
.sortOrder(2)
|
||||
.status("0")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SysLoginResponse createLoginResponse(Long userId, String username, String token) {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(userId);
|
||||
user.setUsername(username);
|
||||
return SysLoginResponse.from(token, "refresh_token_" + token, user);
|
||||
}
|
||||
|
||||
public static List<SysUser> createMultipleUsers(int count) {
|
||||
return Arrays.asList(
|
||||
createDefaultUser(),
|
||||
createAdminUser()).subList(0, Math.min(count, 2));
|
||||
}
|
||||
|
||||
public static List<SysRole> createMultipleRoles(int count) {
|
||||
return Arrays.asList(
|
||||
createAdminRole(),
|
||||
createDefaultRole()).subList(0, Math.min(count, 2));
|
||||
}
|
||||
|
||||
public static List<SysMenu> createMultipleMenus(int count) {
|
||||
return Arrays.asList(
|
||||
createDefaultMenu(),
|
||||
createSubMenu()).subList(0, Math.min(count, 2));
|
||||
}
|
||||
|
||||
public static List<Long> createUserIds(int count) {
|
||||
return Arrays.asList(1L, 2L, 3L).subList(0, Math.min(count, 3));
|
||||
}
|
||||
|
||||
public static List<Long> createRoleIds(int count) {
|
||||
return Arrays.asList(1L, 2L, 3L).subList(0, Math.min(count, 3));
|
||||
}
|
||||
|
||||
public static List<Long> createMenuIds(int count) {
|
||||
return Arrays.asList(1L, 2L, 3L).subList(0, Math.min(count, 3));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user