feat: 实现登录日志和操作日志的分页查询功能
refactor: 重构日志服务层代码,将分页逻辑移至Repository层 test: 添加日志分页查询的单元测试和组件测试 docs: 更新README文档,记录API响应格式修复过程 chore: 清理无用文件,更新.gitignore配置 build: 添加Jacoco代码覆盖率插件配置 ci: 添加测试环境配置文件application-h2-test.yml style: 统一日志服务代码格式,添加必要的日志输出
This commit is contained in:
@@ -72,12 +72,12 @@
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.r2dbc</groupId>
|
||||
<artifactId>r2dbc-h2</artifactId>
|
||||
<scope>test</scope>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
|
||||
+1
@@ -115,6 +115,7 @@ public class SystemRouter {
|
||||
.GET("/api/logs/login/page", logHandler::getLoginLogsByPage)
|
||||
.GET("/api/logs/login/count", logHandler::getLoginLogCount)
|
||||
.GET("/api/logs/login/today/count", logHandler::getTodayLoginCount)
|
||||
.GET("/api/logs/login/recent", logHandler::getRecentLoginLogs)
|
||||
.GET("/api/logs/login/{id}", logHandler::getLoginLogById)
|
||||
.POST("/api/logs/login", logHandler::createLoginLog)
|
||||
.GET("/api/logs/exception", logHandler::getAllExceptionLogs)
|
||||
|
||||
@@ -31,7 +31,9 @@ spring:
|
||||
sql:
|
||||
init:
|
||||
mode: always
|
||||
continue-on-error: true
|
||||
continue-on-error: false
|
||||
schema-locations: classpath:schema-h2.sql
|
||||
data-locations: classpath:data-h2.sql
|
||||
|
||||
# 测试专用配置
|
||||
test:
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
-- H2数据库测试数据
|
||||
-- 用于测试环境
|
||||
|
||||
-- 插入测试角色
|
||||
INSERT INTO sys_role (id, role_name, role_key, role_sort, status, create_by, update_by)
|
||||
VALUES
|
||||
(1, '超级管理员', 'admin', 1, 1, 'system', 'system'),
|
||||
(2, '测试管理员', 'test_admin', 2, 1, 'system', 'system'),
|
||||
(3, '普通用户', 'normal_user', 3, 1, 'system', 'system'),
|
||||
(4, '访客', 'guest', 4, 1, 'system', 'system');
|
||||
|
||||
-- 插入测试用户
|
||||
-- BCrypt哈希值对应明文密码: Test@123
|
||||
INSERT INTO sys_user (id, username, password, email, phone, nickname, status, create_by, update_by)
|
||||
VALUES
|
||||
(1, 'admin', '$2a$12$fVKAbdBtjaUaosAbKVA2Ku0rAzr4kHe9ELRJzW3L3YzXGRiYVBAfS', 'admin@novalon.com', '13800138000', '超级管理员', 1, 'system', 'system'),
|
||||
(2, 'testadmin', '$2a$12$fVKAbdBtjaUaosAbKVA2Ku0rAzr4kHe9ELRJzW3L3YzXGRiYVBAfS', 'testadmin@novalon.com', '13800138001', '测试管理员', 1, 'system', 'system'),
|
||||
(3, 'normaluser', '$2a$12$fVKAbdBtjaUaosAbKVA2Ku0rAzr4kHe9ELRJzW3L3YzXGRiYVBAfS', 'normaluser@novalon.com', '13800138002', '普通用户', 1, 'system', 'system'),
|
||||
(4, 'guestuser', '$2a$12$fVKAbdBtjaUaosAbKVA2Ku0rAzr4kHe9ELRJzW3L3YzXGRiYVBAfS', 'guestuser@novalon.com', '13800138003', '访客用户', 1, 'system', 'system'),
|
||||
(5, 'disableduser', '$2a$12$fVKAbdBtjaUaosAbKVA2Ku0rAzr4kHe9ELRJzW3L3YzXGRiYVBAfS', 'disableduser@novalon.com', '13800138004', '禁用用户', 0, 'system', 'system');
|
||||
|
||||
-- 为用户分配角色
|
||||
INSERT INTO user_role (user_id, role_id, created_by)
|
||||
VALUES
|
||||
(1, 1, 'system'),
|
||||
(2, 2, 'system'),
|
||||
(3, 3, 'system'),
|
||||
(4, 4, 'system');
|
||||
|
||||
-- 插入测试菜单
|
||||
INSERT INTO sys_menu (id, menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, update_by)
|
||||
VALUES
|
||||
(1, '系统管理', 0, 1, '/system', 'Layout', 'M', '1', '1', '', 'system', 'system', 'system'),
|
||||
(2, '用户管理', 1, 1, 'user', 'system/user/index', 'C', '1', '1', 'system:user:list', 'user', 'system', 'system'),
|
||||
(3, '角色管理', 1, 2, 'role', 'system/role/index', 'C', '1', '1', 'system:role:list', 'role', 'system', 'system'),
|
||||
(4, '菜单管理', 1, 3, 'menu', 'system/menu/index', 'C', '1', '1', 'system:menu:list', 'menu', 'system', 'system'),
|
||||
(5, '测试菜单', 0, 99, '/test', 'Layout', 'M', '1', '1', '', 'test', 'system', 'system'),
|
||||
(6, '用户测试', 5, 1, 'user-test', 'system/user-test/index', 'C', '1', '1', 'system:user:test', 'user', 'system', 'system');
|
||||
|
||||
-- 插入测试权限
|
||||
INSERT INTO sys_permission (id, permission_name, permission_code, resource, action, description, status, create_by, update_by)
|
||||
VALUES
|
||||
(1, '系统管理', 'system:manage', '/api/system', 'GET', '系统管理权限', 1, 'system', 'system'),
|
||||
(2, '用户管理', 'system:user:manage', '/api/users', 'GET', '用户管理权限', 1, 'system', 'system'),
|
||||
(3, '用户查询', 'system:user:list', '/api/users', 'GET', '用户查询权限', 1, 'system', 'system'),
|
||||
(4, '用户新增', 'system:user:add', '/api/users', 'POST', '用户新增权限', 1, 'system', 'system'),
|
||||
(5, '用户编辑', 'system:user:edit', '/api/users', 'PUT', '用户编辑权限', 1, 'system', 'system'),
|
||||
(6, '用户删除', 'system:user:delete', '/api/users', 'DELETE', '用户删除权限', 1, 'system', 'system'),
|
||||
(7, '测试权限', 'test:permission', '/api/test', 'GET', '测试权限', 1, 'system', 'system'),
|
||||
(8, '用户测试权限', 'system:user:test', '/api/users/test', 'GET', '用户测试权限', 1, 'system', 'system');
|
||||
|
||||
-- 为角色分配权限
|
||||
INSERT INTO sys_role_permission (role_id, permission_id, created_by, updated_by)
|
||||
SELECT 1, id, 'system', 'system' FROM sys_permission
|
||||
UNION ALL
|
||||
SELECT 2, id, 'system', 'system' FROM sys_permission WHERE id IN (7, 8);
|
||||
|
||||
-- 插入字典类型
|
||||
INSERT INTO sys_dict_type (id, dict_name, dict_type, status, remark, create_by, update_by)
|
||||
VALUES
|
||||
(1, '用户状态', 'user_status', '0', '用户状态列表', 'system', 'system'),
|
||||
(2, '菜单状态', 'menu_status', '0', '菜单状态列表', 'system', 'system'),
|
||||
(3, '角色状态', 'role_status', '0', '角色状态列表', 'system', 'system');
|
||||
|
||||
-- 插入字典数据
|
||||
INSERT INTO sys_dict_data (id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, update_by)
|
||||
VALUES
|
||||
(1, 1, '正常', '1', 'user_status', '', 'primary', 'Y', '0', 'system', 'system'),
|
||||
(2, 2, '停用', '0', 'user_status', '', 'danger', 'N', '0', 'system', 'system'),
|
||||
(3, 1, '正常', '0', 'menu_status', '', 'primary', 'Y', '0', 'system', 'system'),
|
||||
(4, 2, '停用', '1', 'menu_status', '', 'danger', 'N', '0', 'system', 'system'),
|
||||
(5, 1, '正常', '0', 'role_status', '', 'primary', 'Y', '0', 'system', 'system'),
|
||||
(6, 2, '停用', '1', 'role_status', '', 'danger', 'N', '0', 'system', 'system');
|
||||
|
||||
-- 插入系统配置
|
||||
INSERT INTO sys_config (id, config_name, config_key, config_value, config_type, create_by, update_by)
|
||||
VALUES
|
||||
(1, '用户管理-用户初始密码', 'sys.user.initPassword', '123456', 'Y', 'system', 'system'),
|
||||
(2, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'system', 'system'),
|
||||
(3, '用户自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'system', 'system');
|
||||
@@ -0,0 +1,253 @@
|
||||
-- H2数据库Schema for Integration Testing
|
||||
-- 创建用户表
|
||||
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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);
|
||||
@@ -2,7 +2,7 @@
|
||||
-- 用于测试环境
|
||||
|
||||
-- 插入测试角色
|
||||
INSERT INTO roles (id, role_name, role_key, role_sort, status, created_by, updated_by)
|
||||
INSERT INTO sys_role (id, role_name, role_key, role_sort, status, create_by, update_by)
|
||||
VALUES
|
||||
(1, '超级管理员', 'admin', 1, 1, 'system', 'system'),
|
||||
(2, '测试管理员', 'test_admin', 2, 1, 'system', 'system'),
|
||||
@@ -11,7 +11,7 @@ VALUES
|
||||
|
||||
-- 插入测试用户
|
||||
-- BCrypt哈希值对应明文密码: Test@123
|
||||
INSERT INTO users (id, username, password, email, phone, nickname, status, created_by, updated_by)
|
||||
INSERT INTO sys_user (id, username, password, email, phone, nickname, status, create_by, update_by)
|
||||
VALUES
|
||||
(1, 'admin', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/X4.VTtYA/7.J6LlZy', 'admin@novalon.com', '13800138000', '超级管理员', 1, 'system', 'system'),
|
||||
(2, 'testadmin', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/X4.VTtYA/7.J6LlZy', 'testadmin@novalon.com', '13800138001', '测试管理员', 1, 'system', 'system'),
|
||||
@@ -20,12 +20,12 @@ VALUES
|
||||
(5, 'disableduser', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/X4.VTtYA/7.J6LlZy', 'disableduser@novalon.com', '13800138004', '禁用用户', 0, 'system', 'system');
|
||||
|
||||
-- 为用户分配角色
|
||||
INSERT INTO user_roles (user_id, role_id, created_by, updated_by)
|
||||
INSERT INTO user_role (user_id, role_id, created_by)
|
||||
VALUES
|
||||
(1, 1, 'system', 'system'),
|
||||
(2, 2, 'system', 'system'),
|
||||
(3, 3, 'system', 'system'),
|
||||
(4, 4, 'system', 'system');
|
||||
(1, 1, 'system'),
|
||||
(2, 2, 'system'),
|
||||
(3, 3, 'system'),
|
||||
(4, 4, 'system');
|
||||
|
||||
-- 插入测试菜单
|
||||
INSERT INTO sys_menu (id, menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, created_by, updated_by)
|
||||
|
||||
+44
@@ -2,6 +2,8 @@ package cn.novalon.manage.db.repository;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.SysExceptionLog;
|
||||
import cn.novalon.manage.sys.core.repository.ISysExceptionLogRepository;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import cn.novalon.manage.db.converter.SysExceptionLogConverter;
|
||||
import cn.novalon.manage.db.dao.SysExceptionLogDao;
|
||||
import cn.novalon.manage.db.dao.QueryUtil;
|
||||
@@ -16,6 +18,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 异常日志仓储实现类
|
||||
@@ -89,4 +92,45 @@ public class SysExceptionLogRepository implements ISysExceptionLogRepository {
|
||||
public Mono<Long> count() {
|
||||
return sysExceptionLogDao.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<PageResponse<SysExceptionLog>> findExceptionLogsByPage(PageRequest pageRequest) {
|
||||
int page = pageRequest.getPage();
|
||||
int size = pageRequest.getSize();
|
||||
String sort = pageRequest.getSort();
|
||||
String order = pageRequest.getOrder();
|
||||
String keyword = pageRequest.getKeyword();
|
||||
|
||||
SysExceptionLogQueryCriteria criteria = new SysExceptionLogQueryCriteria();
|
||||
|
||||
if (keyword != null && !keyword.isEmpty()) {
|
||||
criteria.setKeyword(keyword);
|
||||
}
|
||||
|
||||
Query queryObj = QueryUtil.getQuery(criteria);
|
||||
|
||||
Sort sortObj = Sort.unsorted();
|
||||
if (sort != null && !sort.isEmpty()) {
|
||||
sortObj = Sort.by(Sort.Direction.fromString(order), sort);
|
||||
} else {
|
||||
sortObj = Sort.by(Sort.Direction.DESC, "createTime");
|
||||
}
|
||||
|
||||
org.springframework.data.domain.PageRequest pageable = org.springframework.data.domain.PageRequest.of(page,
|
||||
size, sortObj);
|
||||
|
||||
return r2dbcEntityTemplate.select(SysExceptionLogEntity.class)
|
||||
.matching(queryObj.with(pageable))
|
||||
.all()
|
||||
.collectList()
|
||||
.zipWith(r2dbcEntityTemplate.count(queryObj, SysExceptionLogEntity.class))
|
||||
.map(tuple -> {
|
||||
long total = tuple.getT2();
|
||||
int totalPages = (int) Math.ceil((double) total / size);
|
||||
List<SysExceptionLog> logList = tuple.getT1().stream()
|
||||
.map(sysExceptionLogConverter::toDomain)
|
||||
.toList();
|
||||
return new PageResponse<>(logList, totalPages, total, page, size);
|
||||
});
|
||||
}
|
||||
}
|
||||
+44
@@ -2,6 +2,8 @@ package cn.novalon.manage.db.repository;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.SysLoginLog;
|
||||
import cn.novalon.manage.sys.core.repository.ISysLoginLogRepository;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import cn.novalon.manage.db.converter.SysLoginLogConverter;
|
||||
import cn.novalon.manage.db.dao.SysLoginLogDao;
|
||||
import cn.novalon.manage.db.dao.QueryUtil;
|
||||
@@ -16,6 +18,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 登录日志仓储实现类
|
||||
@@ -94,4 +97,45 @@ public class SysLoginLogRepository implements ISysLoginLogRepository {
|
||||
LocalDateTime todayEnd = todayStart.plusDays(1);
|
||||
return findByLoginTimeBetweenOrderByLoginTimeDesc(todayStart, todayEnd).count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<PageResponse<SysLoginLog>> findLoginLogsByPage(PageRequest pageRequest) {
|
||||
int page = pageRequest.getPage();
|
||||
int size = pageRequest.getSize();
|
||||
String sort = pageRequest.getSort();
|
||||
String order = pageRequest.getOrder();
|
||||
String keyword = pageRequest.getKeyword();
|
||||
|
||||
SysLoginLogQueryCriteria criteria = new SysLoginLogQueryCriteria();
|
||||
|
||||
if (keyword != null && !keyword.isEmpty()) {
|
||||
criteria.setKeyword(keyword);
|
||||
}
|
||||
|
||||
Query queryObj = QueryUtil.getQuery(criteria);
|
||||
|
||||
Sort sortObj = Sort.unsorted();
|
||||
if (sort != null && !sort.isEmpty()) {
|
||||
sortObj = Sort.by(Sort.Direction.fromString(order), sort);
|
||||
} else {
|
||||
sortObj = Sort.by(Sort.Direction.DESC, "loginTime");
|
||||
}
|
||||
|
||||
org.springframework.data.domain.PageRequest pageable = org.springframework.data.domain.PageRequest.of(page,
|
||||
size, sortObj);
|
||||
|
||||
return r2dbcEntityTemplate.select(SysLoginLogEntity.class)
|
||||
.matching(queryObj.with(pageable))
|
||||
.all()
|
||||
.collectList()
|
||||
.zipWith(r2dbcEntityTemplate.count(queryObj, SysLoginLogEntity.class))
|
||||
.map(tuple -> {
|
||||
long total = tuple.getT2();
|
||||
int totalPages = (int) Math.ceil((double) total / size);
|
||||
List<SysLoginLog> logList = tuple.getT1().stream()
|
||||
.map(sysLoginLogConverter::toDomain)
|
||||
.toList();
|
||||
return new PageResponse<>(logList, totalPages, total, page, size);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
codec:
|
||||
max-in-memory-size: 10MB
|
||||
application:
|
||||
name: manage-gateway
|
||||
cloud:
|
||||
gateway:
|
||||
routes:
|
||||
- id: manage-app
|
||||
uri: http://localhost:8084
|
||||
predicates:
|
||||
- Path=/api/**
|
||||
|
||||
jwt:
|
||||
secret: test-secret-key-for-e2e-testing-novalon-manage-system-2026
|
||||
expiration: 86400000
|
||||
key:
|
||||
encryption:
|
||||
password: test-encryption-password
|
||||
rotation:
|
||||
enabled: false
|
||||
interval:
|
||||
days: 30
|
||||
|
||||
rate:
|
||||
limit:
|
||||
enabled: false
|
||||
global:
|
||||
limit-for-period: 10000
|
||||
limit-refresh-period: 1s
|
||||
timeout-duration: 0
|
||||
ip:
|
||||
limit-for-period: 1000
|
||||
limit-refresh-period: 1s
|
||||
timeout-duration: 0
|
||||
user:
|
||||
limit-for-period: 2000
|
||||
limit-refresh-period: 1s
|
||||
timeout-duration: 0
|
||||
|
||||
signature:
|
||||
enabled: false
|
||||
secret: TestSecretKey2026
|
||||
max-age-minutes: 30
|
||||
nonce-cache-size: 10000
|
||||
whitelist:
|
||||
paths: /actuator/health,/actuator/info,/api/auth/login,/api/auth/register
|
||||
|
||||
resilience:
|
||||
enabled: true
|
||||
circuit-breaker:
|
||||
enabled: true
|
||||
failure-rate-threshold: 50
|
||||
slow-call-rate-threshold: 100
|
||||
slow-call-duration-threshold: 2s
|
||||
permitted-number-of-calls-in-half-open-state: 10
|
||||
sliding-window-type: COUNT_BASED
|
||||
sliding-window-size: 100
|
||||
minimum-number-of-calls: 10
|
||||
wait-duration-in-open-state: 10s
|
||||
retry:
|
||||
enabled: true
|
||||
max-attempts: 3
|
||||
wait-duration: 500ms
|
||||
timeout:
|
||||
enabled: true
|
||||
duration: 5s
|
||||
|
||||
user:
|
||||
service:
|
||||
url: http://localhost:8084
|
||||
|
||||
permission:
|
||||
cache:
|
||||
expiry:
|
||||
minutes: 1
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info,metrics
|
||||
base-path: /actuator
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
health:
|
||||
livenessstate:
|
||||
enabled: true
|
||||
readinessstate:
|
||||
enabled: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
cn.novalon.manage: DEBUG
|
||||
org.springframework.cloud.gateway: DEBUG
|
||||
+4
@@ -1,6 +1,8 @@
|
||||
package cn.novalon.manage.sys.core.repository;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.SysExceptionLog;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -25,4 +27,6 @@ public interface ISysExceptionLogRepository {
|
||||
Mono<SysExceptionLog> findById(Long id);
|
||||
|
||||
Mono<Long> count();
|
||||
|
||||
Mono<PageResponse<SysExceptionLog>> findExceptionLogsByPage(PageRequest pageRequest);
|
||||
}
|
||||
+4
@@ -1,6 +1,8 @@
|
||||
package cn.novalon.manage.sys.core.repository;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.SysLoginLog;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -27,4 +29,6 @@ public interface ISysLoginLogRepository {
|
||||
Mono<Long> count();
|
||||
|
||||
Mono<Long> countToday();
|
||||
|
||||
Mono<PageResponse<SysLoginLog>> findLoginLogsByPage(PageRequest pageRequest);
|
||||
}
|
||||
+1
@@ -23,4 +23,5 @@ public interface ISysLoginLogService {
|
||||
Mono<PageResponse<SysLoginLog>> findLoginLogsByPage(PageRequest pageRequest);
|
||||
Mono<Long> count();
|
||||
Mono<Long> countToday();
|
||||
Flux<SysLoginLog> findRecent(int limit);
|
||||
}
|
||||
|
||||
+5
-69
@@ -5,12 +5,13 @@ import cn.novalon.manage.sys.core.repository.ISysExceptionLogRepository;
|
||||
import cn.novalon.manage.sys.core.service.ISysExceptionLogService;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 异常日志服务实现类
|
||||
@@ -21,6 +22,7 @@ import java.util.List;
|
||||
@Service
|
||||
public class SysExceptionLogService implements ISysExceptionLogService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SysExceptionLogService.class);
|
||||
private final ISysExceptionLogRepository repository;
|
||||
|
||||
public SysExceptionLogService(ISysExceptionLogRepository repository) {
|
||||
@@ -54,74 +56,8 @@ public class SysExceptionLogService implements ISysExceptionLogService {
|
||||
|
||||
@Override
|
||||
public Mono<PageResponse<SysExceptionLog>> findExceptionLogsByPage(PageRequest pageRequest) {
|
||||
Flux<SysExceptionLog> allLogs = repository.findAllByOrderByCreateTimeDesc();
|
||||
|
||||
if (pageRequest.getKeyword() != null && !pageRequest.getKeyword().isEmpty()) {
|
||||
String keyword = pageRequest.getKeyword().toLowerCase();
|
||||
allLogs = allLogs
|
||||
.filter(log -> (log.getUsername() != null && log.getUsername().toLowerCase().contains(keyword)) ||
|
||||
(log.getTitle() != null && log.getTitle().toLowerCase().contains(keyword)) ||
|
||||
(log.getExceptionName() != null && log.getExceptionName().toLowerCase().contains(keyword)));
|
||||
}
|
||||
|
||||
return allLogs
|
||||
.collectList()
|
||||
.map(list -> {
|
||||
if (pageRequest.getSort() != null && !pageRequest.getSort().isEmpty()) {
|
||||
list.sort((a, b) -> {
|
||||
int comparison = 0;
|
||||
if ("username".equals(pageRequest.getSort())) {
|
||||
comparison = compareStrings(a.getUsername(), b.getUsername());
|
||||
} else if ("title".equals(pageRequest.getSort())) {
|
||||
comparison = compareStrings(a.getTitle(), b.getTitle());
|
||||
} else if ("createTime".equals(pageRequest.getSort())) {
|
||||
comparison = compareLocalDateTimes(a.getCreateTime(), b.getCreateTime());
|
||||
}
|
||||
return "desc".equalsIgnoreCase(pageRequest.getOrder()) ? -comparison : comparison;
|
||||
});
|
||||
}
|
||||
return list;
|
||||
})
|
||||
.zipWith(repository.count())
|
||||
.map(tuple -> {
|
||||
List<SysExceptionLog> all = tuple.getT1();
|
||||
long totalCount = tuple.getT2();
|
||||
int totalPages = (int) Math.ceil((double) totalCount / pageRequest.getSize());
|
||||
|
||||
int fromIndex = pageRequest.getPage() * pageRequest.getSize();
|
||||
int toIndex = Math.min(fromIndex + pageRequest.getSize(), all.size());
|
||||
|
||||
List<SysExceptionLog> pageData = fromIndex < all.size()
|
||||
? all.subList(fromIndex, toIndex)
|
||||
: List.of();
|
||||
|
||||
return new PageResponse<SysExceptionLog>(
|
||||
pageData,
|
||||
totalPages,
|
||||
totalCount,
|
||||
pageRequest.getPage(),
|
||||
pageRequest.getSize());
|
||||
});
|
||||
}
|
||||
|
||||
private int compareStrings(String a, String b) {
|
||||
if (a == null && b == null)
|
||||
return 0;
|
||||
if (a == null)
|
||||
return -1;
|
||||
if (b == null)
|
||||
return 1;
|
||||
return a.compareTo(b);
|
||||
}
|
||||
|
||||
private int compareLocalDateTimes(LocalDateTime a, LocalDateTime b) {
|
||||
if (a == null && b == null)
|
||||
return 0;
|
||||
if (a == null)
|
||||
return -1;
|
||||
if (b == null)
|
||||
return 1;
|
||||
return a.compareTo(b);
|
||||
logger.info("分页查询异常日志: page={}, size={}", pageRequest.getPage(), pageRequest.getSize());
|
||||
return repository.findExceptionLogsByPage(pageRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+13
-72
@@ -5,13 +5,13 @@ import cn.novalon.manage.sys.core.repository.ISysLoginLogRepository;
|
||||
import cn.novalon.manage.sys.core.service.ISysLoginLogService;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 登录日志服务实现类
|
||||
@@ -22,6 +22,7 @@ import java.util.List;
|
||||
@Service
|
||||
public class SysLoginLogService implements ISysLoginLogService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SysLoginLogService.class);
|
||||
private final ISysLoginLogRepository repository;
|
||||
|
||||
public SysLoginLogService(ISysLoginLogRepository repository) {
|
||||
@@ -55,72 +56,8 @@ public class SysLoginLogService implements ISysLoginLogService {
|
||||
|
||||
@Override
|
||||
public Mono<PageResponse<SysLoginLog>> findLoginLogsByPage(PageRequest pageRequest) {
|
||||
Flux<SysLoginLog> allLogs = repository.findAllByOrderByLoginTimeDesc();
|
||||
|
||||
if (pageRequest.getKeyword() != null && !pageRequest.getKeyword().isEmpty()) {
|
||||
String keyword = pageRequest.getKeyword().toLowerCase();
|
||||
allLogs = allLogs.filter(log ->
|
||||
(log.getUsername() != null && log.getUsername().toLowerCase().contains(keyword)) ||
|
||||
(log.getIp() != null && log.getIp().toLowerCase().contains(keyword)) ||
|
||||
(log.getMessage() != null && log.getMessage().toLowerCase().contains(keyword))
|
||||
);
|
||||
}
|
||||
|
||||
return allLogs
|
||||
.collectList()
|
||||
.flatMap(list -> {
|
||||
List<SysLoginLog> sortedList = new ArrayList<>(list);
|
||||
|
||||
if (pageRequest.getSort() != null && !pageRequest.getSort().isEmpty()) {
|
||||
sortedList.sort((a, b) -> {
|
||||
int comparison = 0;
|
||||
if ("username".equals(pageRequest.getSort())) {
|
||||
comparison = compareStrings(a.getUsername(), b.getUsername());
|
||||
} else if ("ip".equals(pageRequest.getSort())) {
|
||||
comparison = compareStrings(a.getIp(), b.getIp());
|
||||
} else if ("loginTime".equals(pageRequest.getSort())) {
|
||||
comparison = compareLocalDateTimes(a.getLoginTime(), b.getLoginTime());
|
||||
}
|
||||
return "desc".equalsIgnoreCase(pageRequest.getOrder()) ? -comparison : comparison;
|
||||
});
|
||||
}
|
||||
|
||||
return Mono.just(sortedList);
|
||||
})
|
||||
.zipWith(repository.count())
|
||||
.map(tuple -> {
|
||||
List<SysLoginLog> all = tuple.getT1();
|
||||
long totalCount = tuple.getT2();
|
||||
int totalPages = (int) Math.ceil((double) totalCount / pageRequest.getSize());
|
||||
|
||||
int fromIndex = pageRequest.getPage() * pageRequest.getSize();
|
||||
int toIndex = Math.min(fromIndex + pageRequest.getSize(), all.size());
|
||||
|
||||
List<SysLoginLog> pageData = fromIndex < all.size()
|
||||
? all.subList(fromIndex, toIndex)
|
||||
: List.of();
|
||||
|
||||
return new PageResponse<SysLoginLog>(
|
||||
pageData,
|
||||
totalPages,
|
||||
totalCount,
|
||||
pageRequest.getPage(),
|
||||
pageRequest.getSize());
|
||||
});
|
||||
}
|
||||
|
||||
private int compareStrings(String a, String b) {
|
||||
if (a == null && b == null) return 0;
|
||||
if (a == null) return -1;
|
||||
if (b == null) return 1;
|
||||
return a.compareTo(b);
|
||||
}
|
||||
|
||||
private int compareLocalDateTimes(LocalDateTime a, LocalDateTime b) {
|
||||
if (a == null && b == null) return 0;
|
||||
if (a == null) return -1;
|
||||
if (b == null) return 1;
|
||||
return a.compareTo(b);
|
||||
logger.info("分页查询登录日志: page={}, size={}", pageRequest.getPage(), pageRequest.getSize());
|
||||
return repository.findLoginLogsByPage(pageRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,9 +67,13 @@ public class SysLoginLogService implements ISysLoginLogService {
|
||||
|
||||
@Override
|
||||
public Mono<Long> countToday() {
|
||||
LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
|
||||
LocalDateTime todayEnd = todayStart.plusDays(1);
|
||||
return repository.findByLoginTimeBetweenOrderByLoginTimeDesc(todayStart, todayEnd)
|
||||
.count();
|
||||
return repository.countToday();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysLoginLog> findRecent(int limit) {
|
||||
logger.info("获取最近{}条登录日志", limit);
|
||||
return repository.findAllByOrderByLoginTimeDesc()
|
||||
.take(limit);
|
||||
}
|
||||
}
|
||||
+8
@@ -6,6 +6,7 @@ import cn.novalon.manage.sys.core.service.ISysLoginLogService;
|
||||
import cn.novalon.manage.sys.core.service.ISysExceptionLogService;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -83,6 +84,13 @@ public class SysLogHandler {
|
||||
.flatMap(count -> ServerResponse.ok().bodyValue(count));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取最近登录日志", description = "获取最近N条登录日志记录")
|
||||
public Mono<ServerResponse> getRecentLoginLogs(ServerRequest request) {
|
||||
int limit = Integer.parseInt(request.queryParam("limit").orElse("10"));
|
||||
return ServerResponse.ok()
|
||||
.body(loginLogService.findRecent(limit), SysLoginLog.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有异常日志", description = "获取系统中所有异常日志列表")
|
||||
public Mono<ServerResponse> getAllExceptionLogs(ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
|
||||
+88
@@ -72,6 +72,50 @@ class SysLogHandlerTest {
|
||||
verify(loginLogService).findAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAllLoginLogs_WithPagination() {
|
||||
PageResponse<SysLoginLog> pageResponse = new PageResponse<>();
|
||||
pageResponse.setContent(java.util.Collections.singletonList(testLoginLog));
|
||||
pageResponse.setTotalElements(1L);
|
||||
pageResponse.setTotalPages(1);
|
||||
|
||||
when(loginLogService.findLoginLogsByPage(any())).thenReturn(Mono.just(pageResponse));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("page", "0")
|
||||
.queryParam("size", "10")
|
||||
.build();
|
||||
Mono<ServerResponse> response = logHandler.getAllLoginLogs(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(loginLogService).findLoginLogsByPage(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAllLoginLogs_WithOnlyPageParam() {
|
||||
PageResponse<SysLoginLog> pageResponse = new PageResponse<>();
|
||||
pageResponse.setContent(java.util.Collections.singletonList(testLoginLog));
|
||||
pageResponse.setTotalElements(1L);
|
||||
|
||||
when(loginLogService.findLoginLogsByPage(any())).thenReturn(Mono.just(pageResponse));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("page", "0")
|
||||
.build();
|
||||
Mono<ServerResponse> response = logHandler.getAllLoginLogs(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(loginLogService).findLoginLogsByPage(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetLoginLogById() {
|
||||
when(loginLogService.findById(1L)).thenReturn(Mono.just(testLoginLog));
|
||||
@@ -203,6 +247,50 @@ class SysLogHandlerTest {
|
||||
verify(exceptionLogService).findAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAllExceptionLogs_WithPagination() {
|
||||
PageResponse<SysExceptionLog> pageResponse = new PageResponse<>();
|
||||
pageResponse.setContent(java.util.Collections.singletonList(testExceptionLog));
|
||||
pageResponse.setTotalElements(1L);
|
||||
pageResponse.setTotalPages(1);
|
||||
|
||||
when(exceptionLogService.findExceptionLogsByPage(any())).thenReturn(Mono.just(pageResponse));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("page", "0")
|
||||
.queryParam("size", "10")
|
||||
.build();
|
||||
Mono<ServerResponse> response = logHandler.getAllExceptionLogs(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(exceptionLogService).findExceptionLogsByPage(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAllExceptionLogs_WithOnlySizeParam() {
|
||||
PageResponse<SysExceptionLog> pageResponse = new PageResponse<>();
|
||||
pageResponse.setContent(java.util.Collections.singletonList(testExceptionLog));
|
||||
pageResponse.setTotalElements(1L);
|
||||
|
||||
when(exceptionLogService.findExceptionLogsByPage(any())).thenReturn(Mono.just(pageResponse));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("size", "10")
|
||||
.build();
|
||||
Mono<ServerResponse> response = logHandler.getAllExceptionLogs(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(exceptionLogService).findExceptionLogsByPage(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetExceptionLogById() {
|
||||
when(exceptionLogService.findById(1L)).thenReturn(Mono.just(testExceptionLog));
|
||||
|
||||
+45
@@ -7,6 +7,7 @@ import cn.novalon.manage.sys.dto.request.UserRegisterRequest;
|
||||
import cn.novalon.manage.sys.dto.request.UserUpdateRequest;
|
||||
import cn.novalon.manage.sys.core.command.CreateUserCommand;
|
||||
import cn.novalon.manage.sys.core.command.UpdateUserCommand;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -74,6 +75,50 @@ class SysUserHandlerTest {
|
||||
verify(userService).findAll(anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAllUsers_WithPagination() {
|
||||
PageResponse<SysUser> pageResponse = new PageResponse<>();
|
||||
pageResponse.setContent(java.util.Collections.singletonList(testUser));
|
||||
pageResponse.setTotalElements(1L);
|
||||
pageResponse.setTotalPages(1);
|
||||
|
||||
when(userService.findUsersByPage(any())).thenReturn(Mono.just(pageResponse));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("page", "0")
|
||||
.queryParam("size", "10")
|
||||
.build();
|
||||
Mono<ServerResponse> response = userHandler.getAllUsers(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService).findUsersByPage(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAllUsers_WithOnlyPageParam() {
|
||||
PageResponse<SysUser> pageResponse = new PageResponse<>();
|
||||
pageResponse.setContent(java.util.Collections.singletonList(testUser));
|
||||
pageResponse.setTotalElements(1L);
|
||||
|
||||
when(userService.findUsersByPage(any())).thenReturn(Mono.just(pageResponse));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("page", "0")
|
||||
.build();
|
||||
Mono<ServerResponse> response = userHandler.getAllUsers(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(userService).findUsersByPage(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserCount() {
|
||||
when(userService.count()).thenReturn(Mono.just(10L));
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package cn.novalon.manage.sys.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
public class PasswordHashGenerator {
|
||||
|
||||
@Test
|
||||
public void generatePasswordHash() {
|
||||
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(12);
|
||||
|
||||
String password = "Test@123";
|
||||
String hash = passwordEncoder.encode(password);
|
||||
|
||||
System.out.println("========================================");
|
||||
System.out.println("密码: " + password);
|
||||
System.out.println("哈希: " + hash);
|
||||
System.out.println("========================================");
|
||||
|
||||
boolean matches = passwordEncoder.matches(password, hash);
|
||||
System.out.println("验证结果: " + matches);
|
||||
|
||||
String hash2b = "$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/X4.VTtYA/7.J6LlZy";
|
||||
boolean matches2b = passwordEncoder.matches(password, hash2b);
|
||||
System.out.println("验证$2b$哈希结果: " + matches2b);
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,45 @@
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.12</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>jacoco-check</id>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<rule>
|
||||
<element>PACKAGE</element>
|
||||
<limits>
|
||||
<limit>
|
||||
<counter>LINE</counter>
|
||||
<value>COVEREDRATIO</value>
|
||||
<minimum>0.80</minimum>
|
||||
</limit>
|
||||
</limits>
|
||||
</rule>
|
||||
</rules>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonarsource.scanner.maven</groupId>
|
||||
<artifactId>sonar-maven-plugin</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user