From e5a96bc789ca0252e31fd2e01bec5e471a02c0d6 Mon Sep 17 00:00:00 2001
From: sfd <sun.sunshine@163.com>
Date: 星期四, 22 五月 2025 17:57:29 +0800
Subject: [PATCH] Merge branch 'dev' of http://192.168.50.149:8085/r/aps-backend into dev

---
 aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsGasPipingRouteStatServiceImpl.java |  630 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 627 insertions(+), 3 deletions(-)

diff --git a/aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsGasPipingRouteStatServiceImpl.java b/aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsGasPipingRouteStatServiceImpl.java
index 756bec0..99c75aa 100644
--- a/aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsGasPipingRouteStatServiceImpl.java
+++ b/aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsGasPipingRouteStatServiceImpl.java
@@ -6,9 +6,7 @@
 import com.aps.common.core.utils.uuid.IdUtils;
 import com.aps.common.security.utils.SecurityUtils;
 import com.aps.core.domain.*;
-import com.aps.core.mapper.ApsGasPipelineCapacityPlanMapper;
-import com.aps.core.mapper.ApsGasPipingPlanMapper;
-import com.aps.core.mapper.ApsGasPipingRouteStatMapper;
+import com.aps.core.mapper.*;
 import com.aps.core.service.IApsGasMaterialUsageService;
 import com.aps.core.service.IApsGasPipingRouteStatService;
 import com.aps.core.service.IApsStandardProcessService;
@@ -1009,4 +1007,630 @@
         log.info("鎵归噺鎻掑叆鏁版嵁瀹屾垚,batchNum:"+batchNum);
     }
 
+    /**
+     * 鏍规嵁鎵嬪伐姘斾綋棰勬祴鏁版嵁鍜屾墜宸ユ皵浣撳伐鍗曟暟鎹敓鎴愮粺璁℃暟鎹�
+     * 
+     * @return 缁撴灉
+     */
+    @Override
+    @Transactional
+    public int generateGasPipingRouteStatData() {
+        int count = 0;
+        
+        // 娓呯┖涔嬪墠鐨勬暟鎹�
+        apsGasPipingRouteStatMapper.deleteAll();
+        
+        // 鐢熸垚鎵规鍙�
+        String batchNumber = IdUtils.fastSimpleUUID();
+        List<ApsGasPipingRouteStat> statList = new ArrayList<>();
+        
+        // 澶勭悊鎵嬪伐姘斾綋棰勬祴鏁版嵁 - 浣跨敤鑱斿悎鏌ヨ鑾峰彇鎵�鏈夌浉鍏虫暟鎹�
+        List<Map<String, Object>> predictionDataList = apsGasPipingRouteStatMapper.selectPredictionRouteData();
+        
+        // 鎸夌墿鏂欎唬鐮佸垎缁勶紝渚夸簬澶勭悊鍚屼竴鐗╂枡鐨勪笉鍚屽伐搴�
+        Map<String, List<Map<String, Object>>> predictionGroups = predictionDataList.stream()
+                .collect(Collectors.groupingBy(data -> data.get("material_code").toString()));
+        
+        // 澶勭悊姣忎釜鐗╂枡鐨勫伐鑹鸿矾绾�
+        for (String materialCode : predictionGroups.keySet()) {
+            List<Map<String, Object>> materialData = predictionGroups.get(materialCode);
+            
+            // 鎸夊伐搴忓彿鎺掑簭
+            materialData.sort((a, b) -> {
+                String numA = a.get("process_number").toString();
+                String numB = b.get("process_number").toString();
+                return numA.compareTo(numB);
+            });
+            
+            // 鑾峰彇鎵�鏈夊伐搴忕殑淇℃伅骞跺垱寤虹粺璁¤褰�
+            List<ApsGasPipingRouteStat> processList = new ArrayList<>();
+            for (Map<String, Object> data : materialData) {
+                ApsGasPipingRouteStat stat = new ApsGasPipingRouteStat();
+                stat.setId(IdUtils.fastSimpleUUID());
+                stat.setWorkOrderNo(""); // 棰勬祴鏁版嵁娌℃湁宸ュ崟鍙�
+                stat.setItemNumber(data.get("material_code").toString());
+                stat.setProcessName(data.get("process_name").toString());
+                stat.setRoadProcessNumber(new BigDecimal(data.get("process_number").toString()));
+                
+                // 鐢熶骇鏁伴噺鍜屾爣鍑嗗伐鏃�
+                BigDecimal quantity = new BigDecimal(data.get("predict_quantity").toString()).setScale(4, RoundingMode.HALF_UP);
+                BigDecimal standardTime = new BigDecimal(data.get("standard_time").toString()).setScale(4, RoundingMode.HALF_UP);
+                
+                stat.setProductionQuantity(quantity);
+                stat.setStandardTime(standardTime);
+                stat.setProcessTotalTime(standardTime.multiply(quantity).setScale(4, RoundingMode.HALF_UP));
+                
+                // 宸ュ巶銆佷笓涓氬拰杞﹂棿
+                stat.setPlant(data.get("factory").toString());
+                stat.setMajor(data.get("domain") != null ? data.get("domain").toString() : "");
+                stat.setWorkshop(data.get("workshop") != null ? data.get("workshop").toString() : "");
+                
+                // 鎵规鍙峰拰鍒涘缓淇℃伅
+                stat.setBatchNumber(batchNumber);
+                stat.setCreateBy(SecurityUtils.getUsername());
+                stat.setCreateTime(truncateToSeconds(DateUtils.getNowDate()));
+                
+                processList.add(stat);
+            }
+            
+            // 璁$畻璁″垝瀹屽伐鏃ュ拰璁″垝寮�宸ユ棩
+            if (!processList.isEmpty()) {
+                int lastIndex = processList.size() - 1;
+                
+                // 鏈�鍚庝竴閬撳伐搴忕殑璁″垝瀹屽伐鏃ユ湡涓洪娴嬫棩鏈�
+                ApsGasPipingRouteStat lastProcess = processList.get(lastIndex);
+                Date predictDate = (Date) materialData.get(lastIndex).get("predict_date");
+                lastProcess.setProcessPlanEndDay(truncateToSeconds(predictDate));
+                
+                // 鏍囪鏄惁鍑虹幇杩囨椂闂村啿绐佺殑鎯呭喌
+                boolean hasTimeConflict = false;
+                
+                // 璁$畻姣忎釜宸ュ簭鐨勮鍒掑紑宸ユ棩鍜屽畬宸ユ棩 - 浠庢渶鍚庝竴閬撳伐搴忓紑濮嬪�掓帹
+                for (int i = lastIndex; i >= 0; i--) {
+                    ApsGasPipingRouteStat current = processList.get(i);
+                    
+                    // 濡傛灉褰撳墠鏄渶鍚庝竴閬撳伐搴忥紝璁″垝寮�宸ユ棩 = 璁″垝瀹屽伐鏃� - 宸ュ簭鎬诲伐鏃�
+                    if (i == lastIndex) {
+                        hasTimeConflict = calculateProcessPlanStartDay(current, hasTimeConflict);
+                    } else {
+                        // 闈炴渶鍚庝竴閬撳伐搴忥紝璁″垝瀹屽伐鏃ヤ负涓嬩竴閬撳伐搴忕殑璁″垝寮�宸ユ棩
+                        ApsGasPipingRouteStat next = processList.get(i + 1);
+                        current.setProcessPlanEndDay(next.getProcessPlanStartDay());
+                        
+                        // 濡傛灉宸茬粡鍑虹幇鏃堕棿鍐茬獊锛屽悗缁伐搴忕殑璁″垝寮�宸ユ棩鍜岃鍒掑畬宸ユ棩閮借涓哄綋鍓嶆椂闂�
+                        if (hasTimeConflict) {
+                            current.setProcessPlanStartDay(truncateToSeconds(new Date()));
+                        } else {
+                            // 鍚﹀垯姝e父璁$畻锛屽苟妫�鏌ユ槸鍚﹀嚭鐜版椂闂村啿绐�
+                            hasTimeConflict = calculateProcessPlanStartDay(current, hasTimeConflict);
+                        }
+                    }
+                    
+                    // 璁剧疆骞淬�佹湀銆佹棩
+                    setDateComponents(current);
+                }
+                
+                statList.addAll(processList);
+            }
+        }
+        
+        // 澶勭悊鎵嬪伐姘斾綋宸ュ崟鏁版嵁 - 浣跨敤鑱斿悎鏌ヨ鑾峰彇鎵�鏈夌浉鍏虫暟鎹�
+        List<Map<String, Object>> moDataList = apsGasPipingRouteStatMapper.selectMoRouteData();
+        
+        // 鎸夊伐鍗曞彿鍒嗙粍锛屼究浜庡鐞嗗悓涓�宸ュ崟鐨勪笉鍚屽伐搴�
+        Map<String, List<Map<String, Object>>> moGroups = moDataList.stream()
+                .collect(Collectors.groupingBy(data -> data.get("work_order_no").toString()));
+        
+        // 澶勭悊姣忎釜宸ュ崟鐨勫伐鑹鸿矾绾�
+        for (String workOrderNo : moGroups.keySet()) {
+            List<Map<String, Object>> workOrderData = moGroups.get(workOrderNo);
+            
+            // 鎸夊伐搴忓彿鎺掑簭
+            workOrderData.sort((a, b) -> {
+                String numA = a.get("process_number").toString();
+                String numB = b.get("process_number").toString();
+                return numA.compareTo(numB);
+            });
+            
+            // 鑾峰彇鎵�鏈夊伐搴忕殑淇℃伅骞跺垱寤虹粺璁¤褰�
+            List<ApsGasPipingRouteStat> processList = new ArrayList<>();
+            for (Map<String, Object> data : workOrderData) {
+                ApsGasPipingRouteStat stat = new ApsGasPipingRouteStat();
+                stat.setId(IdUtils.fastSimpleUUID());
+                stat.setWorkOrderNo(data.get("work_order_no").toString());
+                stat.setItemNumber(data.get("material_code").toString());
+                stat.setProcessName(data.get("process_name").toString());
+                stat.setRoadProcessNumber(new BigDecimal(data.get("process_number").toString()));
+                
+                // 鐢熶骇鏁伴噺鍜屾爣鍑嗗伐鏃�
+                BigDecimal quantity = new BigDecimal(data.get("quantity").toString()).setScale(4, RoundingMode.HALF_UP);
+                BigDecimal standardTime = new BigDecimal(data.get("standard_time").toString()).setScale(4, RoundingMode.HALF_UP);
+                
+                stat.setProductionQuantity(quantity);
+                stat.setStandardTime(standardTime);
+                stat.setProcessTotalTime(standardTime.multiply(quantity).setScale(4, RoundingMode.HALF_UP));
+                
+                // 宸ュ巶銆佷笓涓氬拰杞﹂棿
+                stat.setPlant(data.get("factory").toString());
+                stat.setMajor(data.get("domain") != null ? data.get("domain").toString() : "");
+                stat.setWorkshop(data.get("workshop") != null ? data.get("workshop").toString() : "");
+                
+                // 鎵规鍙峰拰鍒涘缓淇℃伅
+                stat.setBatchNumber(batchNumber);
+                stat.setCreateBy(SecurityUtils.getUsername());
+                stat.setCreateTime(truncateToSeconds(DateUtils.getNowDate()));
+                
+                processList.add(stat);
+            }
+            
+            // 璁$畻璁″垝瀹屽伐鏃ュ拰璁″垝寮�宸ユ棩
+            if (!processList.isEmpty()) {
+                int lastIndex = processList.size() - 1;
+                
+                // 鏈�鍚庝竴閬撳伐搴忕殑璁″垝瀹屽伐鏃ユ湡涓哄伐鍗曡鍒掑畬宸ユ棩鏈�
+                ApsGasPipingRouteStat lastProcess = processList.get(lastIndex);
+                Date planEnd = (Date) workOrderData.get(0).get("plan_end");
+                lastProcess.setProcessPlanEndDay(truncateToSeconds(planEnd));
+                
+                // 鏍囪鏄惁鍑虹幇杩囨椂闂村啿绐佺殑鎯呭喌
+                boolean hasTimeConflict = false;
+                
+                // 璁$畻姣忎釜宸ュ簭鐨勮鍒掑紑宸ユ棩鍜屽畬宸ユ棩 - 浠庢渶鍚庝竴閬撳伐搴忓紑濮嬪�掓帹
+                for (int i = lastIndex; i >= 0; i--) {
+                    ApsGasPipingRouteStat current = processList.get(i);
+                    
+                    // 濡傛灉褰撳墠鏄渶鍚庝竴閬撳伐搴忥紝璁″垝寮�宸ユ棩 = 璁″垝瀹屽伐鏃� - 宸ュ簭鎬诲伐鏃�
+                    if (i == lastIndex) {
+                        hasTimeConflict = calculateProcessPlanStartDay(current, hasTimeConflict);
+                    } else {
+                        // 闈炴渶鍚庝竴閬撳伐搴忥紝璁″垝瀹屽伐鏃ヤ负涓嬩竴閬撳伐搴忕殑璁″垝寮�宸ユ棩
+                        ApsGasPipingRouteStat next = processList.get(i + 1);
+                        current.setProcessPlanEndDay(next.getProcessPlanStartDay());
+                        
+                        // 濡傛灉宸茬粡鍑虹幇鏃堕棿鍐茬獊锛屽悗缁伐搴忕殑璁″垝寮�宸ユ棩鍜岃鍒掑畬宸ユ棩閮借涓哄綋鍓嶆椂闂�
+                        if (hasTimeConflict) {
+                            current.setProcessPlanStartDay(truncateToSeconds(new Date()));
+                        } else {
+                            // 鍚﹀垯姝e父璁$畻锛屽苟妫�鏌ユ槸鍚﹀嚭鐜版椂闂村啿绐�
+                            hasTimeConflict = calculateProcessPlanStartDay(current, hasTimeConflict);
+                        }
+                    }
+                    
+                    // 璁剧疆骞淬�佹湀銆佹棩
+                    setDateComponents(current);
+                }
+                
+                statList.addAll(processList);
+            }
+        }
+        
+        // 鎵归噺鎻掑叆鏁版嵁
+        if (!statList.isEmpty()) {
+            for (int i = 0; i < statList.size(); i += 500) {
+                int endIndex = Math.min(i + 500, statList.size());
+                List<ApsGasPipingRouteStat> batch = statList.subList(i, endIndex);
+                count += apsGasPipingRouteStatMapper.insertApsGasPipingRouteStatBatch(batch);
+            }
+        }
+        
+        return count;
+    }
+
+    /**
+     * 璁$畻宸ュ簭璁″垝寮�宸ユ棩锛屽苟鍒ゆ柇鏄惁鍙戠敓鏃堕棿鍐茬獊
+     * 
+     * @param stat 褰撳墠宸ュ簭缁熻瀵硅薄
+     * @param hasTimeConflict 涔嬪墠鏄惁宸茬粡鍙戠敓鏃堕棿鍐茬獊
+     * @return 鏄惁鍙戠敓鏃堕棿鍐茬獊
+     */
+    private boolean calculateProcessPlanStartDay(ApsGasPipingRouteStat stat, boolean hasTimeConflict) {
+        Date planEndDay = stat.getProcessPlanEndDay();
+        if (planEndDay == null) {
+            return hasTimeConflict;
+        }
+        
+        // 璁$畻宸ュ簭鎬诲伐鏃跺搴旂殑绉掓暟
+        long processTotalTimeSeconds = stat.getProcessTotalTime()
+                .multiply(BigDecimal.valueOf(60 * 60))
+                .longValue();
+        
+        // 鑾峰彇褰撳墠鏃堕棿骞剁簿纭埌绉掔骇鍒�
+        Date now = truncateToSeconds(new Date());
+        
+        // 璁$畻寮�宸ユ棩鏈熷苟绮剧‘鍒扮绾у埆
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(planEndDay);
+        
+        // 鍒嗘壒鍑忓幓绉掓暟锛岄伩鍏峣nt婧㈠嚭
+        long seconds = processTotalTimeSeconds;
+        while (seconds > 0) {
+            int step = (seconds > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) seconds;
+            calendar.add(Calendar.SECOND, -step);
+            seconds -= step;
+        }
+        
+        Date calculatedStartDay = truncateToSeconds(calendar.getTime());
+        
+        // 濡傛灉璁″垝瀹屽伐鏃ユ湡灏忎簬绛変簬褰撳墠鏃堕棿锛屽垯璁″垝寮�宸ユ棩涔熻涓鸿鍒掑畬宸ユ棩锛堝綋鍓嶆椂闂达級
+        if (planEndDay.compareTo(now) <= 0) {
+            stat.setProcessPlanStartDay(truncateToSeconds(planEndDay));
+            return true; // 鍙戠敓鏃堕棿鍐茬獊
+        } 
+        // 濡傛灉璁$畻鍑虹殑璁″垝寮�宸ユ棩灏忎簬绛変簬褰撳墠鏃堕棿锛屽垯璁句负褰撳墠鏃堕棿
+        else if (calculatedStartDay.compareTo(now) <= 0) {
+            stat.setProcessPlanStartDay(now);
+            return true; // 棣栨鍑虹幇鏃堕棿鍐茬獊
+        } 
+        // 鍚﹀垯姝e父璁剧疆璁″垝寮�宸ユ棩
+        else {
+            stat.setProcessPlanStartDay(calculatedStartDay);
+            return hasTimeConflict; // 淇濇寔鍘熸湁鍐茬獊鐘舵��
+        }
+    }
+    
+    /**
+     * 灏嗘棩鏈熺簿纭埌绉掔骇鍒紝鍘婚櫎姣淇℃伅
+     * 
+     * @param date 鍘熷鏃ユ湡
+     * @return 绮剧‘鍒扮鐨勬棩鏈�
+     */
+    private Date truncateToSeconds(Date date) {
+        if (date == null) {
+            return null;
+        }
+        
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(Calendar.MILLISECOND, 0);
+        return calendar.getTime();
+    }
+    
+    /**
+     * 璁剧疆鏃ユ湡鐩稿叧缁勪欢锛堝勾銆佹湀銆佹棩锛�
+     */
+    private void setDateComponents(ApsGasPipingRouteStat stat) {
+        if (stat.getProcessPlanStartDay() != null) {
+            SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
+            SimpleDateFormat monthFormat = new SimpleDateFormat("MM");
+            SimpleDateFormat dayFormat = new SimpleDateFormat("dd");
+            
+            stat.setPlanStartYear(yearFormat.format(stat.getProcessPlanStartDay()));
+            stat.setPlanStartMonth(monthFormat.format(stat.getProcessPlanStartDay()));
+            stat.setPlanStartDay(dayFormat.format(stat.getProcessPlanStartDay()));
+        }
+    }
+
+    /**
+     * 鑱氬悎姘斾綋绠¤矾浜ц兘璐熻浇缁熻鏁版嵁
+     * 鍦⊿ervice灞傚畬鎴愯仛鍚堝鐞嗭紝鏀寔澶氱淮搴﹀垎缁�
+     * 
+     * @param params 鑱氬悎鍙傛暟
+     * @return 鑱氬悎缁撴灉
+     */
+    @Override
+    public Map<String, Object> aggregateGasPipingRouteStat(Map<String, Object> params) {
+        Map<String, Object> result = new HashMap<>();
+        List<Map<String, Object>> plantTable = new ArrayList<>();
+        
+        // 鑾峰彇鏃堕棿棰楃矑搴﹀弬鏁�
+        String timeGranularity = params.containsKey("timeGranularity") ? 
+                (String) params.get("timeGranularity") : "day"; // 榛樿涓�"鏃�"绮掑害
+        
+        // 鑾峰彇row缁村害鐨勮仛鍚堝瓧娈� - 榛樿涓哄伐搴忓悕绉�(processName)
+        String rowGroupBy = params.containsKey("rowGroupBy") ? 
+                (String) params.get("rowGroupBy") : "processName";
+        
+        // 纭畾鍒嗙粍缁村害 - 宸ュ巶銆佷笓涓氥�佽溅闂�
+        boolean groupByPlant = params.containsKey("groupByPlant") && Boolean.TRUE.equals(params.get("groupByPlant"));
+        boolean groupByMajor = params.containsKey("groupByMajor") && Boolean.TRUE.equals(params.get("groupByMajor"));
+        boolean groupByWorkshop = params.containsKey("groupByWorkshop") && Boolean.TRUE.equals(params.get("groupByWorkshop"));
+        // 濡傛灉rowGroupBy宸茬粡鏄煇涓淮搴︼紝鍒欒缁村害涓嶉渶瑕佸啀娆″垎缁�
+        if ("plant".equals(rowGroupBy)) groupByPlant = false;
+        if ("major".equals(rowGroupBy)) groupByMajor = false;
+        if ("workshop".equals(rowGroupBy)) groupByWorkshop = false;
+        
+        // 鏋勫缓鏌ヨ鏉′欢
+        Map<String, Object> queryParams = new HashMap<>();
+        
+        // 璁剧疆鏌ヨ鏉′欢 - 鍒嗙被鏉′欢
+        if (params.containsKey("plant")) {
+            Object plantParam = params.get("plant");
+            if (plantParam instanceof List) {
+                queryParams.put("plants", plantParam);
+            } else if (plantParam instanceof String) {
+                // 澶勭悊鍙兘鐨勯�楀彿鍒嗛殧瀛楃涓�
+                String plantStr = (String) plantParam;
+                if (plantStr.contains(",")) {
+                    List<String> plantList = Arrays.asList(plantStr.split(","));
+                    queryParams.put("plants", plantList);
+                } else {
+                    queryParams.put("plant", plantStr);
+                }
+            }
+        }
+        
+        if (params.containsKey("major")) {
+            Object majorParam = params.get("major");
+            if (majorParam instanceof List) {
+                queryParams.put("majors", majorParam);
+            } else if (majorParam instanceof String) {
+                // 澶勭悊鍙兘鐨勯�楀彿鍒嗛殧瀛楃涓�
+                String majorStr = (String) majorParam;
+                if (majorStr.contains(",")) {
+                    List<String> majorList = Arrays.asList(majorStr.split(","));
+                    queryParams.put("majors", majorList);
+                } else {
+                    queryParams.put("major", majorStr);
+                }
+            }
+        }
+        
+        if (params.containsKey("workshop")) {
+            Object workshopParam = params.get("workshop");
+            if (workshopParam instanceof List) {
+                queryParams.put("workshops", workshopParam);
+            } else if (workshopParam instanceof String) {
+                // 澶勭悊鍙兘鐨勯�楀彿鍒嗛殧瀛楃涓�
+                String workshopStr = (String) workshopParam;
+                if (workshopStr.contains(",")) {
+                    List<String> workshopList = Arrays.asList(workshopStr.split(","));
+                    queryParams.put("workshops", workshopList);
+                } else {
+                    queryParams.put("workshop", workshopStr);
+                }
+            }
+        }
+        
+        // 纭畾鏃堕棿鑼冨洿
+        List<String> timePoints = new ArrayList<>();
+        
+        if ("day".equalsIgnoreCase(timeGranularity)) {
+            // "鏃�"绮掑害锛氫紶鍏ヤ竴涓湀浠斤紝鑱氬悎璇ユ湀姣忎竴澶╃殑鏁版嵁
+            if (!params.containsKey("yearMonth")) {
+                result.put("plantTable", plantTable);
+                result.put("message", "鏃ョ矑搴﹁仛鍚堥渶瑕佹寚瀹歽earMonth鍙傛暟");
+                return result;
+            }
+            
+            String yearMonth = (String) params.get("yearMonth");
+            // 鐢熸垚鎸囧畾鏈堜唤涓殑姣忎竴澶�
+            YearMonth ym = YearMonth.parse(yearMonth);
+            int daysInMonth = ym.lengthOfMonth();
+            
+            for (int day = 1; day <= daysInMonth; day++) {
+                String dayStr = String.format("%02d", day);
+                timePoints.add(yearMonth + "-" + dayStr);
+            }
+            
+            // 璁剧疆鏌ヨ鍙傛暟
+            String[] ymParts = yearMonth.split("-");
+            queryParams.put("yearStart", ymParts[0]);
+            queryParams.put("monthStart", ymParts[1]);
+            queryParams.put("yearEnd", ymParts[0]);
+            queryParams.put("monthEnd", ymParts[1]);
+            
+        } else if ("month".equalsIgnoreCase(timeGranularity)) {
+            // "鏈�"绮掑害锛氫紶鍏ヤ竴涓椂闂村尯闂达紝鑱氬悎璇ュ尯闂村唴姣忎釜鏈堢殑鏁版嵁
+            if (!params.containsKey("startDate") || !params.containsKey("endDate")) {
+                result.put("plantTable", plantTable);
+                result.put("message", "鏈堢矑搴﹁仛鍚堥渶瑕佹寚瀹歴tartDate鍜宔ndDate鍙傛暟");
+                return result;
+            }
+            
+            String startDate = (String) params.get("startDate");
+            String endDate = (String) params.get("endDate");
+            
+            // 瑙f瀽寮�濮嬪拰缁撴潫骞存湀
+            YearMonth start = YearMonth.parse(startDate);
+            YearMonth end = YearMonth.parse(endDate);
+            
+            // 鐢熸垚鍖洪棿鍐呯殑姣忎釜鏈�
+            timePoints = getYearMonthsInRange(start, end);
+            
+            // 璁剧疆鏌ヨ鍙傛暟
+            String[] startParts = startDate.split("-");
+            String[] endParts = endDate.split("-");
+            queryParams.put("yearStart", startParts[0]);
+            queryParams.put("monthStart", startParts[1]);
+            queryParams.put("yearEnd", endParts[0]);
+            queryParams.put("monthEnd", endParts[1]);
+        }
+        
+        if (timePoints.isEmpty()) {
+            result.put("plantTable", plantTable);
+            result.put("message", "鏈兘鐢熸垚鏈夋晥鐨勬椂闂寸偣鍒楄〃");
+            return result;
+        }
+        
+        // 鏌ヨ鍘熷鏁版嵁锛堜笉渚濊禆鏁版嵁搴撹仛鍚堬級
+        List<Map<String, Object>> rawData = apsGasPipingRouteStatMapper.selectRawStatData(queryParams);
+        
+        if (rawData.isEmpty()) {
+            result.put("plantTable", plantTable);
+            result.put("timePoints", timePoints);
+            return result;
+        }
+        
+        // 鍦⊿ervice灞傚畬鎴愯仛鍚�
+        // 浣跨敤缁勫悎key鏉ュ疄鐜板缁村害鍒嗙粍锛堝姩鎬乺owGroupBy瀛楁 + 鍙�夌殑宸ュ巶/涓撲笟/杞﹂棿锛�
+        Map<String, Map<String, Object>> groupInfoMap = new HashMap<>();
+        Map<String, Map<String, BigDecimal>> groupTimeDataMap = new HashMap<>();
+        
+        // 閬嶅巻鍘熷鏁版嵁锛屾寜澶氱淮搴﹀垎缁勮繘琛岃仛鍚�
+        for (Map<String, Object> data : rawData) {
+            // 杩囨护宸ュ簭鍚嶇О涓虹┖鐨勬暟鎹�
+            String processName = getStringValue(data, "processName");
+            if (processName == null || processName.trim().isEmpty()) {
+                log.warn("璺宠繃澶勭悊锛氬伐搴忓悕绉颁负绌�");
+                continue;
+            }
+            
+            // 杩囨护杞﹂棿涓虹┖鐨勬暟鎹�
+            String workshop = getStringValue(data, "workshop");
+            if (workshop == null || workshop.trim().isEmpty()) {
+                log.warn("璺宠繃澶勭悊锛氳溅闂翠负绌猴紝宸ュ簭鍚嶇О={}", processName);
+                continue;
+            }
+            
+            // 鑾峰彇琛屽垎缁勫瓧娈靛��
+            String rowGroupValue = getStringValue(data, rowGroupBy);
+            if (rowGroupValue == null) {
+                log.warn("璺宠繃澶勭悊锛歿} 瀛楁鍊间负null", rowGroupBy);
+                continue;
+            }
+            
+            // 澶勭悊寮�宸ユ棩鏈�
+            Date processPlanStartDay = (Date) data.get("processPlanStartDay");
+            if (processPlanStartDay == null) {
+                log.warn("璺宠繃澶勭悊锛氳鍒掑紑宸ユ棩涓簄ull, {}={}", rowGroupBy, rowGroupValue);
+                continue;
+            }
+            
+            // 鏋勫缓鍒嗙粍閿� - 鍩轰簬row鍒嗙粍瀛楁鍜屽彲閫夌殑鍏朵粬缁村害
+            String plant = getStringValue(data, "plant");
+            String major = getStringValue(data, "major");
+            
+            StringBuilder groupKeyBuilder = new StringBuilder(rowGroupValue);
+            
+            // 鏍规嵁鐢ㄦ埛閫夋嫨鐨勫垎缁勭淮搴︽坊鍔犲埌鍒嗙粍閿�
+            if (groupByPlant && plant != null) {
+                groupKeyBuilder.append("_PLANT_").append(plant);
+            }
+            if (groupByMajor && major != null) {
+                groupKeyBuilder.append("_MAJOR_").append(major);
+            }
+            if (groupByWorkshop && workshop != null) {
+                groupKeyBuilder.append("_WORKSHOP_").append(workshop);
+            }
+            
+            String groupKey = groupKeyBuilder.toString();
+            
+            // 璁板綍鍒嗙粍鐨勫熀鏈俊鎭紙鍙褰曚竴娆★級
+            if (!groupInfoMap.containsKey(groupKey)) {
+                Map<String, Object> groupInfo = new HashMap<>();
+                groupInfo.put(rowGroupBy, rowGroupValue);
+                groupInfo.put("plant", plant);
+                groupInfo.put("major", major);
+                groupInfo.put("workshop", workshop);
+                groupInfo.put("processName", processName);
+                groupInfoMap.put(groupKey, groupInfo);
+            }
+            
+            // 璁$畻鏃堕棿鐐筀ey
+            String timeKey;
+            LocalDate planStartLocalDate = processPlanStartDay.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+            
+            if ("day".equalsIgnoreCase(timeGranularity)) {
+                // 鏃ョ矑搴�: 2025-05-25
+                timeKey = planStartLocalDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
+            } else {
+                // 鏈堢矑搴�: 2025-05
+                timeKey = planStartLocalDate.getYear() + "-" + String.format("%02d", planStartLocalDate.getMonthValue());
+            }
+            
+            // 鑾峰彇璇ュ垎缁勭殑鏃堕棿鐐规槧灏勶紝濡傛灉涓嶅瓨鍦ㄥ垯鍒涘缓
+            if (!groupTimeDataMap.containsKey(groupKey)) {
+                groupTimeDataMap.put(groupKey, new HashMap<>());
+            }
+            Map<String, BigDecimal> timeMap = groupTimeDataMap.get(groupKey);
+            
+            // 绱姞璇ュ垎缁勫湪璇ユ椂闂寸偣鐨勫伐鏃舵暟鎹�
+            BigDecimal processTotalTime = getBigDecimalValue(data, "processTotalTime");
+            if (processTotalTime != null) {
+                BigDecimal currentTotal = timeMap.getOrDefault(timeKey, BigDecimal.ZERO);
+                timeMap.put(timeKey, currentTotal.add(processTotalTime));
+            }
+        }
+        
+        // 鏋勫缓鏈�缁堣繑鍥炵殑鏁版嵁缁撴瀯
+        for (String groupKey : groupInfoMap.keySet()) {
+            Map<String, Object> rowEntry = new HashMap<>();
+            Map<String, Object> rowDetail = new HashMap<>();
+            
+            // 鑾峰彇璇ュ垎缁勭殑鍩烘湰淇℃伅
+            Map<String, Object> groupInfo = groupInfoMap.get(groupKey);
+            String rowGroupValue = (String) groupInfo.get(rowGroupBy);
+            
+            // 娣诲姞鍒嗙粍鍩烘湰淇℃伅
+            if (groupByPlant) {
+                rowDetail.put("plant", groupInfo.get("plant"));
+            }
+            if (groupByMajor) {
+                rowDetail.put("major", groupInfo.get("major"));
+            }
+            if (groupByWorkshop) {
+                rowDetail.put("workshop", groupInfo.get("workshop"));
+            }
+            
+            // 淇濈暀宸ュ簭鍚嶇О淇℃伅锛屼互渚垮墠绔睍绀�
+            if (!"processName".equals(rowGroupBy)) {
+                rowDetail.put("processName", groupInfo.get("processName"));
+            }
+            
+            // 娣诲姞鏃堕棿鏁版嵁
+            List<Map<String, Object>> timeDataList = new ArrayList<>();
+            Map<String, BigDecimal> timeMap = groupTimeDataMap.getOrDefault(groupKey, new HashMap<>());
+            
+            for (String timePoint : timePoints) {
+                Map<String, Object> pointData = new HashMap<>();
+                pointData.put("planDay", timePoint);
+                pointData.put("designTimes", 0); // 璁捐宸ユ椂鏆傛椂缁�0
+                
+                // 鑾峰彇璇ユ椂闂寸偣鐨勯渶姹傚伐鏃讹紝濡傛灉涓嶅瓨鍦ㄥ垯璁句负0
+                BigDecimal requireTimes = timeMap.getOrDefault(timePoint, BigDecimal.ZERO);
+                pointData.put("requireTimes", requireTimes);
+                
+                // 璁$畻浜ц兘璐熻嵎锛堟殏鏃朵负0锛�
+                pointData.put("capacityLoad", 0);
+                
+                timeDataList.add(pointData);
+            }
+            
+            rowDetail.put("timeData", timeDataList);
+            rowEntry.put(rowGroupValue, rowDetail);
+            plantTable.add(rowEntry);
+        }
+        
+        result.put("plantTable", plantTable);
+        result.put("timePoints", timePoints);
+        result.put("rowGroupBy", rowGroupBy);
+        
+        return result;
+    }
+    
+    /**
+     * 浠嶮ap涓畨鍏ㄨ幏鍙朣tring鍊�
+     */
+    private String getStringValue(Map<String, Object> data, String key) {
+        Object value = data.get(key);
+        if (value == null) {
+            return null;
+        }
+        return value.toString();
+    }
+    
+    /**
+     * 浠嶮ap涓畨鍏ㄨ幏鍙朆igDecimal鍊�
+     */
+    private BigDecimal getBigDecimalValue(Map<String, Object> data, String key) {
+        Object value = data.get(key);
+        if (value == null) {
+            return null;
+        }
+        
+        if (value instanceof BigDecimal) {
+            return (BigDecimal) value;
+        } else if (value instanceof Number) {
+            return new BigDecimal(((Number) value).toString());
+        } else if (value instanceof String) {
+            try {
+                return new BigDecimal((String) value);
+            } catch (NumberFormatException e) {
+                log.warn("鏃犳硶灏嗗�艰浆鎹负BigDecimal: {}", value);
+                return null;
+            }
+        }
+        
+        return null;
+    }
 }

--
Gitblit v1.9.3