“mitao”
2026-03-14 118c4440c0e7ccf4f44850ab8876920c8b46a79e
bugfix
5个文件已修改
133 ■■■■■ 已修改文件
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/maintenance/XpTask.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpTaskServiceImpl.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/OperationStrategyIndex.java 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/exportExcle/XpWhitelistLogExportExcel.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/maintenance/XpTask.java
@@ -1,8 +1,6 @@
package com.xiaopeng.api.model.maintenance;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.*;
import com.xiaopeng.common.core.web.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -57,6 +55,7 @@
    private String taskImage;
    @ApiModelProperty(value = "车辆id")
    @TableField(updateStrategy = FieldStrategy.NEVER)
    private Long vehicleId;
    @ApiModelProperty(value = "车辆位置")
    private String vehicleLocation;
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpTaskServiceImpl.java
@@ -405,7 +405,8 @@
        }
        return R.ok(res);
    }
    @ShardingSphereTransactionType(TransactionType.BASE)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public R handleVehicleAccident(HandleVehicleAccidentDto dto, XpOperationsUser xpOperationsUser) {
        XpTask task = this.getById(dto.getId());
@@ -419,6 +420,7 @@
        task.setHandleTime(LocalDateTime.now());
        task.setHandleUserId(xpOperationsUser.getId().toString());
        task.setStatus(2);
        updateById(task);
        // 存储事故处理日志
        XpTaskHandleDetail xpTaskHandleDetail = new XpTaskHandleDetail();
        xpTaskHandleDetail.setTaskId(task.getId());
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java
@@ -660,7 +660,6 @@
                                UpdateWrapper<XpPoint> xpPointUpdateWrapper = new UpdateWrapper<>();
                                xpPointUpdateWrapper.eq("id", xpPoint.getId());
                                xpPointUpdateWrapper.set("point_type", 2);
                                xpPointUpdateWrapper.set("electronic_areaId", null);
                                xpPointMapper.update(xpPoint, xpPointUpdateWrapper);
                            }
                        }
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-23:59\"]";
        String strategyJson = "[{\"name\": \"策略组1\", \"time\": [\"06:00-12:00\", \"13:00-23:59\"], \"type\": 0, \"typeStr\": []}, {\"name\": \"策略组2\", \"time\": [\"05: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);
    }
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/exportExcle/XpWhitelistLogExportExcel.java
@@ -32,7 +32,7 @@
    private String operationAccount;
    @ApiModelProperty(value =" 操作类型 1=新增 2=编辑 3=删除 4=导入 5=导出 6=批量冻结")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "操作类型",width = 20,replace = {"1_新增","2_编辑","3_删除","4_导入","5_导出","6_批量冻结"})
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "操作类型",width = 20,replace = {"新增_1","编辑_2","删除_3","导入_4","导出_5","批量冻结_6"})
    private Integer operationType;