fix(gitignore): correct log/ pattern to only match root directory

The previous `log/` pattern was too broad and matched any directory
named 'log' anywhere in the path, causing Java source files in
`handler/log/` directories to be incorrectly ignored.

Changed to `/log/` to only match the log directory at project root.

Added previously ignored files:
- OperationLogHandler.java
- SysLogHandler.java
- OperationLogHandlerTest.java
- SysLogHandlerTest.java
This commit is contained in:
张翔
2026-04-21 19:51:35 +08:00
parent 11fafa0f67
commit 2312f50010
5 changed files with 883 additions and 1 deletions
@@ -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<ServerResponse> getAllOperationLogs(ServerRequest request) {
return ServerResponse.ok()
.body(logService.findAll(), OperationLog.class);
}
@Operation(summary = "根据ID获取操作日志", description = "根据操作日志ID获取详细信息")
public Mono<ServerResponse> 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<ServerResponse> 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<ServerResponse> getOperationLogCount(ServerRequest request) {
return logService.count()
.flatMap(count -> ServerResponse.ok().bodyValue(count));
}
@Operation(summary = "创建操作日志", description = "手动创建操作日志")
public Mono<ServerResponse> 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<ServerResponse> 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());
}
});
}
}
@@ -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<ServerResponse> getAllLoginLogs(ServerRequest request) {
return ServerResponse.ok()
.body(loginLogService.findAll(), SysLoginLog.class);
}
@Operation(summary = "根据ID获取登录日志", description = "根据登录日志ID获取详细信息")
public Mono<ServerResponse> 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<ServerResponse> createLoginLog(ServerRequest request) {
return request.bodyToMono(SysLoginLog.class)
.flatMap(loginLogService::save)
.flatMap(log -> ServerResponse.status(HttpStatus.CREATED).bodyValue(log));
}
@Operation(summary = "分页获取登录日志", description = "根据分页参数获取登录日志列表")
public Mono<ServerResponse> 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<ServerResponse> getLoginLogCount(ServerRequest request) {
return loginLogService.count()
.flatMap(count -> ServerResponse.ok().bodyValue(count));
}
@Operation(summary = "获取今日登录次数", description = "获取今日登录次数统计")
public Mono<ServerResponse> getTodayLoginCount(ServerRequest request) {
return loginLogService.countToday()
.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()
.body(exceptionLogService.findAll(), SysExceptionLog.class);
}
@Operation(summary = "根据ID获取异常日志", description = "根据异常日志ID获取详细信息")
public Mono<ServerResponse> 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<ServerResponse> createExceptionLog(ServerRequest request) {
return request.bodyToMono(SysExceptionLog.class)
.flatMap(exceptionLogService::save)
.flatMap(log -> ServerResponse.status(HttpStatus.CREATED).bodyValue(log));
}
@Operation(summary = "分页获取异常日志", description = "根据分页参数获取异常日志列表")
public Mono<ServerResponse> 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<ServerResponse> getExceptionLogCount(ServerRequest request) {
return exceptionLogService.count()
.flatMap(count -> ServerResponse.ok().bodyValue(count));
}
}