#
Junjie
8 天以前 ce13e25ed685ba5c961832d023ceafecf4f30d47
src/main/java/com/zy/asrs/controller/DeviceLogController.java
@@ -1,24 +1,29 @@
package com.zy.asrs.controller;
import com.alibaba.fastjson.JSON;
import com.core.annotations.ManagerAuth;
import com.core.common.Cools;
import com.core.common.R;
import com.zy.asrs.entity.DeviceDataLog;
import com.zy.common.web.BaseController;
import com.zy.core.enums.SlaveType;
import lombok.extern.slf4j.Slf4j;
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 javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Slf4j
@RestController
public class DeviceLogController extends BaseController {
@@ -130,6 +135,186 @@
            return R.ok(res);
        } catch (Exception 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 = "offset", required = false) Integer offset,
                     @RequestParam(value = "limit", required = false) Integer limit) {
        try {
            String dayClean = day == null ? null : day.replaceAll("\\D", "");
            if (dayClean == null || dayClean.length() != 8 || !dayClean.chars().allMatch(Character::isDigit)) {
                return R.error("日期格式错误");
            }
            if (type == null || SlaveType.findInstance(type) == null) {
                return R.error("设备类型错误");
            }
            if (deviceNo == null || !deviceNo.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;
                }
            }));
            int from = offset == null || offset < 0 ? 0 : offset;
            int max = limit == null || limit <= 0 ? 5 : limit; // 默认读取5个文件
            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);
                            } catch (Exception e) {
                                // 忽略解析错误
                            }
                        }
                    });
                } catch (Exception e) {
                    log.error("读取日志文件失败: " + f, e);
                }
            }
            // 按时间排序
            resultLogs.sort(Comparator.comparing(DeviceDataLog::getCreateTime, Comparator.nullsLast(Date::compareTo)));
            return R.ok(resultLogs);
        } catch (Exception e) {
            log.error("预览日志失败", e);
            return R.error("预览日志失败");
        }
    }
    @RequestMapping(value = "/deviceLog/day/{day}/seek/auth")
    @ManagerAuth
    public R seek(@PathVariable("day") String day,
                  @RequestParam("type") String type,
                  @RequestParam("deviceNo") String deviceNo,
                  @RequestParam("timestamp") Long timestamp) {
        try {
            String dayClean = day == null ? null : day.replaceAll("\\D", "");
            if (dayClean == null || dayClean.length() != 8 || !dayClean.chars().allMatch(Character::isDigit)) {
                return R.error("日期格式错误");
            }
            if (type == null || SlaveType.findInstance(type) == null) {
                return R.error("设备类型错误");
            }
            if (deviceNo == null || !deviceNo.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;
                }
            }));
            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);
                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
                } 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) {
        try {
            String firstLine = null;
            try (Stream<String> lines = Files.lines(file, StandardCharsets.UTF_8)) {
                firstLine = lines.findFirst().orElse(null);
            }
            if (firstLine == null) return null;
            DeviceDataLog firstLog = JSON.parseObject(firstLine, DeviceDataLog.class);
            return firstLog.getCreateTime().getTime();
        } catch (Exception e) {
            return null;
        }
    }
@@ -335,4 +520,36 @@
        res.put("finished", info.finished);
        return R.ok(res);
    }
    @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)));
        enums.put("DualCrnForkPosType", Arrays.stream(com.zy.core.enums.DualCrnForkPosType.values())
                .collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
        enums.put("DualCrnLiftPosType", Arrays.stream(com.zy.core.enums.DualCrnLiftPosType.values())
                .collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
        enums.put("RgvModeType", Arrays.stream(com.zy.core.enums.RgvModeType.values())
                .collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
        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);
    }
}