package com.zy.core.task;
|
|
import jakarta.annotation.PostConstruct;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.stereotype.Component;
|
|
import java.nio.file.FileVisitResult;
|
import java.nio.file.Files;
|
import java.nio.file.Path;
|
import java.nio.file.Paths;
|
import java.nio.file.SimpleFileVisitor;
|
import java.nio.file.attribute.BasicFileAttributes;
|
import java.time.LocalDate;
|
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeParseException;
|
import java.time.format.ResolverStyle;
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.stream.Stream;
|
|
@Slf4j
|
@Component
|
public class DeviceLogScheduler {
|
|
private static final ReentrantLock FILE_OP_LOCK = new ReentrantLock();
|
private static final int DEFAULT_EXPIRE_DAYS = 1;
|
private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("uuuuMMdd")
|
.withResolverStyle(ResolverStyle.STRICT);
|
|
@Value("${deviceLogStorage.type}")
|
private String storageType;
|
@Value("${deviceLogStorage.loggingPath}")
|
private String loggingPath;
|
@Value("${deviceLogStorage.expireDays}")
|
private Integer expireDays;
|
|
@PostConstruct
|
public void validateStorageMode() {
|
if (!"file".equalsIgnoreCase(storageType)) {
|
throw new IllegalStateException("deviceLogStorage.type 仅支持 file,当前配置为: " + storageType);
|
}
|
ensureLoggingDirectoryExistsOrThrow();
|
}
|
|
@Scheduled(cron = "${deviceLogStorage.cleanupScanCron:0 0 3 * * ?}")
|
public void delDeviceLog() {
|
if (!FILE_OP_LOCK.tryLock()) {
|
return;
|
}
|
try {
|
ensureLoggingDirectoryExists();
|
clearFileLog(resolveExpireDays());
|
} finally {
|
FILE_OP_LOCK.unlock();
|
}
|
}
|
|
private void ensureLoggingDirectoryExistsOrThrow() {
|
Path loggingRootPath = Paths.get(loggingPath);
|
try {
|
Files.createDirectories(loggingRootPath);
|
} catch (Exception e) {
|
throw new IllegalStateException("初始化设备日志目录失败, path=" + loggingPath, e);
|
}
|
}
|
|
private void ensureLoggingDirectoryExists() {
|
try {
|
Files.createDirectories(Paths.get(loggingPath));
|
} catch (Exception e) {
|
log.warn("初始化设备日志目录失败, path={}", loggingPath, e);
|
}
|
}
|
|
private int resolveExpireDays() {
|
if (expireDays == null || expireDays <= 0) {
|
log.warn("deviceLogStorage.expireDays 配置无效,使用默认值: {}", DEFAULT_EXPIRE_DAYS);
|
return DEFAULT_EXPIRE_DAYS;
|
}
|
return expireDays;
|
}
|
|
private void clearFileLog(int days) {
|
try {
|
Path baseDir = Paths.get(loggingPath);
|
if (!Files.exists(baseDir)) {
|
return;
|
}
|
LocalDate earliestReservedDay = LocalDate.now().minusDays(days - 1L);
|
List<Path> expiredDayDirs = new ArrayList<>();
|
try (Stream<Path> stream = Files.list(baseDir)) {
|
stream.filter(Files::isDirectory)
|
.forEach(dayDir -> collectExpiredDayDirectory(dayDir, earliestReservedDay, expiredDayDirs));
|
}
|
for (Path dayDir : expiredDayDirs) {
|
Files.walkFileTree(dayDir, new SimpleFileVisitor<>() {
|
@Override
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws java.io.IOException {
|
Files.deleteIfExists(file);
|
return FileVisitResult.CONTINUE;
|
}
|
|
@Override
|
public FileVisitResult postVisitDirectory(Path dir, java.io.IOException exc) throws java.io.IOException {
|
Files.deleteIfExists(dir);
|
return FileVisitResult.CONTINUE;
|
}
|
});
|
}
|
} catch (Exception e) {
|
log.error("删除设备日志文件失败, path={}", loggingPath, e);
|
}
|
}
|
|
private void collectExpiredDayDirectory(Path dayDir, LocalDate earliestReservedDay, List<Path> expiredDayDirs) {
|
LocalDate dayValue = parseDayDirectory(dayDir.getFileName().toString());
|
if (dayValue == null || !dayValue.isBefore(earliestReservedDay)) {
|
return;
|
}
|
expiredDayDirs.add(dayDir);
|
}
|
|
private LocalDate parseDayDirectory(String dayDirectoryName) {
|
try {
|
return LocalDate.parse(dayDirectoryName, DAY_FORMATTER);
|
} catch (DateTimeParseException ignored) {
|
return null;
|
}
|
}
|
}
|