chore: 清理旧迁移脚本并添加本地开发配置
- 删除旧的V10和V11迁移脚本(已被V12和V13替代) - 更新BaseDomain和自动配置文件 - 删除旧的测试文件 - 添加本地开发配置文件 - 添加简化版应用启动类
This commit was merged in pull request #3.
This commit is contained in:
+42
@@ -0,0 +1,42 @@
|
||||
package cn.novalon.manage.app;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 最小化应用程序启动类
|
||||
* 避免复杂的自动配置问题,专注于核心功能
|
||||
*/
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = {
|
||||
"cn.novalon.manage.app.config",
|
||||
"cn.novalon.manage.app.controller",
|
||||
"cn.novalon.manage.app.service"
|
||||
}
|
||||
)
|
||||
public class MinimalApplication {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MinimalApplication.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
logger.info("最小化应用程序启动中...");
|
||||
|
||||
// 设置系统属性,避免自动配置问题
|
||||
System.setProperty("spring.autoconfigure.exclude",
|
||||
"org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration");
|
||||
|
||||
// 禁用复杂的自动配置
|
||||
System.setProperty("spring.main.lazy-initialization", "true");
|
||||
System.setProperty("spring.main.banner-mode", "off");
|
||||
|
||||
try {
|
||||
SpringApplication.run(MinimalApplication.class, args);
|
||||
logger.info("最小化应用程序启动完成");
|
||||
} catch (Exception e) {
|
||||
logger.error("应用程序启动失败: {}", e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package cn.novalon.manage.app;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;
|
||||
|
||||
/**
|
||||
* 简化的应用程序启动类
|
||||
* 避免复杂的自动配置问题
|
||||
*/
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = "cn.novalon.manage.app",
|
||||
exclude = {ReactiveUserDetailsServiceAutoConfiguration.class}
|
||||
)
|
||||
public class SimpleManageApplication {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SimpleManageApplication.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
logger.info("简化版应用程序启动中...");
|
||||
logger.info("包扫描路径: cn.novalon.manage.app");
|
||||
|
||||
// 设置系统属性,避免自动配置问题
|
||||
System.setProperty("spring.autoconfigure.exclude",
|
||||
"org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration");
|
||||
|
||||
SpringApplication.run(SimpleManageApplication.class, args);
|
||||
logger.info("简化版应用程序启动完成");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# 本地开发环境配置
|
||||
spring:
|
||||
config:
|
||||
activate:
|
||||
on-profile: local
|
||||
r2dbc:
|
||||
url: r2dbc:postgresql://localhost:55432/manage_system
|
||||
username: novalon
|
||||
password: novalon123
|
||||
pool:
|
||||
initial-size: 5
|
||||
max-size: 20
|
||||
max-idle-time: 10m
|
||||
max-life-time: 30m
|
||||
acquire-timeout: 3s
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost:55432/manage_system
|
||||
username: novalon
|
||||
password: novalon123
|
||||
driver-class-name: org.postgresql.Driver
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: classpath:db/migration
|
||||
baseline-on-migrate: true
|
||||
baseline-version: 0
|
||||
validate-on-migrate: true
|
||||
sql:
|
||||
init:
|
||||
mode: always
|
||||
|
||||
logging:
|
||||
level:
|
||||
cn.novalon.manage: DEBUG
|
||||
org.springframework.r2dbc: DEBUG
|
||||
cn.novalon.manage.db: DEBUG
|
||||
org.flywaydb: DEBUG
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package cn.novalon.manage.common.handler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 默认异常日志服务实现
|
||||
* 临时实现,用于解决启动时的依赖注入问题
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-15
|
||||
*/
|
||||
@Service
|
||||
public class DefaultExceptionLogService implements IExceptionLogService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionLogService.class);
|
||||
|
||||
@Override
|
||||
public Mono<Void> logException(String title, String exceptionName, String exceptionMsg,
|
||||
String methodName, String ip, String stackTrace) {
|
||||
logger.warn("异常日志记录 (临时实现): title={}, exceptionName={}, methodName={}, ip={}",
|
||||
title, exceptionName, methodName, ip);
|
||||
logger.warn("异常信息: {}", exceptionMsg);
|
||||
if (stackTrace != null && stackTrace.length() > 500) {
|
||||
logger.warn("堆栈跟踪 (截断): {}", stackTrace.substring(0, 500) + "...");
|
||||
} else if (stackTrace != null) {
|
||||
logger.warn("堆栈跟踪: {}", stackTrace);
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
-- Novalon管理系统普通用户角色和数据
|
||||
-- 版本: V10
|
||||
-- 描述: 创建普通用户角色并分配权限
|
||||
|
||||
-- 插入普通用户角色
|
||||
INSERT INTO sys_role (role_name, role_key, role_sort, status, create_by, update_by)
|
||||
VALUES ('普通用户', 'user', 2, 1, 'system', 'system')
|
||||
ON CONFLICT (role_key) DO UPDATE SET
|
||||
role_name = EXCLUDED.role_name,
|
||||
role_sort = EXCLUDED.role_sort,
|
||||
status = EXCLUDED.status;
|
||||
|
||||
-- 为普通用户分配基本权限(查看个人信息、修改密码等)
|
||||
-- 注意:这里只分配基本权限,不包含管理功能权限
|
||||
INSERT INTO sys_permission (permission_name, permission_key, permission_type, parent_id, path, component, icon, sort, status, create_by, update_by)
|
||||
VALUES
|
||||
('个人中心', 'profile', 'MENU', 0, '/profile', 'views/profile/index', 'user', 1, 1, 'system', 'system'),
|
||||
('个人信息', 'profile:info', 'BUTTON', (SELECT id FROM sys_permission WHERE permission_key = 'profile'), '', '', '', 1, 1, 'system', 'system'),
|
||||
('修改密码', 'profile:password', 'BUTTON', (SELECT id FROM sys_permission WHERE permission_key = 'profile'), '', '', '', 2, 1, 'system', 'system')
|
||||
ON CONFLICT (permission_key) DO NOTHING;
|
||||
|
||||
-- 为普通用户角色分配权限
|
||||
INSERT INTO sys_role_permission (role_id, permission_id, create_by, update_by)
|
||||
SELECT
|
||||
r.id as role_id,
|
||||
p.id as permission_id,
|
||||
'system' as create_by,
|
||||
'system' as update_by
|
||||
FROM sys_role r
|
||||
CROSS JOIN sys_permission p
|
||||
WHERE r.role_key = 'user'
|
||||
AND p.permission_key IN ('profile', 'profile:info', 'profile:password')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 将测试用户分配给普通用户角色
|
||||
INSERT INTO user_role (user_id, role_id, create_by, update_by)
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
r.id as role_id,
|
||||
'system' as create_by,
|
||||
'system' as update_by
|
||||
FROM sys_user u
|
||||
CROSS JOIN sys_role r
|
||||
WHERE u.username = 'user' AND r.role_key = 'user'
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 重置序列值
|
||||
SELECT setval('sys_role_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_role));
|
||||
SELECT setval('sys_permission_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_permission));
|
||||
SELECT setval('sys_role_permission_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_role_permission));
|
||||
SELECT setval('user_role_id_seq', (SELECT COALESCE(MAX(id), 1) FROM user_role));
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
-- Novalon管理系统测试数据脚本
|
||||
-- 版本: V11
|
||||
-- 描述: 更新测试用户密码为Test@123,插入E2E测试所需数据
|
||||
|
||||
-- 更新admin用户密码为Test@123
|
||||
-- BCrypt哈希值对应明文密码: Test@123
|
||||
UPDATE sys_user
|
||||
SET password = '$2a$12$nZ1EMUpZQljbnEdIKzH72eHlDJKUmHmHppnTTVth/SlHs5VpSAr8C'
|
||||
WHERE username = 'admin';
|
||||
|
||||
-- 更新user用户密码为Test@123
|
||||
UPDATE sys_user
|
||||
SET password = '$2a$12$nZ1EMUpZQljbnEdIKzH72eHlDJKUmHmHppnTTVth/SlHs5VpSAr8C'
|
||||
WHERE username = 'user';
|
||||
|
||||
-- 插入测试角色(如果不存在)
|
||||
INSERT INTO sys_role (role_name, role_key, role_sort, status, create_by, update_by)
|
||||
VALUES
|
||||
('测试管理员', 'test_admin', 2, 1, 'system', 'system'),
|
||||
('普通用户', 'normal_user', 3, 1, 'system', 'system'),
|
||||
('访客', 'guest', 4, 1, 'system', 'system')
|
||||
ON CONFLICT (role_key) DO NOTHING;
|
||||
|
||||
-- 为admin用户分配超级管理员角色
|
||||
INSERT INTO user_role (user_id, role_id, created_by)
|
||||
SELECT 1, id, 'system' FROM sys_role WHERE role_key = 'admin'
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 为user用户分配普通用户角色
|
||||
INSERT INTO user_role (user_id, role_id, created_by)
|
||||
SELECT 2, id, 'system' FROM sys_role WHERE role_key = 'normal_user'
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 插入E2E测试专用用户
|
||||
-- BCrypt哈希值对应明文密码: Test@123
|
||||
INSERT INTO sys_user (id, username, password, email, phone, nickname, status, create_by, update_by)
|
||||
VALUES
|
||||
(10, 'e2e_test_user', '$2a$12$nZ1EMUpZQljbnEdIKzH72eHlDJKUmHmHppnTTVth/SlHs5VpSAr8C', 'e2e@test.com', '13900139000', 'E2E测试用户', 1, 'system', 'system')
|
||||
ON CONFLICT (username) DO UPDATE SET
|
||||
password = EXCLUDED.password,
|
||||
status = EXCLUDED.status;
|
||||
|
||||
-- 为E2E测试用户分配超级管理员角色
|
||||
INSERT INTO user_role (user_id, role_id, created_by)
|
||||
SELECT 10, id, 'system' FROM sys_role WHERE role_key = 'admin'
|
||||
ON CONFLICT DO NOTHING;
|
||||
@@ -0,0 +1,38 @@
|
||||
# 本地开发环境配置
|
||||
spring:
|
||||
config:
|
||||
activate:
|
||||
on-profile: local
|
||||
cloud:
|
||||
gateway:
|
||||
routes:
|
||||
- id: manage-app
|
||||
uri: http://localhost:8084
|
||||
predicates:
|
||||
- Path=/api/**
|
||||
default-filters:
|
||||
- name: JwtAuthentication
|
||||
- name: RbacAuthorization
|
||||
- name: Retry
|
||||
args:
|
||||
retries: 3
|
||||
statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE
|
||||
methods: GET,POST
|
||||
backoff:
|
||||
firstBackoff: 10ms
|
||||
maxBackoff: 50ms
|
||||
factor: 2
|
||||
basedOnPreviousValue: false
|
||||
- name: DedupeResponseHeader
|
||||
args:
|
||||
name: Content-Encoding
|
||||
strategy: RETAIN_FIRST
|
||||
|
||||
jwt:
|
||||
secret: U2FsdGVkX1+vZ5Y9QmKxL8nN3rP7tW2jH4fG6dA8sB1cE5yN0zX3qV7wM4
|
||||
expiration: 86400000
|
||||
|
||||
logging:
|
||||
level:
|
||||
cn.novalon.manage.gateway: DEBUG
|
||||
org.springframework.cloud.gateway: DEBUG
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package cn.novalon.manage.sys.config;
|
||||
|
||||
import cn.novalon.manage.sys.core.service.impl.SysExceptionLogService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
|
||||
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
||||
|
||||
/**
|
||||
* 异常日志配置类
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-15
|
||||
*/
|
||||
@Configuration
|
||||
public class ExceptionLogConfig {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExceptionLogConfig.class);
|
||||
|
||||
/**
|
||||
* 配置异常日志的路由
|
||||
*/
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> exceptionLogRoutes(SysExceptionLogService exceptionLogService) {
|
||||
logger.info("配置异常日志路由");
|
||||
|
||||
return route()
|
||||
.GET("/api/exception-logs", request ->
|
||||
ServerResponse.ok().body(exceptionLogService.findAll(), cn.novalon.manage.sys.core.domain.SysExceptionLog.class))
|
||||
.GET("/api/exception-logs/{id}", request -> {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return exceptionLogService.findById(id)
|
||||
.flatMap(log -> ServerResponse.ok().bodyValue(log))
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
})
|
||||
.GET("/api/exception-logs/username/{username}", request -> {
|
||||
String username = request.pathVariable("username");
|
||||
return ServerResponse.ok().body(exceptionLogService.findByUsername(username),
|
||||
cn.novalon.manage.sys.core.domain.SysExceptionLog.class);
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+13
@@ -75,4 +75,17 @@ public abstract class BaseDomain {
|
||||
this.id = SnowflakeId.nextId();
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
BaseDomain that = (BaseDomain) o;
|
||||
return id != null && id.equals(that.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id != null ? id.hashCode() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,2 +1,2 @@
|
||||
cn.novalon.manage.sys.config.SecurityConfig
|
||||
cn.novalon.manage.sys.config.ExceptionLogConfig
|
||||
cn.novalon.manage.sys.config.ExceptionLogConfig
|
||||
cn.novalon.manage.sys.config.SystemRouter
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
package cn.novalon.manage.sys.audit.controller;
|
||||
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLog;
|
||||
import cn.novalon.manage.sys.audit.dto.AuditLogQueryRequest;
|
||||
import cn.novalon.manage.sys.audit.dto.AuditLogStatistics;
|
||||
import cn.novalon.manage.sys.audit.service.IAuditLogService;
|
||||
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.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.*;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* AuditLogController 单元测试
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AuditLogControllerTest {
|
||||
|
||||
@Mock
|
||||
private IAuditLogService auditLogService;
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
private AuditLogController auditLogController;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
auditLogController = new AuditLogController(auditLogService);
|
||||
webTestClient = WebTestClient.bindToController(auditLogController).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("根据ID查询审计日志 - 成功")
|
||||
void findById_whenExists_shouldReturnAuditLog() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
when(auditLogService.findById(1L)).thenReturn(Mono.just(auditLog));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/api/audit-logs/1")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(AuditLog.class)
|
||||
.isEqualTo(auditLog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("根据ID查询审计日志 - 不存在")
|
||||
void findById_whenNotExists_shouldReturnNotFound() {
|
||||
when(auditLogService.findById(999L)).thenReturn(Mono.empty());
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/api/audit-logs/999")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按实体类型查询审计日志")
|
||||
void findByEntityType_shouldReturnAuditLogs() {
|
||||
AuditLog auditLog1 = createTestAuditLog(1L);
|
||||
AuditLog auditLog2 = createTestAuditLog(2L);
|
||||
when(auditLogService.findByEntityType("User")).thenReturn(Flux.just(auditLog1, auditLog2));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/api/audit-logs/entity-type/User")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBodyList(AuditLog.class)
|
||||
.hasSize(2)
|
||||
.contains(auditLog1, auditLog2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按实体ID查询审计日志")
|
||||
void findByEntityId_shouldReturnAuditLogs() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
when(auditLogService.findByEntityId(100L)).thenReturn(Flux.just(auditLog));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/api/audit-logs/entity/100")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBodyList(AuditLog.class)
|
||||
.hasSize(1)
|
||||
.contains(auditLog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按操作人查询审计日志")
|
||||
void findByOperator_shouldReturnAuditLogs() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
when(auditLogService.findByOperator("admin")).thenReturn(Flux.just(auditLog));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/api/audit-logs/operator/admin")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBodyList(AuditLog.class)
|
||||
.hasSize(1)
|
||||
.contains(auditLog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按操作类型查询审计日志")
|
||||
void findByOperationType_shouldReturnAuditLogs() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
when(auditLogService.findByOperationType("CREATE")).thenReturn(Flux.just(auditLog));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/api/audit-logs/operation-type/CREATE")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBodyList(AuditLog.class)
|
||||
.hasSize(1)
|
||||
.contains(auditLog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按时间范围查询审计日志")
|
||||
void findByTimeRange_shouldReturnAuditLogs() {
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
|
||||
when(auditLogService.findByOperationTimeBetween(startTime, endTime))
|
||||
.thenReturn(Flux.just(auditLog));
|
||||
|
||||
webTestClient.get()
|
||||
.uri(uriBuilder -> uriBuilder
|
||||
.path("/api/audit-logs/time-range")
|
||||
.queryParam("startTime", startTime)
|
||||
.queryParam("endTime", endTime)
|
||||
.build())
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBodyList(AuditLog.class)
|
||||
.hasSize(1)
|
||||
.contains(auditLog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取审计日志统计信息")
|
||||
void getStatistics_shouldReturnStatistics() {
|
||||
webTestClient.get()
|
||||
.uri("/api/audit-logs/statistics")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(AuditLogStatistics.class)
|
||||
.value(returnedStatistics -> {
|
||||
assertNotNull(returnedStatistics);
|
||||
assertNull(returnedStatistics.getTotalCount());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按实体类型统计数量")
|
||||
void countByEntityType_shouldReturnCount() {
|
||||
when(auditLogService.countByEntityType("User")).thenReturn(Mono.just(10L));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/api/audit-logs/count/entity-type/User")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(Long.class)
|
||||
.isEqualTo(10L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按操作人统计数量")
|
||||
void countByOperator_shouldReturnCount() {
|
||||
when(auditLogService.countByOperator("admin")).thenReturn(Mono.just(5L));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/api/audit-logs/count/operator/admin")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(Long.class)
|
||||
.isEqualTo(5L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按操作类型统计数量")
|
||||
void countByOperationType_shouldReturnCount() {
|
||||
when(auditLogService.countByOperationType("CREATE")).thenReturn(Mono.just(3L));
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/api/audit-logs/count/operation-type/CREATE")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(Long.class)
|
||||
.isEqualTo(3L);
|
||||
}
|
||||
|
||||
private AuditLog createTestAuditLog(Long id) {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setId(id);
|
||||
auditLog.setEntityType("User");
|
||||
auditLog.setEntityId(100L);
|
||||
auditLog.setOperator("admin");
|
||||
auditLog.setOperationType("CREATE");
|
||||
auditLog.setOperationTime(LocalDateTime.now());
|
||||
auditLog.setDescription("创建用户");
|
||||
auditLog.setIpAddress("192.168.1.1");
|
||||
auditLog.setUserAgent("Mozilla/5.0");
|
||||
return auditLog;
|
||||
}
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
package cn.novalon.manage.sys.audit.domain;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* AuditLog 单元测试
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
class AuditLogTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("创建默认审计日志")
|
||||
void createDefaultAuditLog_shouldHaveNullFields() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
|
||||
assertNull(auditLog.getId());
|
||||
assertNull(auditLog.getEntityType());
|
||||
assertNull(auditLog.getEntityId());
|
||||
assertNull(auditLog.getOperator());
|
||||
assertNull(auditLog.getOperationType());
|
||||
assertNull(auditLog.getOperationTime());
|
||||
assertNull(auditLog.getDescription());
|
||||
assertNull(auditLog.getIpAddress());
|
||||
assertNull(auditLog.getUserAgent());
|
||||
assertNull(auditLog.getDeletedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取ID")
|
||||
void setAndGetId_shouldWorkCorrectly() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setId(1L);
|
||||
|
||||
assertEquals(1L, auditLog.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取实体类型")
|
||||
void setAndGetEntityType_shouldWorkCorrectly() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setEntityType("User");
|
||||
|
||||
assertEquals("User", auditLog.getEntityType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取实体ID")
|
||||
void setAndGetEntityId_shouldWorkCorrectly() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setEntityId(100L);
|
||||
|
||||
assertEquals(100L, auditLog.getEntityId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取操作人")
|
||||
void setAndGetOperator_shouldWorkCorrectly() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setOperator("admin");
|
||||
|
||||
assertEquals("admin", auditLog.getOperator());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取操作类型")
|
||||
void setAndGetOperationType_shouldWorkCorrectly() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setOperationType("CREATE");
|
||||
|
||||
assertEquals("CREATE", auditLog.getOperationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取操作时间")
|
||||
void setAndGetOperationTime_shouldWorkCorrectly() {
|
||||
LocalDateTime operationTime = LocalDateTime.now();
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setOperationTime(operationTime);
|
||||
|
||||
assertEquals(operationTime, auditLog.getOperationTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取描述")
|
||||
void setAndGetDescription_shouldWorkCorrectly() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setDescription("创建用户");
|
||||
|
||||
assertEquals("创建用户", auditLog.getDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取IP地址")
|
||||
void setAndGetIpAddress_shouldWorkCorrectly() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setIpAddress("192.168.1.1");
|
||||
|
||||
assertEquals("192.168.1.1", auditLog.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取用户代理")
|
||||
void setAndGetUserAgent_shouldWorkCorrectly() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setUserAgent("Mozilla/5.0");
|
||||
|
||||
assertEquals("Mozilla/5.0", auditLog.getUserAgent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取删除时间")
|
||||
void setAndGetDeletedAt_shouldWorkCorrectly() {
|
||||
LocalDateTime deletedAt = LocalDateTime.now();
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setDeletedAt(deletedAt);
|
||||
|
||||
assertEquals(deletedAt, auditLog.getDeletedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("toString方法应包含所有字段")
|
||||
void toString_shouldContainAllFields() {
|
||||
LocalDateTime operationTime = LocalDateTime.now();
|
||||
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setId(1L);
|
||||
auditLog.setEntityType("User");
|
||||
auditLog.setEntityId(100L);
|
||||
auditLog.setOperator("admin");
|
||||
auditLog.setOperationType("CREATE");
|
||||
auditLog.setOperationTime(operationTime);
|
||||
auditLog.setDescription("创建用户");
|
||||
auditLog.setIpAddress("192.168.1.1");
|
||||
auditLog.setUserAgent("Mozilla/5.0");
|
||||
|
||||
String toString = auditLog.toString();
|
||||
|
||||
assertTrue(toString.contains("1"));
|
||||
assertTrue(toString.contains("User"));
|
||||
assertTrue(toString.contains("100"));
|
||||
assertTrue(toString.contains("admin"));
|
||||
assertTrue(toString.contains("CREATE"));
|
||||
assertTrue(toString.contains("创建用户"));
|
||||
assertTrue(toString.contains("192.168.1.1"));
|
||||
assertTrue(toString.contains("Mozilla/5.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("equals和hashCode方法应基于字段值")
|
||||
void equalsAndHashCode_shouldBeBasedOnFieldValues() {
|
||||
LocalDateTime operationTime = LocalDateTime.now();
|
||||
|
||||
AuditLog auditLog1 = new AuditLog();
|
||||
auditLog1.setId(1L);
|
||||
auditLog1.setEntityType("User");
|
||||
auditLog1.setEntityId(100L);
|
||||
auditLog1.setOperator("admin");
|
||||
auditLog1.setOperationType("CREATE");
|
||||
auditLog1.setOperationTime(operationTime);
|
||||
auditLog1.setDescription("创建用户");
|
||||
auditLog1.setIpAddress("192.168.1.1");
|
||||
auditLog1.setUserAgent("Mozilla/5.0");
|
||||
|
||||
AuditLog auditLog2 = new AuditLog();
|
||||
auditLog2.setId(1L);
|
||||
auditLog2.setEntityType("User");
|
||||
auditLog2.setEntityId(100L);
|
||||
auditLog2.setOperator("admin");
|
||||
auditLog2.setOperationType("CREATE");
|
||||
auditLog2.setOperationTime(operationTime);
|
||||
auditLog2.setDescription("创建用户");
|
||||
auditLog2.setIpAddress("192.168.1.1");
|
||||
auditLog2.setUserAgent("Mozilla/5.0");
|
||||
|
||||
assertEquals(auditLog1, auditLog2);
|
||||
assertEquals(auditLog1.hashCode(), auditLog2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("不同ID的对象应不相等")
|
||||
void differentIds_shouldNotBeEqual() {
|
||||
AuditLog auditLog1 = new AuditLog();
|
||||
auditLog1.setId(1L);
|
||||
|
||||
AuditLog auditLog2 = new AuditLog();
|
||||
auditLog2.setId(2L);
|
||||
|
||||
assertNotEquals(auditLog1, auditLog2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("null对象应不相等")
|
||||
void nullObject_shouldNotBeEqual() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setId(1L);
|
||||
|
||||
assertNotEquals(auditLog, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("不同类型对象应不相等")
|
||||
void differentTypeObject_shouldNotBeEqual() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setId(1L);
|
||||
|
||||
assertNotEquals(auditLog, "not an audit log");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("相同对象引用应相等")
|
||||
void sameObjectReference_shouldBeEqual() {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setId(1L);
|
||||
|
||||
assertEquals(auditLog, auditLog);
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package cn.novalon.manage.sys.audit.dto;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* AuditLogQueryRequest 单元测试
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
class AuditLogQueryRequestTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("创建默认查询请求")
|
||||
void createDefaultRequest_shouldHaveNullFields() {
|
||||
AuditLogQueryRequest request = new AuditLogQueryRequest();
|
||||
|
||||
assertNull(request.getEntityType());
|
||||
assertNull(request.getEntityId());
|
||||
assertNull(request.getOperator());
|
||||
assertNull(request.getOperationType());
|
||||
assertNull(request.getStartTime());
|
||||
assertNull(request.getEndTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取实体类型")
|
||||
void setAndGetEntityType_shouldWorkCorrectly() {
|
||||
AuditLogQueryRequest request = new AuditLogQueryRequest();
|
||||
request.setEntityType("User");
|
||||
|
||||
assertEquals("User", request.getEntityType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取实体ID")
|
||||
void setAndGetEntityId_shouldWorkCorrectly() {
|
||||
AuditLogQueryRequest request = new AuditLogQueryRequest();
|
||||
request.setEntityId(100L);
|
||||
|
||||
assertEquals(100L, request.getEntityId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取操作人")
|
||||
void setAndGetOperator_shouldWorkCorrectly() {
|
||||
AuditLogQueryRequest request = new AuditLogQueryRequest();
|
||||
request.setOperator("admin");
|
||||
|
||||
assertEquals("admin", request.getOperator());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取操作类型")
|
||||
void setAndGetOperationType_shouldWorkCorrectly() {
|
||||
AuditLogQueryRequest request = new AuditLogQueryRequest();
|
||||
request.setOperationType("CREATE");
|
||||
|
||||
assertEquals("CREATE", request.getOperationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取开始时间")
|
||||
void setAndGetStartTime_shouldWorkCorrectly() {
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(1);
|
||||
AuditLogQueryRequest request = new AuditLogQueryRequest();
|
||||
request.setStartTime(startTime);
|
||||
|
||||
assertEquals(startTime, request.getStartTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("设置和获取结束时间")
|
||||
void setAndGetEndTime_shouldWorkCorrectly() {
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
AuditLogQueryRequest request = new AuditLogQueryRequest();
|
||||
request.setEndTime(endTime);
|
||||
|
||||
assertEquals(endTime, request.getEndTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("toString方法应包含所有字段")
|
||||
void toString_shouldContainAllFields() {
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
|
||||
AuditLogQueryRequest request = new AuditLogQueryRequest();
|
||||
request.setEntityType("User");
|
||||
request.setEntityId(100L);
|
||||
request.setOperator("admin");
|
||||
request.setOperationType("CREATE");
|
||||
request.setStartTime(startTime);
|
||||
request.setEndTime(endTime);
|
||||
|
||||
String toString = request.toString();
|
||||
|
||||
assertTrue(toString.contains("User"));
|
||||
assertTrue(toString.contains("100"));
|
||||
assertTrue(toString.contains("admin"));
|
||||
assertTrue(toString.contains("CREATE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("equals和hashCode方法应基于字段值")
|
||||
void equalsAndHashCode_shouldBeBasedOnFieldValues() {
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
|
||||
AuditLogQueryRequest request1 = new AuditLogQueryRequest();
|
||||
request1.setEntityType("User");
|
||||
request1.setEntityId(100L);
|
||||
request1.setOperator("admin");
|
||||
request1.setOperationType("CREATE");
|
||||
request1.setStartTime(startTime);
|
||||
request1.setEndTime(endTime);
|
||||
|
||||
AuditLogQueryRequest request2 = new AuditLogQueryRequest();
|
||||
request2.setEntityType("User");
|
||||
request2.setEntityId(100L);
|
||||
request2.setOperator("admin");
|
||||
request2.setOperationType("CREATE");
|
||||
request2.setStartTime(startTime);
|
||||
request2.setEndTime(endTime);
|
||||
|
||||
assertEquals(request1, request2);
|
||||
assertEquals(request1.hashCode(), request2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("不同字段值的对象应不相等")
|
||||
void differentFieldValues_shouldNotBeEqual() {
|
||||
AuditLogQueryRequest request1 = new AuditLogQueryRequest();
|
||||
request1.setEntityType("User");
|
||||
|
||||
AuditLogQueryRequest request2 = new AuditLogQueryRequest();
|
||||
request2.setEntityType("Role");
|
||||
|
||||
assertNotEquals(request1, request2);
|
||||
}
|
||||
}
|
||||
+350
@@ -0,0 +1,350 @@
|
||||
package cn.novalon.manage.sys.audit.service.impl;
|
||||
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLog;
|
||||
import cn.novalon.manage.sys.audit.repository.IAuditLogRepository;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
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 reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* AuditLogService 单元测试
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AuditLogServiceTest {
|
||||
|
||||
@Mock
|
||||
private IAuditLogRepository auditLogRepository;
|
||||
|
||||
@Mock
|
||||
private Executor auditLogExecutor;
|
||||
|
||||
private AuditLogService auditLogService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
auditLogService = new AuditLogService(auditLogRepository, auditLogExecutor);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("根据ID查询审计日志 - 成功")
|
||||
void findById_whenExists_shouldReturnAuditLog() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
when(auditLogRepository.findById(1L)).thenReturn(Mono.just(auditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.findById(1L))
|
||||
.expectNext(auditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("根据ID查询审计日志 - 不存在")
|
||||
void findById_whenNotExists_shouldReturnEmpty() {
|
||||
when(auditLogRepository.findById(999L)).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(auditLogService.findById(999L))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("查询所有审计日志")
|
||||
void findAll_shouldReturnAllAuditLogs() {
|
||||
AuditLog auditLog1 = createTestAuditLog(1L);
|
||||
AuditLog auditLog2 = createTestAuditLog(2L);
|
||||
when(auditLogRepository.findAll()).thenReturn(Flux.just(auditLog1, auditLog2));
|
||||
|
||||
StepVerifier.create(auditLogService.findAll())
|
||||
.expectNext(auditLog1)
|
||||
.expectNext(auditLog2)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("分页查询审计日志")
|
||||
void findAuditLogsByPage_shouldReturnPageResponse() {
|
||||
AuditLog auditLog1 = createTestAuditLog(1L);
|
||||
AuditLog auditLog2 = createTestAuditLog(2L);
|
||||
AuditLog auditLog3 = createTestAuditLog(3L);
|
||||
|
||||
when(auditLogRepository.findAll()).thenReturn(Flux.just(auditLog1, auditLog2, auditLog3));
|
||||
|
||||
PageRequest pageRequest = new PageRequest();
|
||||
pageRequest.setPage(0);
|
||||
pageRequest.setSize(2);
|
||||
|
||||
StepVerifier.create(auditLogService.findAuditLogsByPage(pageRequest))
|
||||
.expectNextMatches(pageResponse ->
|
||||
pageResponse.getContent().size() == 2 &&
|
||||
pageResponse.getTotalPages() == 2 &&
|
||||
pageResponse.getTotalElements() == 3)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("统计审计日志总数")
|
||||
void count_shouldReturnTotalCount() {
|
||||
when(auditLogRepository.findAll()).thenReturn(Flux.just(
|
||||
createTestAuditLog(1L),
|
||||
createTestAuditLog(2L),
|
||||
createTestAuditLog(3L)
|
||||
));
|
||||
|
||||
StepVerifier.create(auditLogService.count())
|
||||
.expectNext(3L)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按实体类型查询审计日志")
|
||||
void findByEntityType_shouldReturnAuditLogs() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
when(auditLogRepository.findByEntityType("User")).thenReturn(Flux.just(auditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.findByEntityType("User"))
|
||||
.expectNext(auditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按实体ID查询审计日志")
|
||||
void findByEntityId_shouldReturnAuditLogs() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
when(auditLogRepository.findByEntityId(100L)).thenReturn(Flux.just(auditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.findByEntityId(100L))
|
||||
.expectNext(auditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按实体类型和实体ID查询审计日志")
|
||||
void findByEntityTypeAndEntityId_shouldReturnAuditLogs() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
when(auditLogRepository.findByEntityTypeAndEntityId("User", 100L))
|
||||
.thenReturn(Flux.just(auditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.findByEntityTypeAndEntityId("User", 100L))
|
||||
.expectNext(auditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按操作人查询审计日志")
|
||||
void findByOperator_shouldReturnAuditLogs() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
when(auditLogRepository.findByOperator("admin")).thenReturn(Flux.just(auditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.findByOperator("admin"))
|
||||
.expectNext(auditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按操作类型查询审计日志")
|
||||
void findByOperationType_shouldReturnAuditLogs() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
when(auditLogRepository.findByOperationType("CREATE")).thenReturn(Flux.just(auditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.findByOperationType("CREATE"))
|
||||
.expectNext(auditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按时间范围查询审计日志")
|
||||
void findByOperationTimeBetween_shouldReturnAuditLogs() {
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
|
||||
when(auditLogRepository.findByOperationTimeBetween(startTime, endTime))
|
||||
.thenReturn(Flux.just(auditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.findByOperationTimeBetween(startTime, endTime))
|
||||
.expectNext(auditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按实体类型和时间范围查询审计日志")
|
||||
void findByEntityTypeAndOperationTimeBetween_shouldReturnAuditLogs() {
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
|
||||
when(auditLogRepository.findByEntityTypeAndOperationTimeBetween("User", startTime, endTime))
|
||||
.thenReturn(Flux.just(auditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.findByEntityTypeAndOperationTimeBetween("User", startTime, endTime))
|
||||
.expectNext(auditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按操作人和时间范围查询审计日志")
|
||||
void findByOperatorAndOperationTimeBetween_shouldReturnAuditLogs() {
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
|
||||
when(auditLogRepository.findByOperatorAndOperationTimeBetween("admin", startTime, endTime))
|
||||
.thenReturn(Flux.just(auditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.findByOperatorAndOperationTimeBetween("admin", startTime, endTime))
|
||||
.expectNext(auditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按实体类型统计数量")
|
||||
void countByEntityType_shouldReturnCount() {
|
||||
when(auditLogRepository.countByEntityType("User")).thenReturn(Mono.just(5L));
|
||||
|
||||
StepVerifier.create(auditLogService.countByEntityType("User"))
|
||||
.expectNext(5L)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按操作类型统计数量")
|
||||
void countByOperationType_shouldReturnCount() {
|
||||
when(auditLogRepository.countByOperationType("CREATE")).thenReturn(Mono.just(3L));
|
||||
|
||||
StepVerifier.create(auditLogService.countByOperationType("CREATE"))
|
||||
.expectNext(3L)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按操作人统计数量")
|
||||
void countByOperator_shouldReturnCount() {
|
||||
when(auditLogRepository.countByOperator("admin")).thenReturn(Mono.just(2L));
|
||||
|
||||
StepVerifier.create(auditLogService.countByOperator("admin"))
|
||||
.expectNext(2L)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("按时间范围统计数量")
|
||||
void countByOperationTimeBetween_shouldReturnCount() {
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
|
||||
when(auditLogRepository.countByOperationTimeBetween(startTime, endTime))
|
||||
.thenReturn(Mono.just(10L));
|
||||
|
||||
StepVerifier.create(auditLogService.countByOperationTimeBetween(startTime, endTime))
|
||||
.expectNext(10L)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("保存审计日志")
|
||||
void save_shouldReturnSavedAuditLog() {
|
||||
AuditLog auditLog = createTestAuditLog(null);
|
||||
AuditLog savedAuditLog = createTestAuditLog(1L);
|
||||
|
||||
when(auditLogRepository.save(auditLog)).thenReturn(Mono.just(savedAuditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.save(auditLog))
|
||||
.expectNext(savedAuditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("异步保存审计日志")
|
||||
void saveAsync_shouldReturnSavedAuditLog() {
|
||||
AuditLog auditLog = createTestAuditLog(null);
|
||||
AuditLog savedAuditLog = createTestAuditLog(1L);
|
||||
|
||||
when(auditLogRepository.save(auditLog)).thenReturn(Mono.just(savedAuditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.saveAsync(auditLog))
|
||||
.expectNext(savedAuditLog)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("根据ID删除审计日志")
|
||||
void deleteById_shouldDeleteAuditLog() {
|
||||
when(auditLogRepository.deleteById(1L)).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(auditLogService.deleteById(1L))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("逻辑删除审计日志")
|
||||
void logicalDeleteById_shouldSetDeletedAt() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
AuditLog deletedAuditLog = createTestAuditLog(1L);
|
||||
deletedAuditLog.setDeletedAt(LocalDateTime.now());
|
||||
|
||||
when(auditLogRepository.findById(1L)).thenReturn(Mono.just(auditLog));
|
||||
when(auditLogRepository.save(auditLog)).thenReturn(Mono.just(deletedAuditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.logicalDeleteById(1L))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("批量逻辑删除审计日志")
|
||||
void logicalDeleteByIds_shouldDeleteMultipleAuditLogs() {
|
||||
AuditLog auditLog1 = createTestAuditLog(1L);
|
||||
AuditLog auditLog2 = createTestAuditLog(2L);
|
||||
|
||||
when(auditLogRepository.findById(1L)).thenReturn(Mono.just(auditLog1));
|
||||
when(auditLogRepository.findById(2L)).thenReturn(Mono.just(auditLog2));
|
||||
when(auditLogRepository.save(any(AuditLog.class))).thenReturn(Mono.just(auditLog1));
|
||||
|
||||
StepVerifier.create(auditLogService.logicalDeleteByIds(List.of(1L, 2L)))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("恢复逻辑删除的审计日志")
|
||||
void restoreById_shouldClearDeletedAt() {
|
||||
AuditLog auditLog = createTestAuditLog(1L);
|
||||
auditLog.setDeletedAt(LocalDateTime.now());
|
||||
AuditLog restoredAuditLog = createTestAuditLog(1L);
|
||||
restoredAuditLog.setDeletedAt(null);
|
||||
|
||||
when(auditLogRepository.findById(1L)).thenReturn(Mono.just(auditLog));
|
||||
when(auditLogRepository.save(auditLog)).thenReturn(Mono.just(restoredAuditLog));
|
||||
|
||||
StepVerifier.create(auditLogService.restoreById(1L))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
private AuditLog createTestAuditLog(Long id) {
|
||||
AuditLog auditLog = new AuditLog();
|
||||
auditLog.setId(id);
|
||||
auditLog.setEntityType("User");
|
||||
auditLog.setEntityId(100L);
|
||||
auditLog.setOperator("admin");
|
||||
auditLog.setOperationType("CREATE");
|
||||
auditLog.setOperationTime(LocalDateTime.now());
|
||||
auditLog.setDescription("创建用户");
|
||||
auditLog.setIpAddress("192.168.1.1");
|
||||
auditLog.setUserAgent("Mozilla/5.0");
|
||||
return auditLog;
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package cn.novalon.manage.sys.config;
|
||||
|
||||
import cn.novalon.manage.common.handler.ExceptionLogService;
|
||||
import cn.novalon.manage.sys.handler.ExceptionLogServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ExceptionLogConfigTest {
|
||||
|
||||
@Mock
|
||||
private ExceptionLogServiceImpl exceptionLogServiceImpl;
|
||||
|
||||
private ExceptionLogConfig exceptionLogConfig;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
exceptionLogConfig = new ExceptionLogConfig();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExceptionLogService() {
|
||||
ExceptionLogService exceptionLogService = exceptionLogConfig.exceptionLogService(exceptionLogServiceImpl);
|
||||
|
||||
assertThat(exceptionLogService).isNotNull();
|
||||
assertThat(exceptionLogService).isSameAs(exceptionLogServiceImpl);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExceptionLogService_DifferentInstance() {
|
||||
ExceptionLogService exceptionLogService1 = exceptionLogConfig.exceptionLogService(exceptionLogServiceImpl);
|
||||
ExceptionLogService exceptionLogService2 = exceptionLogConfig.exceptionLogService(exceptionLogServiceImpl);
|
||||
|
||||
assertThat(exceptionLogService1).isNotNull();
|
||||
assertThat(exceptionLogService2).isNotNull();
|
||||
assertThat(exceptionLogService1).isSameAs(exceptionLogServiceImpl);
|
||||
assertThat(exceptionLogService2).isSameAs(exceptionLogServiceImpl);
|
||||
}
|
||||
}
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
package cn.novalon.manage.sys.handler;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.SysExceptionLog;
|
||||
import cn.novalon.manage.sys.core.service.ISysExceptionLogService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ExceptionLogServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private ISysExceptionLogService exceptionLogService;
|
||||
|
||||
private ExceptionLogServiceImpl exceptionLogServiceImpl;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
exceptionLogServiceImpl = new ExceptionLogServiceImpl(exceptionLogService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogException() {
|
||||
SysExceptionLog savedLog = new SysExceptionLog();
|
||||
savedLog.setId(1L);
|
||||
savedLog.setTitle("测试异常");
|
||||
savedLog.setExceptionName("TestException");
|
||||
savedLog.setExceptionMsg("测试异常消息");
|
||||
savedLog.setMethodName("testMethod");
|
||||
savedLog.setIp("127.0.0.1");
|
||||
savedLog.setExceptionStack("测试堆栈信息");
|
||||
savedLog.setCreateTime(LocalDateTime.now());
|
||||
|
||||
when(exceptionLogService.save(any(SysExceptionLog.class))).thenReturn(Mono.just(savedLog));
|
||||
|
||||
StepVerifier.create(exceptionLogServiceImpl.logException(
|
||||
"测试异常",
|
||||
"TestException",
|
||||
"测试异常消息",
|
||||
"testMethod",
|
||||
"127.0.0.1",
|
||||
"测试堆栈信息"
|
||||
))
|
||||
.verifyComplete();
|
||||
|
||||
verify(exceptionLogService).save(any(SysExceptionLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogException_WithEmptyFields() {
|
||||
SysExceptionLog savedLog = new SysExceptionLog();
|
||||
savedLog.setId(1L);
|
||||
|
||||
when(exceptionLogService.save(any(SysExceptionLog.class))).thenReturn(Mono.just(savedLog));
|
||||
|
||||
StepVerifier.create(exceptionLogServiceImpl.logException(
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
))
|
||||
.verifyComplete();
|
||||
|
||||
verify(exceptionLogService).save(any(SysExceptionLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogException_WithNullFields() {
|
||||
SysExceptionLog savedLog = new SysExceptionLog();
|
||||
savedLog.setId(1L);
|
||||
|
||||
when(exceptionLogService.save(any(SysExceptionLog.class))).thenReturn(Mono.just(savedLog));
|
||||
|
||||
StepVerifier.create(exceptionLogServiceImpl.logException(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
))
|
||||
.verifyComplete();
|
||||
|
||||
verify(exceptionLogService).save(any(SysExceptionLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogException_WithLongStackTrace() {
|
||||
String longStackTrace = "a".repeat(10000);
|
||||
|
||||
SysExceptionLog savedLog = new SysExceptionLog();
|
||||
savedLog.setId(1L);
|
||||
|
||||
when(exceptionLogService.save(any(SysExceptionLog.class))).thenReturn(Mono.just(savedLog));
|
||||
|
||||
StepVerifier.create(exceptionLogServiceImpl.logException(
|
||||
"测试异常",
|
||||
"TestException",
|
||||
"测试异常消息",
|
||||
"testMethod",
|
||||
"127.0.0.1",
|
||||
longStackTrace
|
||||
))
|
||||
.verifyComplete();
|
||||
|
||||
verify(exceptionLogService).save(any(SysExceptionLog.class));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user