package com.zy.core.task; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import com.zy.asrs.entity.DeviceDataLog; import com.zy.asrs.service.DeviceDataLogService; import com.zy.common.utils.RedisUtil; import com.zy.core.enums.RedisKeyType; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.text.SimpleDateFormat; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Set; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.stream.Collectors; @Slf4j @Component public class DeviceLogScheduler { @Value("${deviceLogStorage.type}") private String storageType; @Value("${deviceLogStorage.loggingPath}") private String loggingPath; @Value("${deviceLogStorage.expireDays}") private Integer expireDays; @Autowired private DeviceDataLogService deviceDataLogService; @Autowired private RedisUtil redisUtil; @Scheduled(cron = "0/3 * * * * ? ") public void delDeviceLog() { if ("mysql".equals(storageType)) { deviceDataLogService.clearLog(expireDays == null ? 1 : expireDays); }else if ("file".equals(storageType)) { clearFileLog(expireDays == null ? 1 : expireDays); }else { log.error("未定义的存储类型:{}", storageType); } } @Scheduled(cron = "0/3 * * * * ? ") public void execute() { int maxCount = 100; Set keys = redisUtil.scanKeys(RedisKeyType.DEVICE_LOG_KEY.key, maxCount); if (keys == null || keys.isEmpty()) { return; } List values = redisUtil.multiGet(keys); List list = new ArrayList<>(); for (Object object : values) { if (object instanceof DeviceDataLog) { list.add((DeviceDataLog) object); } } if (!list.isEmpty()) { if ("mysql".equals(storageType)) { mysqlSave(keys, list); }else if ("file".equals(storageType)) { fileSave(keys, list); }else { log.error("未定义的存储类型:{}", storageType); } } } private void mysqlSave(Set keys, List list) { if (deviceDataLogService.saveBatch(list)) { redisUtil.del(keys.toArray(new String[0])); } } private void fileSave(Set keys, List list) { try { Path baseDir = Paths.get(loggingPath); Files.createDirectories(baseDir); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); Map>> group = new HashMap<>(); for (DeviceDataLog logItem : list) { String typeName = logItem.getType(); String datePart = sdf.format(logItem.getCreateTime() == null ? new Date() : logItem.getCreateTime()); String prefix = typeName + "_" + String.valueOf(logItem.getDeviceNo()) + "_" + datePart + "_"; group.computeIfAbsent(datePart, k -> new HashMap<>()) .computeIfAbsent(prefix, k -> new ArrayList<>()) .add(logItem); } for (Map.Entry>> dateEntry : group.entrySet()) { Path dayDir = baseDir.resolve(dateEntry.getKey()); Files.createDirectories(dayDir); for (Map.Entry> entry : dateEntry.getValue().entrySet()) { String prefix = entry.getKey(); List logs = entry.getValue(); logs.sort(Comparator.comparing(DeviceDataLog::getCreateTime, Comparator.nullsLast(Date::compareTo))); int index = findStartIndex(dayDir, prefix); Path current = dayDir.resolve(prefix + index + ".log"); if (!Files.exists(current)) { Files.createFile(current); } long size = Files.size(current); long max = 1024L * 1024L; for (DeviceDataLog d : logs) { String json = JSON.toJSONStringWithDateFormat(d, "yyyy-MM-dd HH:mm:ss.SSS", SerializerFeature.WriteDateUseDateFormat); byte[] line = (json + System.lineSeparator()).getBytes(StandardCharsets.UTF_8); if (size + line.length > max) { index++; current = dayDir.resolve(prefix + index + ".log"); Files.createFile(current); size = 0; } Files.write(current, line, StandardOpenOption.CREATE, StandardOpenOption.APPEND); size += line.length; } } } redisUtil.del(keys.toArray(new String[0])); } catch (Exception e) { log.error("设备日志文件存储失败", e); } } private int findStartIndex(Path baseDir, String prefix) throws Exception { List matched = Files.list(baseDir) .filter(p -> { String n = p.getFileName().toString(); return n.startsWith(prefix) && n.endsWith(".log"); }) .collect(Collectors.toList()); int maxIdx = 0; for (Path p : matched) { String name = p.getFileName().toString(); String suf = name.substring(prefix.length()); if (!suf.isEmpty()) { try { int val = Integer.parseInt(suf.replace(".log", "")); if (val > maxIdx) { maxIdx = val; } } catch (NumberFormatException ignored) {} } } int candidate = maxIdx == 0 ? 1 : maxIdx; Path path = baseDir.resolve(prefix + candidate + ".log"); if (Files.exists(path)) { long size = Files.size(path); if (size >= 1024L * 1024L) { return candidate + 1; } } return candidate; } private void clearFileLog(int days) { try { Path baseDir = Paths.get(loggingPath); if (!Files.exists(baseDir)) { return; } SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); long cutoff = System.currentTimeMillis() - (long) days * 24 * 60 * 60 * 1000; List dirs = Files.list(baseDir).filter(Files::isDirectory).collect(Collectors.toList()); for (Path dir : dirs) { String name = dir.getFileName().toString(); if (name.length() == 8 && name.chars().allMatch(Character::isDigit)) { Date d = sdf.parse(name); if (d.getTime() < cutoff) { List all = Files.walk(dir).sorted(Comparator.reverseOrder()).collect(Collectors.toList()); for (Path p : all) { try { Files.deleteIfExists(p); } catch (Exception ignored) {} } } } } } catch (Exception e) { log.error("设备日志文件清理失败", e); } } }