diff --git a/.gitignore b/.gitignore index 7756618..7a1739e 100644 --- a/.gitignore +++ b/.gitignore @@ -91,7 +91,7 @@ $RECYCLE.BIN/ # Logs logs/ *.log -log/ +/log/ # Testing coverage/ diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/BaseDomain.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/BaseDomain.java index 4ae64f9..8c9b09d 100644 --- a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/BaseDomain.java +++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/BaseDomain.java @@ -76,10 +76,29 @@ public abstract class BaseDomain { return this.id; } + /** + * 删除(幂等操作) + * 已删除的对象不会更新删除时间 + */ + public void delete() { + if (this.deletedAt == null) { + this.deletedAt = LocalDateTime.now(); + } + } + + /** + * 恢复已删除的对象 + */ + public void restore() { + this.deletedAt = null; + } + @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; BaseDomain that = (BaseDomain) o; return id != null && id.equals(that.id); } diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysPermission.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysPermission.java index 1f45d9a..72e76a0 100644 --- a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysPermission.java +++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysPermission.java @@ -77,18 +77,4 @@ public class SysPermission extends BaseDomain { public void setStatus(Integer status) { this.status = status; } - - /** - * 删除权限 - */ - public void delete() { - this.deletedAt = java.time.LocalDateTime.now(); - } - - /** - * 恢复权限 - */ - public void restore() { - this.deletedAt = null; - } } \ No newline at end of file diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysRole.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysRole.java index 4d376b6..f822793 100644 --- a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysRole.java +++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysRole.java @@ -57,18 +57,4 @@ public class SysRole extends BaseDomain { public void setStatus(Integer status) { this.status = status; } - - /** - * 删除角色 - */ - public void delete() { - this.deletedAt = LocalDateTime.now(); - } - - /** - * 恢复角色 - */ - public void restore() { - this.deletedAt = null; - } } diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysUser.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysUser.java index 8c365bc..380fffb 100644 --- a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysUser.java +++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/SysUser.java @@ -100,11 +100,4 @@ public class SysUser extends BaseDomain { public void setStatus(Integer status) { this.status = status; } - - /** - * 删除用户 - */ - public void delete() { - this.deletedAt = LocalDateTime.now(); - } } diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/handler/log/OperationLogHandler.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/handler/log/OperationLogHandler.java new file mode 100644 index 0000000..1349de3 --- /dev/null +++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/handler/log/OperationLogHandler.java @@ -0,0 +1,153 @@ +package cn.novalon.gym.manage.sys.handler.log; + +import cn.novalon.gym.manage.sys.core.domain.OperationLog; +import cn.novalon.gym.manage.sys.core.query.OperationLogQuery; +import cn.novalon.gym.manage.sys.core.service.IOperationLogService; +import cn.novalon.gym.manage.sys.core.util.ExcelExportUtil; +import cn.novalon.gym.manage.common.dto.PageRequest; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * 操作日志处理器 + * + * 文件定义:处理操作日志相关的HTTP请求 + * 涉及业务:操作日志查询、分页、统计、导出 + * 算法:使用WebFlux函数式编程模型处理响应式请求 + * + * @author 张翔 + * @date 2026-03-18 + */ +@Component +@Tag(name = "操作日志", description = "操作日志相关操作") +public class OperationLogHandler { + + private final IOperationLogService logService; + + public OperationLogHandler(IOperationLogService logService) { + this.logService = logService; + } + + @Operation(summary = "获取所有操作日志", description = "获取系统中所有操作日志列表") + public Mono getAllOperationLogs(ServerRequest request) { + return ServerResponse.ok() + .body(logService.findAll(), OperationLog.class); + } + + @Operation(summary = "根据ID获取操作日志", description = "根据操作日志ID获取详细信息") + public Mono getOperationLogById(ServerRequest request) { + Long id = Long.valueOf(request.pathVariable("id")); + return logService.findById(id) + .flatMap(log -> ServerResponse.ok().bodyValue(log)) + .switchIfEmpty(ServerResponse.notFound().build()); + } + + @Operation(summary = "分页获取操作日志", description = "根据分页参数获取操作日志列表") + public Mono getOperationLogsByPage(ServerRequest request) { + int page = Integer.parseInt(request.queryParam("page").orElse("0")); + int size = Integer.parseInt(request.queryParam("size").orElse("10")); + String sort = request.queryParam("sort").orElse("createdAt"); + String order = request.queryParam("order").orElse("desc"); + String keyword = request.queryParam("keyword").orElse(null); + String username = request.queryParam("username").orElse(null); + String operation = request.queryParam("operation").orElse(null); + String status = request.queryParam("status").orElse(null); + String startTimeStr = request.queryParam("startTime").orElse(null); + String endTimeStr = request.queryParam("endTime").orElse(null); + String ip = request.queryParam("ip").orElse(null); + String method = request.queryParam("method").orElse(null); + + PageRequest pageRequest = new PageRequest(); + pageRequest.setPage(page); + pageRequest.setSize(size); + pageRequest.setSort(sort); + pageRequest.setOrder(order); + pageRequest.setKeyword(keyword); + + OperationLogQuery query = new OperationLogQuery(); + query.setUsername(username); + query.setOperation(operation); + query.setStatus(status); + query.setKeyword(keyword); + query.setIp(ip); + query.setMethod(method); + + if (startTimeStr != null && !startTimeStr.isEmpty()) { + query.setStartTime(LocalDateTime.parse(startTimeStr)); + } + if (endTimeStr != null && !endTimeStr.isEmpty()) { + query.setEndTime(LocalDateTime.parse(endTimeStr)); + } + + return logService.findByQueryWithPagination(query, pageRequest) + .flatMap(response -> ServerResponse.ok().bodyValue(response)); + } + + @Operation(summary = "获取操作日志总数", description = "获取系统中操作日志总数") + public Mono getOperationLogCount(ServerRequest request) { + return logService.count() + .flatMap(count -> ServerResponse.ok().bodyValue(count)); + } + + @Operation(summary = "创建操作日志", description = "手动创建操作日志") + public Mono createOperationLog(ServerRequest request) { + return request.bodyToMono(OperationLog.class) + .flatMap(logService::save) + .flatMap(log -> ServerResponse.status(HttpStatus.CREATED).bodyValue(log)); + } + + @Operation(summary = "导出操作日志", description = "导出操作日志为Excel文件") + public Mono exportOperationLogs(ServerRequest request) { + String username = request.queryParam("username").orElse(null); + String operation = request.queryParam("operation").orElse(null); + String status = request.queryParam("status").orElse(null); + String startTimeStr = request.queryParam("startTime").orElse(null); + String endTimeStr = request.queryParam("endTime").orElse(null); + String ip = request.queryParam("ip").orElse(null); + String method = request.queryParam("method").orElse(null); + String keyword = request.queryParam("keyword").orElse(null); + + OperationLogQuery query = new OperationLogQuery(); + query.setUsername(username); + query.setOperation(operation); + query.setStatus(status); + query.setIp(ip); + query.setMethod(method); + query.setKeyword(keyword); + + if (startTimeStr != null && !startTimeStr.isEmpty()) { + query.setStartTime(LocalDateTime.parse(startTimeStr)); + } + if (endTimeStr != null && !endTimeStr.isEmpty()) { + query.setEndTime(LocalDateTime.parse(endTimeStr)); + } + + return logService.findAll() + .collectList() + .flatMap(logs -> { + try { + byte[] excelData = ExcelExportUtil.exportOperationLogs(logs); + String filename = "operation_logs_" + + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + + ".xlsx"; + + return ServerResponse.ok() + .header("Content-Disposition", "attachment; filename=\"" + filename + "\"") + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .bodyValue(excelData); + } catch (Exception e) { + return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR) + .bodyValue("导出失败: " + e.getMessage()); + } + }); + } +} diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/handler/log/SysLogHandler.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/handler/log/SysLogHandler.java new file mode 100644 index 0000000..4717990 --- /dev/null +++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/handler/log/SysLogHandler.java @@ -0,0 +1,139 @@ +package cn.novalon.gym.manage.sys.handler.log; + +import cn.novalon.gym.manage.sys.core.domain.SysLoginLog; +import cn.novalon.gym.manage.sys.core.domain.SysExceptionLog; +import cn.novalon.gym.manage.sys.core.service.ISysLoginLogService; +import cn.novalon.gym.manage.sys.core.service.ISysExceptionLogService; +import cn.novalon.gym.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; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +/** + * 系统日志处理器 + * + * @author 张翔 + * @date 2026-03-14 + */ +@Component +@Tag(name = "日志管理", description = "登录日志和异常日志相关操作") +public class SysLogHandler { + + private final ISysLoginLogService loginLogService; + private final ISysExceptionLogService exceptionLogService; + + public SysLogHandler(ISysLoginLogService loginLogService, ISysExceptionLogService exceptionLogService) { + this.loginLogService = loginLogService; + this.exceptionLogService = exceptionLogService; + } + + @Operation(summary = "获取所有登录日志", description = "获取系统中所有登录日志列表") + public Mono getAllLoginLogs(ServerRequest request) { + return ServerResponse.ok() + .body(loginLogService.findAll(), SysLoginLog.class); + } + + @Operation(summary = "根据ID获取登录日志", description = "根据登录日志ID获取详细信息") + public Mono getLoginLogById(ServerRequest request) { + Long id = Long.valueOf(request.pathVariable("id")); + return loginLogService.findById(id) + .flatMap(log -> ServerResponse.ok().bodyValue(log)) + .switchIfEmpty(ServerResponse.notFound().build()); + } + + @Operation(summary = "创建登录日志", description = "创建新的登录日志") + public Mono createLoginLog(ServerRequest request) { + return request.bodyToMono(SysLoginLog.class) + .flatMap(loginLogService::save) + .flatMap(log -> ServerResponse.status(HttpStatus.CREATED).bodyValue(log)); + } + + @Operation(summary = "分页获取登录日志", description = "根据分页参数获取登录日志列表") + public Mono getLoginLogsByPage(ServerRequest request) { + int page = Integer.parseInt(request.queryParam("page").orElse("0")); + int size = Integer.parseInt(request.queryParam("size").orElse("10")); + String sort = request.queryParam("sort").orElse("loginTime"); + String order = request.queryParam("order").orElse("desc"); + String keyword = request.queryParam("keyword").orElse(null); + + PageRequest pageRequest = new PageRequest(); + pageRequest.setPage(page); + pageRequest.setSize(size); + pageRequest.setSort(sort); + pageRequest.setOrder(order); + pageRequest.setKeyword(keyword); + + return loginLogService.findLoginLogsByPage(pageRequest) + .flatMap(response -> ServerResponse.ok().bodyValue(response)); + } + + @Operation(summary = "获取登录日志总数", description = "获取系统中登录日志总数") + public Mono getLoginLogCount(ServerRequest request) { + return loginLogService.count() + .flatMap(count -> ServerResponse.ok().bodyValue(count)); + } + + @Operation(summary = "获取今日登录次数", description = "获取今日登录次数统计") + public Mono getTodayLoginCount(ServerRequest request) { + return loginLogService.countToday() + .flatMap(count -> ServerResponse.ok().bodyValue(count)); + } + + @Operation(summary = "获取最近登录日志", description = "获取最近N条登录日志记录") + public Mono 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 getAllExceptionLogs(ServerRequest request) { + return ServerResponse.ok() + .body(exceptionLogService.findAll(), SysExceptionLog.class); + } + + @Operation(summary = "根据ID获取异常日志", description = "根据异常日志ID获取详细信息") + public Mono getExceptionLogById(ServerRequest request) { + Long id = Long.valueOf(request.pathVariable("id")); + return exceptionLogService.findById(id) + .flatMap(log -> ServerResponse.ok().bodyValue(log)) + .switchIfEmpty(ServerResponse.notFound().build()); + } + + @Operation(summary = "创建异常日志", description = "创建新的异常日志") + public Mono createExceptionLog(ServerRequest request) { + return request.bodyToMono(SysExceptionLog.class) + .flatMap(exceptionLogService::save) + .flatMap(log -> ServerResponse.status(HttpStatus.CREATED).bodyValue(log)); + } + + @Operation(summary = "分页获取异常日志", description = "根据分页参数获取异常日志列表") + public Mono getExceptionLogsByPage(ServerRequest request) { + int page = Integer.parseInt(request.queryParam("page").orElse("0")); + int size = Integer.parseInt(request.queryParam("size").orElse("10")); + String sort = request.queryParam("sort").orElse("id"); + String order = request.queryParam("order").orElse("desc"); + String keyword = request.queryParam("keyword").orElse(null); + + PageRequest pageRequest = new PageRequest(); + pageRequest.setPage(page); + pageRequest.setSize(size); + pageRequest.setSort(sort); + pageRequest.setOrder(order); + pageRequest.setKeyword(keyword); + + return exceptionLogService.findExceptionLogsByPage(pageRequest) + .flatMap(response -> ServerResponse.ok().bodyValue(response)); + } + + @Operation(summary = "获取异常日志总数", description = "获取系统中异常日志总数") + public Mono getExceptionLogCount(ServerRequest request) { + return exceptionLogService.count() + .flatMap(count -> ServerResponse.ok().bodyValue(count)); + } +} \ No newline at end of file diff --git a/gym-manage-api/manage-sys/src/test/java/cn/novalon/gym/manage/sys/core/domain/SysUserTest.java b/gym-manage-api/manage-sys/src/test/java/cn/novalon/gym/manage/sys/core/domain/SysUserTest.java index 520cf82..35f029a 100644 --- a/gym-manage-api/manage-sys/src/test/java/cn/novalon/gym/manage/sys/core/domain/SysUserTest.java +++ b/gym-manage-api/manage-sys/src/test/java/cn/novalon/gym/manage/sys/core/domain/SysUserTest.java @@ -59,7 +59,7 @@ class SysUserTest { assertNotNull(firstDeleteTime); assertNotNull(secondDeleteTime); - assertNotEquals(firstDeleteTime, secondDeleteTime); + assertEquals(firstDeleteTime, secondDeleteTime); } @Test diff --git a/gym-manage-api/manage-sys/src/test/java/cn/novalon/gym/manage/sys/handler/log/OperationLogHandlerTest.java b/gym-manage-api/manage-sys/src/test/java/cn/novalon/gym/manage/sys/handler/log/OperationLogHandlerTest.java new file mode 100644 index 0000000..4aa52ba --- /dev/null +++ b/gym-manage-api/manage-sys/src/test/java/cn/novalon/gym/manage/sys/handler/log/OperationLogHandlerTest.java @@ -0,0 +1,180 @@ +package cn.novalon.gym.manage.sys.handler.log; + +import cn.novalon.gym.manage.sys.core.domain.OperationLog; +import cn.novalon.gym.manage.sys.core.query.OperationLogQuery; +import cn.novalon.gym.manage.sys.core.service.IOperationLogService; +import cn.novalon.gym.manage.common.dto.PageRequest; +import cn.novalon.gym.manage.common.dto.PageResponse; +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 org.springframework.http.HttpStatus; +import org.springframework.mock.web.reactive.function.server.MockServerRequest; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.time.LocalDateTime; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class OperationLogHandlerTest { + + @Mock + private IOperationLogService logService; + + private OperationLogHandler logHandler; + private OperationLog testOperationLog; + + @BeforeEach + void setUp() { + logHandler = new OperationLogHandler(logService); + + testOperationLog = new OperationLog(); + testOperationLog.setId(1L); + testOperationLog.setUsername("testuser"); + testOperationLog.setOperation("测试操作"); + testOperationLog.setMethod("testMethod"); + testOperationLog.setParams("test params"); + testOperationLog.setDuration(100L); + testOperationLog.setIp("192.168.1.1"); + testOperationLog.setCreatedAt(LocalDateTime.now()); + } + + @Test + void testGetAllOperationLogs() { + when(logService.findAll()).thenReturn(Flux.just(testOperationLog)); + + ServerRequest request = MockServerRequest.builder().build(); + Mono response = logHandler.getAllOperationLogs(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(logService).findAll(); + } + + @Test + void testGetOperationLogById() { + when(logService.findById(1L)).thenReturn(Mono.just(testOperationLog)); + + ServerRequest request = MockServerRequest.builder() + .pathVariable("id", "1") + .build(); + Mono response = logHandler.getOperationLogById(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(logService).findById(1L); + } + + @Test + void testGetOperationLogById_NotFound() { + when(logService.findById(999L)).thenReturn(Mono.empty()); + + ServerRequest request = MockServerRequest.builder() + .pathVariable("id", "999") + .build(); + Mono response = logHandler.getOperationLogById(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.NOT_FOUND) + .verifyComplete(); + + verify(logService).findById(999L); + } + + @Test + void testGetOperationLogsByPage() { + PageResponse pageResponse = new PageResponse<>(); + pageResponse.setContent(java.util.Collections.singletonList(testOperationLog)); + pageResponse.setTotalElements(1L); + pageResponse.setTotalPages(1); + pageResponse.setPageSize(10); + pageResponse.setCurrentPage(0); + + when(logService.findByQueryWithPagination(any(OperationLogQuery.class), any(PageRequest.class))) + .thenReturn(Mono.just(pageResponse)); + + ServerRequest request = MockServerRequest.builder() + .queryParam("page", "0") + .queryParam("size", "10") + .queryParam("sort", "createdAt") + .queryParam("order", "desc") + .build(); + Mono response = logHandler.getOperationLogsByPage(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(logService).findByQueryWithPagination(any(OperationLogQuery.class), any(PageRequest.class)); + } + + @Test + void testGetOperationLogsByPageWithKeyword() { + PageResponse pageResponse = new PageResponse<>(); + pageResponse.setContent(java.util.Collections.singletonList(testOperationLog)); + pageResponse.setTotalElements(1L); + pageResponse.setTotalPages(1); + pageResponse.setPageSize(10); + pageResponse.setCurrentPage(0); + + when(logService.findByQueryWithPagination(any(OperationLogQuery.class), any(PageRequest.class))) + .thenReturn(Mono.just(pageResponse)); + + ServerRequest request = MockServerRequest.builder() + .queryParam("page", "0") + .queryParam("size", "10") + .queryParam("sort", "createdAt") + .queryParam("order", "desc") + .queryParam("keyword", "test") + .build(); + Mono response = logHandler.getOperationLogsByPage(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(logService).findByQueryWithPagination(any(OperationLogQuery.class), any(PageRequest.class)); + } + + @Test + void testGetOperationLogCount() { + when(logService.count()).thenReturn(Mono.just(100L)); + + ServerRequest request = MockServerRequest.builder().build(); + Mono response = logHandler.getOperationLogCount(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(logService).count(); + } + + @Test + void testCreateOperationLog() { + when(logService.save(any(OperationLog.class))).thenReturn(Mono.just(testOperationLog)); + + ServerRequest request = MockServerRequest.builder() + .body(Mono.just(testOperationLog)); + Mono response = logHandler.createOperationLog(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.CREATED) + .verifyComplete(); + + verify(logService).save(any(OperationLog.class)); + } +} diff --git a/gym-manage-api/manage-sys/src/test/java/cn/novalon/gym/manage/sys/handler/log/SysLogHandlerTest.java b/gym-manage-api/manage-sys/src/test/java/cn/novalon/gym/manage/sys/handler/log/SysLogHandlerTest.java new file mode 100644 index 0000000..5f43602 --- /dev/null +++ b/gym-manage-api/manage-sys/src/test/java/cn/novalon/gym/manage/sys/handler/log/SysLogHandlerTest.java @@ -0,0 +1,410 @@ +package cn.novalon.gym.manage.sys.handler.log; + +import cn.novalon.gym.manage.sys.core.domain.SysLoginLog; +import cn.novalon.gym.manage.sys.core.domain.SysExceptionLog; +import cn.novalon.gym.manage.sys.core.service.ISysLoginLogService; +import cn.novalon.gym.manage.sys.core.service.ISysExceptionLogService; +import cn.novalon.gym.manage.common.dto.PageResponse; +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 org.springframework.http.HttpStatus; +import org.springframework.mock.web.reactive.function.server.MockServerRequest; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.time.LocalDateTime; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class SysLogHandlerTest { + + @Mock + private ISysLoginLogService loginLogService; + + @Mock + private ISysExceptionLogService exceptionLogService; + + private SysLogHandler logHandler; + private SysLoginLog testLoginLog; + private SysExceptionLog testExceptionLog; + + @BeforeEach + void setUp() { + logHandler = new SysLogHandler(loginLogService, exceptionLogService); + + testLoginLog = new SysLoginLog(); + testLoginLog.setId(1L); + testLoginLog.setUsername("testuser"); + testLoginLog.setIp("192.168.1.1"); + testLoginLog.setStatus("1"); + testLoginLog.setLoginTime(LocalDateTime.now()); + + testExceptionLog = new SysExceptionLog(); + testExceptionLog.setId(1L); + testExceptionLog.setUsername("testuser"); + testExceptionLog.setTitle("test operation"); + testExceptionLog.setExceptionName("NullPointerException"); + testExceptionLog.setExceptionMsg("Test exception"); + testExceptionLog.setCreateTime(LocalDateTime.now()); + } + + @Test + void testGetAllLoginLogs() { + when(loginLogService.findAll()).thenReturn(Flux.just(testLoginLog)); + + ServerRequest request = MockServerRequest.builder().build(); + Mono response = logHandler.getAllLoginLogs(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(loginLogService).findAll(); + } + + @Test + void testGetAllLoginLogs_WithPagination() { + PageResponse 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 response = logHandler.getLoginLogsByPage(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(loginLogService).findLoginLogsByPage(any()); + } + + @Test + void testGetAllLoginLogs_WithOnlyPageParam() { + PageResponse 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 response = logHandler.getLoginLogsByPage(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)); + + ServerRequest request = MockServerRequest.builder() + .pathVariable("id", "1") + .build(); + Mono response = logHandler.getLoginLogById(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(loginLogService).findById(1L); + } + + @Test + void testGetLoginLogById_NotFound() { + when(loginLogService.findById(999L)).thenReturn(Mono.empty()); + + ServerRequest request = MockServerRequest.builder() + .pathVariable("id", "999") + .build(); + Mono response = logHandler.getLoginLogById(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.NOT_FOUND) + .verifyComplete(); + + verify(loginLogService).findById(999L); + } + + @Test + void testCreateLoginLog() { + SysLoginLog newLoginLog = new SysLoginLog(); + newLoginLog.setUsername("newuser"); + newLoginLog.setIp("192.168.1.2"); + newLoginLog.setStatus("1"); + + when(loginLogService.save(any())).thenReturn(Mono.just(testLoginLog)); + + ServerRequest request = MockServerRequest.builder() + .body(Mono.just(newLoginLog)); + Mono response = logHandler.createLoginLog(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.CREATED) + .verifyComplete(); + + verify(loginLogService).save(any()); + } + + @Test + void testGetLoginLogsByPage() { + PageResponse 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 response = logHandler.getLoginLogsByPage(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(loginLogService).findLoginLogsByPage(any()); + } + + @Test + void testGetLoginLogsByPage_WithKeyword() { + PageResponse 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") + .queryParam("size", "10") + .queryParam("keyword", "test") + .build(); + Mono response = logHandler.getLoginLogsByPage(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(loginLogService).findLoginLogsByPage(any()); + } + + @Test + void testGetLoginLogCount() { + when(loginLogService.count()).thenReturn(Mono.just(100L)); + + ServerRequest request = MockServerRequest.builder().build(); + Mono response = logHandler.getLoginLogCount(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(loginLogService).count(); + } + + @Test + void testGetAllExceptionLogs() { + when(exceptionLogService.findAll()).thenReturn(Flux.just(testExceptionLog)); + + ServerRequest request = MockServerRequest.builder().build(); + Mono response = logHandler.getAllExceptionLogs(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(exceptionLogService).findAll(); + } + + @Test + void testGetAllExceptionLogs_WithPagination() { + PageResponse 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 response = logHandler.getExceptionLogsByPage(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(exceptionLogService).findExceptionLogsByPage(any()); + } + + @Test + void testGetAllExceptionLogs_WithOnlySizeParam() { + PageResponse 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 response = logHandler.getExceptionLogsByPage(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)); + + ServerRequest request = MockServerRequest.builder() + .pathVariable("id", "1") + .build(); + Mono response = logHandler.getExceptionLogById(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(exceptionLogService).findById(1L); + } + + @Test + void testGetExceptionLogById_NotFound() { + when(exceptionLogService.findById(999L)).thenReturn(Mono.empty()); + + ServerRequest request = MockServerRequest.builder() + .pathVariable("id", "999") + .build(); + Mono response = logHandler.getExceptionLogById(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.NOT_FOUND) + .verifyComplete(); + + verify(exceptionLogService).findById(999L); + } + + @Test + void testCreateExceptionLog() { + SysExceptionLog newExceptionLog = new SysExceptionLog(); + newExceptionLog.setUsername("newuser"); + newExceptionLog.setTitle("new operation"); + newExceptionLog.setExceptionName("RuntimeException"); + newExceptionLog.setExceptionMsg("New exception"); + + when(exceptionLogService.save(any())).thenReturn(Mono.just(testExceptionLog)); + + ServerRequest request = MockServerRequest.builder() + .body(Mono.just(newExceptionLog)); + Mono response = logHandler.createExceptionLog(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.CREATED) + .verifyComplete(); + + verify(exceptionLogService).save(any()); + } + + @Test + void testGetExceptionLogsByPage() { + PageResponse 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 response = logHandler.getExceptionLogsByPage(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(exceptionLogService).findExceptionLogsByPage(any()); + } + + @Test + void testGetExceptionLogsByPage_WithKeyword() { + PageResponse 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("page", "0") + .queryParam("size", "10") + .queryParam("keyword", "test") + .build(); + Mono response = logHandler.getExceptionLogsByPage(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(exceptionLogService).findExceptionLogsByPage(any()); + } + + @Test + void testGetExceptionLogCount() { + when(exceptionLogService.count()).thenReturn(Mono.just(50L)); + + ServerRequest request = MockServerRequest.builder().build(); + Mono response = logHandler.getExceptionLogCount(request); + + StepVerifier.create(response) + .expectNextMatches(serverResponse -> + serverResponse.statusCode() == HttpStatus.OK) + .verifyComplete(); + + verify(exceptionLogService).count(); + } +} \ No newline at end of file