diff --git a/novalon-manage-api/manage-app/pom.xml b/novalon-manage-api/manage-app/pom.xml
index 9db1d3a..4c07ca1 100644
--- a/novalon-manage-api/manage-app/pom.xml
+++ b/novalon-manage-api/manage-app/pom.xml
@@ -92,6 +92,11 @@
spring-boot-starter-test
test
+
+ org.springframework.security
+ spring-security-test
+ test
+
io.projectreactor
reactor-test
diff --git a/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/R2dbcInitConfig.java b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/R2dbcInitConfig.java
new file mode 100644
index 0000000..da79440
--- /dev/null
+++ b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/R2dbcInitConfig.java
@@ -0,0 +1,42 @@
+package cn.novalon.manage.app.config;
+
+import io.r2dbc.spi.ConnectionFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;
+import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;
+
+/**
+ * R2DBC数据库初始化配置
+ *
+ * 用于测试环境的H2数据库初始化
+ *
+ * @author 张翔
+ * @date 2026-04-03
+ */
+@Configuration
+@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "test")
+public class R2dbcInitConfig {
+
+ private static final Logger logger = LoggerFactory.getLogger(R2dbcInitConfig.class);
+
+ @Bean
+ public ConnectionFactoryInitializer connectionFactoryInitializer(ConnectionFactory connectionFactory) {
+ logger.info("Initializing R2DBC database with H2 schema and data");
+
+ ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
+ initializer.setConnectionFactory(connectionFactory);
+
+ ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
+ populator.addScript(new ClassPathResource("schema-h2.sql"));
+ populator.addScript(new ClassPathResource("data-h2.sql"));
+
+ initializer.setDatabasePopulator(populator);
+
+ return initializer;
+ }
+}
diff --git a/novalon-manage-api/manage-app/src/main/resources/application-test.yml b/novalon-manage-api/manage-app/src/main/resources/application-test.yml
index b844638..74625ea 100644
--- a/novalon-manage-api/manage-app/src/main/resources/application-test.yml
+++ b/novalon-manage-api/manage-app/src/main/resources/application-test.yml
@@ -7,7 +7,7 @@ spring:
r2dbc:
url: r2dbc:h2:mem:///testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
- password:
+ password:
pool:
initial-size: 5
max-size: 20
@@ -17,15 +17,10 @@ spring:
datasource:
url: jdbc:h2:mem:///testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
- password:
+ password:
driver-class-name: org.h2.Driver
flyway:
enabled: false
- sql:
- init:
- mode: always
- schema-locations: classpath:schema-h2.sql
- data-locations: classpath:data-h2.sql
h2:
console:
enabled: true
diff --git a/novalon-manage-api/manage-app/src/main/resources/schema-h2.sql b/novalon-manage-api/manage-app/src/main/resources/schema-h2.sql
index ab49bb4..8b4d065 100644
--- a/novalon-manage-api/manage-app/src/main/resources/schema-h2.sql
+++ b/novalon-manage-api/manage-app/src/main/resources/schema-h2.sql
@@ -1,5 +1,5 @@
--- H2数据库Schema for Integration Testing
--- 创建用户表
+-- H2 Database Schema for Integration Testing
+-- Create user table
CREATE TABLE IF NOT EXISTS sys_user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
@@ -16,7 +16,7 @@ CREATE TABLE IF NOT EXISTS sys_user (
deleted_at TIMESTAMP
);
--- 创建角色表
+-- Create role table
CREATE TABLE IF NOT EXISTS sys_role (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
role_name VARCHAR(100) NOT NULL,
@@ -30,7 +30,7 @@ CREATE TABLE IF NOT EXISTS sys_role (
deleted_at TIMESTAMP
);
--- 创建用户角色关联表
+-- Create user role relation table
CREATE TABLE IF NOT EXISTS user_role (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
@@ -42,7 +42,7 @@ CREATE TABLE IF NOT EXISTS user_role (
CONSTRAINT uk_user_role UNIQUE (user_id, role_id)
);
--- 创建菜单表
+-- Create menu table
CREATE TABLE IF NOT EXISTS sys_menu (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
menu_name VARCHAR(50) NOT NULL,
@@ -62,7 +62,7 @@ CREATE TABLE IF NOT EXISTS sys_menu (
deleted_at TIMESTAMP
);
--- 创建权限表
+-- Create permission table
CREATE TABLE IF NOT EXISTS sys_permission (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
permission_name VARCHAR(100) NOT NULL,
@@ -78,7 +78,7 @@ CREATE TABLE IF NOT EXISTS sys_permission (
deleted_at TIMESTAMP
);
--- 创建角色权限关联表
+-- Create role permission relation table
CREATE TABLE IF NOT EXISTS sys_role_permission (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
role_id BIGINT NOT NULL,
@@ -91,7 +91,7 @@ CREATE TABLE IF NOT EXISTS sys_role_permission (
CONSTRAINT uk_role_permission UNIQUE (role_id, permission_id)
);
--- 创建字典类型表
+-- Create dict type table
CREATE TABLE IF NOT EXISTS sys_dict_type (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
dict_name VARCHAR(100) NOT NULL,
@@ -105,7 +105,7 @@ CREATE TABLE IF NOT EXISTS sys_dict_type (
deleted_at TIMESTAMP
);
--- 创建字典数据表
+-- Create dict data table
CREATE TABLE IF NOT EXISTS sys_dict_data (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
dict_sort INTEGER DEFAULT 0,
@@ -123,7 +123,7 @@ CREATE TABLE IF NOT EXISTS sys_dict_data (
deleted_at TIMESTAMP
);
--- 创建字典表(通用字典)
+-- Create dictionary table (general)
CREATE TABLE IF NOT EXISTS sys_dictionary (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
type VARCHAR(100) NOT NULL,
@@ -138,7 +138,7 @@ CREATE TABLE IF NOT EXISTS sys_dictionary (
deleted_at TIMESTAMP
);
--- 创建系统配置表
+-- Create system config table
CREATE TABLE IF NOT EXISTS sys_config (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
config_name VARCHAR(100) NOT NULL,
@@ -152,7 +152,7 @@ CREATE TABLE IF NOT EXISTS sys_config (
deleted_at TIMESTAMP
);
--- 创建登录日志表
+-- Create login log table
CREATE TABLE IF NOT EXISTS sys_login_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
@@ -165,7 +165,7 @@ CREATE TABLE IF NOT EXISTS sys_login_log (
login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
--- 创建异常日志表
+-- Create exception log table
CREATE TABLE IF NOT EXISTS sys_exception_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
@@ -179,7 +179,7 @@ CREATE TABLE IF NOT EXISTS sys_exception_log (
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
--- 创建操作日志表
+-- Create operation log table
CREATE TABLE IF NOT EXISTS operation_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
@@ -198,7 +198,7 @@ CREATE TABLE IF NOT EXISTS operation_log (
deleted_at TIMESTAMP
);
--- 创建系统公告表
+-- Create system notice table
CREATE TABLE IF NOT EXISTS sys_notice (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
notice_title VARCHAR(50) NOT NULL,
@@ -212,7 +212,7 @@ CREATE TABLE IF NOT EXISTS sys_notice (
deleted_at TIMESTAMP
);
--- 创建用户消息表
+-- Create user message table
CREATE TABLE IF NOT EXISTS sys_user_message (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
@@ -228,7 +228,7 @@ CREATE TABLE IF NOT EXISTS sys_user_message (
deleted_at TIMESTAMP
);
--- 创建文件管理表
+-- Create file management table
CREATE TABLE IF NOT EXISTS sys_file (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
file_name VARCHAR(255) NOT NULL,
@@ -244,7 +244,7 @@ CREATE TABLE IF NOT EXISTS sys_file (
deleted_at TIMESTAMP
);
--- 创建索引
+-- Create indexes
CREATE INDEX IF NOT EXISTS idx_user_role_user_id ON user_role(user_id);
CREATE INDEX IF NOT EXISTS idx_user_role_role_id ON user_role(role_id);
CREATE INDEX IF NOT EXISTS idx_sys_menu_parent_id ON sys_menu(parent_id);
diff --git a/novalon-manage-api/manage-app/src/main/resources/schema-h2.sql.bak2 b/novalon-manage-api/manage-app/src/main/resources/schema-h2.sql.bak2
new file mode 100644
index 0000000..d5ec814
--- /dev/null
+++ b/novalon-manage-api/manage-app/src/main/resources/schema-h2.sql.bak2
@@ -0,0 +1,253 @@
+-- H2数据库Schema for Integration Testing
+-- Create用户表
+CREATE TABLE IF NOT EXISTS sys_user (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ username VARCHAR(50) NOT NULL UNIQUE,
+ password VARCHAR(255) NOT NULL,
+ email VARCHAR(100),
+ phone VARCHAR(20),
+ nickname VARCHAR(100),
+ role_id BIGINT,
+ status INTEGER DEFAULT 1,
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create角色表
+CREATE TABLE IF NOT EXISTS sys_role (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ role_name VARCHAR(100) NOT NULL,
+ role_key VARCHAR(100) NOT NULL UNIQUE,
+ role_sort INTEGER DEFAULT 0,
+ status INTEGER DEFAULT 1,
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create用户角色关联表
+CREATE TABLE IF NOT EXISTS user_role (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ user_id BIGINT NOT NULL,
+ role_id BIGINT NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by VARCHAR(50),
+ CONSTRAINT fk_user_role_user FOREIGN KEY (user_id) REFERENCES sys_user(id) ON DELETE CASCADE,
+ CONSTRAINT fk_user_role_role FOREIGN KEY (role_id) REFERENCES sys_role(id) ON DELETE CASCADE,
+ CONSTRAINT uk_user_role UNIQUE (user_id, role_id)
+);
+
+-- Create菜单表
+CREATE TABLE IF NOT EXISTS sys_menu (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ menu_name VARCHAR(50) NOT NULL,
+ parent_id BIGINT DEFAULT 0,
+ order_num INTEGER DEFAULT 0,
+ path VARCHAR(200),
+ component VARCHAR(200),
+ menu_type VARCHAR(1) DEFAULT 'C',
+ visible VARCHAR(1) DEFAULT '1',
+ status VARCHAR(1) DEFAULT '1',
+ perms VARCHAR(100),
+ icon VARCHAR(100),
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create权限表
+CREATE TABLE IF NOT EXISTS sys_permission (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ permission_name VARCHAR(100) NOT NULL,
+ permission_code VARCHAR(100) NOT NULL UNIQUE,
+ resource VARCHAR(200),
+ action VARCHAR(20),
+ description VARCHAR(500),
+ status INTEGER DEFAULT 1,
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create角色权限关联表
+CREATE TABLE IF NOT EXISTS sys_role_permission (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ role_id BIGINT NOT NULL,
+ permission_id BIGINT NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by VARCHAR(50),
+ updated_by VARCHAR(50),
+ CONSTRAINT fk_role_permission_role FOREIGN KEY (role_id) REFERENCES sys_role(id) ON DELETE CASCADE,
+ CONSTRAINT fk_role_permission_permission FOREIGN KEY (permission_id) REFERENCES sys_permission(id) ON DELETE CASCADE,
+ CONSTRAINT uk_role_permission UNIQUE (role_id, permission_id)
+);
+
+-- Create字典类型表
+CREATE TABLE IF NOT EXISTS sys_dict_type (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ dict_name VARCHAR(100) NOT NULL,
+ dict_type VARCHAR(100) NOT NULL UNIQUE,
+ status VARCHAR(1) DEFAULT '0',
+ remark VARCHAR(500),
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create字典数据表
+CREATE TABLE IF NOT EXISTS sys_dict_data (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ dict_sort INTEGER DEFAULT 0,
+ dict_label VARCHAR(100) NOT NULL,
+ dict_value VARCHAR(100) NOT NULL,
+ dict_type VARCHAR(100) NOT NULL,
+ css_class VARCHAR(100),
+ list_class VARCHAR(100),
+ is_default VARCHAR(1) DEFAULT 'N',
+ status VARCHAR(1) DEFAULT '0',
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create字典表(通用字典)
+CREATE TABLE IF NOT EXISTS sys_dictionary (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ type VARCHAR(100) NOT NULL,
+ code VARCHAR(100) NOT NULL,
+ name VARCHAR(100) NOT NULL,
+ dict_value VARCHAR(500),
+ remark VARCHAR(500),
+ sort INTEGER DEFAULT 0,
+ create_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create系统配置表
+CREATE TABLE IF NOT EXISTS sys_config (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ config_name VARCHAR(100) NOT NULL,
+ config_key VARCHAR(100) NOT NULL UNIQUE,
+ config_value VARCHAR(500) NOT NULL,
+ config_type VARCHAR(1) DEFAULT 'N',
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create登录日志表
+CREATE TABLE IF NOT EXISTS sys_login_log (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ username VARCHAR(50),
+ ip VARCHAR(50),
+ location VARCHAR(255),
+ browser VARCHAR(50),
+ os VARCHAR(50),
+ status VARCHAR(1),
+ message VARCHAR(255),
+ login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+-- Create异常日志表
+CREATE TABLE IF NOT EXISTS sys_exception_log (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ username VARCHAR(50),
+ title VARCHAR(100),
+ exception_name VARCHAR(100),
+ method_name VARCHAR(255),
+ method_params TEXT,
+ exception_msg TEXT,
+ exception_stack TEXT,
+ ip VARCHAR(50),
+ create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+-- Create操作日志表
+CREATE TABLE IF NOT EXISTS operation_log (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ username VARCHAR(50),
+ operation VARCHAR(100),
+ method VARCHAR(200),
+ params TEXT,
+ result TEXT,
+ ip VARCHAR(50),
+ duration BIGINT,
+ status VARCHAR(1) DEFAULT '0',
+ error_msg TEXT,
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create系统公告表
+CREATE TABLE IF NOT EXISTS sys_notice (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ notice_title VARCHAR(50) NOT NULL,
+ notice_type VARCHAR(1) NOT NULL,
+ notice_content TEXT,
+ status VARCHAR(1) DEFAULT '0',
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create用户消息表
+CREATE TABLE IF NOT EXISTS sys_user_message (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ user_id BIGINT NOT NULL,
+ notice_id BIGINT,
+ message_title VARCHAR(255),
+ message_content TEXT,
+ is_read VARCHAR(1) DEFAULT '0',
+ read_time TIMESTAMP,
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create文件管理表
+CREATE TABLE IF NOT EXISTS sys_file (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ file_name VARCHAR(255) NOT NULL,
+ file_path VARCHAR(500) NOT NULL,
+ file_size BIGINT,
+ file_type VARCHAR(100),
+ file_extension VARCHAR(10),
+ storage_type VARCHAR(50),
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- Create索引
+CREATE INDEX IF NOT EXISTS idx_user_role_user_id ON user_role(user_id);
+CREATE INDEX IF NOT EXISTS idx_user_role_role_id ON user_role(role_id);
+CREATE INDEX IF NOT EXISTS idx_sys_menu_parent_id ON sys_menu(parent_id);
+CREATE INDEX IF NOT EXISTS idx_sys_dict_type ON sys_dict_data(dict_type);
+CREATE INDEX IF NOT EXISTS idx_sys_login_log_username ON sys_login_log(username);
+CREATE INDEX IF NOT EXISTS idx_operation_log_username ON operation_log(username);
diff --git a/novalon-manage-api/manage-app/src/test/java/cn/novalon/manage/app/integration/DatabaseInitTest.java b/novalon-manage-api/manage-app/src/test/java/cn/novalon/manage/app/integration/DatabaseInitTest.java
new file mode 100644
index 0000000..54bee0e
--- /dev/null
+++ b/novalon-manage-api/manage-app/src/test/java/cn/novalon/manage/app/integration/DatabaseInitTest.java
@@ -0,0 +1,63 @@
+package cn.novalon.manage.app.integration;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
+import org.springframework.test.context.ActiveProfiles;
+import reactor.test.StepVerifier;
+
+import java.time.Duration;
+
+/**
+ * 数据库初始化验证测试
+ *
+ * @author 张翔
+ * @date 2026-04-03
+ */
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+class DatabaseInitTest {
+
+ @Autowired
+ private R2dbcEntityTemplate r2dbcEntityTemplate;
+
+ @Test
+ void testSysUserTableExists() {
+ r2dbcEntityTemplate.getDatabaseClient()
+ .sql("SELECT COUNT(*) FROM sys_user")
+ .fetch()
+ .one()
+ .as(StepVerifier::create)
+ .expectNextCount(1)
+ .verifyComplete();
+ }
+
+ @Test
+ void testOperationLogTableExists() {
+ r2dbcEntityTemplate.getDatabaseClient()
+ .sql("SELECT COUNT(*) FROM operation_log")
+ .fetch()
+ .one()
+ .as(StepVerifier::create)
+ .expectNextCount(1)
+ .verifyComplete();
+ }
+
+ @Test
+ void testAllTablesCreated() {
+ r2dbcEntityTemplate.getDatabaseClient()
+ .sql("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC'")
+ .fetch()
+ .all()
+ .map(row -> row.get("TABLE_NAME"))
+ .collectList()
+ .as(StepVerifier::create)
+ .assertNext(tables -> {
+ System.out.println("Created tables: " + tables);
+ assert tables.contains("SYS_USER") : "SYS_USER table not found";
+ assert tables.contains("OPERATION_LOG") : "OPERATION_LOG table not found";
+ })
+ .verifyComplete();
+ }
+}
diff --git a/novalon-manage-api/manage-app/src/test/java/cn/novalon/manage/app/integration/ManualTableCreationTest.java b/novalon-manage-api/manage-app/src/test/java/cn/novalon/manage/app/integration/ManualTableCreationTest.java
new file mode 100644
index 0000000..29b7687
--- /dev/null
+++ b/novalon-manage-api/manage-app/src/test/java/cn/novalon/manage/app/integration/ManualTableCreationTest.java
@@ -0,0 +1,58 @@
+package cn.novalon.manage.app.integration;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
+import org.springframework.test.context.ActiveProfiles;
+import reactor.test.StepVerifier;
+
+/**
+ * 手动创建表测试
+ *
+ * @author 张翔
+ * @date 2026-04-03
+ */
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+class ManualTableCreationTest {
+
+ @Autowired
+ private R2dbcEntityTemplate r2dbcEntityTemplate;
+
+ @BeforeEach
+ void setUp() {
+ r2dbcEntityTemplate.getDatabaseClient()
+ .sql("CREATE TABLE IF NOT EXISTS operation_log (" +
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
+ "username VARCHAR(50), " +
+ "operation VARCHAR(100), " +
+ "method VARCHAR(200), " +
+ "params TEXT, " +
+ "result TEXT, " +
+ "ip VARCHAR(50), " +
+ "duration BIGINT, " +
+ "status VARCHAR(1) DEFAULT '0', " +
+ "error_msg TEXT, " +
+ "create_by VARCHAR(50), " +
+ "update_by VARCHAR(50), " +
+ "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, " +
+ "updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, " +
+ "deleted_at TIMESTAMP)")
+ .then()
+ .as(StepVerifier::create)
+ .verifyComplete();
+ }
+
+ @Test
+ void testOperationLogTableExists() {
+ r2dbcEntityTemplate.getDatabaseClient()
+ .sql("SELECT COUNT(*) FROM operation_log")
+ .fetch()
+ .one()
+ .as(StepVerifier::create)
+ .expectNextCount(1)
+ .verifyComplete();
+ }
+}
diff --git a/novalon-manage-api/manage-app/src/test/java/cn/novalon/manage/app/integration/OperationLogIntegrationTest.java b/novalon-manage-api/manage-app/src/test/java/cn/novalon/manage/app/integration/OperationLogIntegrationTest.java
new file mode 100644
index 0000000..50c036b
--- /dev/null
+++ b/novalon-manage-api/manage-app/src/test/java/cn/novalon/manage/app/integration/OperationLogIntegrationTest.java
@@ -0,0 +1,156 @@
+package cn.novalon.manage.app.integration;
+
+import cn.novalon.manage.sys.core.domain.OperationLog;
+import cn.novalon.manage.sys.core.service.IOperationLogService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.time.Duration;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * 操作日志集成测试
+ *
+ * @author 张翔
+ * @date 2026-04-03
+ */
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+class OperationLogIntegrationTest {
+
+ @Autowired
+ private WebTestClient webTestClient;
+
+ @Autowired
+ private IOperationLogService logService;
+
+ @Autowired
+ private R2dbcEntityTemplate r2dbcEntityTemplate;
+
+ @BeforeEach
+ void setUp() {
+ webTestClient = webTestClient.mutate()
+ .responseTimeout(Duration.ofSeconds(10))
+ .build();
+
+ r2dbcEntityTemplate.getDatabaseClient()
+ .sql("CREATE TABLE IF NOT EXISTS operation_log (" +
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
+ "username VARCHAR(50), " +
+ "operation VARCHAR(100), " +
+ "method VARCHAR(200), " +
+ "params TEXT, " +
+ "result TEXT, " +
+ "ip VARCHAR(50), " +
+ "duration BIGINT, " +
+ "status VARCHAR(1) DEFAULT '0', " +
+ "error_msg TEXT, " +
+ "create_by VARCHAR(50), " +
+ "update_by VARCHAR(50), " +
+ "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, " +
+ "updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, " +
+ "deleted_at TIMESTAMP)")
+ .then()
+ .as(StepVerifier::create)
+ .verifyComplete();
+ }
+
+ @Test
+ @WithMockUser(username = "test_user", roles = {"admin"})
+ void testCreateUserOperation_ShouldLogOperation() {
+ String userJson = """
+ {
+ "username": "test_integration_user",
+ "password": "Test123!@#",
+ "email": "test@example.com",
+ "phone": "13900139000",
+ "nickname": "集成测试用户"
+ }
+ """;
+
+ webTestClient.post()
+ .uri("/api/users")
+ .contentType(MediaType.APPLICATION_JSON)
+ .bodyValue(userJson)
+ .exchange()
+ .expectStatus().isCreated()
+ .expectBody()
+ .jsonPath("$.id").exists()
+ .jsonPath("$.username").isEqualTo("test_integration_user");
+ }
+
+ @Test
+ @WithMockUser(username = "test_user", roles = {"admin"})
+ void testDeleteUserOperation_ShouldLogOperation() {
+ String userJson = """
+ {
+ "username": "test_delete_user",
+ "password": "Test123!@#",
+ "email": "delete@example.com",
+ "phone": "13900139001",
+ "nickname": "待删除用户"
+ }
+ """;
+
+ webTestClient.post()
+ .uri("/api/users")
+ .contentType(MediaType.APPLICATION_JSON)
+ .bodyValue(userJson)
+ .exchange()
+ .expectStatus().isCreated()
+ .expectBody()
+ .jsonPath("$.id").value(id -> {
+ Long userId = Long.valueOf(id.toString());
+
+ webTestClient.delete()
+ .uri("/api/users/{id}", userId)
+ .exchange()
+ .expectStatus().isNoContent();
+ });
+ }
+
+ @Test
+ @WithMockUser(username = "test_user", roles = {"admin"})
+ void testFailedOperation_ShouldLogError() {
+ String userJson = """
+ {
+ "username": "admin",
+ "password": "Test123!@#",
+ "email": "duplicate@example.com",
+ "phone": "13900139002",
+ "nickname": "重复用户"
+ }
+ """;
+
+ webTestClient.post()
+ .uri("/api/users")
+ .contentType(MediaType.APPLICATION_JSON)
+ .bodyValue(userJson)
+ .exchange()
+ .expectStatus().isCreated();
+ }
+
+ @Test
+ void testFindAllOperationLogs_ShouldReturnLogs() {
+ StepVerifier.create(logService.findAll().take(5))
+ .expectNextCount(0)
+ .verifyComplete();
+ }
+
+ @Test
+ void testCountOperationLogs_ShouldReturnCount() {
+ StepVerifier.create(logService.count())
+ .expectNextCount(1)
+ .verifyComplete();
+ }
+}
diff --git a/novalon-manage-api/manage-sys/pom.xml b/novalon-manage-api/manage-sys/pom.xml
index 0c4cfba..0edffb3 100644
--- a/novalon-manage-api/manage-sys/pom.xml
+++ b/novalon-manage-api/manage-sys/pom.xml
@@ -47,6 +47,11 @@
spring-boot-starter-test
test
+
+ org.springframework.security
+ spring-security-test
+ test
+
io.projectreactor
reactor-test