feat: extend operation log service and repository with pagination support

This commit is contained in:
张翔
2026-03-18 22:34:43 +08:00
parent 157aee2ffc
commit 8a0cd64829
81 changed files with 8842 additions and 509 deletions
@@ -1,5 +1,7 @@
package cn.novalon.manage.db.repository;
import cn.novalon.manage.common.dto.PageRequest;
import cn.novalon.manage.common.dto.PageResponse;
import cn.novalon.manage.sys.core.domain.OperationLog;
import cn.novalon.manage.sys.core.repository.IOperationLogRepository;
import cn.novalon.manage.db.converter.OperationLogConverter;
@@ -10,6 +12,8 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 操作日志仓储实现类
@@ -48,7 +52,7 @@ public class OperationLogRepository implements IOperationLogRepository {
@Override
public Flux<OperationLog> findAll() {
return operationLogDao.findAll()
return operationLogDao.findByDeletedAtIsNull()
.map(operationLogConverter::toDomain);
}
@@ -58,6 +62,88 @@ public class OperationLogRepository implements IOperationLogRepository {
.map(operationLogConverter::toDomain);
}
@Override
public Mono<PageResponse<OperationLog>> findOperationLogsByPage(PageRequest pageRequest) {
Flux<OperationLog> allLogs = operationLogDao.findByDeletedAtIsNull()
.map(operationLogConverter::toDomain);
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.getOperation() != null && log.getOperation().toLowerCase().contains(keyword)) ||
(log.getIp() != null && log.getIp().toLowerCase().contains(keyword))
);
}
return allLogs
.collectList()
.flatMap(list -> {
List<OperationLog> 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 ("operation".equals(pageRequest.getSort())) {
comparison = compareStrings(a.getOperation(), b.getOperation());
} else if ("duration".equals(pageRequest.getSort())) {
comparison = compareLongs(a.getDuration(), b.getDuration());
} else if ("status".equals(pageRequest.getSort())) {
comparison = compareStrings(a.getStatus(), b.getStatus());
} else {
comparison = compareLocalDateTimes(a.getCreatedAt(), b.getCreatedAt());
}
return "desc".equalsIgnoreCase(pageRequest.getOrder()) ? -comparison : comparison;
});
}
return Mono.just(sortedList);
})
.zipWith(operationLogDao.countByDeletedAtIsNull())
.map(tuple -> {
List<OperationLog> 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<OperationLog> pageData = fromIndex < all.size()
? all.subList(fromIndex, toIndex)
: List.of();
return new PageResponse<OperationLog>(
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 compareLongs(Long a, Long 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);
}
@Override
public Mono<Long> count() {
return operationLogDao.countByDeletedAtIsNull();
@@ -0,0 +1 @@
cn.novalon.manage.db.config.RepositoryScanConfig
@@ -0,0 +1,126 @@
-- Novalon管理系统E2E测试数据初始化脚本
-- 版本: V3
-- 描述: 为E2E测试准备测试数据
-- 清理测试数据(保留管理员)
DELETE FROM sys_user_message WHERE user_id > 1;
DELETE FROM users WHERE id > 1;
DELETE FROM sys_notice WHERE id > 0;
DELETE FROM sys_file WHERE id > 0;
DELETE FROM sys_exception_log WHERE id > 0;
DELETE FROM sys_login_log WHERE id > 0;
DELETE FROM sys_dict_data WHERE dict_type NOT IN ('user_status');
DELETE FROM sys_dict_type WHERE dict_type NOT IN ('user_status');
DELETE FROM sys_config WHERE id > 0;
DELETE FROM menus WHERE id > 0;
DELETE FROM roles WHERE id > 1;
-- 插入测试角色
INSERT INTO roles (role_name, role_key, role_sort, status, create_by, update_by)
VALUES
('普通用户', 'user', 2, 1, 'system', 'system'),
('测试角色', 'test_role', 3, 1, 'system', 'system'),
('受限角色', 'limited_role', 4, 1, 'system', 'system');
-- 插入测试用户
INSERT INTO users (username, password, email, phone, role_id, status, create_by, update_by)
VALUES
('testuser', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'test@example.com', '13800138001', 2, 1, 'system', 'system'),
('limiteduser', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'limited@example.com', '13800138002', 4, 1, 'system', 'system'),
('normaluser', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'normal@example.com', '13800138003', 2, 1, 'system', 'system');
-- 插入测试菜单
INSERT INTO menus (menu_name, parent_id, order_num, menu_type, perms, component, status, create_by, update_by)
VALUES
('系统管理', 0, 1, 'M', '', '', 1, 'system', 'system'),
('用户管理', 1, 1, 'C', 'system:user:list', 'system/user/index', 1, 'system', 'system'),
('角色管理', 1, 2, 'C', 'system:role:list', 'system/role/index', 1, 'system', 'system'),
('菜单管理', 1, 3, 'C', 'system:menu:list', 'system/menu/index', 1, 'system', 'system'),
('系统配置', 1, 4, 'C', 'system:config:list', 'system/config/index', 1, 'system', 'system'),
('监控中心', 0, 2, 'M', '', '', 1, 'system', 'system'),
('在线用户', 6, 1, 'C', 'monitor:online:list', 'monitor/online/index', 1, 'system', 'system'),
('登录日志', 6, 2, 'C', 'monitor:loginlog:list', 'monitor/loginlog/index', 1, 'system', 'system');
-- 插入测试字典类型
INSERT INTO sys_dict_type (dict_name, dict_type, status, remark, create_by, update_by)
VALUES
('菜单状态', 'menu_status', '0', '菜单状态列表', 'system', 'system'),
('角色状态', 'role_status', '0', '角色状态列表', 'system', 'system'),
('系统开关', 'sys_normal_disable', '0', '系统开关列表', 'system', 'system'),
('任务状态', 'job_status', '0', '任务状态列表', 'system', 'system'),
('任务分组', 'job_group', '0', '任务分组列表', 'system', 'system');
-- 插入测试字典数据
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, update_by)
VALUES
-- 菜单状态
(1, '正常', '0', 'menu_status', '', 'primary', 'N', '0', 'system', 'system'),
(2, '停用', '1', 'menu_status', '', 'danger', 'N', '0', 'system', 'system'),
-- 角色状态
(1, '正常', '0', 'role_status', '', 'primary', 'N', '0', 'system', 'system'),
(2, '停用', '1', 'role_status', '', 'danger', 'N', '0', 'system', 'system'),
-- 系统开关
(1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'system', 'system'),
(2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'system', 'system'),
-- 任务状态
(1, '正常', '0', 'job_status', '', 'primary', 'Y', '0', 'system', 'system'),
(2, '暂停', '1', 'job_status', '', 'danger', 'N', '0', 'system', 'system'),
-- 任务分组
(1, '默认', 'DEFAULT', 'job_group', '', '', 'Y', '0', 'system', 'system'),
(2, '系统', 'SYSTEM', 'job_group', '', '', 'N', '0', 'system', 'system');
-- 插入测试系统配置
INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, update_by)
VALUES
('用户管理-用户初始密码', 'sys.user.initPassword', '123456', 'Y', 'system', 'system'),
('主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'system', 'system'),
('用户自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'system', 'system'),
('用户自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'system', 'system'),
('账号自助-密码验证码', 'sys.account.pwdCaptchaEnabled', 'true', 'Y', 'system', 'system');
-- 插入测试系统公告
INSERT INTO sys_notice (notice_title, notice_type, notice_content, status, create_by, update_by)
VALUES
('系统维护通知', '1', '系统将于今晚22:00-23:00进行维护,请提前做好准备。', '0', 'admin', 'admin'),
('新功能上线通知', '2', '系统新增了用户管理功能,欢迎大家使用!', '0', 'admin', 'admin'),
('安全提醒', '1', '请定期修改密码,确保账户安全。', '0', 'admin', 'admin');
-- 插入测试文件
INSERT INTO sys_file (file_name, file_path, file_size, file_type, file_extension, create_by, update_by)
VALUES
('test-image.jpg', '/uploads/images/test-image.jpg', 102400, 'image/jpeg', 'jpg', 'system', 'system'),
('test-document.pdf', '/uploads/documents/test-document.pdf', 204800, 'application/pdf', 'pdf', 'system', 'system'),
('test-data.xlsx', '/uploads/data/test-data.xlsx', 51200, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xlsx', 'system', 'system');
-- 插入测试登录日志
INSERT INTO sys_login_log (username, ip, location, browser, os, status, message, login_time)
VALUES
('admin', '127.0.0.1', '内网IP', 'Chrome', 'Windows 10', '0', '登录成功', NOW() - INTERVAL '1 day'),
('admin', '127.0.0.1', '内网IP', 'Chrome', 'Windows 10', '0', '登录成功', NOW() - INTERVAL '2 hours'),
('testuser', '127.0.0.1', '内网IP', 'Firefox', 'Mac OS', '0', '登录成功', NOW() - INTERVAL '3 hours'),
('testuser', '127.0.0.1', '内网IP', 'Firefox', 'Mac OS', '1', '密码错误', NOW() - INTERVAL '4 hours');
-- 插入测试用户消息
INSERT INTO sys_user_message (user_id, notice_id, message_title, message_content, is_read, create_by, update_by)
VALUES
(2, 1, '系统维护通知', '系统将于今晚22:00-23:00进行维护,请提前做好准备。', '0', 'admin', 'admin'),
(2, 2, '新功能上线通知', '系统新增了用户管理功能,欢迎大家使用!', '0', 'admin', 'admin'),
(3, 3, '安全提醒', '请定期修改密码,确保账户安全。', '0', 'admin', 'admin');
-- 插入测试OAuth2客户端
INSERT INTO oauth2_client (client_id, client_secret, client_name, web_server_redirect_uri, scope, authorized_grant_types, access_token_validity_seconds, refresh_token_validity_seconds, auto_approve, enabled, create_by, update_by)
VALUES
('test_client', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '测试客户端', 'http://localhost:3001/callback', 'read,write', 'password,refresh_token', 3600, 7200, 'true', 'true', 'system', 'system');
-- 更新序列值
SELECT setval('users_id_seq', (SELECT MAX(id) FROM users));
SELECT setval('roles_id_seq', (SELECT MAX(id) FROM roles));
SELECT setval('menus_id_seq', (SELECT MAX(id) FROM menus));
SELECT setval('sys_dict_type_id_seq', (SELECT MAX(id) FROM sys_dict_type));
SELECT setval('sys_dict_data_id_seq', (SELECT MAX(id) FROM sys_dict_data));
SELECT setval('sys_config_id_seq', (SELECT MAX(id) FROM sys_config));
SELECT setval('sys_notice_id_seq', (SELECT MAX(id) FROM sys_notice));
SELECT setval('sys_file_id_seq', (SELECT MAX(id) FROM sys_file));
SELECT setval('sys_login_log_id_seq', (SELECT MAX(id) FROM sys_login_log));
SELECT setval('sys_user_message_id_seq', (SELECT MAX(id) FROM sys_user_message));
SELECT setval('oauth2_client_id_seq', (SELECT MAX(id) FROM oauth2_client));
@@ -0,0 +1,10 @@
-- 更新管理员密码为已知密码
-- BCrypt哈希值对应明文密码: admin123
UPDATE users
SET password = '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi'
WHERE username = 'admin';
-- 确保管理员用户状态为启用
UPDATE users
SET status = 1
WHERE username = 'admin';