Explorar el Código

fix: 业务策略电价超4档越界导致应用启动失败,按相近价格取低价压缩到4档并记录原始数据;费率定时任务异常不再阻塞启动

- PlatformBusinessPolicy: 平台返回超过4档电价时记录原始价格列表,反复合并最相近的两档(取低价为标准)临时恢复到4档
- StationPolicyInfoJob: 单个站点失败沿用上次缓存,任务整体异常不影响应用启动

Co-Authored-By: Claude <noreply@anthropic.com>
skyline hace 1 día
padre
commit
4453afbb84

+ 50 - 1
entity/src/main/java/com/kym/entity/platform/response/PlatformBusinessPolicy.java

@@ -5,8 +5,12 @@ import com.alibaba.fastjson2.annotation.JSONCreator;
 import com.alibaba.fastjson2.annotation.JSONField;
 import com.kym.entity.platform.PlatformPolicyInfo;
 import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 /**
@@ -15,6 +19,7 @@ import java.util.stream.Collectors;
  * @date 2023-08-15 15:58
  */
 @Data
+@Slf4j
 public class PlatformBusinessPolicy {
     /**
      * 业务策略查询流水号
@@ -66,12 +71,56 @@ public class PlatformBusinessPolicy {
         // 将尖峰平谷信息填入EnPolicyInfo中
         var elecPriceSet = policyInfos.stream().map(PlatformPolicyInfo::getElecPrice).collect(Collectors.toSet()).stream().sorted().toList();
         if (elecPriceSet.size() > 1) {
-            policyInfos = policyInfos.stream().peek(price -> price.setPricePeriod(PlatformPolicyInfo.PRICE_PERIOD[elecPriceSet.indexOf(price.getElecPrice())])).toList();
+            // 平台数据错误时可能返回超过4档电价,记录原始价格并将相近的价格取低价作为标准,临时压缩到4档
+            Map<Double, Double> priceStandardMap = new HashMap<>();
+            var levelPrices = elecPriceSet;
+            if (elecPriceSet.size() > PlatformPolicyInfo.PRICE_PERIOD.length) {
+                log.error("业务策略电价档数异常,共{}档,原始电价列表:{}", elecPriceSet.size(), elecPriceSet);
+                priceStandardMap = compressPriceToFourLevels(elecPriceSet);
+                levelPrices = priceStandardMap.values().stream().distinct().sorted().toList();
+                log.error("相近价格取低价压缩后电价列表:{}", levelPrices);
+            }
+            var finalLevelPrices = levelPrices;
+            var finalPriceStandardMap = priceStandardMap;
+            policyInfos = policyInfos.stream().peek(price -> {
+                var standardPrice = finalPriceStandardMap.getOrDefault(price.getElecPrice(), price.getElecPrice());
+                price.setElecPrice(standardPrice);
+                price.setPricePeriod(PlatformPolicyInfo.PRICE_PERIOD[finalLevelPrices.indexOf(standardPrice)]);
+            }).toList();
         } else {
             // 统一电价
             policyInfos = policyInfos.stream().peek(price -> price.setPricePeriod("统")).toList();
         }
     }
 
+    /**
+     * 价格档数超过4档时,反复合并价格最相近的两档(取低价为标准),压缩到4档
+     *
+     * @param prices 升序排列的电价列表
+     * @return 原始电价 -> 标准电价 的映射
+     */
+    private static Map<Double, Double> compressPriceToFourLevels(List<Double> prices) {
+        var priceStandardMap = new HashMap<Double, Double>();
+        prices.forEach(price -> priceStandardMap.put(price, price));
+        var levels = new ArrayList<>(prices);
+        while (levels.size() > PlatformPolicyInfo.PRICE_PERIOD.length) {
+            // 找出差值最小的相邻两档
+            var mergeIndex = 0;
+            var minGap = Double.MAX_VALUE;
+            for (var i = 0; i < levels.size() - 1; i++) {
+                var gap = levels.get(i + 1) - levels.get(i);
+                if (gap < minGap) {
+                    minGap = gap;
+                    mergeIndex = i;
+                }
+            }
+            // 取低价作为标准
+            var standard = levels.get(mergeIndex);
+            var merged = levels.remove(mergeIndex + 1);
+            priceStandardMap.replaceAll((price, v) -> v.equals(merged) ? standard : v);
+        }
+        return priceStandardMap;
+    }
+
 
 }

+ 22 - 9
miniapp/src/main/java/com/kym/miniapp/jobs/StationPolicyInfoJob.java

@@ -41,15 +41,28 @@ public class StationPolicyInfoJob {
         log.info("站点费率信息定时更新启动...");
         // 手动切换数据源
         DynamicDataSourceContextHolder.push("db-admin");
-        // 获取每个站点的第一个connectorId
-        var stationConnectorMap = equipmentRelationMapper.selectLatestConnectorByStation();
-        DynamicDataSourceContextHolder.poll();
-        var maps = new HashMap<String, List<PlatformPolicyInfoVo>>(stationConnectorMap.size());
-        stationConnectorMap.forEach(stationConnector -> {
-            var policy = chargeService.queryEquipBusinessPolicy(stationConnector.get("connectorId"));
-            maps.put(stationConnector.get("stationId"), policy.getPolicyInfos().stream().map(PlatformPolicyInfo::toVo).toList());
-        });
-        KymCache.INSTANCE.putStationId2PolicyInfo(maps);
+        try {
+            // 获取每个站点的第一个connectorId
+            var stationConnectorMap = equipmentRelationMapper.selectLatestConnectorByStation();
+            var maps = new HashMap<String, List<PlatformPolicyInfoVo>>(stationConnectorMap.size());
+            stationConnectorMap.forEach(stationConnector -> {
+                try {
+                    var policy = chargeService.queryEquipBusinessPolicy(stationConnector.get("connectorId"));
+                    if (policy.getPolicyInfos() != null) {
+                        maps.put(stationConnector.get("stationId"), policy.getPolicyInfos().stream().map(PlatformPolicyInfo::toVo).toList());
+                    }
+                } catch (Exception e) {
+                    // 单个站点费率获取失败不影响其他站点,沿用该站点上次缓存
+                    log.error("站点:{}费率信息获取失败", stationConnector.get("stationId"), e);
+                }
+            });
+            KymCache.INSTANCE.putStationId2PolicyInfo(maps);
+        } catch (Exception e) {
+            // 任务异常不能影响应用启动,费率缓存沿用上次结果
+            log.error("站点费率信息定时更新失败", e);
+        } finally {
+            DynamicDataSourceContextHolder.poll();
+        }
         log.info("站点费率信息定时更新结束...");
     }
 }