From 0c336d5c5c0596691c9b33c08643c03486d47d5f Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期一, 27 四月 2026 18:56:39 +0800
Subject: [PATCH] refactor: move station buffer capacity to bas station
---
src/main/java/com/zy/asrs/controller/DeviceLogController.java | 374 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 369 insertions(+), 5 deletions(-)
diff --git a/src/main/java/com/zy/asrs/controller/DeviceLogController.java b/src/main/java/com/zy/asrs/controller/DeviceLogController.java
index 79cd046..a67a5da 100644
--- a/src/main/java/com/zy/asrs/controller/DeviceLogController.java
+++ b/src/main/java/com/zy/asrs/controller/DeviceLogController.java
@@ -18,12 +18,21 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpServletResponse;
+import java.io.BufferedWriter;
+import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
@@ -48,6 +57,15 @@
@Value("${deviceLogStorage.loggingPath}")
private String loggingPath;
+
+ @Value("${logging.file.path:./stock/out/@pom.build.finalName@/logs}")
+ private String systemLoggingPath;
+
+ private static final DateTimeFormatter SYSTEM_LOG_DATE = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ private static final DateTimeFormatter SYSTEM_LOG_DATE_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ private static final DateTimeFormatter SYSTEM_LOG_EXPORT_TIME = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
+ private static final Pattern SYSTEM_LOG_TIMESTAMP = Pattern.compile("^(\\d{2}:\\d{2}:\\d{2}\\.\\d{3})");
+ private static final Pattern SYSTEM_LOG_ROLLED_FILE = Pattern.compile("^(info|error)_(\\d{4}-\\d{2}-\\d{2})\\.(\\d+)\\.log$");
private static class ProgressInfo {
long totalRaw;
@@ -85,7 +103,27 @@
Path lastFile;
}
+ private static class SystemLogFileInfo {
+ String logType;
+ LocalDate day;
+ Integer index;
+ boolean active;
+ Path path;
+ }
+
+ private static class SystemLogDownloadRequest {
+ String logType;
+ LocalDateTime startTime;
+ LocalDateTime endTime;
+ List<Path> files;
+ }
+
private static final Map<String, ProgressInfo> DOWNLOAD_PROGRESS = new ConcurrentHashMap<>();
+ private static final int SYSTEM_LOG_MAX_RANGE_DAYS = 10;
+ private static final int SYSTEM_LOG_MATCH_BUFFER_LINES = 200000;
+ private static final String SYSTEM_LOG_ENTRY_NAME = "system.log";
+
+ private static final Map<String, SystemLogDownloadRequest> SYSTEM_LOG_DOWNLOAD_REQUESTS = new ConcurrentHashMap<>();
@RequestMapping(value = "/deviceLog/dates/auth")
@ManagerAuth
@@ -651,20 +689,145 @@
return R.ok(res);
}
+ @RequestMapping(value = "/deviceLog/system/download/init/auth")
+ @ManagerAuth
+ public R initSystemDownload(@org.springframework.web.bind.annotation.RequestBody com.alibaba.fastjson.JSONObject param) {
+ try {
+ String logType = normalizeSystemLogType(param.getString("logType"));
+ if (logType == null) {
+ return R.error("鏃ュ織绫诲瀷閿欒");
+ }
+ LocalDateTime startTime = parseSystemLogDateTime(param.getString("startTime"));
+ LocalDateTime endTime = parseSystemLogDateTime(param.getString("endTime"));
+ if (startTime == null || endTime == null) {
+ return R.error("鏃堕棿鏍煎紡閿欒");
+ }
+ if (startTime.isAfter(endTime)) {
+ return R.error("寮�濮嬫椂闂翠笉鑳芥櫄浜庣粨鏉熸椂闂�");
+ }
+ long daySpan = java.time.temporal.ChronoUnit.DAYS.between(startTime.toLocalDate(), endTime.toLocalDate());
+ if (daySpan > SYSTEM_LOG_MAX_RANGE_DAYS) {
+ return R.error("鏃堕棿鑼冨洿涓嶈兘瓒呰繃" + SYSTEM_LOG_MAX_RANGE_DAYS + "澶�");
+ }
+ List<Path> files = findSystemLogFiles(logType, startTime, endTime);
+ if (files.isEmpty()) {
+ return R.error("鏈壘鍒版棩蹇楁枃浠�");
+ }
+ String id = UUID.randomUUID().toString();
+ ProgressInfo info = new ProgressInfo();
+ info.totalCount = files.size();
+ long sum = 0L;
+ for (Path f : files) {
+ try { sum += Files.size(f); } catch (Exception ignored) {}
+ }
+ info.totalRaw = sum;
+ info.processedRaw = 0L;
+ info.processedCount = 0;
+ info.finished = false;
+ DOWNLOAD_PROGRESS.put(id, info);
+ SystemLogDownloadRequest request = new SystemLogDownloadRequest();
+ request.logType = logType;
+ request.startTime = startTime;
+ request.endTime = endTime;
+ request.files = files;
+ SYSTEM_LOG_DOWNLOAD_REQUESTS.put(id, request);
+ Map<String, Object> res = new HashMap<>();
+ res.put("progressId", id);
+ res.put("totalSize", info.totalRaw);
+ res.put("fileCount", info.totalCount);
+ return R.ok(res);
+ } catch (Exception e) {
+ log.error("鍒濆鍖栫郴缁熸棩蹇椾笅杞藉け璐�", e);
+ return R.error("鍒濆鍖栧け璐�");
+ }
+ }
+
+ @RequestMapping(value = "/deviceLog/system/download/auth")
+ @ManagerAuth
+ public void downloadSystemLog(@RequestParam("logType") String logTypeParam,
+ @RequestParam("startTime") String startTimeParam,
+ @RequestParam("endTime") String endTimeParam,
+ @RequestParam(value = "progressId", required = false) String progressId,
+ HttpServletResponse response) {
+ String progressKey = null;
+ try {
+ String logType = normalizeSystemLogType(logTypeParam);
+ LocalDateTime startTime = parseSystemLogDateTime(startTimeParam);
+ LocalDateTime endTime = parseSystemLogDateTime(endTimeParam);
+ if (logType == null || startTime == null || endTime == null || startTime.isAfter(endTime)) {
+ response.setStatus(400);
+ return;
+ }
+ SystemLogDownloadRequest requestInfo = null;
+ if (!Cools.isEmpty(progressId)) {
+ requestInfo = SYSTEM_LOG_DOWNLOAD_REQUESTS.get(progressId);
+ progressKey = progressId;
+ }
+ List<Path> files;
+ if (requestInfo != null
+ && Objects.equals(requestInfo.logType, logType)
+ && Objects.equals(requestInfo.startTime, startTime)
+ && Objects.equals(requestInfo.endTime, endTime)) {
+ files = requestInfo.files == null ? Collections.emptyList() : requestInfo.files;
+ } else {
+ files = findSystemLogFiles(logType, startTime, endTime);
+ }
+ if (files.isEmpty()) {
+ response.setStatus(404);
+ return;
+ }
+ if (Cools.isEmpty(progressKey)) {
+ progressKey = UUID.randomUUID().toString();
+ }
+ ProgressInfo info = prepareProgress(progressKey, files);
+ response.reset();
+ response.setContentType("application/zip");
+ String filename = logType + "_" + formatSystemExportTime(startTime) + "_" + formatSystemExportTime(endTime) + ".zip";
+ response.setHeader("Content-Disposition", "attachment; filename=" + filename);
+ response.setHeader("X-Progress-Id", progressKey);
+ try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
+ zos.putNextEntry(new ZipEntry(SYSTEM_LOG_ENTRY_NAME));
+ try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zos, StandardCharsets.UTF_8))) {
+ boolean written = writeSystemLogContent(files, logType, startTime, endTime, writer, info);
+ writer.flush();
+ if (!written) {
+ response.reset();
+ response.setStatus(404);
+ return;
+ }
+ }
+ zos.closeEntry();
+ zos.finish();
+ info.finished = true;
+ }
+ } catch (Exception e) {
+ log.error("涓嬭浇绯荤粺鏃ュ織澶辫触", e);
+ try {
+ response.reset();
+ response.setStatus(500);
+ } catch (Exception ignore) {
+ }
+ } finally {
+ if (!Cools.isEmpty(progressId)) {
+ SYSTEM_LOG_DOWNLOAD_REQUESTS.remove(progressId);
+ }
+ }
+ }
+
@RequestMapping(value = "/deviceLog/enums/auth")
@ManagerAuth
public R getEnums() {
Map<String, Map<String, String>> enums = new HashMap<>();
-
+
enums.put("CrnModeType", Arrays.stream(com.zy.core.enums.CrnModeType.values())
.collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
-
+
enums.put("CrnStatusType", Arrays.stream(com.zy.core.enums.CrnStatusType.values())
.collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
-
+
enums.put("CrnForkPosType", Arrays.stream(com.zy.core.enums.CrnForkPosType.values())
.collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
-
+
enums.put("CrnLiftPosType", Arrays.stream(com.zy.core.enums.CrnLiftPosType.values())
.collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
@@ -679,10 +842,211 @@
enums.put("RgvStatusType", Arrays.stream(com.zy.core.enums.RgvStatusType.values())
.collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
-
+
return R.ok(enums);
}
+ private ProgressInfo prepareProgress(String progressId, List<Path> files) {
+ return DOWNLOAD_PROGRESS.compute(progressId, (key, existing) -> {
+ ProgressInfo next = existing == null ? new ProgressInfo() : existing;
+ next.totalCount = files == null ? 0 : files.size();
+ long total = 0L;
+ if (files != null) {
+ for (Path f : files) {
+ try { total += Files.size(f); } catch (Exception ignored) {}
+ }
+ }
+ next.totalRaw = total;
+ next.processedRaw = 0L;
+ next.processedCount = 0;
+ next.finished = false;
+ return next;
+ });
+ }
+
+ private boolean writeSystemLogContent(List<Path> files,
+ String logType,
+ LocalDateTime startTime,
+ LocalDateTime endTime,
+ BufferedWriter writer,
+ ProgressInfo progressInfo) throws Exception {
+ boolean wroteAny = false;
+ for (Path file : files) {
+ SystemLogFileInfo fileInfo = parseSystemLogFile(file);
+ if (fileInfo == null) {
+ updateProgress(progressInfo, file);
+ continue;
+ }
+ LocalDate baseDate = fileInfo.day != null ? fileInfo.day : startTime.toLocalDate();
+ try (Stream<String> lines = Files.lines(file, StandardCharsets.UTF_8)) {
+ List<String> pending = new ArrayList<>();
+ boolean pendingMatched = false;
+ for (Iterator<String> iterator = lines.iterator(); iterator.hasNext(); ) {
+ String line = iterator.next();
+ Matcher matcher = SYSTEM_LOG_TIMESTAMP.matcher(line == null ? "" : line);
+ if (matcher.find()) {
+ if (pendingMatched && !pending.isEmpty()) {
+ for (String pendingLine : pending) {
+ writer.write(pendingLine == null ? "" : pendingLine);
+ writer.newLine();
+ }
+ wroteAny = true;
+ }
+ pending.clear();
+ pendingMatched = isSystemLogLineInRange(baseDate, matcher.group(1), startTime, endTime);
+ }
+ if (pendingMatched) {
+ pending.add(line);
+ if (pending.size() > SYSTEM_LOG_MATCH_BUFFER_LINES) {
+ for (String pendingLine : pending) {
+ writer.write(pendingLine == null ? "" : pendingLine);
+ writer.newLine();
+ }
+ wroteAny = true;
+ pending.clear();
+ }
+ } else if (!pending.isEmpty()) {
+ pending.clear();
+ }
+ }
+ if (pendingMatched && !pending.isEmpty()) {
+ for (String pendingLine : pending) {
+ writer.write(pendingLine == null ? "" : pendingLine);
+ writer.newLine();
+ }
+ wroteAny = true;
+ }
+ }
+ updateProgress(progressInfo, file);
+ }
+ if (progressInfo != null) {
+ progressInfo.finished = true;
+ }
+ return wroteAny;
+ }
+
+ private void updateProgress(ProgressInfo progressInfo, Path file) {
+ if (progressInfo == null) {
+ return;
+ }
+ try {
+ progressInfo.processedRaw += Files.size(file);
+ } catch (Exception ignored) {
+ }
+ progressInfo.processedCount += 1;
+ }
+
+ private List<Path> findSystemLogFiles(String logType, LocalDateTime startTime, LocalDateTime endTime) throws Exception {
+ Path baseDir = Paths.get(systemLoggingPath);
+ if (!Files.exists(baseDir) || !Files.isDirectory(baseDir)) {
+ return Collections.emptyList();
+ }
+ LocalDate startDate = startTime.toLocalDate();
+ LocalDate endDate = endTime.toLocalDate();
+ List<SystemLogFileInfo> matched = new ArrayList<>();
+ try (Stream<Path> stream = Files.list(baseDir)) {
+ stream.filter(path -> !Files.isDirectory(path)).forEach(path -> {
+ SystemLogFileInfo info = parseSystemLogFile(path);
+ if (info == null || !Objects.equals(info.logType, logType)) {
+ return;
+ }
+ if (info.active || info.day == null || (!info.day.isBefore(startDate) && !info.day.isAfter(endDate))) {
+ matched.add(info);
+ }
+ });
+ }
+ matched.sort((left, right) -> {
+ LocalDate leftDay = left.day == null ? LocalDate.MAX : left.day;
+ LocalDate rightDay = right.day == null ? LocalDate.MAX : right.day;
+ int cmp = leftDay.compareTo(rightDay);
+ if (cmp != 0) {
+ return cmp;
+ }
+ int leftIndex = left.index == null ? Integer.MAX_VALUE : left.index;
+ int rightIndex = right.index == null ? Integer.MAX_VALUE : right.index;
+ if (left.active != right.active) {
+ return left.active ? 1 : -1;
+ }
+ return Integer.compare(leftIndex, rightIndex);
+ });
+ return matched.stream().map(item -> item.path).collect(Collectors.toList());
+ }
+
+ private SystemLogFileInfo parseSystemLogFile(Path path) {
+ if (path == null) {
+ return null;
+ }
+ String name = path.getFileName().toString();
+ if ("info.log".equals(name) || "error.log".equals(name)) {
+ SystemLogFileInfo info = new SystemLogFileInfo();
+ info.logType = name.startsWith("info") ? "info" : "error";
+ info.active = true;
+ info.path = path;
+ return info;
+ }
+ Matcher matcher = SYSTEM_LOG_ROLLED_FILE.matcher(name);
+ if (!matcher.matches()) {
+ return null;
+ }
+ try {
+ SystemLogFileInfo info = new SystemLogFileInfo();
+ info.logType = matcher.group(1);
+ info.day = LocalDate.parse(matcher.group(2), SYSTEM_LOG_DATE);
+ info.index = Integer.parseInt(matcher.group(3));
+ info.active = false;
+ info.path = path;
+ return info;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private String normalizeSystemLogType(String logType) {
+ if (Cools.isEmpty(logType)) {
+ return null;
+ }
+ String value = logType.trim().toLowerCase(Locale.ROOT);
+ if ("info".equals(value) || "error".equals(value)) {
+ return value;
+ }
+ return null;
+ }
+
+ private LocalDateTime parseSystemLogDateTime(String value) {
+ if (Cools.isEmpty(value)) {
+ return null;
+ }
+ try {
+ return LocalDateTime.parse(value.trim(), SYSTEM_LOG_DATE_TIME);
+ } catch (DateTimeParseException e) {
+ return null;
+ }
+ }
+
+ private boolean isSystemLogLineInRange(LocalDate baseDate,
+ String timePart,
+ LocalDateTime startTime,
+ LocalDateTime endTime) {
+ if (baseDate == null || Cools.isEmpty(timePart) || startTime == null || endTime == null) {
+ return false;
+ }
+ try {
+ LocalTime time = LocalTime.parse(timePart);
+ LocalDateTime timestamp = LocalDateTime.of(baseDate, time);
+ return !timestamp.isBefore(startTime) && !timestamp.isAfter(endTime);
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ }
+
+ private String formatSystemExportTime(LocalDateTime dateTime) {
+ if (dateTime == null) {
+ return "unknown";
+ }
+ return SYSTEM_LOG_EXPORT_TIME.format(dateTime);
+ }
+
+
private Map<String, Object> buildEmptySummary() {
return buildSummaryResponse(Collections.emptyList());
}
--
Gitblit v1.9.1