fix: 改进成功消息等待策略,修复测试失败问题

- 添加waitForSuccessMessage()方法到UserManagementPage和RoleManagementPage
- 改进submitForm()方法,添加等待时间
- 更新测试用例使用新的等待方法
- 增加错误消息检测和日志输出
- 修复权限选择器问题(使用.el-tree替代固定value)
This commit is contained in:
张翔
2026-04-04 10:03:19 +08:00
parent 0e367a8873
commit f882599072
35 changed files with 874 additions and 95 deletions
@@ -0,0 +1,111 @@
package cn.novalon.manage.sys.core.util;
import cn.novalon.manage.sys.core.domain.OperationLog;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* Excel导出工具类
*
* @author 张翔
* @date 2026-04-03
*/
public class ExcelExportUtil {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 导出操作日志到Excel
*
* @param logs 操作日志列表
* @return Excel文件字节数组
* @throws IOException IO异常
*/
public static byte[] exportOperationLogs(List<OperationLog> logs) throws IOException {
try (Workbook workbook = new XSSFWorkbook();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
Sheet sheet = workbook.createSheet("操作日志");
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle dateStyle = createDateStyle(workbook);
Row headerRow = sheet.createRow(0);
String[] headers = {"ID", "操作人", "操作模块", "请求方法", "请求参数", "执行结果",
"IP地址", "耗时(ms)", "状态", "错误信息", "操作时间"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
sheet.setColumnWidth(i, 20 * 256);
}
int rowNum = 1;
for (OperationLog log : logs) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(log.getId() != null ? log.getId() : 0);
row.createCell(1).setCellValue(log.getUsername() != null ? log.getUsername() : "");
row.createCell(2).setCellValue(log.getOperation() != null ? log.getOperation() : "");
row.createCell(3).setCellValue(log.getMethod() != null ? log.getMethod() : "");
row.createCell(4).setCellValue(truncateText(log.getParams(), 1000));
row.createCell(5).setCellValue(truncateText(log.getResult(), 1000));
row.createCell(6).setCellValue(log.getIp() != null ? log.getIp() : "");
row.createCell(7).setCellValue(log.getDuration() != null ? log.getDuration() : 0);
row.createCell(8).setCellValue("0".equals(log.getStatus()) ? "成功" : "失败");
row.createCell(9).setCellValue(log.getErrorMsg() != null ? log.getErrorMsg() : "");
Cell dateCell = row.createCell(10);
if (log.getCreatedAt() != null) {
dateCell.setCellValue(log.getCreatedAt().format(DATE_TIME_FORMATTER));
dateCell.setCellStyle(dateStyle);
}
}
workbook.write(outputStream);
return outputStream.toByteArray();
}
}
private static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 12);
style.setFont(font);
return style;
}
private static CellStyle createDateStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
private static String truncateText(String text, int maxLength) {
if (text == null) {
return "";
}
if (text.length() <= maxLength) {
return text;
}
return text.substring(0, maxLength) + "...";
}
}
@@ -3,20 +3,25 @@ package cn.novalon.manage.sys.handler.log;
import cn.novalon.manage.sys.core.domain.OperationLog;
import cn.novalon.manage.sys.core.query.OperationLogQuery;
import cn.novalon.manage.sys.core.service.IOperationLogService;
import cn.novalon.manage.sys.core.util.ExcelExportUtil;
import cn.novalon.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 张翔
@@ -77,10 +82,10 @@ public class OperationLogHandler {
query.setMethod(method);
if (startTimeStr != null && !startTimeStr.isEmpty()) {
query.setStartTime(java.time.LocalDateTime.parse(startTimeStr));
query.setStartTime(LocalDateTime.parse(startTimeStr));
}
if (endTimeStr != null && !endTimeStr.isEmpty()) {
query.setEndTime(java.time.LocalDateTime.parse(endTimeStr));
query.setEndTime(LocalDateTime.parse(endTimeStr));
}
return logService.findByQueryWithPagination(query, pageRequest)
@@ -99,4 +104,50 @@ public class OperationLogHandler {
.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());
}
});
}
}