|
|
@@ -37,17 +37,26 @@ import java.util.stream.Stream;
|
|
|
@Service
|
|
|
public class StorageDataLoader {
|
|
|
private static final long CACHE_EXPIRE_MILLIS = 300_000;
|
|
|
+ private static final long TEMP_CACHE_EXPIRE_MILLIS = 120_000;
|
|
|
+ private static final long TEMP_DATA_TTL_MILLIS = 30 * 60 * 1000;
|
|
|
+ private static final int TEMP_MAX_ROWS = 200_000;
|
|
|
|
|
|
@Value("${storage.data.path:}")
|
|
|
private String storageDataPath;
|
|
|
|
|
|
private Path basePath;
|
|
|
+ private Path defaultBasePath;
|
|
|
+ private Path tempBasePath;
|
|
|
+ private volatile long tempExpireAt;
|
|
|
+ private volatile boolean tempMode;
|
|
|
|
|
|
private final ConcurrentMap<String, CacheEntry<?>> cache = new ConcurrentHashMap<>();
|
|
|
+ private final ConcurrentMap<String, Object> cacheLocks = new ConcurrentHashMap<>();
|
|
|
|
|
|
@PostConstruct
|
|
|
public void init() {
|
|
|
- this.basePath = resolveBasePath();
|
|
|
+ this.defaultBasePath = resolveBasePath();
|
|
|
+ this.basePath = defaultBasePath;
|
|
|
System.out.println("StorageDataLoader basePath: " + (basePath == null ? "<null>" : basePath.toAbsolutePath()));
|
|
|
warmUp();
|
|
|
}
|
|
|
@@ -93,6 +102,43 @@ public class StorageDataLoader {
|
|
|
cache.clear();
|
|
|
}
|
|
|
|
|
|
+ public synchronized void useTemporaryBasePath(Path path) {
|
|
|
+ if (path == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.tempBasePath = path;
|
|
|
+ this.tempMode = true;
|
|
|
+ this.tempExpireAt = System.currentTimeMillis() + TEMP_DATA_TTL_MILLIS;
|
|
|
+ this.basePath = path;
|
|
|
+ clearCache();
|
|
|
+ }
|
|
|
+
|
|
|
+ public synchronized void resetBasePath() {
|
|
|
+ this.tempMode = false;
|
|
|
+ this.tempExpireAt = 0L;
|
|
|
+ this.tempBasePath = null;
|
|
|
+ this.basePath = defaultBasePath;
|
|
|
+ clearCache();
|
|
|
+ }
|
|
|
+
|
|
|
+ public synchronized Path resolveWritableBasePath() {
|
|
|
+ ensureTempValid();
|
|
|
+ if (basePath == null) {
|
|
|
+ basePath = resolveBasePath();
|
|
|
+ if (defaultBasePath == null) {
|
|
|
+ defaultBasePath = basePath;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (basePath != null && !Files.exists(basePath)) {
|
|
|
+ try {
|
|
|
+ Files.createDirectories(basePath);
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.out.println("StorageDataLoader create basePath failed: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return basePath;
|
|
|
+ }
|
|
|
+
|
|
|
private List<PurchaseRecord> loadPurchaseRecords() {
|
|
|
ExcelSheet sheet = loadPurchaseSheet();
|
|
|
if (sheet.getRows().isEmpty()) {
|
|
|
@@ -217,7 +263,7 @@ public class StorageDataLoader {
|
|
|
if (file == null) {
|
|
|
return new ExcelSheet(Collections.emptyList(), Collections.emptyList());
|
|
|
}
|
|
|
- return ExcelUtils.readSheet(file, 0);
|
|
|
+ return readSheet(file, 0);
|
|
|
}
|
|
|
|
|
|
private ExcelSheet loadPurchaseSheet() {
|
|
|
@@ -225,7 +271,7 @@ public class StorageDataLoader {
|
|
|
if (file == null) {
|
|
|
return new ExcelSheet(Collections.emptyList(), Collections.emptyList());
|
|
|
}
|
|
|
- return ExcelUtils.readSheet(file, 0);
|
|
|
+ return readSheet(file, 0);
|
|
|
}
|
|
|
|
|
|
private ExcelSheet loadSalesSheet() {
|
|
|
@@ -233,7 +279,7 @@ public class StorageDataLoader {
|
|
|
if (file == null) {
|
|
|
return new ExcelSheet(Collections.emptyList(), Collections.emptyList());
|
|
|
}
|
|
|
- return ExcelUtils.readSheet(file, 0);
|
|
|
+ return readSheet(file, 0);
|
|
|
}
|
|
|
|
|
|
private ExcelSheet loadAssemblySheet() {
|
|
|
@@ -241,7 +287,7 @@ public class StorageDataLoader {
|
|
|
if (file == null) {
|
|
|
return new ExcelSheet(Collections.emptyList(), Collections.emptyList());
|
|
|
}
|
|
|
- return ExcelUtils.readSheet(file, 1);
|
|
|
+ return readSheet(file, 1);
|
|
|
}
|
|
|
|
|
|
private ExcelSheet loadProductInfoSheet() {
|
|
|
@@ -249,7 +295,14 @@ public class StorageDataLoader {
|
|
|
if (file == null) {
|
|
|
return new ExcelSheet(Collections.emptyList(), Collections.emptyList());
|
|
|
}
|
|
|
- return ExcelUtils.readSheet(file, 0);
|
|
|
+ return readSheet(file, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ private ExcelSheet readSheet(Path file, int headerRowIndex) {
|
|
|
+ if (tempMode) {
|
|
|
+ return ExcelUtils.readSheet(file, headerRowIndex, TEMP_MAX_ROWS);
|
|
|
+ }
|
|
|
+ return ExcelUtils.readSheet(file, headerRowIndex);
|
|
|
}
|
|
|
|
|
|
private Path resolveBasePath() {
|
|
|
@@ -314,6 +367,7 @@ public class StorageDataLoader {
|
|
|
}
|
|
|
|
|
|
private Path resolveDir(String dirName) {
|
|
|
+ ensureTempValid();
|
|
|
if (basePath == null) {
|
|
|
return null;
|
|
|
}
|
|
|
@@ -324,6 +378,34 @@ public class StorageDataLoader {
|
|
|
return basePath;
|
|
|
}
|
|
|
|
|
|
+ private synchronized void ensureTempValid() {
|
|
|
+ if (!tempMode) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (tempExpireAt > 0 && System.currentTimeMillis() > tempExpireAt) {
|
|
|
+ Path toDelete = tempBasePath;
|
|
|
+ resetBasePath();
|
|
|
+ if (toDelete != null) {
|
|
|
+ deleteDirectory(toDelete);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void deleteDirectory(Path dir) {
|
|
|
+ if (dir == null || !Files.exists(dir)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try (Stream<Path> walk = Files.walk(dir)) {
|
|
|
+ walk.sorted(Comparator.reverseOrder()).forEach(path -> {
|
|
|
+ try {
|
|
|
+ Files.deleteIfExists(path);
|
|
|
+ } catch (Exception ignored) {
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception ignored) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private Path findFile(Path dir, String... keywords) {
|
|
|
if (dir == null || !Files.exists(dir)) {
|
|
|
return null;
|
|
|
@@ -459,12 +541,21 @@ public class StorageDataLoader {
|
|
|
private <T> T getCached(String key, Supplier<T> supplier) {
|
|
|
CacheEntry<T> entry = (CacheEntry<T>) cache.get(key);
|
|
|
long now = System.currentTimeMillis();
|
|
|
- if (entry != null && now - entry.timestamp < CACHE_EXPIRE_MILLIS) {
|
|
|
+ long ttl = tempMode ? TEMP_CACHE_EXPIRE_MILLIS : CACHE_EXPIRE_MILLIS;
|
|
|
+ if (entry != null && now - entry.timestamp < ttl) {
|
|
|
return entry.value;
|
|
|
}
|
|
|
- T value = supplier.get();
|
|
|
- cache.put(key, new CacheEntry<>(value, now));
|
|
|
- return value;
|
|
|
+ Object lock = cacheLocks.computeIfAbsent(key, k -> new Object());
|
|
|
+ synchronized (lock) {
|
|
|
+ CacheEntry<T> recheck = (CacheEntry<T>) cache.get(key);
|
|
|
+ long nowCheck = System.currentTimeMillis();
|
|
|
+ if (recheck != null && nowCheck - recheck.timestamp < ttl) {
|
|
|
+ return recheck.value;
|
|
|
+ }
|
|
|
+ T value = supplier.get();
|
|
|
+ cache.put(key, new CacheEntry<>(value, nowCheck));
|
|
|
+ return value;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private static class CacheEntry<T> {
|