add:新增日志管理,日志下载功能。2.二维码下载。3.异常信息的导出导出功能。
This commit is contained in:
@@ -2,9 +2,11 @@ package org.nl.sys.modular.anomalyInfo.controller;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.nl.config.language.LangProcess;
|
||||
import org.nl.exception.BadRequestException;
|
||||
import org.nl.logging.annotation.Log;
|
||||
import org.nl.response.WebResponse;
|
||||
import org.nl.sys.modular.anomalyInfo.dao.ErrorHandling;
|
||||
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
|
||||
import org.nl.sys.modular.anomalyInfo.listener.ErrorHandlingListener;
|
||||
@@ -23,6 +25,8 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
@@ -66,7 +70,7 @@ public class AnomalyInfoController {
|
||||
EasyExcel.read(file.getInputStream(), ErrorInfo.class, listener)
|
||||
.sheet() // 默认读取第一个sheet
|
||||
.doRead();
|
||||
return new ResponseEntity<>("操作成功",HttpStatus.OK);
|
||||
return new ResponseEntity<>(WebResponse.requestOk(),HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/importErrorHandlingExcel")
|
||||
@@ -85,7 +89,7 @@ public class AnomalyInfoController {
|
||||
EasyExcel.read(file.getInputStream(), ErrorHandling.class, listener)
|
||||
.sheet() // 默认读取第一个sheet
|
||||
.doRead();
|
||||
return new ResponseEntity<>("操作成功",HttpStatus.OK);
|
||||
return new ResponseEntity<>(WebResponse.requestOk(),HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/importErrorImage")
|
||||
@@ -112,7 +116,7 @@ public class AnomalyInfoController {
|
||||
parseZip.processCompressedFile(file);
|
||||
|
||||
|
||||
return new ResponseEntity<>("上传成功!",HttpStatus.OK);
|
||||
return new ResponseEntity<>(LangProcess.msg("successful"),HttpStatus.OK);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new BadRequestException(LangProcess.msg("anomaly_file_parse_failed",e.getMessage()));
|
||||
@@ -131,4 +135,36 @@ public class AnomalyInfoController {
|
||||
return new ResponseEntity<>(errorInfoService.updateErrorInfo(errorInfo),HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping("/exportErrorInfoExcel")
|
||||
@Log("导出异常信息")
|
||||
public void exportErrorInfoExcel(HttpServletResponse response) throws IOException {
|
||||
|
||||
List<ErrorInfo> taskInfoList = errorInfoService.exportErrorInfoExcel();
|
||||
|
||||
// 设置响应头
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(LangProcess.msg("error_info")+".xlsx", "UTF-8"));
|
||||
|
||||
// 使用EasyExcel写入数据到响应输出流
|
||||
EasyExcel.write(response.getOutputStream(), ErrorInfo.class)
|
||||
.sheet(LangProcess.msg("error_info"))
|
||||
.doWrite(taskInfoList);
|
||||
}
|
||||
|
||||
@GetMapping("/exportErrorHandlingInfoExcel")
|
||||
@Log("导出异常处理信息")
|
||||
public void exportErrorHandlingInfoExcel(HttpServletResponse response) throws IOException {
|
||||
|
||||
List<ErrorHandling> taskInfoList = errorHandlingService.exportErrorHandlingInfoExcel();
|
||||
|
||||
// 设置响应头
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(LangProcess.msg("error_handling_info")+".xlsx", "UTF-8"));
|
||||
|
||||
// 使用EasyExcel写入数据到响应输出流
|
||||
EasyExcel.write(response.getOutputStream(), ErrorHandling.class)
|
||||
.sheet(LangProcess.msg("error_handling_info"))
|
||||
.doWrite(taskInfoList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,10 +3,16 @@ package org.nl.sys.modular.anomalyInfo.service;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.nl.sys.modular.anomalyInfo.dao.ErrorHandling;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/12/29
|
||||
*/
|
||||
public interface ErrorHandlingService extends IService<ErrorHandling> {
|
||||
|
||||
/**
|
||||
* 导出异常处理信息
|
||||
* @return
|
||||
*/
|
||||
List<ErrorHandling> exportErrorHandlingInfoExcel();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import org.nl.response.WebResponse;
|
||||
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
|
||||
import org.nl.sys.modular.anomalyInfo.param.QueryErrorInfoPageParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/12/29
|
||||
@@ -24,4 +26,11 @@ public interface ErrorInfoService extends IService<ErrorInfo> {
|
||||
* @return
|
||||
*/
|
||||
WebResponse updateErrorInfo(ErrorInfo errorInfo);
|
||||
|
||||
/**
|
||||
* 导出异常信息
|
||||
* @return
|
||||
*/
|
||||
List<ErrorInfo> exportErrorInfoExcel();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
package org.nl.sys.modular.anomalyInfo.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.nl.sys.modular.anomalyInfo.dao.ErrorHandling;
|
||||
import org.nl.sys.modular.anomalyInfo.dao.ErrorInfo;
|
||||
import org.nl.sys.modular.anomalyInfo.service.ErrorHandlingService;
|
||||
import org.nl.sys.modular.anomalyInfo.mapper.ErrorHandlingMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/12/29
|
||||
*/
|
||||
@Service
|
||||
public class ErrorHandlingServiceImpl extends ServiceImpl<ErrorHandlingMapper, ErrorHandling> implements ErrorHandlingService {
|
||||
|
||||
@Resource
|
||||
private ErrorHandlingMapper errorHandlingMapper;
|
||||
|
||||
@Override
|
||||
public List<ErrorHandling> exportErrorHandlingInfoExcel() {
|
||||
return errorHandlingMapper.selectList(new LambdaQueryWrapper<>(ErrorHandling.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ import org.nl.sys.modular.anomalyInfo.service.ErrorInfoService;
|
||||
import org.nl.sys.modular.anomalyInfo.mapper.ErrorInfoMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/12/29
|
||||
@@ -45,4 +47,10 @@ public class ErrorInfoServiceImpl extends ServiceImpl<ErrorInfoMapper, ErrorInfo
|
||||
}
|
||||
return WebResponse.requestOk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ErrorInfo> exportErrorInfoExcel() {
|
||||
return errorInfoMapper.selectList(new LambdaQueryWrapper<>(ErrorInfo.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.nl.sys.modular.logging.controller;
|
||||
|
||||
import org.nl.exception.BadRequestException;
|
||||
import org.nl.logging.annotation.Log;
|
||||
import org.nl.sys.modular.logging.service.LogService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/11/3
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/logs")
|
||||
public class LogController {
|
||||
|
||||
@Autowired
|
||||
private LogService logService;
|
||||
|
||||
/**
|
||||
* 获取日志文件列表
|
||||
*/
|
||||
@Log("获取日志文件列表")
|
||||
@GetMapping("/list")
|
||||
public ResponseEntity<Object> getLogFiles() {
|
||||
try {
|
||||
return new ResponseEntity<>(logService.getLogFiles(), HttpStatus.OK);
|
||||
} catch (Exception e) {
|
||||
throw new BadRequestException("获取日志文件失败:"+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载日志文件
|
||||
*/
|
||||
@Log("下载日志文件")
|
||||
@GetMapping("/download/{fileName}")
|
||||
public ResponseEntity<Resource> downloadLogFile(@PathVariable String fileName) {
|
||||
try {
|
||||
File file = logService.getLogFile(fileName);
|
||||
|
||||
Path filePath = file.toPath();
|
||||
Resource resource = new InputStreamResource(Files.newInputStream(filePath));
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + file.getName() + "\"")
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.contentLength(file.length())
|
||||
.body(resource);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
return ResponseEntity.notFound().build();
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.nl.sys.modular.logging.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/11/3
|
||||
*/
|
||||
@Data
|
||||
public class LogFileDTO {
|
||||
private String fileName;
|
||||
private String filePath;
|
||||
private Long fileSize;
|
||||
private LocalDateTime lastModified;
|
||||
private String readableSize;
|
||||
|
||||
public LogFileDTO(String fileName, String filePath, Long fileSize, LocalDateTime lastModified) {
|
||||
this.fileName = fileName;
|
||||
this.filePath = filePath;
|
||||
this.fileSize = fileSize;
|
||||
this.lastModified = lastModified;
|
||||
this.readableSize = formatFileSize(fileSize);
|
||||
}
|
||||
|
||||
private String formatFileSize(long size) {
|
||||
if (size <= 0) return "0 B";
|
||||
final String[] units = new String[]{"B", "KB", "MB", "GB"};
|
||||
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
|
||||
return String.format("%.1f %s", size / Math.pow(1024, digitGroups), units[digitGroups]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.nl.sys.modular.logging.service;
|
||||
|
||||
import org.nl.sys.modular.logging.dto.LogFileDTO;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/11/3
|
||||
*/
|
||||
@Service
|
||||
public class LogService {
|
||||
|
||||
@Value("${customize.log-dir}")
|
||||
private String logDir;
|
||||
|
||||
/**
|
||||
* 获取日志文件列表
|
||||
*/
|
||||
public List<LogFileDTO> getLogFiles() {
|
||||
List<LogFileDTO> logFiles = new ArrayList<>();
|
||||
File directory = new File(logDir);
|
||||
|
||||
if (!directory.exists() || !directory.isDirectory()) {
|
||||
return logFiles;
|
||||
}
|
||||
|
||||
File[] files = directory.listFiles((dir, name) ->
|
||||
name.endsWith(".log") || name.endsWith(".txt"));
|
||||
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isFile()) {
|
||||
LogFileDTO logFile = new LogFileDTO(
|
||||
file.getName(),
|
||||
file.getAbsolutePath(),
|
||||
file.length(),
|
||||
LocalDateTime.ofInstant(
|
||||
Instant.ofEpochMilli(file.lastModified()),
|
||||
ZoneId.systemDefault()
|
||||
)
|
||||
);
|
||||
logFiles.add(logFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 按修改时间倒序排列
|
||||
logFiles.sort((a, b) -> b.getLastModified().compareTo(a.getLastModified()));
|
||||
return logFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日志文件
|
||||
*/
|
||||
public File getLogFile(String fileName) {
|
||||
// 防止路径遍历攻击
|
||||
if (fileName.contains("..") || fileName.contains("/") || fileName.contains("\\")) {
|
||||
throw new IllegalArgumentException("Invalid file name");
|
||||
}
|
||||
|
||||
File file = new File(logDir, fileName);
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
throw new RuntimeException("File not found: " + fileName);
|
||||
}
|
||||
|
||||
// 确保文件在日志目录内
|
||||
if (!file.getAbsolutePath().startsWith(new File(logDir).getAbsolutePath())) {
|
||||
throw new SecurityException("Access denied");
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,18 @@ import org.nl.sys.modular.qrcode.param.GenerateQRCodeParam;
|
||||
import org.nl.sys.modular.qrcode.param.QueryQRCodeParam;
|
||||
import org.nl.sys.modular.qrcode.param.UpdateQRCodeParam;
|
||||
import org.nl.sys.modular.qrcode.service.QRCodeService;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/12/22
|
||||
@@ -72,4 +79,30 @@ public class QRCodeController {
|
||||
public ResponseEntity<Object> taskOperationConfirm(@RequestParam String taskCode){
|
||||
return new ResponseEntity<>(qrCodeService.taskOperationConfirm(taskCode),HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载二维码文件
|
||||
*/
|
||||
@Log("下载二维码文件")
|
||||
@GetMapping("/download/{fileName}")
|
||||
public ResponseEntity<org.springframework.core.io.Resource> downloadQRCodeFile(@PathVariable String fileName) {
|
||||
try {
|
||||
File file = qrCodeService.getQRCdoeFile(fileName);
|
||||
|
||||
Path filePath = file.toPath();
|
||||
org.springframework.core.io.Resource resource = new InputStreamResource(Files.newInputStream(filePath));
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + file.getName() + "\"")
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.contentLength(file.length())
|
||||
.body(resource);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
return ResponseEntity.notFound().build();
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import org.nl.sys.modular.qrcode.param.GenerateQRCodeParam;
|
||||
import org.nl.sys.modular.qrcode.param.QueryQRCodeParam;
|
||||
import org.nl.sys.modular.qrcode.param.UpdateQRCodeParam;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/12/22
|
||||
@@ -74,4 +76,11 @@ public interface QRCodeService {
|
||||
* @return
|
||||
*/
|
||||
boolean verifyQRCodeByRoom(String room);
|
||||
|
||||
/**
|
||||
* 获取二维码文件
|
||||
* @param fileName
|
||||
* @return
|
||||
*/
|
||||
File getQRCdoeFile(String fileName);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/12/22
|
||||
@@ -176,4 +178,24 @@ public class QRCodeServiceImpl implements QRCodeService {
|
||||
.eq(QRcodeInfo::getRoom_code,room)
|
||||
)>0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getQRCdoeFile(String fileName) {
|
||||
// 防止路径遍历攻击
|
||||
if (fileName.contains("..") || fileName.contains("/") || fileName.contains("\\")) {
|
||||
throw new IllegalArgumentException("Invalid file name");
|
||||
}
|
||||
|
||||
File file = new File(fileProperties.getPath().getQrcode(), fileName);
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
throw new RuntimeException("File not found: " + fileName);
|
||||
}
|
||||
|
||||
// 确保文件在日志目录内
|
||||
if (!file.getAbsolutePath().startsWith(new File(fileProperties.getPath().getQrcode()).getAbsolutePath())) {
|
||||
throw new SecurityException("Access denied");
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ task_confirm_arrival_failed = 任务到达确认失败
|
||||
task_next_station_failed = 任务下一站失败:{0}
|
||||
|
||||
# 异常信息相关
|
||||
error_info = 异常信息
|
||||
error_handling_info = 异常处理信息
|
||||
anomaly_file_not_selected = 请选择文件上传
|
||||
anomaly_file_name_empty = 文件名称为空
|
||||
anomaly_file_format_error = 目前只支持ZIP格式
|
||||
|
||||
@@ -47,6 +47,8 @@ task_confirm_arrival_failed = Failed to confirm task arrival
|
||||
task_next_station_failed = Failed to go to next station:{0}
|
||||
|
||||
# Anomaly related
|
||||
error_info = Anomaly information
|
||||
error_handling_info = Exception handling information
|
||||
anomaly_file_not_selected = Please select a file to upload
|
||||
anomaly_file_name_empty = File name is empty
|
||||
anomaly_file_format_error = Currently only ZIP format is supported
|
||||
|
||||
@@ -47,6 +47,8 @@ task_confirm_arrival_failed = 任务到达确认失败
|
||||
task_next_station_failed = 任务下一站失败:{0}
|
||||
|
||||
# 异常信息相关
|
||||
error_info = 异常信息
|
||||
error_handling_info = 异常处理信息
|
||||
anomaly_file_not_selected = 请选择文件上传
|
||||
anomaly_file_name_empty = 文件名称为空
|
||||
anomaly_file_format_error = 目前只支持ZIP格式
|
||||
|
||||
Reference in New Issue
Block a user