xuhy
2026-03-26 bbd837a72bb9ec1bcef044624960345f98969a49
Merge remote-tracking branch 'origin/master'
28个文件已修改
993 ■■■■■ 已修改文件
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/order/XpOrderClientFallbackFactory.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/order/XpTestOrderClientFallbackFactory.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/order/XpOrderClient.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/order/XpTestOrderClient.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/OrderType.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/XpPricingServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/constant/I18nKeyConstant.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpChargingRecordController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/model/vo/TaskChargingDetailVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpChargingRecordService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpChargingRecordServiceImpl.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpTaskServiceImpl.java 493 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/resources/i18n/messages.properties 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/resources/i18n/messages_en_US.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/resources/i18n/messages_zh_CN.properties 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpCruiseOrderController.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpTestOrderController.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/exportExcel/XpCruiseOrderExportExcel.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/IXpTestOrderService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpTestOrderServiceImpl.java 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/order/XpOrderClientFallbackFactory.java
@@ -79,6 +79,12 @@
            }
            @Override
            public R<Integer> todayCancellationCount(Long userId) {
                log.error("获取当日取消订单次数失败,原因: {}", cause.getMessage());
                return R.fail("获取当日取消订单次数失败");
            }
            @Override
            public R<Integer> countActiveOrdersByCityId(Long cityId) {
                log.error("查询进行中订单数量失败,原因: {}", cause.getMessage());
                return R.ok(0);
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/order/XpTestOrderClientFallbackFactory.java
@@ -27,6 +27,13 @@
                log.error("更新订单状态失败:{}",  cause.getMessage());
                return R.fail("更新订单状态失败");
            }
            @Override
            public R<?> cancelTestOrderById(Long orderId) {
                log.error("取消订单失败:{}",  cause.getMessage());
                return R.fail("取消订单失败");
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/order/XpOrderClient.java
@@ -85,6 +85,17 @@
    @PostMapping("/inner/order/getOrderNum")
    R<Integer> getOrderNum(@RequestBody GetOrderNumDto dto);
    /**
     * 当日取消订单次数
     *
     * @param
     * @return 订单数量
     */
    @PostMapping("/inner/order/todayCancellationCount")
    R<Integer> todayCancellationCount(@RequestParam Long userId);
    /**
     * 查询指定城市进行中的订单数量(status=1)
     *
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/order/XpTestOrderClient.java
@@ -33,4 +33,14 @@
    @PostMapping("/inner/v1/testOrder/updateOrderStatus")
    R<String> updateOrderStatus(@RequestParam Long orderId,
                                @RequestParam Integer status);
    /**
     * 根据订单ID查询取消订单
     *
     * @param orderId
     * @return
     */
    @PostMapping("/inner/testOrder/cancelTestOrderById")
    R<?> cancelTestOrderById(@RequestParam Long orderId);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/OrderType.java
@@ -16,4 +16,8 @@
     * 测试订单
     */
    String TEST_ORDER = "test_order";
    /**
     * 测试订单
     */
    String TEST_CRUISE_ORDER = "test_cruise_order";
}
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java
@@ -19,7 +19,6 @@
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.order.XpOrder;import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.OrderType;
import com.xiaopeng.api.vo.SystemEventType;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.api.vo.automaticPilot.protobuf.*;
@@ -104,6 +103,7 @@
            taskBuilder.addOrderPhase(OrderPhase.ROUTE_TO_CRUISE);
            taskListBuilder.addTasks(taskBuilder.build());
        }else if(dto.getOrderType().equals(com.xiaopeng.api.vo.OrderType.USER_ORDER)){
            //乘客单
            for (int i = 1; i < 3; i++) {
                RtTask.Builder taskBuilder = RtTask.newBuilder();
                taskBuilder.setOrderId(dto.getOrderId().toString());
@@ -162,6 +162,7 @@
                taskListBuilder.addTasks(taskBuilder.build());
            }
        }else if(dto.getOrderType().equals(com.xiaopeng.api.vo.OrderType.TEST_ORDER)){
            //测试单
            for (int i = 1; i < 3; i++) {
                RtTask.Builder taskBuilder = RtTask.newBuilder();
                taskBuilder.setOrderId(dto.getOrderId().toString());
@@ -219,12 +220,44 @@
                }
                taskListBuilder.addTasks(taskBuilder.build());
            }
        }else if(dto.getOrderType().equals(com.xiaopeng.api.vo.OrderType.TEST_CRUISE_ORDER)){
            //测试单巡游单
            RtTask.Builder taskBuilder = RtTask.newBuilder();
            taskBuilder.setOrderId(dto.getOrderId().toString());
            taskBuilder.setTaskId(snowflake.nextId() + "");
            taskBuilder.setTaskType(OrderType.ORDER_TYPE_CRUISE);
            //构建起点坐标数据
            pointBuilderStart.setWaypointId(dto.getOrderId() + "-001");
            pointBuilderStart.setPointType(PointType.START_POINT);
            pointBuilderStart.setSequence(1);
            pointBuilderStart.setActionType(ActionType.ACTION_UNSPECIFIED);
            GeoPoint.Builder geoPointBuilder = GeoPoint.newBuilder();
            geoPointBuilder.setLatitude(dto.getStartLatitude());
            geoPointBuilder.setLongitude(dto.getStartLongitude());
            pointBuilderStart.setPoint(geoPointBuilder.build());
            taskBuilder.addPoints(pointBuilderStart);
            //构建终点数据
            pointBuilderEnd.setWaypointId(dto.getOrderId() + "-002");
            pointBuilderEnd.setPointType(PointType.CRUISE_END_POINT);
            pointBuilderEnd.setSequence(2);
            pointBuilderEnd.setActionType(ActionType.DRIVE_THROUGH);
            geoPointBuilder = GeoPoint.newBuilder();
            geoPointBuilder.setLatitude(dto.getEndLatitude());
            geoPointBuilder.setLongitude(dto.getEndLongitude());
            pointBuilderEnd.setPoint(geoPointBuilder.build());
            taskBuilder.addPoints(pointBuilderEnd);
            taskBuilder.setSequence(1);
            taskBuilder.addOrderPhase(OrderPhase.ROUTE_TO_CRUISE);
            taskListBuilder.addTasks(taskBuilder.build());
        }
        long sequence = snowflake.nextId();
        MqttPublishDto mqttPublishDto = xpuUtil.cloudCreateOrder(taskListBuilder.build(), dto.getVin(),sequence);
        //先将车辆修改状态,防止在异步通知之前被其他订单选中
        if (!dto.getOrderId().contains("XU") && mqttPublishDto.getCode()== 200) {
        if(!dto.getOrderType().equals(com.xiaopeng.api.vo.OrderType.CRUISE_ORDER) &&
                !dto.getOrderType().equals(com.xiaopeng.api.vo.OrderType.TEST_CRUISE_ORDER)
                && mqttPublishDto.getCode()== 200){
            // 修改车子为接单状态
            xpVehicleClient.updateVehicleStatus(new UpdateVehicleStateDto(dto.getVin(), 1));
        }
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java
@@ -6,6 +6,7 @@
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import com.xiaopeng.api.dto.automaticPilot.MqttPublishDto;
import com.xiaopeng.api.dto.automaticPilot.PushOrderDto;
import com.xiaopeng.api.dto.automaticPilot.VehicleEventDTO;
import com.xiaopeng.api.dto.map.OrderRouteAndFareDto;
import com.xiaopeng.api.dto.operation.CruisePointDto;
@@ -423,7 +424,7 @@
        if (abnormalReason == AbnormalReason.FAULT) {
            log.info("车辆故障,开始处理故障");
            String orderId = systemRuntimeStatus.getRunningOrderStatusInfo().getOrderId();
            if(!orderId.contains("XU")){
            if(orderId.contains("XU")){
                //巡游订单,需要关闭订单
                xpCruiseOrderClient.cancelCruiseOrderById(Long.parseLong(orderId.substring(2)));
                XpCruiseOrder cruiseOrder = xpCruiseOrderClient.getCruiseOrder(Long.parseLong(orderId.substring(2))).getData();
@@ -435,7 +436,19 @@
                    vehicle1.setId(vehicle.getId());
                    xpVehicleClient.updateVehicleById(vehicle1);
                }
            }else{
            }else if(orderId.contains("TEST")){
                //测试订单故障则取消订单
                XpTestOrder testOrder = testOrderClient.queryOrderById(Long.parseLong(orderId.substring(4))).getData();
                vehicle = xpVehicleClient.queryById(testOrder.getVehicleId()).getData();
                if (vehicle != null) {
                    // 修改车辆状态
                    XpVehicle vehicle1 = new XpVehicle();
                    vehicle1.setOperatingStatus(4);
                    vehicle1.setId(vehicle.getId());
                    xpVehicleClient.updateVehicleById(vehicle1);
                    log.info("车辆故障,车辆状态修改0 {}", vehicle.getPlateNumber());
                }
            }else {
                //正常订单
                XpOrder xpOrder = xpOrderClient.queryOrderById(Long.parseLong(orderId)).getData();
                vehicle = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData();
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/XpPricingServiceImpl.java
@@ -489,15 +489,11 @@
        }
        // 判断当天取消的次数 后台配置 redis 查询
        Object cancelOrder = redisService.getCacheObject("CANCEL_ORDER_" + uid);
        log.info("取消次数:{}", cancelOrder);
        // 静默处理
        boolean isSilent = false;
        if (cancelOrder != null) {
            Integer num = Integer.valueOf(cancelOrder.toString());
            if (num >= cancelCount) {
                isSilent = true;
            }
        Integer num = xpOrderClient.todayCancellationCount(Long.valueOf(uid)).getData();
        if (num >= cancelCount) {
            isSilent = true;
        }
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/constant/I18nKeyConstant.java
@@ -10,4 +10,6 @@
    String TASK_NOT_EXIST = "task.not.exist";
    String VEHICLE_NOT_EXIST = "vehicle.not.exist";
    String REPAIR_MONEY_TOO_LONG = "repair.money.too.long";
    String TASK_SAVE_ERROR = "task.save.error";
    String TASK_VEHICLE_NOT_EXIST = "task.vehicle.not.exist";
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpChargingRecordController.java
@@ -3,6 +3,7 @@
import com.xiaopeng.api.feignClient.account.XpOperationUserClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.maintenance.XpChargingRecord;
import com.xiaopeng.maintenance.model.query.XpChargingRecordPageQuery;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.web.page.PageInfo;
@@ -66,9 +67,9 @@
    public R<String> stopCharging(@RequestBody XpChargingRecordStopDTO dto) {
        Long id = tokenService.getLoginOperationUser().getId();
        xpChargingRecordService.stopCharging(dto, id);
        XpChargingRecord chargingRecord = xpChargingRecordService.stopCharging(dto, id);
        //更新任务状态
        xpTaskService.updateTaskStatus(dto.getTaskId(), 2, id);
        xpTaskService.updateTaskStatus(chargingRecord.getTaskId(), 2, id);
        return R.ok();
    }
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/model/vo/TaskChargingDetailVO.java
@@ -55,6 +55,7 @@
    private String startChargingPicture;
    @ApiModelProperty(value = "开始充电时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private LocalDateTime startTime;
    @ApiModelProperty(value = "结束剩余电量")
@@ -64,6 +65,7 @@
    private String endChargingPicture;
    @ApiModelProperty(value = "结束充电时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private LocalDateTime endTime;
    @ApiModelProperty(value = "开始充电操作人")
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpChargingRecordService.java
@@ -35,7 +35,7 @@
     * @param dto
     * @param id
     */
    void stopCharging(XpChargingRecordStopDTO dto, Long id);
    XpChargingRecord stopCharging(XpChargingRecordStopDTO dto, Long id);
    /**
     * 充电记录详情
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpChargingRecordServiceImpl.java
@@ -84,7 +84,7 @@
    }
    @Override
    public void stopCharging(XpChargingRecordStopDTO dto, Long id) {
    public XpChargingRecord stopCharging(XpChargingRecordStopDTO dto, Long id) {
        // 查询车辆充电记录  进行修改
        XpChargingRecord xpChargingRecord = this.getById(dto.getId());
        // 查询车辆信息
@@ -104,6 +104,7 @@
            }
        }
        this.updateById(xpChargingRecord);
        return xpChargingRecord;
    }
    @Override
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpTaskServiceImpl.java
@@ -224,37 +224,33 @@
        String code = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);
        xpTask.setTaskCode(code);
        xpTask.setTaskCategory(2);
        String[] split = dto.getTaskTypeId().split(",");
        List<String> typeIds = new ArrayList<>();
        for (String s : split) {
            typeIds.add(s.trim());
        }
        StringBuilder temp = new StringBuilder(dto.getTaskTypeId());
        List<Long> children = typeIds.stream().map(Long::parseLong).collect(Collectors.toList());
        List<Long> parentIds = errorTypeMapper.selectList(new LambdaQueryWrapper<XpErrorType>()
                .in(XpErrorType::getId, children)).stream().map(XpErrorType::getParentId).distinct().collect(Collectors.toList());
        for (Long parentId : parentIds) {
            temp.append(",").append(parentId);
        }
        xpTask.setTaskTypeId(temp.toString());
        xpTask.setTaskTypeId(normalizeAccidentTaskTypeId(dto.getTaskTypeId()));
        xpTask.setTaskUrgencyLevel(dto.getTaskUrgencyLevel());
        xpTask.setTaskDescription(dto.getTaskDescription());
        xpTask.setTaskImage(dto.getTaskImage());
        xpTask.setVehicleId(dto.getId());
        XpVehicle vehicle = xpVehicleClient.queryById(xpTask.getVehicleId()).getData();
//        if (vehicle!=null){
//            VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vehicle.getVehicleVin())), VehiclePosition.class);
//            if (vehiclePosition != null) {
//                double x = vehiclePosition.getPoint().getX();
//                double y = vehiclePosition.getPoint().getY();
//                xpTask.setVehicleLon(new BigDecimal(x));
//                xpTask.setVehicleLat(new BigDecimal(y));
//            }
//        }
        if (Objects.isNull(vehicle)) {
            throw new ServiceException("上报失败,未获取到车辆信息", I18nKeyConstant.TASK_VEHICLE_NOT_EXIST);
        }
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vehicle.getVehicleVin())), VehiclePosition.class);
        if (Objects.isNull(vehiclePosition)) {
            log.error("事故上报未获取到车辆当前定位");
            throw new ServiceException("获取车辆实时定位信息失败,请稍后重试", I18nKeyConstant.TASK_SAVE_ERROR);
        }
        xpTask.setVehicleLon(new BigDecimal(vehiclePosition.getPoint().getX()));
        xpTask.setVehicleLat(new BigDecimal(vehiclePosition.getPoint().getY()));
        Map<String, String> geocode = gdMapGeocodingUtil.geocode(xpTask.getVehicleLon().toString(), xpTask.getVehicleLat().toString());
        String address = geocode == null ? null : geocode.get("address");
        if (StringUtils.isBlank(address)) {
            log.error("事故上报通过车辆定位从高德获取地址信息失败");
            throw new ServiceException("获取车辆实时定位信息失败,请稍后重试", I18nKeyConstant.TASK_SAVE_ERROR);
        }
        xpTask.setVehicleLocation(address);
// 临时处理
        xpTask.setVehicleLon(new BigDecimal("104.055683"));
        xpTask.setVehicleLat(new BigDecimal("30.585991"));
        xpTask.setVehicleLocation("四川省成都市武侯区石羊街道天府新谷");
//         xpTask.setVehicleLon(new BigDecimal("104.055683"));
//         xpTask.setVehicleLat(new BigDecimal("30.585991"));
//         xpTask.setVehicleLocation("四川省成都市武侯区石羊街道天府新谷");
        xpTask.setVehicleId(dto.getId());
        xpTask.setReportUserId(xpOperationsUser.getId());
        xpTask.setReportTime(LocalDateTime.now());
@@ -323,38 +319,58 @@
                }
                // 构建通知内容
                String content = String.format("车辆%s在%s发生了紧急事故,请在%d分钟内到达现场!", plateNumber, xpTask.getVehicleLocation(), processingTimeLimit);
                String bodyJson = buildAccidentPushBodyJson(content, xpTask);
                // 构建通知记录(包含 notificationExt)
                List<XpOperationsUserNotification> notifications = data.stream().map(item -> {
                    XpOperationsUserNotification notification = new XpOperationsUserNotification();
                    notification.setOperationsUserId(item.getId());
                    notification.setNotificationType(NotificationTypeEnum.ACCIDENT_NEW.getCode());
                    notification.setNotificationContent(content);
                    notification.setDataId(xpTask.getId());
                    notification.setReadStatus(0);
                    notification.setNotificationExt(bodyJson);
                    return notification;
                }).collect(Collectors.toList());
                // 批量保存通知记录
                xpOperationUserNotificationClient.addNotificationBatch(notifications);
                log.info("事故通知记录保存成功,共 {} 条", notifications.size());
                // 按设备类型分组发送阿里云推送
                Map<Integer, List<XpOperationsUser>> operationUserMap = data.stream()
                        .filter(u -> u.getDeviceType() != null && u.getPhoneNumber() != null)
                        .collect(Collectors.groupingBy(XpOperationsUser::getDeviceType));
                for (Map.Entry<Integer, List<XpOperationsUser>> entry : operationUserMap.entrySet()) {
                    pushAliMessage(entry.getValue(), content, xpTask);
                }
                String content = buildAccidentNotificationContent(xpTask, plateNumber, processingTimeLimit);
                saveAndPushAccidentNotification(data, content, xpTask);
            } catch (Exception e) {
                log.error("发送事故通知失败,任务ID={}", xpTask.getId(), e);
            }
        });
        executor.shutdown();
    }
    /**
     * 异步发送事故任务分配通知
     *
     * @param xpTask 任务信息
     * @param handlerUserId 当前分配的处理人
     * @param plateNumber 车牌号
     * @param processingTimeLimit 处理时限(分钟)
     */
    private void sendAssignedAccidentNotificationAsync(XpTask xpTask, Long handlerUserId, String plateNumber, Integer processingTimeLimit) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                1, 1, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>()
        );
        executor.execute(() -> {
            try {
                R<List<XpOperationsUser>> xpOperationUserResult = xpOperationUserClient.listByIds(Collections.singletonList(handlerUserId));
                List<XpOperationsUser> data = xpOperationUserResult == null ? Collections.emptyList() : xpOperationUserResult.getData();
                if (CollUtil.isEmpty(data)) {
                    return;
                }
                String content = buildAccidentNotificationContent(xpTask, plateNumber, processingTimeLimit);
                saveAndPushAccidentNotification(data, content, xpTask);
            } catch (Exception e) {
                log.error("发送事故任务分配通知失败,任务ID={}, handlerUserId={}", xpTask.getId(), handlerUserId, e);
            }
        });
        executor.shutdown();
    }
    /**
     * 构建事故通知文案
     *
     * @param xpTask 任务信息
     * @param plateNumber 车牌号
     * @param processingTimeLimit 处理时限(分钟)
     * @return 通知内容
     */
    private String buildAccidentNotificationContent(XpTask xpTask, String plateNumber, Integer processingTimeLimit) {
        return String.format("车辆%s在%s发生了紧急事故,请在%d分钟内到达现场!", plateNumber, xpTask.getVehicleLocation(), processingTimeLimit);
    }
    /**
@@ -370,6 +386,40 @@
        param.put("type", TaskTypeNotificationEnum.ACCIDENT_TASK.getCode());
        param.put("id", xpTask.getId().toString());
        return JSONObject.toJSONString(param);
    }
    /**
     * 保存事故通知记录并发送阿里云推送
     *
     * @param data 接收通知的运维人员
     * @param content 通知内容
     * @param xpTask 任务信息
     */
    private void saveAndPushAccidentNotification(List<XpOperationsUser> data, String content, XpTask xpTask) {
        String bodyJson = buildAccidentPushBodyJson(content, xpTask);
        List<XpOperationsUserNotification> notifications = data.stream().map(item -> {
            XpOperationsUserNotification notification = new XpOperationsUserNotification();
            notification.setOperationsUserId(item.getId());
            notification.setNotificationType(NotificationTypeEnum.ACCIDENT_NEW.getCode());
            notification.setNotificationContent(content);
            notification.setDataId(xpTask.getId());
            notification.setReadStatus(0);
            notification.setNotificationExt(bodyJson);
            return notification;
        }).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(notifications)) {
            xpOperationUserNotificationClient.addNotificationBatch(notifications);
            log.info("事故通知记录保存成功,共 {} 条", notifications.size());
        }
        Map<Integer, List<XpOperationsUser>> operationUserMap = data.stream()
                .filter(u -> u.getDeviceType() != null && StringUtils.isNotBlank(u.getPhoneNumber()))
                .collect(Collectors.groupingBy(XpOperationsUser::getDeviceType));
        for (Map.Entry<Integer, List<XpOperationsUser>> entry : operationUserMap.entrySet()) {
            pushAliMessage(entry.getValue(), content, xpTask);
        }
    }
    /**
@@ -606,7 +656,7 @@
        res.setTaskTypeId(task.getTaskTypeId());
        res.setStatus(task.getStatus());
        XpChargingRecord chargingRecord = chargingRecordMapper.selectOne(new LambdaQueryWrapper<XpChargingRecord>()
                .eq(XpChargingRecord::getTaskId, id).last("limit 1"));
                .eq(XpChargingRecord::getTaskId, id).last("limit 1").orderByDesc(XpChargingRecord::getCreateTime));
        if (chargingRecord != null) {
            res.setStartRemainingBattery(chargingRecord.getStartRemainingBattery());
@@ -1335,60 +1385,7 @@
                    taskManagementPageListVO.setHandleUserName(handleUserName.toString());
                }
            }
            if (taskManagementPageListVO.getTaskCategory() == 1) {
                switch (taskManagementPageListVO.getTaskTypeId()) {
                    case "1":
                        taskManagementPageListVO.setTaskTypeName("车辆检查");
                        break;
                    case "2":
                        taskManagementPageListVO.setTaskTypeName("车辆充电");
                        break;
                    case "3":
                        taskManagementPageListVO.setTaskTypeName("车辆整备");
                        break;
                    case "4":
                        taskManagementPageListVO.setTaskTypeName("整备洗车");
                        break;
                    case "5":
                        taskManagementPageListVO.setTaskTypeName("车辆维修");
                        break;
                    case "6":
                        taskManagementPageListVO.setTaskTypeName("车辆保养");
                        break;
                    case "7":
                        taskManagementPageListVO.setTaskTypeName("车辆保险到期");
                        break;
                    case "8":
                        taskManagementPageListVO.setTaskTypeName("车辆年检到期");
                        break;
                    case "9":
                        taskManagementPageListVO.setTaskTypeName("车辆运营资质到期");
                        break;
                    default:
                        taskManagementPageListVO.setTaskTypeName("");
                        break;
                }
            } else {
                StringBuilder taskTypeName = new StringBuilder();
                String[] typeIds = taskManagementPageListVO.getTaskTypeId().split(",");
                List<XpErrorType> xpErrorTypes = errorTypeMapper.selectList(new LambdaQueryWrapper<XpErrorType>()
                        .in(XpErrorType::getId, Arrays.asList(typeIds)).orderByAsc(XpErrorType::getParentId));
                List<XpErrorType> parents = new ArrayList<>();
                for (XpErrorType xpErrorType : xpErrorTypes) {
                    if (xpErrorType.getParentId() == 0) {
                        parents.add(xpErrorType);
                    }
                }
                for (XpErrorType parent : parents) {
                    List<XpErrorType> children = xpErrorTypes.stream().filter(e -> e.getParentId().equals(parent.getId())).collect(Collectors.toList());
                    taskTypeName.append(parent.getErrorName()).append(">");
                    for (XpErrorType child : children) {
                        taskTypeName.append(child.getErrorName()).append(";");
                    }
                }
                taskManagementPageListVO.setTaskTypeName(taskTypeName.substring(0, taskTypeName.length() - 1));
            }
            taskManagementPageListVO.setTaskTypeName(resolveManagementTaskTypeName(taskManagementPageListVO));
        }
        pageInfo.setRecords(list);
        return pageInfo;
@@ -1409,62 +1406,174 @@
                    taskManagementPageListVO.setHandleUserName(handleUserName.toString());
                }
            }
            if (taskManagementPageListVO.getTaskCategory() == 1) {
                switch (taskManagementPageListVO.getTaskTypeId()) {
                    case "1":
                        taskManagementPageListVO.setTaskTypeName("车辆检查");
                        break;
                    case "2":
                        taskManagementPageListVO.setTaskTypeName("车辆充电");
                        break;
                    case "3":
                        taskManagementPageListVO.setTaskTypeName("车辆整备");
                        break;
                    case "4":
                        taskManagementPageListVO.setTaskTypeName("整备洗车");
                        break;
                    case "5":
                        taskManagementPageListVO.setTaskTypeName("车辆维修");
                        break;
                    case "6":
                        taskManagementPageListVO.setTaskTypeName("车辆保养");
                        break;
                    case "7":
                        taskManagementPageListVO.setTaskTypeName("车辆保险到期");
                        break;
                    case "8":
                        taskManagementPageListVO.setTaskTypeName("车辆年检到期");
                        break;
                    case "9":
                        taskManagementPageListVO.setTaskTypeName("车辆运营资质到期");
                        break;
                    default:
                        taskManagementPageListVO.setTaskTypeName("");
                        break;
                }
            } else {
                StringBuilder taskTypeName = new StringBuilder();
                String[] typeIds = taskManagementPageListVO.getTaskTypeId().split(",");
                List<XpErrorType> xpErrorTypes = errorTypeMapper.selectList(new LambdaQueryWrapper<XpErrorType>()
                        .in(XpErrorType::getId, Arrays.asList(typeIds)).orderByAsc(XpErrorType::getParentId));
                List<XpErrorType> parents = new ArrayList<>();
                for (XpErrorType xpErrorType : xpErrorTypes) {
                    if (xpErrorType.getParentId() == 0) {
                        parents.add(xpErrorType);
                    }
                }
                for (XpErrorType parent : parents) {
                    List<XpErrorType> children = xpErrorTypes.stream().filter(e -> e.getParentId().equals(parent.getId())).collect(Collectors.toList());
                    taskTypeName.append(parent.getErrorName()).append(">");
                    for (XpErrorType child : children) {
                        taskTypeName.append(child.getErrorName()).append(";");
                    }
                }
                taskManagementPageListVO.setTaskTypeName(taskTypeName.substring(0, taskTypeName.length() - 1));
            }
            taskManagementPageListVO.setTaskTypeName(resolveManagementTaskTypeName(taskManagementPageListVO));
        }
        return list;
    }
    private String resolveManagementTaskTypeName(TaskManagementPageListVO taskManagementPageListVO) {
        if (Integer.valueOf(1).equals(taskManagementPageListVO.getTaskCategory())) {
            return resolveRoutineTaskTypeName(taskManagementPageListVO.getTaskTypeId());
        }
        if (Integer.valueOf(2).equals(taskManagementPageListVO.getTaskCategory())) {
            return resolveAccidentTaskTypeName(taskManagementPageListVO.getTaskTypeId());
        }
        return "";
    }
    private String resolveRoutineTaskTypeName(String taskTypeId) {
        if (StringUtils.isBlank(taskTypeId)) {
            return "";
        }
        switch (taskTypeId) {
            case "1":
                return "车辆检查";
            case "2":
                return "车辆充电";
            case "3":
                return "车辆整备";
            case "4":
                return "整备洗车";
            case "5":
                return "车辆维修";
            case "6":
                return "车辆保养";
            case "7":
                return "车辆保险到期";
            case "8":
                return "车辆年检到期";
            case "9":
                return "车辆运营资质到期";
            default:
                return "";
        }
    }
    private String resolveAccidentTaskTypeName(String taskTypeId) {
        List<Long> orderedTypeIds = parseAccidentTaskTypeIds(taskTypeId);
        if (orderedTypeIds.isEmpty()) {
            return "";
        }
        Map<Long, XpErrorType> errorTypeMap = getAccidentErrorTypeMap(orderedTypeIds);
        if (errorTypeMap.isEmpty()) {
            return "";
        }
        LinkedHashSet<Long> orderedParentIds = new LinkedHashSet<>();
        LinkedHashMap<Long, List<XpErrorType>> childrenByParent = new LinkedHashMap<>();
        for (Long typeId : orderedTypeIds) {
            XpErrorType errorType = errorTypeMap.get(typeId);
            if (errorType == null || StringUtils.isBlank(errorType.getErrorName())) {
                continue;
            }
            if (isTopLevelErrorType(errorType)) {
                orderedParentIds.add(errorType.getId());
                continue;
            }
            Long parentId = errorType.getParentId();
            if (parentId == null) {
                continue;
            }
            orderedParentIds.add(parentId);
            childrenByParent.computeIfAbsent(parentId, key -> new ArrayList<>()).add(errorType);
        }
        List<String> displayNames = new ArrayList<>();
        for (Long parentId : orderedParentIds) {
            XpErrorType parent = errorTypeMap.get(parentId);
            List<XpErrorType> children = childrenByParent.get(parentId);
            if (CollectionUtils.isEmpty(children)) {
                if (parent != null && StringUtils.isNotBlank(parent.getErrorName())) {
                    displayNames.add(parent.getErrorName());
                }
                continue;
            }
            String parentName = parent == null ? null : parent.getErrorName();
            for (XpErrorType child : children) {
                if (StringUtils.isBlank(parentName)) {
                    displayNames.add(child.getErrorName());
                } else {
                    displayNames.add(parentName + ">" + child.getErrorName());
                }
            }
        }
        return String.join(";", displayNames);
    }
    private Map<Long, XpErrorType> getAccidentErrorTypeMap(List<Long> orderedTypeIds) {
        Map<Long, XpErrorType> errorTypeMap = getErrorTypeMap(orderedTypeIds);
        if (errorTypeMap.isEmpty()) {
            return errorTypeMap;
        }
        Set<Long> missingParentIds = new LinkedHashSet<>();
        for (Long typeId : orderedTypeIds) {
            XpErrorType errorType = errorTypeMap.get(typeId);
            if (errorType == null || isTopLevelErrorType(errorType)) {
                continue;
            }
            if (errorType.getParentId() != null && !errorTypeMap.containsKey(errorType.getParentId())) {
                missingParentIds.add(errorType.getParentId());
            }
        }
        if (!missingParentIds.isEmpty()) {
            errorTypeMap.putAll(getErrorTypeMap(missingParentIds));
        }
        return errorTypeMap;
    }
    private boolean isTopLevelErrorType(XpErrorType errorType) {
        return errorType.getParentId() == null || errorType.getParentId() == 0L;
    }
    private String normalizeAccidentTaskTypeId(String taskTypeId) {
        List<Long> orderedTypeIds = parseAccidentTaskTypeIds(taskTypeId);
        if (orderedTypeIds.isEmpty()) {
            return taskTypeId;
        }
        LinkedHashSet<Long> normalizedTypeIds = new LinkedHashSet<>(orderedTypeIds);
        Map<Long, XpErrorType> errorTypeMap = getErrorTypeMap(orderedTypeIds);
        for (Long typeId : orderedTypeIds) {
            XpErrorType errorType = errorTypeMap.get(typeId);
            if (errorType == null || errorType.getParentId() == null || errorType.getParentId() == 0L) {
                continue;
            }
            normalizedTypeIds.add(errorType.getParentId());
        }
        return normalizedTypeIds.stream().map(String::valueOf).collect(Collectors.joining(","));
    }
    private List<Long> parseAccidentTaskTypeIds(String taskTypeId) {
        if (StringUtils.isBlank(taskTypeId)) {
            return Collections.emptyList();
        }
        LinkedHashSet<Long> typeIds = new LinkedHashSet<>();
        for (String typeId : taskTypeId.split(",")) {
            if (StringUtils.isBlank(typeId)) {
                continue;
            }
            try {
                typeIds.add(Long.parseLong(typeId.trim()));
            } catch (NumberFormatException ignored) {
            }
        }
        return new ArrayList<>(typeIds);
    }
    private Map<Long, XpErrorType> getErrorTypeMap(Collection<Long> typeIds) {
        if (CollectionUtils.isEmpty(typeIds)) {
            return Collections.emptyMap();
        }
        return errorTypeMapper.selectBatchIds(typeIds).stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(XpErrorType::getId, errorType -> errorType, (left, right) -> left));
    }
    @Override
@@ -1523,16 +1632,41 @@
    @Override
    public R assigneeTask(Long id, Long handlerUserId, Long assigneeUserId) {
        XpTask task = this.getById(id);
        // 查询规则 任务结束时间从任务分配时开始计算
        XpTaskRule taskRule = xpTaskRuleMapper.selectOne(null);
        if (taskRule != null) {
            task.setEndTime(LocalDateTime.now().plusMinutes(taskRule.getProcessingTimeLimit()));
        if (task == null) {
            throw new ServiceException(
                    "任务不存在",
                    I18nKeyConstant.TASK_NOT_EXIST
            );
        }
        boolean isAccidentTask = Integer.valueOf(2).equals(task.getTaskCategory());
        Long vehicleId = task.getVehicleId();
        Integer processingTimeLimit = null;
        String plateNumber = "";
        if (isAccidentTask) {
            // 查询规则 任务结束时间从任务分配时开始计算
            XpTaskRule taskRule = xpTaskRuleMapper.selectOne(null);
            if (taskRule != null) {
                processingTimeLimit = taskRule.getProcessingTimeLimit();
                task.setEndTime(LocalDateTime.now().plusMinutes(taskRule.getProcessingTimeLimit()));
            }
            if (vehicleId != null) {
                XpVehicle vehicle = xpVehicleClient.queryById(vehicleId).getData();
                if (vehicle != null && StringUtils.isNotBlank(vehicle.getPlateNumber())) {
                    plateNumber = vehicle.getPlateNumber();
                }
            }
        } else {
            task.setEndTime(LocalDateTime.now().plusDays(3));
        }
        task.setAssigneeTime(LocalDateTime.now());
        task.setHandleUserId(handlerUserId.toString());
        task.setVehicleId(null);
        task.setAssigneeUserId(assigneeUserId);
        this.updateById(task);
        if (isAccidentTask && processingTimeLimit != null) {
            sendAssignedAccidentNotificationAsync(task, handlerUserId, plateNumber, processingTimeLimit);
        }
        return R.ok();
    }
@@ -1540,6 +1674,7 @@
    public void saveTask(SaveErrorTaskDto dto, Long reportUserId) {
        List<XpTask> saveTask = new ArrayList<>();
        String normalizedTaskTypeId = normalizeAccidentTaskTypeId(dto.getTaskTypeId());
        for (Long vehicleId : dto.getVehicleIds()) {
            XpTask task = new XpTask();
            BeanUtils.copyProperties(dto, task);
@@ -1549,7 +1684,7 @@
            String code = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);
            task.setTaskCode(code);
            task.setTaskCategory(2);
            task.setTaskTypeId(dto.getTaskTypeId());
            task.setTaskTypeId(normalizedTaskTypeId);
            task.setTaskUrgencyLevel(dto.getTaskUrgencyLevel());
            task.setTaskDescription(dto.getTaskDescription());
            task.setTaskImage(dto.getTaskImage());
@@ -1585,19 +1720,7 @@
                    I18nKeyConstant.VEHICLE_NOT_EXIST
            );
        }
        String vehiclePicture = vehicle.getVehiclePictures();
        if (vehicle.getVehiclePictures() != null) {
            JSONObject jsonObject = JSONObject.parseObject(vehicle.getVehiclePictures());
            // 1
            String downloadUrl = jsonObject.getString("url");
            try {
                downloadUrl = aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true);
            } catch (UnsupportedEncodingException e) {
                log.error("获取图片下载地址失败", e);
            }
            jsonObject.put("url", downloadUrl);
            vehiclePicture = jsonObject.toJSONString();
        }
        String vehiclePicture = aliOssDownloadUtil.getPictureJSONObject(vehicle.getVehiclePictures());
        if (task.getTaskCategory() == 1) {
            // 常规任务
@@ -1613,19 +1736,7 @@
                    taskManagementDetectionDetailVO.setVehicleCode(vehicle.getVehicleCode());
                    taskManagementDetectionDetailVO.setVehicleVin(vehicle.getVehicleVin());
                    taskManagementDetectionDetailVO.setVehicleLocation(task.getVehicleLocation());
                    if (vehicle.getVehiclePictures() != null) {
                        JSONObject jsonObject = JSONObject.parseObject(vehicle.getVehiclePictures());
                        // 1
                        String downloadUrl = jsonObject.getString("url");
                        try {
                            downloadUrl = aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true);
                        } catch (UnsupportedEncodingException e) {
                            log.error("获取图片下载地址失败", e);
                        }
                        jsonObject.put("url", downloadUrl);
                        taskManagementDetectionDetailVO.setVehiclePictures(jsonObject.toJSONString());
                    }
                    taskManagementDetectionDetailVO.setVehiclePictures(vehiclePicture);
                    taskManagementDetectionDetailVO.setTaskCode(task.getTaskCode());
                    XpVehicleDetection vehicleDetection = vehicleDetectionMapper.selectOne(new LambdaQueryWrapper<XpVehicleDetection>()
xp-robotaxi-maintenance-boot/src/main/resources/i18n/messages.properties
@@ -2,4 +2,6 @@
welcome.message=欢迎来到我们的应用程序!
task.not.exist=任务不存在
vehicle.not.exist=车辆不存在
repair.money.too.long=维修费用不能超过10字符
repair.money.too.long=维修费用不能超过10字符
task.save.error=获取车辆实时定位信息失败,请稍后重试
task.vehicle.not.exist=上报失败,未获取到车辆信息
xp-robotaxi-maintenance-boot/src/main/resources/i18n/messages_en_US.properties
@@ -3,4 +3,5 @@
task.not.exist=The task does not exist.
vehicle.not.exist=The vehicle does not exist.
repair.money.too.long=The maintenance cost cannot exceed 10 characters
task.save.error=Failed to obtain the real-time positioning information of the vehicle. Please try again later
task.rule.not.config=The vehicle accident task allocation rules have not been configured
xp-robotaxi-maintenance-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -3,4 +3,6 @@
task.not.exist=任务不存在
vehicle.not.exist=车辆不存在
repair.money.too.long=维修费用不能超过10字符
task.save.error=获取车辆实时定位信息失败,请稍后重试
task.vehicle.not.exist=上报失败,未获取到车辆信息
task.rule.not.config=未配置车辆事故任务分配规则
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java
@@ -453,15 +453,11 @@
            throw new ServiceException("当前点位未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
        }
        // 判断当天取消的次数 后台配置 redis 查询
        Object cancelOrder = redisService.getCacheObject("CANCEL_ORDER_" + uid);
        // 静默处理
        boolean isSilent = false;
        if (cancelOrder != null) {
            Integer num = Integer.valueOf(cancelOrder.toString());
            if (num >= cancelCount) {
                isSilent = true;
            }
        Integer num = xpOrderClient.todayCancellationCount(Long.valueOf(uid)).getData();
        if (num >= cancelCount) {
            isSilent = true;
        }
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java
@@ -307,7 +307,6 @@
            // 找出符合条件的
            availableVehiclesNearby = availableVehiclesNearby.stream().filter(e -> data.contains(e.getVin())).collect(Collectors.toList());
//            List<VehiclePositionVo> availableVehiclesNearby =vehiclePositionVos.getData();
            if (!availableVehiclesNearby.isEmpty()) {
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpCruiseOrderController.java
@@ -170,6 +170,7 @@
        // 2. 转换为导出对象
        List<XpCruiseOrderExportExcel> exportList = BeanUtil.copyToList(dataList, XpCruiseOrderExportExcel.class);
        exportList.forEach(item -> item.setOrderSource("系统自动创建"));
        // 3. 生成 Excel
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), XpCruiseOrderExportExcel.class, exportList);
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java
@@ -6,6 +6,7 @@
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xiaopeng.api.dto.QueryOrderRouteDTO;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.dto.order.AddCruiseMissionDto;
@@ -584,4 +585,19 @@
        return R.ok(routeVo);
    }
    /**
     * 当日取消订单次数
     *
     * @param
     * @return 订单数量
     */
    @PostMapping("/inner/order/todayCancellationCount")
    public R<Integer> todayCancellationCount(@RequestParam Long userId){
        long count = xpOrderService.count(new QueryWrapper<XpOrder>().eq("app_user_id", userId).eq("status", 0)
                .eq("is_deleted", 0).last(" and DATE_FORMAT(create_time, '%Y-%m-%d') = DATE_FORMAT(NOW(), '%Y-%m-%d')"));
        return R.ok((int)count);
    }
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpTestOrderController.java
@@ -75,8 +75,7 @@
public class XpTestOrderController {
    private final IXpTestOrderService xpTestOrderService;
    @Autowired
    private XpVehicleClient xpVehicleClient;
    private final XpVehicleClient xpVehicleClient;
    /**
@@ -107,6 +106,18 @@
    }
    /**
     * 根据订单ID查询取消订单
     *
     * @param orderId
     * @return
     */
    @PostMapping("/inner/testOrder/cancelTestOrderById")
    public R<?> cancelTestOrderById(@RequestParam Long orderId) {
        return xpTestOrderService.cancelTestOrderById(orderId);
    }
    /**
     * 生成订单
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/exportExcel/XpCruiseOrderExportExcel.java
@@ -22,6 +22,9 @@
    @Excel(name = "下单时间", width = 20, exportFormat = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @Excel(name = "订单来源", width = 25)
    private String orderSource;
    @Excel(name = "起点", width = 30)
    private String startPointName;
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/IXpTestOrderService.java
@@ -24,6 +24,7 @@
import com.xiaopeng.order.model.vo.*;
import com.xiaopeng.order.vo.OrderDetailVo;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.math.BigDecimal;
import java.util.List;
@@ -121,4 +122,12 @@
     * @param amount
     */
    void changeAmount(Long id, BigDecimal amount);
    /**
     * 取消订单
     * @param orderId
     * @return
     */
    R<?> cancelTestOrderById(Long orderId);
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -941,23 +941,6 @@
        }
        XpVehicle newVehicle = vehicleResult.getData().get(0);
        // 5. 校验新车辆的运营状态
        // 车辆状态:1=待完善 2=待运营 3=保险/年检过期
        // 运营状态:1=运营中 2=维修中 3=已收车 4=车辆异常
        // 当前模式:1=接单 2=测试
        // 模式状态:1=开始接单/开始测试 2=停止接单/停止测试
        if (Objects.isNull(newVehicle.getStatus()) || newVehicle.getStatus() == 3) {
            throw new ServiceException("新车辆保险或年检已过期,无法接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_STATUS_INVALID);
        }
        if (Objects.isNull(newVehicle.getOperatingStatus()) || newVehicle.getOperatingStatus() != 1) {
            throw new ServiceException("新车辆运营状态异常,无法接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_OPERATING_STATUS_INVALID);
        }
        if (Objects.isNull(newVehicle.getAcceptOrTest()) || newVehicle.getAcceptOrTest() != 1) {
            throw new ServiceException("新车辆当前不在接单模式", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_IN_ACCEPT_MODE);
        }
        if (Objects.isNull(newVehicle.getAcceptOrTestStatus()) || newVehicle.getAcceptOrTestStatus() != 1) {
            throw new ServiceException("新车辆未开始接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_START_ACCEPT);
        }
        if (Objects.isNull(newVehicle.getState()) || newVehicle.getState() != 0) {
            throw new ServiceException("新车辆已接单,请重新选择", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_FREE);
        }
@@ -1417,15 +1400,6 @@
        xpOrderCancelService.save(xpOrderCancel);
        // 设置当天的取消次数缓存
        Object cacheObject = redisService.getCacheObject("CANCEL_ORDER_" + uid);
        if (cacheObject == null) {
            redisService.setCacheCancelOrder("CANCEL_ORDER_" + uid, 1);
        } else {
            Integer i = Integer.valueOf(cacheObject.toString());
            redisService.setCacheCancelOrder("CANCEL_ORDER_" + uid, i + 1);
        }
        // 移除mongo数据
        xpPricingClient.deleteXpGeoQueryEntity(order.getId().toString());
@@ -1531,14 +1505,6 @@
        // 移除mongo数据
        xpPricingClient.deleteXpGeoQueryEntity(order.getId().toString());
        // 设置当天的取消次数缓存
        Object cacheObject = redisService.getCacheObject("CANCEL_ORDER_" + appUserId);
        if (cacheObject == null) {
            redisService.setCacheCancelOrder("CANCEL_ORDER_" + appUserId, 1);
        } else {
            Integer i = Integer.valueOf(cacheObject.toString());
            redisService.setCacheCancelOrder("CANCEL_ORDER_" + appUserId, i + 1);
        }
        return order;
    }
@@ -2666,7 +2632,7 @@
    public void saveVehicleLocation(OrderTrajectory orderTrajectory) {
        // 查询订单信息
        Integer status = redisService.getCacheObject("ORDER_STATUS:" + orderTrajectory.getOrderId());
        if(status < 6){
        if(status < 6 || status > 8){
            return;
        }
@@ -3008,7 +2974,7 @@
                xpVehicleClient.updateVehicleById(vehicle1);
                log.info("推单成功后的处理逻辑,车辆状态修改1 {}", vehicle.getPlateNumber());
                xpTestOrder.setVehicleId(null);
                xpTestOrder.setStartTestPointId(null);
                xpTestOrderService.updateById(xpTestOrder);
                redisService.setCacheObject("ORDER_STATUS:" + xpTestOrder.getId(), 2, 24L, TimeUnit.HOURS);
            }
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpTestOrderServiceImpl.java
@@ -2,7 +2,6 @@
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
@@ -11,20 +10,11 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableMap;
import com.xiaopeng.api.dto.account.GetUserCouponDto;
import com.xiaopeng.api.dto.account.UcUserInfo;
import com.xiaopeng.api.dto.account.UseUserCouponDto;
import com.xiaopeng.api.dto.automaticPilot.PushOrderDto;
import com.xiaopeng.api.dto.map.RouteAndFareDto;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.dto.order.GetOrderNumDto;
import com.xiaopeng.api.dto.order.HistoryEndPointsDto;
import com.xiaopeng.api.dto.order.HistoryStartPointsDto;
import com.xiaopeng.api.dto.order.PopOrderDto;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.dto.statistics.DailyCountDTO;
import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto;
import com.xiaopeng.api.feignClient.account.UserResourcesClient;
import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient;
import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient;
@@ -36,7 +26,7 @@
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.maintenance.XpTaskRule;
import com.xiaopeng.api.model.map.XpTestPoint;
import com.xiaopeng.api.model.operation.*;
import com.xiaopeng.api.model.order.*;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
@@ -45,18 +35,14 @@
import com.xiaopeng.api.vo.SystemEventType;
import com.xiaopeng.api.vo.account.UserCouponVo;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.api.vo.operation.FareVo;
import com.xiaopeng.api.vo.operation.RouteAndFareVo;
import com.xiaopeng.api.vo.automaticPilot.protobuf.OrderOperationType;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.api.vo.order.*;
import com.xiaopeng.api.vo.vehicleConvoy.AddTaxiPassengerVo;
import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo;
import com.xiaopeng.api.vo.order.FareBreakdownDetailVo;
import com.xiaopeng.api.vo.order.XpPricingCardVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.constant.Constants;
import com.xiaopeng.common.core.constant.HttpStatus;
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;
@@ -66,36 +52,32 @@
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.common.security.utils.SecurityUtils;
import com.xiaopeng.order.constant.I18nKeyConstant;
import com.xiaopeng.order.dto.MileageDto;
import com.xiaopeng.order.job.base.QuartzManager;
import com.xiaopeng.order.job.base.TimeJobType;
import com.xiaopeng.order.job.jobs.AddCruiseMissionJob;
import com.xiaopeng.order.job.jobs.EndTripJob;
import com.xiaopeng.order.job.jobs.OrderCancelJob;
import com.xiaopeng.order.mangodb.model.OrderTrajectory;
import com.xiaopeng.order.mangodb.service.OrderTrajectoryService;
import com.xiaopeng.order.mapper.*;
import com.xiaopeng.order.model.dto.XpOrderCommentDto;
import com.xiaopeng.order.model.dto.XpOrderPayDto;
import com.xiaopeng.order.model.query.XpHistoryOrderListQuery;
import com.xiaopeng.order.model.query.XpOrderExcelQuery;
import com.xiaopeng.order.model.query.XpOrderPageQuery;
import com.xiaopeng.order.mapper.XpCruiseOrderMapper;
import com.xiaopeng.order.mapper.XpTestOrderDetailMapper;
import com.xiaopeng.order.mapper.XpTestOrderMapper;
import com.xiaopeng.order.model.query.XpTestOrderPageQuery;
import com.xiaopeng.order.model.vo.*;
import com.xiaopeng.order.service.*;
import com.xiaopeng.order.model.vo.AddTestOrderVo;
import com.xiaopeng.order.model.vo.XpTestOrderDetailVO;
import com.xiaopeng.order.model.vo.XpTestOrderPageVO;
import com.xiaopeng.order.service.IXpOrderEvaluateService;
import com.xiaopeng.order.service.IXpOrderReassignService;
import com.xiaopeng.order.service.IXpTestOrderCancelService;
import com.xiaopeng.order.service.IXpTestOrderService;
import com.xiaopeng.order.utils.GDMapGeocodingUtil;
import com.xiaopeng.order.utils.RegionCodeFilterUtil;
import com.xiaopeng.order.utils.TaxiPricingDemoService;
import com.xiaopeng.order.utils.XpPricingUtil;
import com.xiaopeng.order.utils.holiday.HolidayUtil;
import com.xiaopeng.order.vo.OrderDetailVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -106,13 +88,12 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -289,18 +270,10 @@
            pushOrderDto.setStartLongitude(xpOrder.getStartLon());
            pushOrderDto.setEndLatitude(xpOrder.getEndLat());
            pushOrderDto.setEndLongitude(xpOrder.getEndLon());
            pushOrderDto.setOrderType(OrderType.USER_ORDER);
            pushOrderDto.setOrderType(xpOrder.getOrderType() == 1 ? OrderType.TEST_ORDER : OrderType.TEST_CRUISE_ORDER);
            pushOrderDto.setStage(1);
            // 调用推送服务  orderId + carVin
            orderMissionClient.pushOrder(pushOrderDto);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("event", SystemEventType.CREATE_ORDER);
            jsonObject.put("orderType", OrderType.TEST_ORDER);
            jsonObject.put("orderId", xpOrder.getId());
            jsonObject.put("vin", xpVehicle.getVehicleVin());
            redisService.setCacheObject("CREATE_ORDER:" + pushOrderDto.getOrderId(), jsonObject.toJSONString(),30L, TimeUnit.MINUTES);
            log.info("开始推送 {} 订单到XPU。", xpOrder.getOrderNumber());
        }
    }
@@ -654,14 +627,11 @@
            // 下发命令
            PushOrderDto pushOrderDto = new PushOrderDto();
            pushOrderDto.setOrderId(order.getId().toString());
            pushOrderDto.setOrderId("TEST" + order.getId().toString());
            pushOrderDto.setVin(xpVehicle.getVehicleVin());
            pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
            orderMissionClient.cancelOrder(pushOrderDto);
            log.info("运营人员取消订单成功,开始执行巡游订单 {}", order.getId());
            //车辆继续巡游
            addCruiseMission(xpVehicle.getId());
        } else {
            log.info("查询车辆信息失败,订单ID: {}, 下发取消订单命令失败", order.getId());
        }
@@ -692,34 +662,19 @@
        order.setMileageKilometers(new BigDecimal(reduce).divide(new BigDecimal(1000)).setScale(4, RoundingMode.HALF_EVEN).doubleValue());
        long duration = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) - order.getTripBegin().toEpochSecond(ZoneOffset.of("+8"));
        order.setDuration(new BigDecimal(duration).divide(new BigDecimal(60), 2, RoundingMode.HALF_EVEN).doubleValue());
        order.setVehicleId(null);
        order.setStartTestPointId(null);
        this.updateById(order);
        redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 7, 24L, TimeUnit.HOURS);
        //给XPU推送订单取消的报文
        PushOrderDto pushOrderDto = new PushOrderDto();
        pushOrderDto.setOrderId(order.getId().toString());
        pushOrderDto.setOrderId("TEST" + order.getId().toString());
        pushOrderDto.setVin(xpVehicle.getVehicleVin());
        pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
        orderMissionClient.cancelOrder(pushOrderDto);
        //结束行程,修改状态和计算费用
        endTrip(order.getId());
        //创建定时任务执行延迟驶离终点和下发巡游单
        XpTestPoint point = xpTestPointClient.queryById(order.getEndTestPointId()).getData();
        Map<String, ? extends Object> jobMaps =
                new ImmutableMap.Builder<String, String>()
                        .put("id", order.getId().toString())
                        .put("appid", appid)
                        .build();
        QuartzManager.addJob(
                AddCruiseMissionJob.class,
                (AddCruiseMissionJob.name + order.getId()).toUpperCase(),
                TimeJobType.ADD_CRUISE_MISSION,
                DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(point.getParkingTimeLimit())),
                jobMaps
        );
    }
    /**
@@ -787,7 +742,7 @@
                    // 6.1.1 取消原订单指令
                    PushOrderDto cancelDto = new PushOrderDto();
                    cancelDto.setOrderId(order.getId().toString());
                    cancelDto.setOrderId("TEST" + order.getId().toString());
                    cancelDto.setVin(oldVehicle.getVehicleVin());
                    cancelDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
                    orderMissionClient.cancelOrder(cancelDto);
@@ -843,6 +798,7 @@
        if (Objects.nonNull(newFleetId)) {
            order.setFleetId(newFleetId);
        }
        order.setStartTestPointId(null);
        this.updateById(order);
        // 10. 保存改派记录
@@ -868,7 +824,7 @@
            // 11.2 下发订单到新车辆
            PushOrderDto pushOrderDto = new PushOrderDto();
            pushOrderDto.setVin(newVehicle.getVehicleVin());
            pushOrderDto.setOrderId(order.getId().toString());
            pushOrderDto.setOrderId("TEST" + order.getId());
            pushOrderDto.setStartLatitude(order.getStartLat());
            pushOrderDto.setStartLongitude(order.getStartLon());
            pushOrderDto.setEndLatitude(order.getEndLat());
@@ -968,48 +924,17 @@
                    xpOrder.setMileageKilometers(new BigDecimal(reduce).divide(new BigDecimal(1000)).setScale(4, RoundingMode.HALF_EVEN).doubleValue());
                    long duration = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) - xpOrder.getTripBegin().toEpochSecond(ZoneOffset.of("+8"));
                    xpOrder.setDuration(new BigDecimal(duration).divide(new BigDecimal(60), 2, RoundingMode.HALF_EVEN).doubleValue());
                    //创建定时结束订单和等待结束后下发巡游单的定时任务
                    Integer arriveTime = xpSystemSettingClient.getOrderFinishTime().getData();
                    // 添加自动结束行程定时任务job
                    Map<String, ? extends Object> endTripJobMaps =
                            new ImmutableMap.Builder<String, String>()
                                    .put("id", xpOrder.getId().toString())
                                    .put("appid", appid)
                                    .build();
                    QuartzManager.addJob(
                            EndTripJob.class,
                            (EndTripJob.name + xpOrder.getId()).toUpperCase(),
                            TimeJobType.END_TRIP,
                            DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(arriveTime)),
                            endTripJobMaps
                    );
                    // 添加车辆到达终点后限停时间到后驶离的定时任务
                    //时间到后将车辆状态回滚到空闲、添加巡游订单,解绑用户和车辆关系,车辆驶离点位
                    XpTestPoint point = xpTestPointClient.queryById(xpOrder.getEndTestPointId()).getData();
                    Map<String, ? extends Object> jobMaps =
                            new ImmutableMap.Builder<String, String>()
                                    .put("id", xpOrder.getId().toString())
                                    .put("appid", appid)
                                    .build();
                    QuartzManager.addJob(
                            AddCruiseMissionJob.class,
                            (AddCruiseMissionJob.name + xpOrder.getId()).toUpperCase(),
                            TimeJobType.ADD_CRUISE_MISSION,
                            DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(point.getParkingTimeLimit())),
                            jobMaps
                    );
                    break;
            }
            xpOrder.setStatus(status);
            xpOrder.setVehicleId(null);
            xpOrder.setStartTestPointId(null);
            this.updateById(xpOrder);
            redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), status, 24L, TimeUnit.HOURS);
        }else{
            if(status == 10){
                xpOrder.setStatus(status);
                xpOrder.setVehicleId(null);
                xpOrder.setStartTestPointId(null);
                this.updateById(xpOrder);
                redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), status, 24L, TimeUnit.HOURS);
            }
@@ -1037,7 +962,7 @@
            throw new RuntimeException("订单状态错误");
        }
        order.setStatus(8);
        order.setVehicleId(null);
        order.setStartTestPointId(null);
        order.setTripEnd(LocalDateTime.now());
        baseMapper.updateById(order);
        redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 8, 24L, TimeUnit.HOURS);
@@ -1079,7 +1004,7 @@
            JSONObject basicPricingJson = JSONObject.parseObject(pricing.getBasicPricingJson());
            XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationFixedPrice(uid, basicPricingJson, null, mileageKilometers, duration1, 0, 0.0, pricing.getIsDynamicPrice(), order.getCityId(), pricing.getDynamicPricingJson());
            XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationFixedPrice(order.getVehicleId().toString(), basicPricingJson, null, mileageKilometers, duration1, 0, 0.0, pricing.getIsDynamicPrice(), order.getCityId(), pricing.getDynamicPricingJson());
            XpTestOrderDetail xpOrderDetail = new XpTestOrderDetail();
            xpOrderDetail.setDetailType(1);
            xpOrderDetail.setOrderId(orderId);
@@ -1109,7 +1034,7 @@
                xpTestOrderDetailMapper.insert(xpOrderDetail1);
            }
            order.setOrderMoney(totalPrice);
            order.setVehicleId(null);
            order.setStartTestPointId(null);
            baseMapper.updateById(order);
        } else {
            //动态计费
@@ -1174,7 +1099,7 @@
            double priceRate = 1;
            if (pricing.getIsDynamicPrice() == 1) {
                // 先判断是否已经调价过了
                Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid);
                Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + order.getVehicleId());
                if (userDynamicPriceObj != null) {
                    // 是否调价过了
                    priceRate = Double.parseDouble(userDynamicPriceObj.toString());
@@ -1204,7 +1129,7 @@
                            // 冷却时间
                            Integer time = getOrderNum(jsonObject, 6);
                            // 触发调价了
                            redisService.setCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid, priceRate, time.longValue(), TimeUnit.MINUTES);
                            redisService.setCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + order.getVehicleId(), priceRate, time.longValue(), TimeUnit.MINUTES);
                        }
                    }
@@ -1367,41 +1292,13 @@
                    .add(order.getLongDistancemoney() != null ? order.getLongDistancemoney() : BigDecimal.ZERO)
                    .add(order.getWaitMoney() != null ? order.getWaitMoney() : BigDecimal.ZERO);
            order.setOrderMoney(orderMoney);
            order.setVehicleId(null);
            order.setStartTestPointId(null);
            baseMapper.updateById(order);
        }
        // 计算完价格后 挑选优惠券
        GetUserCouponDto getUserCouponDto = new GetUserCouponDto();
        getUserCouponDto.setUserId(Long.valueOf(uid));
        getUserCouponDto.setTotalAmount(order.getOrderMoney().doubleValue());
        getUserCouponDto.setUserCouponId(order.getCouponId());
        getUserCouponDto.setOpenCityId(order.getCityId());
        R<UserCouponVo> userCouponVoR = xpAppUserCouponClient.getUserCoupon(getUserCouponDto);
        UserCouponVo userCouponVo = userCouponVoR.getData();
        // 提示是否换券
        boolean tip =false;
        if (userCouponVo != null) {
            // 重新选券
            if(userCouponVo.getStatus()==2){
                getUserCouponDto.setUserId(Long.valueOf(uid));
                getUserCouponDto.setTotalAmount(order.getOrderMoney().doubleValue());
                getUserCouponDto.setUserCouponId(order.getCouponId());
                getUserCouponDto.setOpenCityId(order.getCityId());
                userCouponVoR = xpAppUserCouponClient.getUserCoupon(getUserCouponDto);
                userCouponVo = userCouponVoR.getData();
                tip=true;
            }
            order.setCouponId(userCouponVo.getId());
            order.setCouponMoney(BigDecimal.valueOf(userCouponVo.getMoney()));
        }
        order.setVehicleId(null);
        order.setStartTestPointId(null);
        baseMapper.updateById(order);
        return tip;
        return true;
    }
@@ -1437,7 +1334,7 @@
    public void saveVehicleLocation(OrderTrajectory orderTrajectory) {
        // 查询订单信息
        Integer status = redisService.getCacheObject("ORDER_STATUS:" + orderTrajectory.getOrderId());
        if(status < 6){
        if(status < 6 || status > 8){
            return;
        }
@@ -1545,66 +1442,6 @@
    /**
     * 发送乘客导航路径数据
     *
     * @param appUserId
     * @param keyStr
     */
    public void sendRoute(String appUserId, Long orderId, String keyStr, String vin) {
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
        Object cacheObject = redisService.getCacheObject(keyStr + ":" + appUserId);
        if (null != cacheObject) {
            RouteVo routeVo = JSON.parseObject(cacheObject.toString(), RouteVo.class);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("type", "ORDER_ROUTE");
            jsonObject.put("timestamp", System.currentTimeMillis());
            JSONObject content = new JSONObject();
            content.put("orderId", orderId.toString());
            content.put("routeType", "ORDER_NAVIGATION_START_DATA".equals(keyStr) ? 1 : 2);
            content.put("direction", null != vehiclePosition ? vehiclePosition.getCourse() : 90);
            JSONObject route = new JSONObject();
            route.put("distance", routeVo.getDistance());
            route.put("duration", routeVo.getDuration());
            route.put("routeID", routeVo.getRouteID());
            List<JSONObject> collect = routeVo.getPolyline().stream().map(item -> {
                JSONObject point = new JSONObject();
                point.put("latitude", item.getLatitude());
                point.put("longitude", item.getLongitude());
                return point;
            }).collect(Collectors.toList());
            route.put("polyline", JSON.toJSONString(collect));
            content.put("route", route.toJSONString());
            jsonObject.put("content", content.toJSONString());
            SendMessageDTO messageDTO = new SendMessageDTO();
            messageDTO.setAppUserId(appUserId);
            messageDTO.setMessage(jsonObject.toJSONString());
            R<String> stringR = xpPushMessageClient.sendRoute(messageDTO);
            XpTestOrder xpOrder = this.getById(orderId);
            //推送失败,重试3次
            if(stringR.getCode() != Constants.SUCCESS){
                ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
                executor.execute(() -> {
                    for (int i = 0; i < 3; i++) {
                        R<String> stringR1 = xpPushMessageClient.sendStatus(messageDTO);
                        if(stringR1.getCode() == Constants.SUCCESS){
                            break;
                        }
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        log.info("推送乘客端导航数据消息失败:{} {} {}", stringR1.getMsg(), JSON.toJSONString(messageDTO), xpOrder.getOrderNumber());
                    }
                });
            }
        }
    }
    @Override
    public void changeAmount(Long id, BigDecimal amount) {
@@ -1643,4 +1480,26 @@
            throw new ServiceException("订单改价失败,请稍后重试", I18nKeyConstant.ORDER_CHANGE_AMOUNT_FAILED);
        }
    }
    @Override
    public R<?> cancelTestOrderById(Long orderId) {
        XpTestOrder xpTestOrder = this.getById(orderId);
        if (xpTestOrder != null) {
            xpTestOrder.setStatus(0);
            xpTestOrder.setStartTestPointId(null);
            this.updateById(xpTestOrder);
            XpVehicle xpVehicle = xpVehicleClient.queryById(xpTestOrder.getVehicleId()).getData();
            PushOrderDto pushOrderDto = new PushOrderDto();
            pushOrderDto.setVin(xpVehicle.getVehicleVin());
            pushOrderDto.setOrderId("TEST" + xpTestOrder.getId());
            pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
            R<Boolean> booleanR = orderMissionClient.cancelOrder(pushOrderDto);
            if(booleanR==null || !booleanR.getData()){
                log.error("XPU取消测试巡游订单失败");
                return R.fail("XPU取消测试巡游订单失败");
            }
            return R.ok();
        }
        return R.fail("获取订单失败");
    }
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -763,7 +763,8 @@
     */
    @PostMapping("/inner/vehicle/queryVehiclePositionByVin")
    public R<List<String>> queryVehiclePositionByVin(@RequestBody VehiclePositionByVinDto dto){
        List<XpVehicle> list = xpVehicleService.list(new LambdaQueryWrapper<XpVehicle>().eq(XpVehicle::getState, 0).eq(XpVehicle::getOperatingStatus, 1).eq(XpVehicle::getElectronicId, dto.getFenceId()).in(XpVehicle::getVehicleVin,
        List<XpVehicle> list = xpVehicleService.list(new LambdaQueryWrapper<XpVehicle>().eq(XpVehicle::getState, 0)
                .eq(XpVehicle::getOperatingStatus, 1).eq(XpVehicle::getElectronicId, dto.getFenceId()).in(XpVehicle::getVehicleVin,
                dto.getVinList()));
        if(list.isEmpty()){
            return R.ok(new ArrayList<>());
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -299,13 +299,17 @@
            if (gearLevel == null || gearLevel != 4) {
                throw new ServiceException("车辆非P档,无法操作后备箱", I18nKeyConstant.VEHICLE_NOT_P_GEAR);
            }
            if(xpOrder.getOpenTrunk()==0){
                xpOrder.setOpenTrunk(1);
                vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_OPEN.getCommand());
            }
            if(xpOrder.getOpenTrunk()==1) {
            //查询后备箱开关状态
            VehicleTemperatureDto vehicleTemperatureDto = vehicleControlUtil.queryVehicleTemperature(String.valueOf(xpOrder.getAppUserId()), xpVehicle.getVehicleVin());
            VehicleStateVo trunkAjarCloseSt = vehicleTemperatureDto.getStateMap().get("trunkAjarCloseSt");
            //打开状态
            if(null != trunkAjarCloseSt && trunkAjarCloseSt.getValue().equals("1")){
                xpOrder.setOpenTrunk(2);
                vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_CLOSE.getCommand());
            }else{
                //关闭状态
                xpOrder.setOpenTrunk(1);
                vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_OPEN.getCommand());
            }
        }
        try {
@@ -319,9 +323,7 @@
            //}
            String result = vehicleControlUtil.vehiclesCommand(vehiclesCommandVo, xpOrder.getAppUserId());
            log.info("车辆控制结果:{}", result);
            JSONObject jsonObject = JSONObject.parseObject(result);
            String msgId = jsonObject.getString("msgId");
            if (StringUtils.isEmpty(msgId)) {
            if (!result.contains("msgId")) {
                return 0;
            }else {
                xpOrderClient.updateOrder(xpOrder);
@@ -359,16 +361,14 @@
        vehiclesCommandVo.setExtParam(vehiclesCommandParamVo);
        try {
            String result = vehicleControlUtil.vehiclesCommand(vehiclesCommandVo, xpOrder.getAppUserId());
            log.info("车辆控制结果:{}", result);
            JSONObject jsonObject = JSONObject.parseObject(result);
            String msgId = jsonObject.getString("msgId");
            if (StringUtils.isEmpty(msgId)) {
            log.info("车辆温度设置结果:{}", result);
            if (!result.contains("msgId")) {
                return 0;
            }else {
                return 1;
            }
        } catch (Exception e) {
            log.error("车辆控制异常:{}", e.getMessage());
            log.info("车辆温度设置异常:{}", e.getMessage());
            return 0;
        }
    }