Explorar o código

库存后端提交

Gogs hai 3 meses
pai
achega
23c7d7159b

+ 2 - 0
dtm-admin/src/main/resources/application.yml

@@ -154,6 +154,8 @@ shop:
 storage:
   data:
     path: ../data/storage
+    require-upload: true
+    warmup: false
 
 # Zhipu AI (GLM) configuration
 zhipu:

+ 69 - 22
dtm-system/src/main/java/com/dtm/storage/service/InventoryService.java

@@ -113,6 +113,19 @@ public class InventoryService {
     public Map<String, Object> getMonthlyComparisonData() {
         List<PurchaseRecord> purchaseRecords = dataLoader.getPurchaseRecords();
         List<SalesRecord> salesRecords = dataLoader.getSalesRecords();
+        List<AssemblyRecord> assemblyRecords = dataLoader.getAssemblyRecords();
+        Set<String> finishedSkus = getFinishedSkus();
+
+        if (!finishedSkus.isEmpty()) {
+            purchaseRecords = purchaseRecords.stream()
+                    .filter(r -> finishedSkus.contains(r.getProductCode()))
+                    .collect(Collectors.toList());
+            salesRecords = salesRecords.stream()
+                    .filter(r -> finishedSkus.contains(r.getProductCode()))
+                    .collect(Collectors.toList());
+        }
+
+        Map<String, Set<String>> semiToFinished = buildSemiToFinishedMap();
 
         Map<YearMonth, Double> purchaseMonthly = new TreeMap<>();
         for (PurchaseRecord record : purchaseRecords) {
@@ -134,9 +147,23 @@ public class InventoryService {
             salesMonthly.merge(month, record.getQuantity(), Double::sum);
         }
 
+        Map<YearMonth, Double> assemblyMonthly = new TreeMap<>();
+        for (AssemblyRecord record : assemblyRecords) {
+            LocalDate date = record.getDate();
+            if (date == null) {
+                continue;
+            }
+            if (!isAssemblyForFinished(record, finishedSkus, semiToFinished)) {
+                continue;
+            }
+            YearMonth month = YearMonth.from(date);
+            assemblyMonthly.merge(month, record.getQuantity(), Double::sum);
+        }
+
         Set<YearMonth> allMonths = new HashSet<>();
         allMonths.addAll(purchaseMonthly.keySet());
         allMonths.addAll(salesMonthly.keySet());
+        allMonths.addAll(assemblyMonthly.keySet());
         List<YearMonth> sorted = new ArrayList<>(allMonths);
         Collections.sort(sorted);
 
@@ -144,17 +171,26 @@ public class InventoryService {
         List<Integer> purchase = new ArrayList<>();
         List<Integer> sales = new ArrayList<>();
         List<Integer> inventory = new ArrayList<>();
-        int cumulativeInventory = 0;
+        double cumulativeInventory = 0.0;
 
         for (YearMonth month : sorted) {
             double purchaseQty = purchaseMonthly.getOrDefault(month, 0.0);
             double salesQty = salesMonthly.getOrDefault(month, 0.0);
-            cumulativeInventory += (int) Math.round(purchaseQty - salesQty);
+            double assemblyQty = assemblyMonthly.getOrDefault(month, 0.0);
+            cumulativeInventory += (purchaseQty + assemblyQty - salesQty);
 
             months.add(month.toString());
             purchase.add((int) Math.round(purchaseQty));
             sales.add((int) Math.round(salesQty));
-            inventory.add(cumulativeInventory);
+            inventory.add((int) Math.round(cumulativeInventory));
+        }
+
+        if (!inventory.isEmpty()) {
+            int totalPurchaseQty = (int) Math.round(purchaseRecords.stream().mapToDouble(PurchaseRecord::getQuantity).sum());
+            int totalSalesQty = (int) Math.round(salesRecords.stream().mapToDouble(SalesRecord::getQuantity).sum());
+            int totalAssemblyQty = calculateAssemblyQuantity(finishedSkus);
+            int totalInventory = totalPurchaseQty + totalAssemblyQty - totalSalesQty;
+            inventory.set(inventory.size() - 1, totalInventory);
         }
 
         Map<String, Object> result = new LinkedHashMap<>();
@@ -327,35 +363,46 @@ public class InventoryService {
             return 0;
         }
         Map<String, Set<String>> semiToFinished = buildSemiToFinishedMap();
-        if (semiToFinished.isEmpty() || finishedSkus == null || finishedSkus.isEmpty()) {
+        if (finishedSkus == null || finishedSkus.isEmpty()) {
             return (int) Math.round(assemblyRecords.stream().mapToDouble(AssemblyRecord::getQuantity).sum());
         }
         double total = 0.0;
         for (AssemblyRecord record : assemblyRecords) {
-            String code = record.getProductCode();
-            if (code == null || code.trim().isEmpty()) {
+            if (!isAssemblyForFinished(record, finishedSkus, semiToFinished)) {
                 continue;
             }
-            boolean counted = false;
-            // 组装明细里可能直接记录成品编码
-            if (finishedSkus.contains(code)) {
-                total += record.getQuantity();
-                counted = true;
-            }
-            // 半成品编码需要映射到成品
-            if (!counted) {
-                Set<String> finished = semiToFinished.get(code);
-                if (finished != null && !finished.isEmpty()) {
-                    boolean match = finished.stream().anyMatch(finishedSkus::contains);
-                    if (match) {
-                        total += record.getQuantity();
-                    }
-                }
-            }
+            total += record.getQuantity();
         }
+
         return (int) Math.round(total);
     }
 
+    private boolean isAssemblyForFinished(AssemblyRecord record,
+                                          Set<String> finishedSkus,
+                                          Map<String, Set<String>> semiToFinished) {
+        if (record == null) {
+            return false;
+        }
+        if (finishedSkus == null || finishedSkus.isEmpty()) {
+            return true;
+        }
+        String code = record.getProductCode();
+        if (code == null || code.trim().isEmpty()) {
+            return false;
+        }
+        if (finishedSkus.contains(code)) {
+            return true;
+        }
+        if (semiToFinished == null || semiToFinished.isEmpty()) {
+            return false;
+        }
+        Set<String> finished = semiToFinished.get(code);
+        if (finished == null || finished.isEmpty()) {
+            return false;
+        }
+        return finished.stream().anyMatch(finishedSkus::contains);
+    }
+
     private Map<String, Set<String>> buildSemiToFinishedMap() {
         List<List<Object>> rows = dataLoader.getSemiMappingRows();
         List<String> headers = dataLoader.getSemiMappingHeaders();

+ 11 - 2
dtm-system/src/main/java/com/dtm/storage/service/StorageDataLoader.java

@@ -43,6 +43,10 @@ public class StorageDataLoader {
 
     @Value("${storage.data.path:}")
     private String storageDataPath;
+    @Value("${storage.data.require-upload:false}")
+    private boolean requireUpload;
+    @Value("${storage.data.warmup:true}")
+    private boolean warmupEnabled;
 
     private Path basePath;
     private Path defaultBasePath;
@@ -55,10 +59,12 @@ public class StorageDataLoader {
 
     @PostConstruct
     public void init() {
-        this.defaultBasePath = resolveBasePath();
+        this.defaultBasePath = requireUpload ? null : resolveBasePath();
         this.basePath = defaultBasePath;
         System.out.println("StorageDataLoader basePath: " + (basePath == null ? "<null>" : basePath.toAbsolutePath()));
-        warmUp();
+        if (warmupEnabled && basePath != null) {
+            warmUp();
+        }
     }
 
     private void warmUp() {
@@ -123,6 +129,9 @@ public class StorageDataLoader {
 
     public synchronized Path resolveWritableBasePath() {
         ensureTempValid();
+        if (requireUpload && !tempMode) {
+            return null;
+        }
         if (basePath == null) {
             basePath = resolveBasePath();
             if (defaultBasePath == null) {