From ee53cfaffb30751dbade9008d482269d62147917 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期六, 25 四月 2026 07:54:06 +0800
Subject: [PATCH] #日志采集功能优化V3.0.0.9
---
src/main/java/com/zy/asrs/controller/DeviceLogController.java | 1096 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 963 insertions(+), 133 deletions(-)
diff --git a/src/main/java/com/zy/asrs/controller/DeviceLogController.java b/src/main/java/com/zy/asrs/controller/DeviceLogController.java
index a498bd5..a67a5da 100644
--- a/src/main/java/com/zy/asrs/controller/DeviceLogController.java
+++ b/src/main/java/com/zy/asrs/controller/DeviceLogController.java
@@ -4,31 +4,68 @@
import com.core.annotations.ManagerAuth;
import com.core.common.Cools;
import com.core.common.R;
+import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.DeviceDataLog;
+import com.zy.asrs.service.BasDevpService;
import com.zy.common.web.BaseController;
import com.zy.core.enums.SlaveType;
+import com.zy.core.model.StationObjModel;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
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;
+import java.util.zip.ZipOutputStream;
@Slf4j
@RestController
public class DeviceLogController extends BaseController {
+ private static final List<String> DEVICE_TYPE_ORDER = Arrays.asList("Crn", "DualCrn", "Rgv", "Devp");
+ private static final Map<String, String> DEVICE_TYPE_LABELS = new LinkedHashMap<>();
+
+ static {
+ DEVICE_TYPE_LABELS.put("Crn", "鍫嗗灈鏈�");
+ DEVICE_TYPE_LABELS.put("DualCrn", "鍙屽伐浣嶅爢鍨涙満");
+ DEVICE_TYPE_LABELS.put("Rgv", "RGV");
+ DEVICE_TYPE_LABELS.put("Devp", "杈撻�佽澶�");
+ }
+
+ @Autowired
+ private BasDevpService basDevpService;
+
@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;
@@ -38,7 +75,55 @@
boolean finished;
}
+ private static class FileNameInfo {
+ String type;
+ String deviceNo;
+ String stationId;
+ String day;
+ int index;
+ }
+
+ private static class FileTimeRange {
+ Long startTime;
+ Long endTime;
+ }
+
+ private static class DeviceAggregate {
+ String type;
+ String typeLabel;
+ String deviceNo;
+ String stationId;
+ List<String> stationIds;
+ int fileCount;
+ Long firstTime;
+ Long lastTime;
+ Integer firstIndex;
+ Integer lastIndex;
+ Path firstFile;
+ 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
@@ -103,31 +188,29 @@
if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
return R.ok(new ArrayList<>());
}
- List<Path> files = Files.list(dayDir)
- .filter(p -> !Files.isDirectory(p) && p.getFileName().toString().endsWith(".log"))
- .collect(Collectors.toList());
+ List<Path> files = listDayLogFiles(dayDir);
Map<String, Map<String, Object>> deviceMap = new HashMap<>();
for (Path p : files) {
- String name = p.getFileName().toString();
- String[] parts = name.split("_");
- if (parts.length < 4) {
+ FileNameInfo info = parseFileName(p.getFileName().toString());
+ if (info == null || !day.equals(info.day)) {
continue;
}
- String deviceNo = parts[1];
- String type = parts[0];
- Map<String, Object> info = deviceMap.computeIfAbsent(deviceNo, k -> {
+ String deviceKey = buildDeviceKey(info.type, info.deviceNo, info.stationId);
+ Map<String, Object> infoMap = deviceMap.computeIfAbsent(deviceKey, k -> {
Map<String, Object> map = new HashMap<>();
- map.put("deviceNo", deviceNo);
+ map.put("deviceNo", info.deviceNo);
+ map.put("stationId", info.stationId);
map.put("types", new HashSet<String>());
map.put("fileCount", 0);
return map;
});
- ((Set<String>) info.get("types")).add(type);
- info.put("fileCount", ((Integer) info.get("fileCount")) + 1);
+ ((Set<String>) infoMap.get("types")).add(info.type);
+ infoMap.put("fileCount", ((Integer) infoMap.get("fileCount")) + 1);
}
List<Map<String, Object>> res = deviceMap.values().stream().map(m -> {
Map<String, Object> x = new HashMap<>();
x.put("deviceNo", m.get("deviceNo"));
+ x.put("stationId", m.get("stationId"));
x.put("types", ((Set<String>) m.get("types")).stream().collect(Collectors.toList()));
x.put("fileCount", m.get("fileCount"));
return x;
@@ -138,11 +221,151 @@
}
}
+ @RequestMapping(value = "/deviceLog/day/{day}/summary/auth")
+ @ManagerAuth
+ public R summary(@PathVariable("day") String day) {
+ try {
+ String dayClean = normalizeDay(day);
+ if (dayClean == null) {
+ return R.error("鏃ユ湡鏍煎紡閿欒");
+ }
+ Path dayDir = Paths.get(loggingPath, dayClean);
+ if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
+ return R.ok(buildEmptySummary());
+ }
+
+ Map<String, DeviceAggregate> aggregateMap = new LinkedHashMap<>();
+ List<Path> files = listDayLogFiles(dayDir);
+ for (Path file : files) {
+ FileNameInfo info = parseFileName(file.getFileName().toString());
+ if (info == null || !dayClean.equals(info.day) || !DEVICE_TYPE_LABELS.containsKey(info.type)) {
+ continue;
+ }
+ String key = buildDeviceKey(info.type, info.deviceNo, info.stationId);
+ DeviceAggregate aggregate = aggregateMap.computeIfAbsent(key, k -> {
+ DeviceAggregate x = new DeviceAggregate();
+ x.type = info.type;
+ x.typeLabel = DEVICE_TYPE_LABELS.get(info.type);
+ x.deviceNo = info.deviceNo;
+ x.stationId = info.stationId;
+ return x;
+ });
+ aggregate.fileCount += 1;
+ if (aggregate.firstIndex == null || info.index < aggregate.firstIndex) {
+ aggregate.firstIndex = info.index;
+ aggregate.firstFile = file;
+ }
+ if (aggregate.lastIndex == null || info.index > aggregate.lastIndex) {
+ aggregate.lastIndex = info.index;
+ aggregate.lastFile = file;
+ }
+ }
+ for (DeviceAggregate aggregate : aggregateMap.values()) {
+ if (aggregate.firstFile != null) {
+ FileTimeRange firstRange = readFileTimeRange(aggregate.firstFile, aggregate.stationId);
+ aggregate.firstTime = firstRange.startTime != null ? firstRange.startTime : firstRange.endTime;
+ }
+ if (aggregate.lastFile != null) {
+ FileTimeRange lastRange = readFileTimeRange(aggregate.lastFile, aggregate.stationId);
+ aggregate.lastTime = lastRange.endTime != null ? lastRange.endTime : lastRange.startTime;
+ }
+ }
+ enrichDevpStationIds(aggregateMap.values());
+ return R.ok(buildSummaryResponse(aggregateMap.values()));
+ } catch (Exception e) {
+ log.error("璇诲彇璁惧鏃ュ織鎽樿澶辫触", e);
+ return R.error("璇诲彇璁惧鏃ュ織鎽樿澶辫触");
+ }
+ }
+
+ @RequestMapping(value = "/deviceLog/day/{day}/timeline/auth")
+ @ManagerAuth
+ public R timeline(@PathVariable("day") String day,
+ @RequestParam("type") String type,
+ @RequestParam("deviceNo") String deviceNo,
+ @RequestParam(value = "stationId", required = false) String stationId) {
+ try {
+ String dayClean = normalizeDay(day);
+ if (dayClean == null) {
+ return R.error("鏃ユ湡鏍煎紡閿欒");
+ }
+ if (type == null || SlaveType.findInstance(type) == null) {
+ return R.error("璁惧绫诲瀷閿欒");
+ }
+ if (deviceNo == null || !deviceNo.chars().allMatch(Character::isDigit)) {
+ return R.error("璁惧缂栧彿閿欒");
+ }
+ if (isDevpType(type) && (stationId == null || !stationId.chars().allMatch(Character::isDigit))) {
+ return R.error("绔欑偣缂栧彿閿欒");
+ }
+ Path dayDir = Paths.get(loggingPath, dayClean);
+ if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
+ return R.error("鏈壘鍒版棩蹇楁枃浠�");
+ }
+ List<Path> files = findDeviceFiles(dayDir, dayClean, type, deviceNo, stationId);
+ if (files.isEmpty()) {
+ return R.error("鏈壘鍒版棩蹇楁枃浠�");
+ }
+
+ List<Map<String, Object>> segments = new ArrayList<>();
+ Long startTime = null;
+ for (int i = 0; i < files.size(); i++) {
+ Long segmentStart = getFileStartTime(files.get(i), stationId);
+ if (segmentStart != null && (startTime == null || segmentStart < startTime)) {
+ startTime = segmentStart;
+ }
+ Map<String, Object> segment = new LinkedHashMap<>();
+ segment.put("offset", i);
+ segment.put("startTime", segmentStart);
+ segment.put("endTime", null);
+ segments.add(segment);
+ }
+ Long endTime = getFileEndTime(files.get(files.size() - 1), stationId);
+ if (endTime == null) {
+ for (int i = segments.size() - 1; i >= 0; i--) {
+ Long segmentStart = (Long) segments.get(i).get("startTime");
+ if (segmentStart != null) {
+ endTime = segmentStart;
+ break;
+ }
+ }
+ }
+ for (int i = 0; i < segments.size(); i++) {
+ Long segmentEnd = null;
+ if (i < segments.size() - 1) {
+ segmentEnd = (Long) segments.get(i + 1).get("startTime");
+ }
+ if (segmentEnd == null && i == segments.size() - 1) {
+ segmentEnd = endTime;
+ }
+ if (segmentEnd == null) {
+ segmentEnd = (Long) segments.get(i).get("startTime");
+ }
+ segments.get(i).put("endTime", segmentEnd);
+ }
+
+ Map<String, Object> result = new HashMap<>();
+ result.put("type", type);
+ result.put("typeLabel", DEVICE_TYPE_LABELS.getOrDefault(type, type));
+ result.put("deviceNo", deviceNo);
+ result.put("stationId", stationId);
+ result.put("startTime", startTime);
+ result.put("endTime", endTime);
+ result.put("totalFiles", files.size());
+ result.put("segments", segments);
+ return R.ok(result);
+ } catch (Exception e) {
+ log.error("璇诲彇璁惧鏃ュ織鏃堕棿杞村け璐�", e);
+ return R.error("璇诲彇璁惧鏃ュ織鏃堕棿杞村け璐�");
+ }
+ }
+
@RequestMapping(value = "/deviceLog/day/{day}/preview/auth")
@ManagerAuth
public R preview(@PathVariable("day") String day,
@RequestParam("type") String type,
@RequestParam("deviceNo") String deviceNo,
+ @RequestParam(value = "stationId", required = false) String stationId,
@RequestParam(value = "offset", required = false) Integer offset,
@RequestParam(value = "limit", required = false) Integer limit) {
try {
@@ -156,48 +379,37 @@
if (deviceNo == null || !deviceNo.chars().allMatch(Character::isDigit)) {
return R.error("璁惧缂栧彿閿欒");
}
+ if (isDevpType(type) && (stationId == null || !stationId.chars().allMatch(Character::isDigit))) {
+ return R.error("绔欑偣缂栧彿閿欒");
+ }
Path dayDir = Paths.get(loggingPath, dayClean);
if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
return R.ok(new ArrayList<>());
}
- String prefix = type + "_" + deviceNo + "_" + dayClean + "_";
- List<Path> files = Files.list(dayDir)
- .filter(p -> {
- String name = p.getFileName().toString();
- return name.endsWith(".log") && name.startsWith(prefix);
- }).collect(Collectors.toList());
-
- files.sort(Comparator.comparingInt(p -> {
- String n = p.getFileName().toString();
- try {
- String suf = n.substring(prefix.length(), n.length() - 4);
- return Integer.parseInt(suf);
- } catch (Exception e) {
- return Integer.MAX_VALUE;
- }
- }));
-
+ List<Path> files = findDeviceFiles(dayDir, dayClean, type, deviceNo, stationId);
+
int from = offset == null || offset < 0 ? 0 : offset;
- int max = limit == null || limit <= 0 ? 5 : limit; // 榛樿璇诲彇5涓枃浠�
- if (max > 10) max = 10; // 闄愬埗鏈�澶ф枃浠舵暟锛岄槻姝㈣秴鏃�
+ int max = limit == null || limit <= 0 ? 5 : limit;
+ if (max > 10) max = 10;
int to = Math.min(files.size(), from + max);
-
+
if (from >= files.size()) {
return R.ok(new ArrayList<>());
}
-
+
List<Path> targetFiles = files.subList(from, to);
List<DeviceDataLog> resultLogs = new ArrayList<>();
-
+
for (Path f : targetFiles) {
try (Stream<String> lines = Files.lines(f, StandardCharsets.UTF_8)) {
lines.forEach(line -> {
if (line != null && !line.trim().isEmpty()) {
try {
DeviceDataLog logItem = JSON.parseObject(line, DeviceDataLog.class);
- resultLogs.add(logItem);
+ if (matchesRequestedStation(logItem, stationId)) {
+ resultLogs.add(logItem);
+ }
} catch (Exception e) {
- // 蹇界暐瑙f瀽閿欒
}
}
});
@@ -205,9 +417,8 @@
log.error("璇诲彇鏃ュ織鏂囦欢澶辫触: " + f, e);
}
}
- // 鎸夋椂闂存帓搴�
resultLogs.sort(Comparator.comparing(DeviceDataLog::getCreateTime, Comparator.nullsLast(Date::compareTo)));
-
+
return R.ok(resultLogs);
} catch (Exception e) {
log.error("棰勮鏃ュ織澶辫触", e);
@@ -220,6 +431,7 @@
public R seek(@PathVariable("day") String day,
@RequestParam("type") String type,
@RequestParam("deviceNo") String deviceNo,
+ @RequestParam(value = "stationId", required = false) String stationId,
@RequestParam("timestamp") Long timestamp) {
try {
String dayClean = day == null ? null : day.replaceAll("\\D", "");
@@ -232,87 +444,72 @@
if (deviceNo == null || !deviceNo.chars().allMatch(Character::isDigit)) {
return R.error("璁惧缂栧彿閿欒");
}
+ if (isDevpType(type) && (stationId == null || !stationId.chars().allMatch(Character::isDigit))) {
+ return R.error("绔欑偣缂栧彿閿欒");
+ }
Path dayDir = Paths.get(loggingPath, dayClean);
if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
return R.error("鏈壘鍒版棩蹇楁枃浠�");
}
-
- String prefix = type + "_" + deviceNo + "_" + dayClean + "_";
- List<Path> files = Files.list(dayDir)
- .filter(p -> {
- String name = p.getFileName().toString();
- return name.endsWith(".log") && name.startsWith(prefix);
- }).collect(Collectors.toList());
-
- files.sort(Comparator.comparingInt(p -> {
- String n = p.getFileName().toString();
- try {
- String suf = n.substring(prefix.length(), n.length() - 4);
- return Integer.parseInt(suf);
- } catch (Exception e) {
- return Integer.MAX_VALUE;
- }
- }));
-
+
+ List<Path> files = findDeviceFiles(dayDir, dayClean, type, deviceNo, stationId);
+
if (files.isEmpty()) {
return R.error("鏈壘鍒版棩蹇楁枃浠�");
}
-
- // Binary search for the file containing the timestamp
- // We want to find the LAST file that has startTime <= targetTime.
- // Because files are sequential: [t0, t1), [t1, t2), ...
- // If we find file[i].startTime <= target < file[i+1].startTime, then target is in file[i].
-
+
int low = 0;
int high = files.size() - 1;
int foundIndex = -1;
-
+
while (low <= high) {
int mid = (low + high) >>> 1;
Path midFile = files.get(mid);
-
- // Read start time of this file
- Long midStart = getFileStartTime(midFile);
+ Long midStart = getFileStartTime(midFile, stationId);
if (midStart == null) {
low = mid + 1;
continue;
}
-
+
if (midStart <= timestamp) {
- // This file starts before or at target. It COULD be the one.
- // But maybe a later file also starts before target?
foundIndex = mid;
- low = mid + 1; // Try to find a later start time
+ low = mid + 1;
} else {
- // This file starts AFTER target. So target must be in an earlier file.
high = mid - 1;
}
}
-
+
if (foundIndex == -1) {
foundIndex = 0;
}
-
- // Return the file index (offset)
+
Map<String, Object> result = new HashMap<>();
result.put("offset", foundIndex);
return R.ok(result);
-
+
} catch (Exception e) {
log.error("瀵诲潃澶辫触", e);
return R.error("瀵诲潃澶辫触");
}
}
- private Long getFileStartTime(Path file) {
+ private Long getFileStartTime(Path file, String stationId) {
try {
- String firstLine = null;
- try (Stream<String> lines = Files.lines(file, StandardCharsets.UTF_8)) {
- firstLine = lines.findFirst().orElse(null);
- }
+ String firstLine = readFirstMatchingLine(file, stationId);
if (firstLine == null) return null;
DeviceDataLog firstLog = JSON.parseObject(firstLine, DeviceDataLog.class);
return firstLog.getCreateTime().getTime();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private Long getFileEndTime(Path file, String stationId) {
+ try {
+ String lastLine = readLastMatchingLine(file, stationId);
+ if (lastLine == null) return null;
+ DeviceDataLog lastLog = JSON.parseObject(lastLine, DeviceDataLog.class);
+ return lastLog.getCreateTime().getTime();
} catch (Exception e) {
return null;
}
@@ -341,36 +538,18 @@
response.setStatus(400);
return;
}
+ String stationId = request.getParameter("stationId");
+ if (isDevpType(type) && (stationId == null || !stationId.chars().allMatch(Character::isDigit))) {
+ response.setStatus(400);
+ return;
+ }
Path dayDir = Paths.get(loggingPath, dayClean);
if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
response.setStatus(404);
return;
}
- List<Path> files = Files.list(dayDir)
- .filter(p -> {
- String name = p.getFileName().toString();
- String prefix = type + "_" + deviceNo + "_" + dayClean + "_";
- return name.endsWith(".log") && name.startsWith(prefix);
- }).collect(Collectors.toList());
- // 鎺掑簭锛堟寜鏂囦欢涓殑绱㈠紩鍙烽�掑锛�
- String prefix = type + "_" + deviceNo + "_" + dayClean + "_";
- files.sort(Comparator.comparingInt(p -> {
- String n = p.getFileName().toString();
- try {
- String suf = n.substring(prefix.length(), n.length() - 4);
- return Integer.parseInt(suf);
- } catch (Exception e) {
- return Integer.MAX_VALUE;
- }
- }));
- int from = offset == null || offset < 0 ? 0 : offset;
- int max = limit == null || limit <= 0 ? 200 : limit;
- int to = Math.min(files.size(), from + max);
- if (from >= files.size()) {
- response.setStatus(404);
- return;
- }
- files = files.subList(from, to);
+ List<Path> files = findDeviceFiles(dayDir, dayClean, type, deviceNo, stationId);
+ files = sliceDownloadFiles(files, offset, limit);
if (files.isEmpty()) {
response.setStatus(404);
return;
@@ -405,9 +584,9 @@
response.setHeader("X-Total-Size", String.valueOf(totalRawSize));
response.setHeader("X-File-Count", String.valueOf(files.size()));
response.setHeader("X-Progress-Id", id);
- try (java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(response.getOutputStream())) {
+ try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
for (Path f : files) {
- java.util.zip.ZipEntry entry = new java.util.zip.ZipEntry(f.getFileName().toString());
+ ZipEntry entry = new ZipEntry(f.getFileName().toString());
zos.putNextEntry(entry);
Files.copy(f, zos);
zos.closeEntry();
@@ -443,33 +622,22 @@
if (Cools.isEmpty(deviceNo) || !deviceNo.chars().allMatch(Character::isDigit)) {
return R.error("璁惧缂栧彿閿欒");
}
+ String stationId = param.getString("stationId");
+ if (isDevpType(type) && (Cools.isEmpty(stationId) || !stationId.chars().allMatch(Character::isDigit))) {
+ return R.error("绔欑偣缂栧彿閿欒");
+ }
Path dayDir = Paths.get(loggingPath, dayClean);
if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
return R.error("褰撴棩鐩綍涓嶅瓨鍦�");
}
- List<Path> files = Files.list(dayDir)
- .filter(p -> {
- String name = p.getFileName().toString();
- String prefix = type + "_" + deviceNo + "_" + dayClean + "_";
- return name.endsWith(".log") && name.startsWith(prefix);
- }).collect(Collectors.toList());
- String prefix = type + "_" + deviceNo + "_" + dayClean + "_";
- files.sort(Comparator.comparingInt(p -> {
- String n = p.getFileName().toString();
- try {
- String suf = n.substring(prefix.length(), n.length() - 4);
- return Integer.parseInt(suf);
- } catch (Exception e) {
- return Integer.MAX_VALUE;
- }
- }));
- int from = offset == null || offset < 0 ? 0 : offset;
- int max = limit == null || limit <= 0 ? 200 : limit;
- int to = Math.min(files.size(), from + max);
- if (from >= files.size()) {
+ List<Path> files = findDeviceFiles(dayDir, dayClean, type, deviceNo, stationId);
+ if ((offset != null && offset >= files.size())) {
return R.error("璧峰搴忓彿瓒呭嚭鑼冨洿");
}
- files = files.subList(from, to);
+ files = sliceDownloadFiles(files, offset, limit);
+ if (files.isEmpty()) {
+ return R.error("鏈壘鍒版棩蹇楁枃浠�");
+ }
String id = UUID.randomUUID().toString();
ProgressInfo info = new ProgressInfo();
info.totalCount = files.size();
@@ -521,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)));
@@ -549,7 +842,544 @@
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());
+ }
+
+ private Map<String, Object> buildSummaryResponse(Collection<DeviceAggregate> aggregates) {
+ List<DeviceAggregate> aggregateList = new ArrayList<>(aggregates);
+ Map<String, Object> stats = new LinkedHashMap<>();
+ Map<String, Object> typeCounts = new LinkedHashMap<>();
+ int totalFiles = 0;
+ for (String type : DEVICE_TYPE_ORDER) {
+ int count = (int) aggregateList.stream().filter(item -> type.equals(item.type)).count();
+ typeCounts.put(type, count);
+ }
+ for (DeviceAggregate aggregate : aggregateList) {
+ totalFiles += aggregate.fileCount;
+ }
+ stats.put("totalDevices", aggregateList.size());
+ stats.put("totalFiles", totalFiles);
+ stats.put("typeCounts", typeCounts);
+
+ List<Map<String, Object>> groups = new ArrayList<>();
+ for (String type : DEVICE_TYPE_ORDER) {
+ List<DeviceAggregate> devices = aggregateList.stream()
+ .filter(item -> type.equals(item.type))
+ .sorted(Comparator.comparingInt(item -> parseDeviceNo(item.deviceNo)))
+ .collect(Collectors.toList());
+ Map<String, Object> group = new LinkedHashMap<>();
+ group.put("type", type);
+ group.put("typeLabel", DEVICE_TYPE_LABELS.get(type));
+ group.put("deviceCount", devices.size());
+ group.put("totalFiles", devices.stream().mapToInt(item -> item.fileCount).sum());
+ group.put("devices", devices.stream().map(item -> {
+ Map<String, Object> x = new LinkedHashMap<>();
+ x.put("type", item.type);
+ x.put("typeLabel", item.typeLabel);
+ x.put("deviceNo", item.deviceNo);
+ x.put("stationId", item.stationId);
+ x.put("fileCount", item.fileCount);
+ x.put("firstTime", item.firstTime);
+ x.put("lastTime", item.lastTime);
+ x.put("stationIds", item.stationIds == null ? Collections.emptyList() : item.stationIds);
+ return x;
+ }).collect(Collectors.toList()));
+ groups.add(group);
+ }
+
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("stats", stats);
+ result.put("groups", groups);
+ return result;
+ }
+
+ private void enrichDevpStationIds(Collection<DeviceAggregate> aggregates) {
+ if (aggregates == null || aggregates.isEmpty() || basDevpService == null) {
+ return;
+ }
+ List<DeviceAggregate> devpAggregates = aggregates.stream()
+ .filter(item -> item != null && isDevpType(item.type) && Cools.isEmpty(item.stationId) && !Cools.isEmpty(item.deviceNo))
+ .collect(Collectors.toList());
+ if (devpAggregates.isEmpty()) {
+ return;
+ }
+ List<Integer> devpNos = devpAggregates.stream()
+ .map(item -> parseInteger(item.deviceNo))
+ .filter(Objects::nonNull)
+ .distinct()
+ .collect(Collectors.toList());
+ if (devpNos.isEmpty()) {
+ return;
+ }
+ Map<Integer, List<String>> stationIdsByDevpNo = basDevpService.listByIds(devpNos).stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toMap(BasDevp::getDevpNo, this::extractStationIds, (left, right) -> left));
+ for (DeviceAggregate aggregate : devpAggregates) {
+ Integer devpNo = parseInteger(aggregate.deviceNo);
+ if (devpNo != null) {
+ aggregate.stationIds = stationIdsByDevpNo.getOrDefault(devpNo, Collections.emptyList());
+ }
+ }
+ }
+
+ private List<String> extractStationIds(BasDevp basDevp) {
+ if (basDevp == null) {
+ return Collections.emptyList();
+ }
+ return basDevp.getStationList$().stream()
+ .map(StationObjModel::getStationId)
+ .filter(Objects::nonNull)
+ .map(String::valueOf)
+ .distinct()
+ .sorted(Comparator.comparingInt(this::parseDeviceNo))
+ .collect(Collectors.toList());
+ }
+
+ private Integer parseInteger(String value) {
+ try {
+ return Integer.parseInt(String.valueOf(value));
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private String normalizeDay(String day) {
+ String dayClean = day == null ? null : day.replaceAll("\\D", "");
+ if (dayClean == null || dayClean.length() != 8 || !dayClean.chars().allMatch(Character::isDigit)) {
+ return null;
+ }
+ return dayClean;
+ }
+
+ private List<Path> findDeviceFiles(Path dayDir, String dayClean, String type, String deviceNo, String stationId) throws Exception {
+ FileNameInfo target = new FileNameInfo();
+ target.type = type;
+ target.deviceNo = deviceNo;
+ target.stationId = stationId;
+ target.day = dayClean;
+ Path deviceDir = resolveDeviceDir(dayDir, type, deviceNo);
+ if (deviceDir == null || !Files.exists(deviceDir) || !Files.isDirectory(deviceDir)) {
+ return Collections.emptyList();
+ }
+ List<Path> files;
+ try (Stream<Path> stream = Files.list(deviceDir)) {
+ files = stream
+ .filter(p -> !Files.isDirectory(p) && matchesFileInfo(parseFileName(p.getFileName().toString()), target))
+ .collect(Collectors.toList());
+ }
+ files.sort(Comparator.comparingInt(p -> {
+ FileNameInfo info = parseFileName(p.getFileName().toString());
+ return info == null ? Integer.MAX_VALUE : info.index;
+ }));
+ return files;
+ }
+
+ private List<Path> listDayLogFiles(Path dayDir) throws Exception {
+ if (dayDir == null || !Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
+ return Collections.emptyList();
+ }
+ List<Path> files = new ArrayList<>();
+ try (Stream<Path> typeStream = Files.list(dayDir)) {
+ List<Path> typeDirs = typeStream.filter(Files::isDirectory).collect(Collectors.toList());
+ for (Path typeDir : typeDirs) {
+ try (Stream<Path> deviceStream = Files.list(typeDir)) {
+ List<Path> deviceDirs = deviceStream.filter(Files::isDirectory).collect(Collectors.toList());
+ for (Path deviceDir : deviceDirs) {
+ try (Stream<Path> fileStream = Files.list(deviceDir)) {
+ fileStream
+ .filter(p -> !Files.isDirectory(p) && p.getFileName().toString().endsWith(".log"))
+ .forEach(files::add);
+ }
+ }
+ }
+ }
+ }
+ return files;
+ }
+
+ private Path resolveDeviceDir(Path dayDir, String type, String deviceNo) {
+ if (dayDir == null || Cools.isEmpty(type) || Cools.isEmpty(deviceNo)) {
+ return null;
+ }
+ return dayDir.resolve(type).resolve(deviceNo);
+ }
+
+ private List<Path> sliceDownloadFiles(List<Path> files, Integer offset, Integer limit) {
+ if (files == null || files.isEmpty()) {
+ return Collections.emptyList();
+ }
+ int from = offset == null || offset < 0 ? 0 : offset;
+ if (from >= files.size()) {
+ return Collections.emptyList();
+ }
+ if (offset == null && limit == null) {
+ return new ArrayList<>(files);
+ }
+ int to;
+ if (limit == null || limit <= 0) {
+ to = files.size();
+ } else {
+ to = Math.min(files.size(), from + limit);
+ }
+ return new ArrayList<>(files.subList(from, to));
+ }
+
+ private FileNameInfo parseFileName(String fileName) {
+ if (fileName == null || !fileName.endsWith(".log")) {
+ return null;
+ }
+ String fileNameNoExt = fileName.substring(0, fileName.length() - 4);
+ String[] parts = fileNameNoExt.split("_");
+ if (parts.length < 4) {
+ return null;
+ }
+ FileNameInfo info = new FileNameInfo();
+ info.type = parts[0];
+ info.deviceNo = parts[1];
+ if (isDevpType(info.type)) {
+ if (parts.length != 6 || !"station".equals(parts[2])) {
+ return null;
+ }
+ info.stationId = parts[3];
+ info.day = parts[4];
+ try {
+ info.index = Integer.parseInt(parts[5]);
+ } catch (Exception e) {
+ return null;
+ }
+ return info;
+ }
+ if (parts.length != 4) {
+ return null;
+ }
+ info.day = parts[2];
+ try {
+ info.index = Integer.parseInt(parts[3]);
+ } catch (Exception e) {
+ return null;
+ }
+ return info;
+ }
+
+ private String buildDeviceKey(String type, String deviceNo, String stationId) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(String.valueOf(type)).append(":").append(String.valueOf(deviceNo));
+ if (isDevpType(type)) {
+ builder.append(":").append(String.valueOf(stationId));
+ }
+ return builder.toString();
+ }
+
+ private boolean matchesFileInfo(FileNameInfo actual, FileNameInfo target) {
+ if (actual == null || target == null) {
+ return false;
+ }
+ if (!Objects.equals(actual.type, target.type)) {
+ return false;
+ }
+ if (!Objects.equals(actual.deviceNo, target.deviceNo)) {
+ return false;
+ }
+ if (!Objects.equals(actual.day, target.day)) {
+ return false;
+ }
+ if (isDevpType(actual.type)) {
+ return Objects.equals(actual.stationId, target.stationId);
+ }
+ return true;
+ }
+
+ private boolean isDevpType(String type) {
+ return SlaveType.Devp.name().equals(type);
+ }
+
+ private int parseDeviceNo(String deviceNo) {
+ try {
+ return Integer.parseInt(String.valueOf(deviceNo));
+ } catch (Exception e) {
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ private FileTimeRange readFileTimeRange(Path file, String stationId) {
+ FileTimeRange range = new FileTimeRange();
+ try {
+ String firstLine = readFirstMatchingLine(file, stationId);
+ String lastLine = readLastMatchingLine(file, stationId);
+ range.startTime = parseLogTime(firstLine, stationId);
+ range.endTime = parseLogTime(lastLine, stationId);
+ return range;
+ } catch (Exception e) {
+ return range;
+ }
+ }
+
+ private Long parseLogTime(String line, String stationId) {
+ try {
+ if (line == null || line.trim().isEmpty()) {
+ return null;
+ }
+ DeviceDataLog logItem = JSON.parseObject(line, DeviceDataLog.class);
+ if (!matchesRequestedStation(logItem, stationId)) {
+ return null;
+ }
+ return logItem != null && logItem.getCreateTime() != null ? logItem.getCreateTime().getTime() : null;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private boolean matchesRequestedStation(DeviceDataLog logItem, String stationId) {
+ if (Cools.isEmpty(stationId)) {
+ return true;
+ }
+ if (logItem == null || logItem.getStationId() == null) {
+ return false;
+ }
+ return Objects.equals(String.valueOf(logItem.getStationId()), stationId);
+ }
+
+ private String readFirstMatchingLine(Path file, String stationId) {
+ try (Stream<String> lines = Files.lines(file, StandardCharsets.UTF_8)) {
+ return lines
+ .filter(line -> line != null && !line.trim().isEmpty())
+ .filter(line -> matchesRequestedStation(parseLogLine(line), stationId))
+ .findFirst()
+ .orElse(null);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private String readLastMatchingLine(Path file, String stationId) {
+ try (Stream<String> lines = Files.lines(file, StandardCharsets.UTF_8)) {
+ List<String> matched = lines
+ .filter(line -> line != null && !line.trim().isEmpty())
+ .filter(line -> matchesRequestedStation(parseLogLine(line), stationId))
+ .collect(Collectors.toList());
+ if (matched.isEmpty()) {
+ return null;
+ }
+ return matched.get(matched.size() - 1);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private DeviceDataLog parseLogLine(String line) {
+ try {
+ if (line == null || line.trim().isEmpty()) {
+ return null;
+ }
+ return JSON.parseObject(line, DeviceDataLog.class);
+ } catch (Exception e) {
+ return null;
+ }
+ }
}
--
Gitblit v1.9.1