From c96fbe6b0aeb413a17c429133ee6d81d138144b9 Mon Sep 17 00:00:00 2001
From: liujie <1793218484@qq.com>
Date: 星期二, 09 六月 2026 10:58:52 +0800
Subject: [PATCH] Merge branch '1.0' of http://120.76.84.145:10101/gitblit/r/java/Robotaxi into 1.0

---
 xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/OperationStrategyIndex.java |  121 +++++++++++++++++++++++-----------------
 1 files changed, 70 insertions(+), 51 deletions(-)

diff --git a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/OperationStrategyIndex.java b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/OperationStrategyIndex.java
index 001b49b..3dc2bea 100644
--- a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/OperationStrategyIndex.java
+++ b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/OperationStrategyIndex.java
@@ -4,30 +4,31 @@
 import com.alibaba.fastjson2.JSONObject;
 
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
  * 围栏运营策略时间范围索引工具类
  */
 public final class OperationStrategyIndex {
-    private final Map<Integer, List<TimeInterval>> allowedByType;
+    private final List<List<TimeInterval>> strategyIntervals;
 
     /**
      * 构造索引(内部使用)。
      *
-     * @param allowedByType 按 type 聚合后的允许时间区间
+     * @param strategyIntervals 策略组时间区间列表(每个策略组已 merge)
      */
-    private OperationStrategyIndex(Map<Integer, List<TimeInterval>> allowedByType) {
-        this.allowedByType = allowedByType;
+    private OperationStrategyIndex(List<List<TimeInterval>> strategyIntervals) {
+        this.strategyIntervals = strategyIntervals == null ? Collections.emptyList() : strategyIntervals;
     }
 
     /**
      * 解析围栏 {@code operation_strategy} JSON 字符串为运营时间范围索引。
      * <p>
-     * 说明:会按 {@code type} 聚合并合并时间段,最终用于“时间覆盖范围”判断。
+     * 说明:会按“策略组”聚合并合并时间段;点位只要被任意一个策略组的时间段完整覆盖,则为正常。
      * </p>
      *
      * @param operationStrategyJson 围栏运营策略 JSON(数组字符串)
@@ -35,39 +36,32 @@
      */
     public static OperationStrategyIndex parse(String operationStrategyJson) {
         if (operationStrategyJson == null || operationStrategyJson.isEmpty()) {
-            return new OperationStrategyIndex(Collections.emptyMap());
+            return new OperationStrategyIndex(Collections.emptyList());
         }
         try {
             JSONArray jsonArray = JSONArray.parseArray(operationStrategyJson);
             if (jsonArray == null || jsonArray.isEmpty()) {
-                return new OperationStrategyIndex(Collections.emptyMap());
+                return new OperationStrategyIndex(Collections.emptyList());
             }
-            Map<Integer, List<TimeInterval>> map = new java.util.HashMap<>();
+            List<List<TimeInterval>> groups = new java.util.ArrayList<>();
             for (int i = 0; i < jsonArray.size(); i++) {
                 JSONObject obj = jsonArray.getJSONObject(i);
                 if (obj == null) {
                     continue;
                 }
-                Integer type = obj.getInteger("type");
                 JSONArray timeArray = obj.getJSONArray("time");
-                if (type == null || timeArray == null || timeArray.isEmpty()) {
+                if (timeArray == null || timeArray.isEmpty()) {
                     continue;
                 }
-                List<TimeInterval> intervals = TimeInterval.parseRanges(timeArray);
-                if (intervals.isEmpty()) {
-                    continue;
+                List<TimeInterval> intervals = TimeInterval.parseRangesDedup(timeArray);
+                List<TimeInterval> merged = TimeInterval.merge(intervals);
+                if (!merged.isEmpty()) {
+                    groups.add(merged);
                 }
-                map.merge(type, intervals, (a, b) -> {
-                    List<TimeInterval> merged = new java.util.ArrayList<>(a.size() + b.size());
-                    merged.addAll(a);
-                    merged.addAll(b);
-                    return TimeInterval.merge(merged);
-                });
             }
-            map.replaceAll((k, v) -> TimeInterval.merge(v));
-            return new OperationStrategyIndex(map);
+            return new OperationStrategyIndex(groups);
         } catch (Exception e) {
-            return new OperationStrategyIndex(Collections.emptyMap());
+            return new OperationStrategyIndex(Collections.emptyList());
         }
     }
 
@@ -78,7 +72,7 @@
      * @return 时间区间列表;解析失败返回空列表
      */
     public static List<TimeInterval> parsePointEffectiveTimePeriod(String jsonArrayStr) {
-        return TimeInterval.parseJsonRanges(jsonArrayStr);
+        return TimeInterval.parseJsonRangesDedup(jsonArrayStr);
     }
 
     /**
@@ -124,10 +118,10 @@
      * @return Union 区间列表(已 merge 并排序)
      */
     public List<TimeInterval> unionAllowed() {
-        if (allowedByType == null || allowedByType.isEmpty()) {
+        if (strategyIntervals == null || strategyIntervals.isEmpty()) {
             return Collections.emptyList();
         }
-        List<TimeInterval> union = allowedByType.values().stream()
+        List<TimeInterval> union = strategyIntervals.stream()
                 .filter(Objects::nonNull)
                 .flatMap(List::stream)
                 .collect(Collectors.toList());
@@ -135,16 +129,27 @@
     }
 
     /**
-     * 判断点位的时间段是否完全落在围栏 Union 运营时间覆盖范围内。
+     * 判断点位的时间段是否为正常。
+     * <p>
+     * 规则:只要点位的全部运营时间段被围栏“任意一个策略组”的时间段范围完整覆盖,则为正常。
+     * </p>
      *
      * @param pointIntervals 点位时间区间列表
-     * @return true 表示点位每段时间都被围栏 Union 覆盖
+     * @return true 表示正常;false 表示不在任何一个策略组覆盖范围内
      */
     public boolean isPointScheduleWithin(List<TimeInterval> pointIntervals) {
         if (pointIntervals == null || pointIntervals.isEmpty()) {
             return true;
         }
-        return TimeInterval.isWithin(pointIntervals, this.unionAllowed());
+        if (strategyIntervals == null || strategyIntervals.isEmpty()) {
+            return false;
+        }
+        for (List<TimeInterval> groupIntervals : strategyIntervals) {
+            if (TimeInterval.isWithin(pointIntervals, groupIntervals)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -202,7 +207,7 @@
          * @param jsonArrayStr JSON 数组字符串(例如 ["08:00-12:00","13:00-18:00"])
          * @return merge 后的区间列表;解析失败返回空列表
          */
-        static List<TimeInterval> parseJsonRanges(String jsonArrayStr) {
+        static List<TimeInterval> parseJsonRangesDedup(String jsonArrayStr) {
             if (jsonArrayStr == null || jsonArrayStr.isEmpty()) {
                 return Collections.emptyList();
             }
@@ -211,7 +216,7 @@
                 if (arr == null || arr.isEmpty()) {
                     return Collections.emptyList();
                 }
-                return merge(parseRanges(arr));
+                return merge(parseRangesDedup(arr));
             } catch (Exception e) {
                 return Collections.emptyList();
             }
@@ -221,7 +226,7 @@
          * 解析 JSONArray 中的时间段为区间列表(不做 merge)。
          *
          * @param timeArray 时间段数组(元素形如 "HH:mm-HH:mm")
-         * @return 区间列表(跨天会拆分为两段)
+         * @return 区间列表
          */
         static List<TimeInterval> parseRanges(JSONArray timeArray) {
             List<TimeInterval> result = new java.util.ArrayList<>();
@@ -233,7 +238,31 @@
         }
 
         /**
-         * 解析单个时间段字符串为区间列表(支持跨天拆分:如 22:00-02:00)。
+         * 解析 JSONArray 中的时间段为区间列表(先按字符串去重,再解析;不做 merge)。
+         */
+        static List<TimeInterval> parseRangesDedup(JSONArray timeArray) {
+            if (timeArray == null || timeArray.isEmpty()) {
+                return Collections.emptyList();
+            }
+            Set<String> uniq = new LinkedHashSet<>();
+            for (int i = 0; i < timeArray.size(); i++) {
+                String range = timeArray.getString(i);
+                if (range != null && !range.isEmpty()) {
+                    uniq.add(range);
+                }
+            }
+            List<TimeInterval> result = new java.util.ArrayList<>();
+            for (String range : uniq) {
+                result.addAll(parseRange(range));
+            }
+            return result;
+        }
+
+        /**
+         * 解析单个时间段字符串为区间列表。
+         * <p>
+         * 注意:业务约束不会出现跨天时间段;若出现 end &lt; start,则视为非法配置并忽略该时间段。
+         * </p>
          */
         private static List<TimeInterval> parseRange(String range) {
             if (range == null || !range.contains("-")) {
@@ -249,10 +278,7 @@
                 return Collections.emptyList();
             }
             if (end < start) {
-                List<TimeInterval> list = new java.util.ArrayList<>(2);
-                list.add(new TimeInterval(start, 23 * 60 + 59));
-                list.add(new TimeInterval(0, end));
-                return list;
+                return Collections.emptyList();
             }
             return java.util.Collections.singletonList(new TimeInterval(start, end));
         }
@@ -344,22 +370,15 @@
      * main 自测:使用固定样例打印 old/new Union、是否修改/缩短,以及点位在旧/新范围内判断结果。
      */
     public static void main(String[] args) {
-        String oldJson = "[{\"name\": \"策略组1\", \"time\": [\"08:00-23:59\"], \"type\": 0, \"typeStr\": []}, {\"name\": \"策略组2\", \"time\": [\"07:00-23:59\"], \"type\": 2, \"typeStr\": \"\"}, {\"name\": \"策略组3\", \"time\": [\"02:00-23:59\"], \"type\": 1, \"typeStr\": \"\"}]";
-        String newJson = "[{\"name\": \"策略组1\", \"time\": [\"08:00-23:59\"], \"type\": 0, \"typeStr\": []}, {\"name\": \"策略组2\", \"time\": [\"07:00-23:59\"], \"type\": 2, \"typeStr\": \"\"}, {\"name\": \"策略组3\", \"time\": [\"02:00-20:59\"], \"type\": 1, \"typeStr\": \"\"}]";
-        String pointJson = "[\"07:00-13:00\", \"13:00-23:59\"]";
+        String pointJson = "[\"05:00-12:00\", \"13:00-21:59\"]";
+        String strategyJson = "[{\"name\": \"策略组1\", \"time\": [\"06:00-22:00\", \"22:00-23:59\"], \"type\": 0, \"typeStr\": []}, {\"name\": \"策略组2\", \"time\": [\"06:00-12:00\", \"12:00-23:00\"], \"type\": 2, \"typeStr\": \"\"}, {\"name\": \"策略组3\", \"time\": [\"09:00-23:59\"], \"type\": 1, \"typeStr\": \"\"}]";
 
-        OperationStrategyIndex oldIndex = OperationStrategyIndex.parse(oldJson);
-        OperationStrategyIndex newIndex = OperationStrategyIndex.parse(newJson);
+        OperationStrategyIndex index = OperationStrategyIndex.parse(strategyJson);
         List<TimeInterval> pointIntervals = OperationStrategyIndex.parsePointEffectiveTimePeriod(pointJson);
 
-        System.out.println("oldUnion=" + oldIndex.unionAllowed());
-        System.out.println("newUnion=" + newIndex.unionAllowed());
-        System.out.println("equalsNormalized=" + oldIndex.equalsNormalized(newIndex));
-        System.out.println("hasAnyShrinkComparedTo=" + oldIndex.hasAnyShrinkComparedTo(newIndex));
-        boolean inOld = oldIndex.isPointScheduleWithin(pointIntervals);
-        boolean inNew = newIndex.isPointScheduleWithin(pointIntervals);
-        System.out.println("point(inOld,inNew)=(" + inOld + "," + inNew + ")");
-        boolean shouldDisable = !newIndex.isPointScheduleWithin(pointIntervals);
-        System.out.println("shouldDisableByUnion=" + shouldDisable);
+        System.out.println("pointIntervals=" + pointIntervals);
+        System.out.println("fenceUnion=" + index.unionAllowed());
+        boolean ok = index.isPointScheduleWithin(pointIntervals);
+        System.out.println("isNormal=" + ok);
     }
 }

--
Gitblit v1.7.1