puzhibing
2026-03-11 4f3466431b6ea9f16235a8021fca009b549f32e5
Merge remote-tracking branch 'origin/master'
16个文件已修改
649 ■■■■■ 已修改文件
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/pushMessage/XpPushMessageClientFallbackFactory.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/XpPushMessageClient.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderDetail.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/PushMessageTypeEnum.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingDemoService.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/webSocket/controller/WebSocketController.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/constant/I18nKeyConstant.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages.properties 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_en_US.properties 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_zh_CN.properties 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/mapper/vehicleConvoy/XpVehicleMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/pushMessage/XpPushMessageClientFallbackFactory.java
@@ -47,6 +47,11 @@
            public R<String> pushOrderRoute(PushOrderRouteDTO dto) {
                return R.fail("通过车辆实时定位推送订单轨迹失败,"+cause.getMessage());
            }
            @Override
            public R<String> sendOpenTrunk(SendMessageDTO dto) {
                return R.fail("通过发送是否开启倒计时失败,"+cause.getMessage());
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/XpPushMessageClient.java
@@ -55,5 +55,13 @@
     */
    @PostMapping("/inner/v1/order/pushOrderRoute")
    R<String> pushOrderRoute(@RequestBody PushOrderRouteDTO dto);
    /**
     * 发送是否开启倒计时
     *
     * @param dto 消息实体
     * @return 发送是否开启倒计时
     */
    @PostMapping("/inner/websocket/order/sendOpenTrunk")
    R<String> sendOpenTrunk(@RequestBody SendMessageDTO dto);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderDetail.java
@@ -34,7 +34,7 @@
    @ApiModelProperty(value = "type=1 普通时段 type=2 夜间行驶时段 3高峰时段 4节假日时段")
    private Integer type;
    @ApiModelProperty(value = "1=起步价 2=里程费")
    @ApiModelProperty(value = "1=起步价 2=里程费 3时长 4远途")
    private Integer detailType;
    @ApiModelProperty(value = "时段")
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/PushMessageTypeEnum.java
@@ -12,7 +12,8 @@
    ORDER_STATUS("ORDER_STATUS"),
    VEHICLE_LOCATION("VEHICLE_LOCATION"),
    ORDER_ROUTE("ORDER_ROUTE");
    ORDER_ROUTE("ORDER_ROUTE"),
    OPEN_TRUNK("OPEN_TRUNK");
    @Getter
    private String desc;
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java
@@ -46,10 +46,7 @@
import javax.annotation.Resource;
import java.awt.geom.Point2D;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -729,14 +726,34 @@
     * @return
     */
    private org.locationtech.jts.geom.Point calculateCentroid(Coordinate[] coordinates) {
        GeometryFactory factory = new GeometryFactory();
        // 创建线性环 (LinearRing)
        LinearRing ring = factory.createLinearRing(coordinates);
        // 创建多边形 (Polygon)
        Polygon polygon = factory.createPolygon(ring);
        // 计算质心 (Centroid)
        org.locationtech.jts.geom.Point centroid = polygon.getCentroid();
        // 1. 判空校验
        if (coordinates == null || coordinates.length == 0) {
            throw new ServiceException("坐标数组不能为空");
        }
        // 2. 过滤掉 null 坐标点
        Coordinate[] validCoords = Arrays.stream(coordinates)
                .filter(Objects::nonNull)
                .toArray(Coordinate[]::new);
        // 3. LinearRing 至少需要 4 个点(首尾相同),且必须闭合
        if (validCoords.length < 4) {
            throw new ServiceException("有效坐标点不足,至少需要4个点");
        }
        // 4. 确保首尾坐标相同(闭合)
        Coordinate first = validCoords[0];
        Coordinate last = validCoords[validCoords.length - 1];
        if (!first.equals2D(last)) {
            // 自动补上闭合点
            validCoords = Arrays.copyOf(validCoords, validCoords.length + 1);
            validCoords[validCoords.length - 1] = new Coordinate(first.x, first.y);
        }
        GeometryFactory factory = new GeometryFactory();
        LinearRing ring = factory.createLinearRing(validCoords);
        Polygon polygon = factory.createPolygon(ring);
        org.locationtech.jts.geom.Point centroid = polygon.getCentroid();
        System.out.println("中心点经度: " + centroid.getX());
        System.out.println("中心点纬度: " + centroid.getY());
        return centroid;
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java
@@ -385,8 +385,8 @@
            if (CollUtil.isNotEmpty(lonAndLat)) {
                query.setPointLon(lonAndLat.get(0));
                query.setPointLat(lonAndLat.get(1));
                query.setPointLonAndLat(null);
            }
            query.setPointLonAndLat(null);
        }
        List<XpPoint> list = this.baseMapper.pageList(pageInfo,query);
        pageInfo.setRecords(list);
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -1342,19 +1342,34 @@
                break;
            case 7:
                // 查询自动结束行程时间
                Integer arriveTime = xpSystemSettingClient.getOrderFinishTime().getData();
                // 添加自动结束行程定时任务job
                Map<String, ? extends Object> endTripJobMaps =
                        new ImmutableMap.Builder<String, Long>().
                                put("id", xpOrder.getId())
                                .build();
                QuartzManager.addJob(
                        EndTripJob.class,
                        (EndTripJob.name + xpOrder.getId()).toUpperCase(),
                        TimeJobType.END_TRIP,
                        DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(arriveTime)),
                        endTripJobMaps
                );
                if(xpOrder.getOpenTrunk() != 1){
                    Integer arriveTime = xpSystemSettingClient.getOrderFinishTime().getData();
                    // 添加自动结束行程定时任务job
                    Map<String, ? extends Object> endTripJobMaps =
                            new ImmutableMap.Builder<String, Long>().
                                    put("id", xpOrder.getId())
                                    .build();
                    QuartzManager.addJob(
                            EndTripJob.class,
                            (EndTripJob.name + xpOrder.getId()).toUpperCase(),
                            TimeJobType.END_TRIP,
                            DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(arriveTime)),
                            endTripJobMaps
                    );
                    // 推送倒计时消息
                    Map<String, Object> params = new HashMap<>();
                    Map<String, Object> content = new HashMap<>();
                    content.put("orderId", xpOrder.getId().toString());
                    content.put("isOpen", 1);
                    params.put("type", PushMessageTypeEnum.OPEN_TRUNK.getDesc());
                    params.put("content", JSONObject.toJSONString(content));
                    params.put("timestamp", new Date().getTime() + "");
                    String message = JSONObject.toJSONString(params);
                    SendMessageDTO sendMessageDTO = new SendMessageDTO();
                    sendMessageDTO.setMessage(message);
                    sendMessageDTO.setAppUserId(String.valueOf(xpOrder.getAppUserId()));
                    xpPushMessageClient.sendStatus(sendMessageDTO);
                }
                break;
        }
        xpOrder.setAppUserId(null);
@@ -1432,20 +1447,15 @@
        TaxiPricingDemoService.PricingConfig config = new TaxiPricingDemoService.PricingConfig();
        JSONArray startingFareJsonArray = JSONArray.parseArray(pricing.getStartingFareJson());
        if (pricing.getPricingType() == 1) {
            JSONObject basicPricingJson = JSONObject.parseObject(pricing.getBasicPricingJson());
            config.peaks.add(new TaxiPricingDemoService.PricingRule("高峰期", basicPricingJson.getString("num21").split(" - ")[0], basicPricingJson.getString("num21").split(" - ")[1], Double.parseDouble(basicPricingJson.getString("num24")), Double.parseDouble(basicPricingJson.getString("num25")), Double.parseDouble(basicPricingJson.getString("num26")), Double.parseDouble(basicPricingJson.getString("num27")), Double.parseDouble(basicPricingJson.getString("num28"))));
            config.peaks.add(new TaxiPricingDemoService.PricingRule("高峰期", basicPricingJson.getString("num22").split(" - ")[0], basicPricingJson.getString("num22").split(" - ")[1], Double.parseDouble(basicPricingJson.getString("num24")), Double.parseDouble(basicPricingJson.getString("num25")), Double.parseDouble(basicPricingJson.getString("num26")), Double.parseDouble(basicPricingJson.getString("num27")), Double.parseDouble(basicPricingJson.getString("num28"))));
            config.night = new TaxiPricingDemoService.PricingRule("夜间行驶", basicPricingJson.getString("num14").split(" - ")[0], basicPricingJson.getString("num14").split(" - ")[1], Double.parseDouble(basicPricingJson.getString("num16")), Double.parseDouble(basicPricingJson.getString("num17")), Double.parseDouble(basicPricingJson.getString("num18")), Double.parseDouble(basicPricingJson.getString("num19")), Double.parseDouble(basicPricingJson.getString("num20")));
            config.holiday = new TaxiPricingDemoService.PricingRule("节假日", "00:00", "23:59", Double.parseDouble(basicPricingJson.getString("num30")), Double.parseDouble(basicPricingJson.getString("num31")), Double.parseDouble(basicPricingJson.getString("num32")), Double.parseDouble(basicPricingJson.getString("num33")), Double.parseDouble(basicPricingJson.getString("num34")));
            // --- 远途加价区间配置 (关键修改) ---
            // 1. 超过 num1(12) 不足 num2(20), 加收 num3(1.2)
            config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num6")), Double.parseDouble(basicPricingJson.getString("num7")), Double.parseDouble(basicPricingJson.getString("num8"))));
            // 2. 超过 num4(20) 不足 num5(35), 加收 num6(2.0)
            config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num9")), Double.parseDouble(basicPricingJson.getString("num10")), Double.parseDouble(basicPricingJson.getString("num11"))));
            // 3. 超过 num7(35) 以后, 加收 num8(3.5) -> numEnd 传个极大值
            config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num12")), 999999, Double.parseDouble(basicPricingJson.getString("num13"))));
        }
        LocalDateTime boardingTime = order.getBoardingTime();
        Date date = DateUtils.localDateTimeToDate(boardingTime);
        double paramNum4 =0.0;
        double paramNum5 =0.0;
        for (int i = 0; i < startingFareJsonArray.size(); i++) {
            com.alibaba.fastjson.JSONObject jsonObject = startingFareJsonArray.getJSONObject(i);
            String num1 = jsonObject.getString("num1");
@@ -1456,10 +1466,50 @@
            String num6 = jsonObject.getString("num6");
            String num7 = jsonObject.getString("num7");
            config.normals.add(new TaxiPricingDemoService.PricingRule("普通时段", num1, num2, Double.parseDouble(num3), Double.parseDouble(num4), Double.parseDouble(num5), Double.parseDouble(num6), Double.parseDouble(num7)));
            // 判断当前是那个时段
            Calendar calendarStart = Calendar.getInstance();
            calendarStart.setTime(date);
            calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(jsonObject.getString("num1").split(":")[0]));
            calendarStart.set(Calendar.MINUTE, Integer.valueOf(jsonObject.getString("num1").split(":")[1]));
            Calendar calendarEnd = Calendar.getInstance();
            calendarEnd.setTime(date);
            calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(jsonObject.getString("num2").split(":")[0]));
            calendarEnd.set(Calendar.MINUTE, Integer.valueOf(jsonObject.getString("num2").split(":")[1]));
            // 找到符合条件的直接跳出循环
            if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) {
                paramNum4 = jsonObject.getDouble("num4");
                paramNum5 = jsonObject.getDouble("num5");
                break;
            }
        }
        if (pricing.getPricingType() == 1) {
            JSONObject basicPricingJson = JSONObject.parseObject(pricing.getBasicPricingJson());
            config.peaks.add(new TaxiPricingDemoService.PricingRule("高峰期", basicPricingJson.getString("num21").split(" - ")[0], basicPricingJson.getString("num21").split(" - ")[1], Double.parseDouble(basicPricingJson.getString("num23")),paramNum4, paramNum5,  Double.parseDouble(basicPricingJson.getString("num24")), Double.parseDouble(basicPricingJson.getString("num25"))));
            config.peaks.add(new TaxiPricingDemoService.PricingRule("高峰期", basicPricingJson.getString("num22").split(" - ")[0], basicPricingJson.getString("num22").split(" - ")[1], Double.parseDouble(basicPricingJson.getString("num23")), paramNum4, paramNum5, Double.parseDouble(basicPricingJson.getString("num24")), Double.parseDouble(basicPricingJson.getString("num25"))));
            config.night = new TaxiPricingDemoService.PricingRule("夜间行驶", basicPricingJson.getString("num14").split(" - ")[0], basicPricingJson.getString("num14").split(" - ")[1], Double.parseDouble(basicPricingJson.getString("num15")), paramNum4, paramNum5, Double.parseDouble(basicPricingJson.getString("num16")), Double.parseDouble(basicPricingJson.getString("num17")));
            config.holiday = new TaxiPricingDemoService.PricingRule("节假日", "00:00", "23:59", Double.parseDouble(basicPricingJson.getString("num29")), paramNum4, paramNum5, Double.parseDouble(basicPricingJson.getString("num30")), Double.parseDouble(basicPricingJson.getString("num31")));
            // --- 远途加价区间配置 (关键修改) ---
            // 1. 超过 num1(12) 不足 num2(20), 加收 num3(1.2)
            config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num6")), Double.parseDouble(basicPricingJson.getString("num7")), Double.parseDouble(basicPricingJson.getString("num8"))));
            // 2. 超过 num4(20) 不足 num5(35), 加收 num6(2.0)
            config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num9")), Double.parseDouble(basicPricingJson.getString("num10")), Double.parseDouble(basicPricingJson.getString("num11"))));
            // 3. 超过 num7(35) 以后, 加收 num8(3.5) -> numEnd 传个极大值
            config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num12")), 999999, Double.parseDouble(basicPricingJson.getString("num13"))));
        }
        TaxiPricingDemoService.TaxiPricingService service = new TaxiPricingDemoService.TaxiPricingService();
        Map<String, BigDecimal> bill = service.calculateOrderPrice(DateUtils.toDate(order.getBoardingTime()), order.getMileageKilometers(), gpsPoints, config);
        Integer holidayToday = holidayUtil.isHolidayToday();
        Map<String, BigDecimal> bill = service.calculateOrderPrice(DateUtils.toDate(order.getBoardingTime()), order.getMileageKilometers(), gpsPoints, config,holidayToday);
        System.err.println("======= 订单计费账单 =======");
        bill.forEach((detail, price) -> System.err.println(detail + ": " + price + " 元"));
@@ -1611,21 +1661,54 @@
            xpOrderDetails.add(xpOrderDetail);
        }
        order.setMileageMoney(mileageMoney);
        for (FareBreakdownDetailVo fareBreakdownDetailVo : longDistance) {
            XpOrderDetail xpOrderDetail = new XpOrderDetail();
            BeanUtils.copyProperties(fareBreakdownDetailVo, xpOrderDetail);
            xpOrderDetail.setOrderId(orderId);
            xpOrderDetail.setDetailType(4);
            xpOrderDetail.setCreateTime(LocalDateTime.now());
            mileageMoney = mileageMoney.add(fareBreakdownDetailVo.getMoney());
            xpOrderDetails.add(xpOrderDetail);
        }
        for (FareBreakdownDetailVo fareBreakdownDetailVo : duration) {
            XpOrderDetail xpOrderDetail = new XpOrderDetail();
            BeanUtils.copyProperties(fareBreakdownDetailVo, xpOrderDetail);
            xpOrderDetail.setOrderId(orderId);
            xpOrderDetail.setDetailType(3);
            xpOrderDetail.setCreateTime(LocalDateTime.now());
            mileageMoney = mileageMoney.add(fareBreakdownDetailVo.getMoney());
            xpOrderDetails.add(xpOrderDetail);
        }
        for (XpOrderDetail xpOrderDetail : xpOrderDetails) {
            xpOrderDetailMapper.insert(xpOrderDetail);
        }
        for (XpOrderDetail xpOrderDetail : xpOrderDetails) {
            if (xpOrderDetail.getDetailType() == 1) {
                order.setDurationType(xpOrderDetail.getType());
                order.setDurationMoney(xpOrderDetail.getMoney());
                order.setDuration(Double.parseDouble(xpOrderDetail.getDuration() + ""));
            } else {
                order.setLongDistanceType(order.getDurationType());
                order.setLongDistance(xpOrderDetail.getMileage().doubleValue());
                order.setLongDistancemoney(xpOrderDetail.getMoney());
            }
        for (FareBreakdownDetailVo fareBreakdownDetailVo : duration) {
            if(order.getDurationMoney()==null){
                order.setDurationMoney(BigDecimal.ZERO);
            }
            BigDecimal add = order.getDurationMoney().add(fareBreakdownDetailVo.getMoney());
            order.setDurationMoney(add);
        }
//        for (XpOrderDetail xpOrderDetail : xpOrderDetails) {
//            if (xpOrderDetail.getDetailType() == 1) {
//                order.setDurationType(xpOrderDetail.getType());
//                order.setDurationMoney(xpOrderDetail.getMoney());
////                order.setDuration(Double.parseDouble(xpOrderDetail.getDuration() + ""));
//            } else {
//                order.setLongDistanceType(order.getDurationType());
//                order.setLongDistance(xpOrderDetail.getMileage().doubleValue());
//                order.setLongDistancemoney(xpOrderDetail.getMoney());
//            }
//
//        }
        BigDecimal orderMoney = startMoney.add(mileageMoney).add(order.getDurationMoney() != null ? order.getDurationMoney() : BigDecimal.ZERO)
                .add(order.getLongDistancemoney() != null ? order.getLongDistancemoney() : BigDecimal.ZERO)
@@ -2052,6 +2135,7 @@
            detail.setTime(xpOrderDetail.getTime());
            detail.setMileage(xpOrderDetail.getMileage());
            detail.setDuration(xpOrderDetail.getDuration());
            detail.setMoney(xpOrderDetail.getMoney());
            if (xpOrderDetail.getDetailType() == 1) {
                start.add(detail);
            } else {
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingDemoService.java
@@ -1,4 +1,6 @@
package com.xiaopeng.order.utils;
import com.xiaopeng.order.utils.holiday.HolidayUtil;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalTime;
@@ -11,21 +13,20 @@
 */
public class TaxiPricingDemoService {
    public static void main(String[] args) {
        PricingConfig config = setupMockConfig();
//
        Calendar cal = Calendar.getInstance();
        cal.set(2026, Calendar.APRIL, 11, 15, 49, 15);
        Date orderTime = cal.getTime();
        Date orderTime = new Date();
//
        // 模拟总里程 25.0 km,以触发多级远途加价
        double totalMillige = 9.148;
        double totalMillige = 2.984;
        // 模拟 GPS 轨迹点
        List<GpsPoint> points = createMockGpsPoints(orderTime);
        TaxiPricingService service = new TaxiPricingService();
        Map<String, BigDecimal> bill = service.calculateOrderPrice(orderTime, totalMillige, points, config);
        Map<String, BigDecimal> bill = service.calculateOrderPrice(orderTime, totalMillige, points, config,0);
        System.err.println("======= 订单计费账单 =======");
        bill.forEach((detail, price) -> System.err.println(detail + ": " + price + " 元"));
@@ -40,12 +41,14 @@
    }
    public static class TaxiPricingService {
        public Map<String, BigDecimal> calculateOrderPrice(Date orderTime, double totalMileageKm, List<GpsPoint> points, PricingConfig config) {
        public Map<String, BigDecimal> calculateOrderPrice(Date orderTime, double totalMileageKm, List<GpsPoint> points, PricingConfig config,Integer holiday) {
            Map<String, BigDecimal> result = new LinkedHashMap<>();
            BigDecimal totalMileage = BigDecimal.valueOf(totalMileageKm);
            PricingRule baseRule = config.findMatchedRule(orderTime);
            PricingRule baseRule = config.findMatchedRule(orderTime,holiday);
            result.put("起步价(" + baseRule.ruleName + ")"+"|"+baseRule.start+"-"+baseRule.end+"|"+baseRule.baseMin+"&"+baseRule.baseKm, baseRule.baseFare);
            if (totalMileage.compareTo(baseRule.baseKm) <= 0) {
@@ -74,7 +77,7 @@
                currentTotalM = currentTotalM.add(segM); // 累计当前已行驶的总里程
                currentTotalMs = currentTotalMs.add(segMs); // 累计当前已行驶的总时间
                PricingRule activeRule = config.findMatchedRule(new Date(curr.getLocatetime()));
                PricingRule activeRule = config.findMatchedRule(new Date(curr.getLocatetime()),holiday);
                // 扣除起步额度逻辑
                BigDecimal billM = BigDecimal.ZERO;
@@ -216,9 +219,11 @@
        public List<PricingRule> normals = new ArrayList<>();
        public List<LongRangeStep> longSteps = new ArrayList<>(); // 修改为区间列表
        public PricingRule findMatchedRule(Date date) {
        public PricingRule findMatchedRule(Date date,Integer isHoliday) {
            LocalTime t = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
            if (holiday.isMatch(t)) return holiday;
            if (isHoliday==1){
                return holiday;
            }
            for (PricingRule r : peaks) if (r.isMatch(t)) return r;
            if (night.isMatch(t)) return night;
            for (PricingRule r : normals) if (r.isMatch(t)) return r;
@@ -257,19 +262,19 @@
    private static PricingConfig setupMockConfig() {
        PricingConfig config = new PricingConfig();
        config.peaks.add(new PricingRule("高峰期", "04:00", "05:00", 15.0, 3.0, 10.0, 4, 4));
        config.peaks.add(new PricingRule("高峰期", "15:00", "16:00", 15.0, 3.0, 10.0, 4, 4));
        config.night = new PricingRule("夜间行驶", "20:04", "23:00", 15.0, 3.0, 10.0, 3, 3);
        config.holiday = new PricingRule("节假日", "18:47", "18:48", 18.0, 3.0, 5.0, 4, 2.0);
        config.normals.add(new PricingRule("普通时段", "00:00", "23:59", 10.0, 3.0, 10.0, 3, 3));
        config.peaks.add(new PricingRule("高峰期", "04:00", "05:00", 15, 1, 10, 4, 4));
        config.peaks.add(new PricingRule("高峰期", "15:00", "16:00", 15, 1, 10, 4, 4));
        config.night = new PricingRule("夜间行驶", "20:04", "23:00", 15, 1, 10, 3, 3);
        config.holiday = new PricingRule("节假日", "00:00", "23:59", 20, 1, 10, 4, 4);
        config.normals.add(new PricingRule("普通时段", "00:00", "23:59", 10.0, 1, 10.0, 3, 3));
        // --- 远途加价区间配置 (关键修改) ---
        // 1. 超过 num1(12) 不足 num2(20), 加收 num3(1.2)
        config.longSteps.add(new LongRangeStep(5.0, 6.0, 1.2));
        config.longSteps.add(new LongRangeStep(1, 3, 3));
        // 2. 超过 num4(20) 不足 num5(35), 加收 num6(2.0)
        config.longSteps.add(new LongRangeStep(6.0, 7.0, 2.0));
        config.longSteps.add(new LongRangeStep(3, 5, 5));
        // 3. 超过 num7(35) 以后, 加收 num8(3.5) -> numEnd 传个极大值
        config.longSteps.add(new LongRangeStep(7.0, 8.0, 3.5));
        config.longSteps.add(new LongRangeStep(5, 999999, 6));
        return config;
    }
@@ -296,39 +301,39 @@
        list.add(new GpsPoint(162, startTs + 2 * 150 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 160 * 1000));
        list.add(new GpsPoint(132, startTs + 2 * 170 * 1000));
        list.add(new GpsPoint(142, startTs + 2 * 180 * 1000));
        list.add(new GpsPoint(262, startTs + 2 * 190 * 1000));
        list.add(new GpsPoint(242, startTs + 2 * 200 * 1000));
        list.add(new GpsPoint(282, startTs + 2 * 210 * 1000));
        list.add(new GpsPoint(222, startTs + 2 * 220 * 1000));
        list.add(new GpsPoint(212, startTs + 2 * 230 * 1000));
        list.add(new GpsPoint(142, startTs + 2 * 240 * 1000));
        list.add(new GpsPoint(262, startTs + 2 * 250 * 1000));
        list.add(new GpsPoint(169, startTs + 2 * 260 * 1000));
        list.add(new GpsPoint(170, startTs + 2 * 270 * 1000));
        list.add(new GpsPoint(180, startTs + 2 * 280 * 1000));
        list.add(new GpsPoint(165, startTs + 2 * 290 * 1000));
        list.add(new GpsPoint(180, startTs + 2 * 300 * 1000));
        list.add(new GpsPoint(198, startTs + 2 * 310 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 320 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 330 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 340 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 350 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 360 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 370 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 380 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 390 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 400 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 410 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 420 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 430 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 440 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 450 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 460 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 470 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 490 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 500 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 510 * 1000));
        list.add(new GpsPoint(122, startTs + 2 * 180 * 1000));
//        list.add(new GpsPoint(262, startTs + 2 * 190 * 1000));
//        list.add(new GpsPoint(242, startTs + 2 * 200 * 1000));
//        list.add(new GpsPoint(282, startTs + 2 * 210 * 1000));
//        list.add(new GpsPoint(222, startTs + 2 * 220 * 1000));
//        list.add(new GpsPoint(212, startTs + 2 * 230 * 1000));
//        list.add(new GpsPoint(142, startTs + 2 * 240 * 1000));
//        list.add(new GpsPoint(262, startTs + 2 * 250 * 1000));
//        list.add(new GpsPoint(169, startTs + 2 * 260 * 1000));
//        list.add(new GpsPoint(170, startTs + 2 * 270 * 1000));
//        list.add(new GpsPoint(180, startTs + 2 * 280 * 1000));
//        list.add(new GpsPoint(165, startTs + 2 * 290 * 1000));
//        list.add(new GpsPoint(180, startTs + 2 * 300 * 1000));
//        list.add(new GpsPoint(198, startTs + 2 * 310 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 320 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 330 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 340 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 350 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 360 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 370 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 380 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 390 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 400 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 410 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 420 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 430 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 440 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 450 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 460 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 470 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 490 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 500 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 510 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 520 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 530 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 540 * 1000));
xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/webSocket/controller/WebSocketController.java
@@ -157,6 +157,40 @@
    }
    /**
     * 发送是否开启倒计时
     * {
     *     "appUserId":"-1",
     *     {"type":"OPEN_TRUNK","content":"{\"orderId\":\"1983875114637811713\",\"isOpen\":1}","timestamp":"176569945845456"}
     * }
     *
     * @param dto 消息实体
     * @return 操作结果
     */
    @ApiOperation(value = "发送是否开启倒计时")
    @PostMapping("/inner/websocket/order/sendOpenTrunk")
    public R<String> sendOpenTrunk(@RequestBody SendMessageDTO dto) {
        try {
            String message = dto.getMessage();
            if (message.trim().isEmpty()) {
                return R.fail("消息内容不能为空");
            }
            String appUserId = dto.getAppUserId();
            if (appUserId.trim().isEmpty()) {
                return R.fail("用户id不能为空");
            }
            int deviceCount = webSocketUserService.sendMessageToUser(appUserId, message);
            if (deviceCount > 0) {
                return R.ok("消息发送成功,发送到 " + deviceCount + " 个设备");
            } else {
                return R.fail("用户不在线或发送失败");
            }
        } catch (Exception e) {
            log.error("向用户发送消息失败", e);
            return R.fail("发送消息失败: " + e.getMessage());
        }
    }
    /**
     * 向指定用户发送JSON格式消息
     * @param appUserId 用户ID
     * @param request 请求参数
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/constant/I18nKeyConstant.java
@@ -50,4 +50,17 @@
    String VEHICLE_COLOR_INVALID = "vehicle.color.invalid";
    String VEHICLE_ELECTRONIC_NOT_EXISTS = "vehicle.electronic.not.exists";
    // ========== 车辆导入校验 ==========
    String VEHICLE_IMPORT_VALIDATE_FAIL = "vehicle.import.validate.fail";
    String VEHICLE_IMPORT_CODE_INVALID = "vehicle.import.code.invalid";
    String VEHICLE_IMPORT_VIN_INVALID = "vehicle.import.vin.invalid";
    String VEHICLE_IMPORT_PLATE_INVALID = "vehicle.import.plate.invalid";
    String VEHICLE_IMPORT_COLOR_INVALID = "vehicle.import.color.invalid";
    String VEHICLE_IMPORT_CODE_DUPLICATE = "vehicle.import.code.duplicate";
    String VEHICLE_IMPORT_VIN_DUPLICATE = "vehicle.import.vin.duplicate";
    String VEHICLE_IMPORT_PLATE_DUPLICATE = "vehicle.import.plate.duplicate";
    String VEHICLE_IMPORT_CODE_EXISTS = "vehicle.import.code.exists";
    String VEHICLE_IMPORT_VIN_EXISTS = "vehicle.import.vin.exists";
    String VEHICLE_IMPORT_PLATE_EXISTS = "vehicle.import.plate.exists";
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -5,6 +5,7 @@
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Validator;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -69,8 +70,13 @@
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
/**
 * <p>
@@ -85,6 +91,9 @@
@Api(tags = "车辆管理")
@RequiredArgsConstructor(onConstructor_ = @Lazy)
public class XpVehicleController {
    private static final Pattern VEHICLE_CODE_PATTERN = Pattern.compile("^[A-Za-z0-9_]{1,5}$");
    private static final Pattern VEHICLE_VIN_PATTERN = Pattern.compile("^[A-Z0-9]{17}$");
    private final IXpVehicleService xpVehicleService;
    private final IXpFleetVehicleService vehicleService;
@@ -294,6 +303,7 @@
            throw new ServiceException("导入数据为空!", I18nKeyConstant.VEHICLE_IMPORT_DATA_EMPTY);
        }
        validateImportRequiredFields(importList, params.getHeadRows());
        validateImportBusinessFields(importList, params.getHeadRows());
        uploadVehiclePicture(importList);
        xpVehicleService.importVehicles(importList);
        return R.ok();
@@ -326,6 +336,189 @@
        }
    }
    private void validateImportBusinessFields(List<XpVehicleImportExcel> importList, int headRows) {
        List<String> errors = new ArrayList<>();
        Locale locale = LocaleContextHolder.getLocale();
        for (int index = 0; index < importList.size(); index++) {
            XpVehicleImportExcel row = importList.get(index);
            int excelRowNumber = index + headRows + 1;
            String vehicleCode = trimToNull(row.getVehicleCode());
            String vehicleVin = normalizeVin(row.getVehicleVin());
            String plateNumber = trimToNull(row.getPlateNumber());
            String vehicleColor = trimToNull(row.getVehicleColor());
            row.setVehicleCode(vehicleCode);
            row.setVehicleVin(vehicleVin);
            row.setPlateNumber(plateNumber);
            row.setVehicleColor(vehicleColor);
            if (org.apache.commons.lang3.StringUtils.isNotBlank(vehicleCode) && !VEHICLE_CODE_PATTERN.matcher(vehicleCode).matches()) {
                errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_CODE_INVALID,
                        new Object[]{excelRowNumber, vehicleCode},
                        I18nKeyConstant.VEHICLE_IMPORT_CODE_INVALID,
                        locale));
            }
            if (org.apache.commons.lang3.StringUtils.isNotBlank(vehicleVin) && !VEHICLE_VIN_PATTERN.matcher(vehicleVin).matches()) {
                errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_VIN_INVALID,
                        new Object[]{excelRowNumber, vehicleVin},
                        I18nKeyConstant.VEHICLE_IMPORT_VIN_INVALID,
                        locale));
            }
            if (org.apache.commons.lang3.StringUtils.isNotBlank(plateNumber) && !Validator.isPlateNumber(plateNumber)) {
                errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_PLATE_INVALID,
                        new Object[]{excelRowNumber, plateNumber},
                        I18nKeyConstant.VEHICLE_IMPORT_PLATE_INVALID,
                        locale));
            }
            if (org.apache.commons.lang3.StringUtils.isNotBlank(vehicleColor) && vehicleColor.length() > 2) {
                errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_COLOR_INVALID,
                        new Object[]{excelRowNumber, vehicleColor},
                        I18nKeyConstant.VEHICLE_IMPORT_COLOR_INVALID,
                        locale));
            }
        }
        Set<String> duplicatedCodes = findDuplicated(importList.stream()
                .map(XpVehicleImportExcel::getVehicleCode)
                .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
                .collect(java.util.stream.Collectors.toList()));
        Set<String> duplicatedVins = findDuplicated(importList.stream()
                .map(XpVehicleImportExcel::getVehicleVin)
                .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
                .collect(java.util.stream.Collectors.toList()));
        Set<String> duplicatedPlates = findDuplicated(importList.stream()
                .map(XpVehicleImportExcel::getPlateNumber)
                .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
                .collect(java.util.stream.Collectors.toList()));
        for (int index = 0; index < importList.size(); index++) {
            XpVehicleImportExcel row = importList.get(index);
            int excelRowNumber = index + headRows + 1;
            if (duplicatedCodes.contains(row.getVehicleCode())) {
                errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_CODE_DUPLICATE,
                        new Object[]{excelRowNumber, row.getVehicleCode()},
                        I18nKeyConstant.VEHICLE_IMPORT_CODE_DUPLICATE,
                        locale));
            }
            if (duplicatedVins.contains(row.getVehicleVin())) {
                errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_VIN_DUPLICATE,
                        new Object[]{excelRowNumber, row.getVehicleVin()},
                        I18nKeyConstant.VEHICLE_IMPORT_VIN_DUPLICATE,
                        locale));
            }
            if (duplicatedPlates.contains(row.getPlateNumber())) {
                errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_PLATE_DUPLICATE,
                        new Object[]{excelRowNumber, row.getPlateNumber()},
                        I18nKeyConstant.VEHICLE_IMPORT_PLATE_DUPLICATE,
                        locale));
            }
        }
        Set<String> codeSet = importList.stream()
                .map(XpVehicleImportExcel::getVehicleCode)
                .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
                .collect(java.util.stream.Collectors.toSet());
        Set<String> vinSet = importList.stream()
                .map(XpVehicleImportExcel::getVehicleVin)
                .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
                .collect(java.util.stream.Collectors.toSet());
        Set<String> plateSet = importList.stream()
                .map(XpVehicleImportExcel::getPlateNumber)
                .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
                .collect(java.util.stream.Collectors.toSet());
        Set<String> existsCodes = Collections.emptySet();
        if (!codeSet.isEmpty()) {
            existsCodes = xpVehicleService.lambdaQuery()
                    .select(XpVehicle::getVehicleCode)
                    .in(XpVehicle::getVehicleCode, codeSet)
                    .list()
                    .stream()
                    .map(XpVehicle::getVehicleCode)
                    .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
                    .collect(java.util.stream.Collectors.toSet());
        }
        Set<String> existsVins = Collections.emptySet();
        if (!vinSet.isEmpty()) {
            existsVins = xpVehicleService.lambdaQuery()
                    .select(XpVehicle::getVehicleVin)
                    .in(XpVehicle::getVehicleVin, vinSet)
                    .list()
                    .stream()
                    .map(XpVehicle::getVehicleVin)
                    .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
                    .collect(java.util.stream.Collectors.toSet());
        }
        Set<String> existsPlates = Collections.emptySet();
        if (!plateSet.isEmpty()) {
            existsPlates = xpVehicleService.lambdaQuery()
                    .select(XpVehicle::getPlateNumber)
                    .in(XpVehicle::getPlateNumber, plateSet)
                    .list()
                    .stream()
                    .map(XpVehicle::getPlateNumber)
                    .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
                    .collect(java.util.stream.Collectors.toSet());
        }
        for (int index = 0; index < importList.size(); index++) {
            XpVehicleImportExcel row = importList.get(index);
            int excelRowNumber = index + headRows + 1;
            if (existsCodes.contains(row.getVehicleCode())) {
                errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_CODE_EXISTS,
                        new Object[]{excelRowNumber, row.getVehicleCode()},
                        I18nKeyConstant.VEHICLE_IMPORT_CODE_EXISTS,
                        locale));
            }
            if (existsVins.contains(row.getVehicleVin())) {
                errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_VIN_EXISTS,
                        new Object[]{excelRowNumber, row.getVehicleVin()},
                        I18nKeyConstant.VEHICLE_IMPORT_VIN_EXISTS,
                        locale));
            }
            if (existsPlates.contains(row.getPlateNumber())) {
                errors.add(messageSource.getMessage(I18nKeyConstant.VEHICLE_IMPORT_PLATE_EXISTS,
                        new Object[]{excelRowNumber, row.getPlateNumber()},
                        I18nKeyConstant.VEHICLE_IMPORT_PLATE_EXISTS,
                        locale));
            }
        }
        if (!errors.isEmpty()) {
            throw new ServiceException("导入校验失败!", I18nKeyConstant.VEHICLE_IMPORT_VALIDATE_FAIL,
                    new Object[]{String.join("\n", errors)});
        }
    }
    private static String normalizeVin(String vin) {
        String trimmed = trimToNull(vin);
        return trimmed == null ? null : trimmed.toUpperCase(Locale.ROOT);
    }
    private static String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }
    private static Set<String> findDuplicated(List<String> values) {
        if (values == null || values.isEmpty()) {
            return Collections.emptySet();
        }
        Set<String> seen = new HashSet<>();
        Set<String> duplicated = new HashSet<>();
        for (String value : values) {
            if (!seen.add(value)) {
                duplicated.add(value);
            }
        }
        return duplicated;
    }
    private void uploadVehiclePicture(List<XpVehicleImportExcel> importList) {
        for (XpVehicleImportExcel xpVehicleImportExcel : importList) {
            xpVehicleImportExcel.setVehiclePictures(getOssUrl(xpVehicleImportExcel.getVehiclePictures()));
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -3,15 +3,20 @@
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableMap;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto;
import com.xiaopeng.api.feignClient.automaticPilot.XpuVinClient;
import com.xiaopeng.api.feignClient.operation.XpElectronicClient;
import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
import com.xiaopeng.api.model.operation.XpElectronicFence;
import com.xiaopeng.api.model.operation.XpElectronicFenceArea;
import com.xiaopeng.api.model.order.XpOrder;
@@ -23,7 +28,10 @@
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.OrderStatusEnum;
import com.xiaopeng.common.core.enums.PushMessageTypeEnum;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.DateUtils;
import com.xiaopeng.common.core.utils.GeodesyUtil;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.redis.service.RedisService;
@@ -73,7 +81,7 @@
@Service
public class XpVehicleServiceImpl extends ServiceImpl<XpVehicleMapper, XpVehicle> implements IXpVehicleService {
    private static final Pattern VEHICLE_CODE_PATTERN = Pattern.compile("^[A-Za-z0-9_]{5}$");
    private static final Pattern VEHICLE_CODE_PATTERN = Pattern.compile("^[A-Za-z0-9_]{1,5}$");
    private static final Pattern VEHICLE_VIN_PATTERN = Pattern.compile("^[A-Z0-9]{17}$");
    @Autowired
@@ -99,6 +107,8 @@
    private MongoTemplate mongoTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private XpPushMessageClient xpPushMessageClient;
    @Override
    public List<XpVehicle> listByIds(List<Long> ids) {
@@ -266,10 +276,32 @@
                throw new ServiceException("车辆非P档,无法开启后备箱", I18nKeyConstant.VEHICLE_NOT_P_GEAR);
            }
            vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_OPEN.getCommand());
            if(xpOrder.getOpenTrunk()==0){
                xpOrder.setOpenTrunk(1);
            }
            if(xpOrder.getOpenTrunk()==1){
                xpOrder.setOpenTrunk(2);
                // 添加定时任务
                xpOrderClient.updateOrderStatus(xpOrder.getId(),7);
                // 推送倒计时消息 消息推送
                Map<String, Object> params = new HashMap<>();
                Map<String, Object> content = new HashMap<>();
                content.put("orderId", xpOrder.getId().toString());
                content.put("isOpen", 1);
                params.put("type", PushMessageTypeEnum.OPEN_TRUNK.getDesc());
                params.put("content", JSONObject.toJSONString(content));
                params.put("timestamp", new Date().getTime() + "");
                String message = JSONObject.toJSONString(params);
                SendMessageDTO sendMessageDTO = new SendMessageDTO();
                sendMessageDTO.setMessage(message);
                sendMessageDTO.setAppUserId(String.valueOf(xpOrder.getAppUserId()));
                xpPushMessageClient.sendStatus(sendMessageDTO);
            }
        }
        try {
            String result = vehicleControlUtil.vehiclesCommand(vehiclesCommandVo, xpOrder.getAppUserId());
            log.info("车辆控制结果:{}", result);
            xpOrderClient.updateOrder(xpOrder);
            return 1;
        } catch (Exception e) {
            log.error("车辆控制异常:{}", e.getMessage());
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages.properties
@@ -21,3 +21,22 @@
vehicle.operation.info.incomplete=以下信息未完善,无法开始运营:{0}
vehicle.batch.operation.condition.not.met=以下车辆不满足运营条件:{0}
vehicle.time.expired=以下时间已过期,请填写有效日期:{0}
vehicle.code.invalid=车辆编码格式不正确,仅支持1-5位字母、数字、下划线:{0}
vehicle.code.exists=车辆编码已存在:{0}
vehicle.vin.invalid=车辆VIN码格式不正确,仅支持17位大写字母与数字:{0}
vehicle.vin.exists=车辆VIN码已存在:{0}
vehicle.plate.invalid=车牌号格式不正确:{0}
vehicle.plate.exists=车牌号已存在:{0}
vehicle.color.invalid=车辆颜色长度不能超过2:{0}
vehicle.electronic.not.exists=选择的运营区域不存在:{0}
vehicle.import.validate.fail=导入校验失败:\n{0}
vehicle.import.code.invalid=第{0}行车辆编码格式不正确(1-5位字母/数字/下划线):{1}
vehicle.import.vin.invalid=第{0}行车辆VIN码格式不正确(17位大写字母与数字):{1}
vehicle.import.plate.invalid=第{0}行车牌号格式不正确:{1}
vehicle.import.color.invalid=第{0}行车辆颜色长度不能超过2:{1}
vehicle.import.code.duplicate=第{0}行车辆编码在导入文件中重复:{1}
vehicle.import.vin.duplicate=第{0}行车辆VIN码在导入文件中重复:{1}
vehicle.import.plate.duplicate=第{0}行车牌号在导入文件中重复:{1}
vehicle.import.code.exists=第{0}行车辆编码已存在:{1}
vehicle.import.vin.exists=第{0}行车辆VIN码已存在:{1}
vehicle.import.plate.exists=第{0}行车牌号已存在:{1}
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_en_US.properties
@@ -25,7 +25,7 @@
vehicle.not.p.gear=The trunk cannot be opened when the vehicle is not in P gear
vehicle.temperature.up=The highest temperature is 28°C
vehicle.temperature.down=The lowest temperature is 22°C
vehicle.code.invalid=Invalid vehicle code (5 chars, letters/numbers/underscore only): {0}
vehicle.code.invalid=Invalid vehicle code (1-5 chars, letters/numbers/underscore only): {0}
vehicle.code.exists=Vehicle code already exists: {0}
vehicle.vin.invalid=Invalid VIN (17 chars, uppercase letters and digits only): {0}
vehicle.vin.exists=VIN already exists: {0}
@@ -33,4 +33,15 @@
vehicle.plate.exists=Plate number already exists: {0}
vehicle.color.invalid=Vehicle color length must be <= 2: {0}
vehicle.electronic.not.exists=Selected operating area does not exist: {0}
vehicle.import.validate.fail=Import validation failed:\n{0}
vehicle.import.code.invalid=Row {0}: invalid vehicle code (1-5 chars, letters/numbers/underscore only): {1}
vehicle.import.vin.invalid=Row {0}: invalid VIN (17 chars, uppercase letters and digits only): {1}
vehicle.import.plate.invalid=Row {0}: invalid plate number: {1}
vehicle.import.color.invalid=Row {0}: vehicle color length must be <= 2: {1}
vehicle.import.code.duplicate=Row {0}: duplicate vehicle code in file: {1}
vehicle.import.vin.duplicate=Row {0}: duplicate VIN in file: {1}
vehicle.import.plate.duplicate=Row {0}: duplicate plate number in file: {1}
vehicle.import.code.exists=Row {0}: vehicle code already exists: {1}
vehicle.import.vin.exists=Row {0}: VIN already exists: {1}
vehicle.import.plate.exists=Row {0}: plate number already exists: {1}
vehicle.distance.too.long=Too far away, unable to open
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -26,7 +26,7 @@
vehicle.not.p.gear=车辆非P档,无法开启后备箱
vehicle.temperature.up=温度最高28℃
vehicle.temperature.down=温度最低22℃
vehicle.code.invalid=车辆编码格式不正确,仅支持5位字母、数字、下划线:{0}
vehicle.code.invalid=车辆编码格式不正确,仅支持1-5位字母、数字、下划线:{0}
vehicle.code.exists=车辆编码已存在:{0}
vehicle.vin.invalid=车辆VIN码格式不正确,仅支持17位大写字母与数字:{0}
vehicle.vin.exists=车辆VIN码已存在:{0}
@@ -34,4 +34,15 @@
vehicle.plate.exists=车牌号已存在:{0}
vehicle.color.invalid=车辆颜色长度不能超过2:{0}
vehicle.electronic.not.exists=选择的运营区域不存在:{0}
vehicle.distance.too.long=距离过远,无法打开
vehicle.import.validate.fail=导入校验失败:\n{0}
vehicle.import.code.invalid=第{0}行车辆编码格式不正确(1-5位字母/数字/下划线):{1}
vehicle.import.vin.invalid=第{0}行车辆VIN码格式不正确(17位大写字母与数字):{1}
vehicle.import.plate.invalid=第{0}行车牌号格式不正确:{1}
vehicle.import.color.invalid=第{0}行车辆颜色长度不能超过2:{1}
vehicle.import.code.duplicate=第{0}行车辆编码在导入文件中重复:{1}
vehicle.import.vin.duplicate=第{0}行车辆VIN码在导入文件中重复:{1}
vehicle.import.plate.duplicate=第{0}行车牌号在导入文件中重复:{1}
vehicle.import.code.exists=第{0}行车辆编码已存在:{1}
vehicle.import.vin.exists=第{0}行车辆VIN码已存在:{1}
vehicle.import.plate.exists=第{0}行车牌号已存在:{1}
vehicle.distance.too.long=距离过远,无法打开
xp-robotaxi-vehicle-convoy-boot/src/main/resources/mapper/vehicleConvoy/XpVehicleMapper.xml
@@ -73,7 +73,7 @@
                </foreach>
            </if>
            AND is_deleted = 0
            AND vehicleType = 2
            AND vehicle_type = 2
        </where>
        ORDER BY create_time DESC
    </select>