From 587308951a024b43417825595eaec87200329621 Mon Sep 17 00:00:00 2001 From: liejiu946 Date: Wed, 4 Feb 2026 17:44:33 +0800 Subject: [PATCH] =?UTF-8?q?add:=E6=96=B0=E5=A2=9E=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=AE=A1=E7=90=86,=E6=97=A5=E5=BF=97=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E3=80=822.=E4=BA=8C=E7=BB=B4=E7=A0=81?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E3=80=823.=E5=BC=82=E5=B8=B8=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E7=9A=84=E5=AF=BC=E5=87=BA=E5=AF=BC=E5=87=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AnomalyInfoController.java | 42 +++++++++- .../service/ErrorHandlingService.java | 8 +- .../anomalyInfo/service/ErrorInfoService.java | 9 +++ .../impl/ErrorHandlingServiceImpl.java | 13 +++ .../service/impl/ErrorInfoServiceImpl.java | 8 ++ .../logging/controller/LogController.java | 71 ++++++++++++++++ .../sys/modular/logging/dto/LogFileDTO.java | 34 ++++++++ .../modular/logging/service/LogService.java | 81 +++++++++++++++++++ .../qrcode/controller/QRCodeController.java | 33 ++++++++ .../modular/qrcode/service/QRCodeService.java | 9 +++ .../service/impl/QRCodeServiceImpl.java | 22 +++++ .../resources/language/buss/buss.properties | 2 + .../language/buss/buss_en_US.properties | 2 + .../language/buss/buss_zh_CN.properties | 2 + 14 files changed, 332 insertions(+), 4 deletions(-) create mode 100644 nl-business-sys/src/main/java/org/nl/sys/modular/logging/controller/LogController.java create mode 100644 nl-business-sys/src/main/java/org/nl/sys/modular/logging/dto/LogFileDTO.java create mode 100644 nl-business-sys/src/main/java/org/nl/sys/modular/logging/service/LogService.java diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/controller/AnomalyInfoController.java b/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/controller/AnomalyInfoController.java index f3d44ca..1df3b1d 100644 --- a/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/controller/AnomalyInfoController.java +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/controller/AnomalyInfoController.java @@ -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 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 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); + } + } diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/ErrorHandlingService.java b/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/ErrorHandlingService.java index 359b9dd..866830a 100644 --- a/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/ErrorHandlingService.java +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/ErrorHandlingService.java @@ -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 { - + /** + * 导出异常处理信息 + * @return + */ + List exportErrorHandlingInfoExcel(); } diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/ErrorInfoService.java b/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/ErrorInfoService.java index 5656291..52bbc0e 100644 --- a/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/ErrorInfoService.java +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/ErrorInfoService.java @@ -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 { * @return */ WebResponse updateErrorInfo(ErrorInfo errorInfo); + + /** + * 导出异常信息 + * @return + */ + List exportErrorInfoExcel(); + } diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/impl/ErrorHandlingServiceImpl.java b/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/impl/ErrorHandlingServiceImpl.java index ad57c0d..2a0d130 100644 --- a/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/impl/ErrorHandlingServiceImpl.java +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/impl/ErrorHandlingServiceImpl.java @@ -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 implements ErrorHandlingService { + + @Resource + private ErrorHandlingMapper errorHandlingMapper; + + @Override + public List exportErrorHandlingInfoExcel() { + return errorHandlingMapper.selectList(new LambdaQueryWrapper<>(ErrorHandling.class)); + } } diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/impl/ErrorInfoServiceImpl.java b/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/impl/ErrorInfoServiceImpl.java index 6bfae13..f3c3b5b 100644 --- a/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/impl/ErrorInfoServiceImpl.java +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/anomalyInfo/service/impl/ErrorInfoServiceImpl.java @@ -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 exportErrorInfoExcel() { + return errorInfoMapper.selectList(new LambdaQueryWrapper<>(ErrorInfo.class)); + } + } diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/logging/controller/LogController.java b/nl-business-sys/src/main/java/org/nl/sys/modular/logging/controller/LogController.java new file mode 100644 index 0000000..f3cc183 --- /dev/null +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/logging/controller/LogController.java @@ -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 getLogFiles() { + try { + return new ResponseEntity<>(logService.getLogFiles(), HttpStatus.OK); + } catch (Exception e) { + throw new BadRequestException("获取日志文件失败:"+e.getMessage()); + } + } + + /** + * 下载日志文件 + */ + @Log("下载日志文件") + @GetMapping("/download/{fileName}") + public ResponseEntity 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(); + } + } +} diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/logging/dto/LogFileDTO.java b/nl-business-sys/src/main/java/org/nl/sys/modular/logging/dto/LogFileDTO.java new file mode 100644 index 0000000..22b42a8 --- /dev/null +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/logging/dto/LogFileDTO.java @@ -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]); + } + +} diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/logging/service/LogService.java b/nl-business-sys/src/main/java/org/nl/sys/modular/logging/service/LogService.java new file mode 100644 index 0000000..d648644 --- /dev/null +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/logging/service/LogService.java @@ -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 getLogFiles() { + List 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; + } +} diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/controller/QRCodeController.java b/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/controller/QRCodeController.java index 3dbd3e6..7895c20 100644 --- a/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/controller/QRCodeController.java +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/controller/QRCodeController.java @@ -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 taskOperationConfirm(@RequestParam String taskCode){ return new ResponseEntity<>(qrCodeService.taskOperationConfirm(taskCode),HttpStatus.OK); } + + /** + * 下载二维码文件 + */ + @Log("下载二维码文件") + @GetMapping("/download/{fileName}") + public ResponseEntity 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(); + } + } } diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/service/QRCodeService.java b/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/service/QRCodeService.java index 84867b3..22f60fb 100644 --- a/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/service/QRCodeService.java +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/service/QRCodeService.java @@ -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); } diff --git a/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/service/impl/QRCodeServiceImpl.java b/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/service/impl/QRCodeServiceImpl.java index 8db88b0..df73bf1 100644 --- a/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/service/impl/QRCodeServiceImpl.java +++ b/nl-business-sys/src/main/java/org/nl/sys/modular/qrcode/service/impl/QRCodeServiceImpl.java @@ -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; + } } diff --git a/nl-web-app/src/main/resources/language/buss/buss.properties b/nl-web-app/src/main/resources/language/buss/buss.properties index ed0b16b..69d34f0 100644 --- a/nl-web-app/src/main/resources/language/buss/buss.properties +++ b/nl-web-app/src/main/resources/language/buss/buss.properties @@ -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格式 diff --git a/nl-web-app/src/main/resources/language/buss/buss_en_US.properties b/nl-web-app/src/main/resources/language/buss/buss_en_US.properties index 254aab8..98b9a20 100644 --- a/nl-web-app/src/main/resources/language/buss/buss_en_US.properties +++ b/nl-web-app/src/main/resources/language/buss/buss_en_US.properties @@ -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 diff --git a/nl-web-app/src/main/resources/language/buss/buss_zh_CN.properties b/nl-web-app/src/main/resources/language/buss/buss_zh_CN.properties index ed0b16b..69d34f0 100644 --- a/nl-web-app/src/main/resources/language/buss/buss_zh_CN.properties +++ b/nl-web-app/src/main/resources/language/buss/buss_zh_CN.properties @@ -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格式