“mitao”
2026-04-10 2b306afe79cd8096dd9394b0d8d04ff467b7e09c
fix:车辆无XPUID返回内容修改、围栏自动停用补偿定时任务
1个文件已添加
8个文件已修改
260 ■■■■■ 已修改文件
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/util/XPUUtil.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/job/jobs/ElectronicEffectJob.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/schedule/ElectronicFenceFailureCompensationTask.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpElectronicFenceService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/vo/XpOrderPageVO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java
@@ -290,16 +290,8 @@
        if (mqttPublishDto.getCode() == 200) {
            if(!dto.getOrderId().contains("XU")){
                Map<String, Object> cancelContext = new HashMap<>();
                cancelContext.put("orderId", dto.getOrderId());
                cancelContext.put("vin", dto.getVin());
                cancelContext.put("cancelScene", StringUtils.isNotEmpty(dto.getCancelScene())
                        ? dto.getCancelScene()
                        : CancelOrderSceneEnum.NORMAL_CANCEL.getCode());
                redisService.setCacheObject("CANCEL_ORDER_CONTEXT:" + taskId,
                        JSONObject.toJSONString(cancelContext), 30L, TimeUnit.MINUTES);
                log.info("记录取消订单上下文成功,orderId: {}, vin: {}, taskId: {}, cancelScene: {}",
                        dto.getOrderId(), dto.getVin(), taskId, cancelContext.get("cancelScene"));
                // 正常订单处理
                redisService.setCacheObject(String.valueOf(sequence), "cancelOrder_" + dto.getOrderId() + "_" + dto.getVin(), 30l, TimeUnit.MINUTES);
            }
            return R.ok(true);
        }
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java
@@ -72,7 +72,7 @@
@Slf4j
@Service
public class KafkaConsumerService {
    private static final String CANCEL_ORDER_CONTEXT_PREFIX = "CANCEL_ORDER_CONTEXT:";
    private static final String REASSIGN_CANCEL_CONTEXT_PREFIX = "REASSIGN_CANCEL_CONTEXT:";
    @Resource
@@ -1166,12 +1166,18 @@
                    //已取消
                    if(!orderId.contains("XU")){
                        String taskId = orderStatusInfo.getTaskId();
                        JSONObject cancelContext = getCancelOrderContext(taskId);
                        if (shouldSkipCancelForReassign(cancelContext)) {
                        JSONObject reassignCancelContext = resolveReassignCancelContext(orderId, orderStatusInfo);
                        if (Objects.nonNull(reassignCancelContext)) {
                            log.info("改派旧车取消回调,跳过订单取消处理,orderId: {}, taskId: {}, cancelScene: {}, context: {}",
                                    orderId, taskId, cancelContext.getString("cancelScene"), cancelContext);
                                    orderId, taskId, reassignCancelContext.getString("cancelScene"), reassignCancelContext);
                            //响应消息
                            responseMessage(sequence, cancelContext.getString("vin"));
                            String responseVin = resolveReassignCancelResponseVin(reassignCancelContext);
                            if (StringUtils.isNotEmpty(responseVin)) {
                                responseMessage(sequence, responseVin);
                            } else {
                                log.warn("改派旧车取消回调未找到响应VIN,orderId: {}, taskId: {}, context: {}",
                                        orderId, taskId, reassignCancelContext);
                            }
                            return;
                        }
                        if(orderId.contains("TEST")){
@@ -1212,11 +1218,11 @@
                                //行程中取消订单,按照行程结束处理
                                buildPushOrderStatus(orderId, OrderStatusEnum.SEE_OFF_GUESTS, sequence);
                                xpOrderClient.updateOrderStatus(Long.parseLong(orderId), OrderStatusEnum.SEE_OFF_GUESTS.getCode());
                                redisService.setCacheObject("ORDER_STATUS:" + orderId.substring(4), OrderStatusEnum.SEE_OFF_GUESTS.getCode(), 24L, TimeUnit.HOURS);
                                redisService.setCacheObject("ORDER_STATUS:" + orderId, OrderStatusEnum.SEE_OFF_GUESTS.getCode(), 24L, TimeUnit.HOURS);
                            }else{
                                buildPushOrderStatus(orderId, OrderStatusEnum.CANCELED, sequence);
                                xpOrderClient.updateOrderStatus(Long.parseLong(orderId), OrderStatusEnum.CANCELED.getCode());
                                redisService.setCacheObject("ORDER_STATUS:" + orderId.substring(4), OrderStatusEnum.CANCELED.getCode(), 24L, TimeUnit.HOURS);
                                redisService.setCacheObject("ORDER_STATUS:" + orderId, OrderStatusEnum.CANCELED.getCode(), 24L, TimeUnit.HOURS);
                            }
                        }
                    }
@@ -1381,33 +1387,62 @@
        }
    }
    /**
     * 根据taskId获取订单取消的上下文信息
     * @param taskId
     * @return
     */
    private JSONObject getCancelOrderContext(String taskId) {
        if (StringUtils.isEmpty(taskId)) {
    private JSONObject getReassignCancelContext(String orderId) {
        if (StringUtils.isEmpty(orderId)) {
            return null;
        }
        Object cacheObject = redisService.getCacheObject(CANCEL_ORDER_CONTEXT_PREFIX + taskId);
        Object cacheObject = redisService.getCacheObject(REASSIGN_CANCEL_CONTEXT_PREFIX + orderId);
        if (cacheObject == null) {
            return null;
        }
        return JSON.parseObject(cacheObject.toString());
    }
    /**
     * 判断是否是改派旧车取消回调,是则跳过订单取消处理
     * @param cancelContext
     * @return
     */
    private boolean shouldSkipCancelForReassign(JSONObject cancelContext) {
        if (cancelContext == null) {
            return false;
    private JSONObject resolveReassignCancelContext(String orderId, OrderStatusInfo orderStatusInfo) {
        if (OrderStateChangeSource.ORDER_STATE_CHANGE_SOURCE_VEHICLE != orderStatusInfo.getChangeSource()) {
            return null;
        }
        return CancelOrderSceneEnum.REASSIGN_OLD_VEHICLE == CancelOrderSceneEnum.fromCode(
                cancelContext.getString("cancelScene"));
        JSONObject cancelContext = getReassignCancelContext(orderId);
        if (cancelContext == null) {
            return null;
        }
        if (CancelOrderSceneEnum.REASSIGN_OLD_VEHICLE != CancelOrderSceneEnum.fromCode(
                cancelContext.getString("cancelScene"))) {
            return null;
        }
        R<XpOrder> orderResult = xpOrderClient.queryOrderById(Long.parseLong(orderId));
        XpOrder xpOrder = orderResult == null ? null : orderResult.getData();
        if (xpOrder == null) {
            log.warn("改派取消回调识别失败,订单不存在,orderId: {}, context: {}", orderId, cancelContext);
            return null;
        }
        Long newVehicleId = cancelContext.getLong("newVehicleId");
        Integer currentStatus = xpOrder.getStatus();
        boolean vehicleMatched = Objects.nonNull(newVehicleId) && Objects.equals(newVehicleId, xpOrder.getVehicleId());
        boolean statusMatched = Objects.nonNull(currentStatus) && currentStatus > 0 && currentStatus < 8;
        if (vehicleMatched && statusMatched) {
            return cancelContext;
        }
        log.info("改派取消回调未命中专用上下文,orderId: {}, currentVehicleId: {}, newVehicleId: {}, currentStatus: {}",
                orderId, xpOrder.getVehicleId(), newVehicleId, currentStatus);
        return null;
    }
    private String resolveReassignCancelResponseVin(JSONObject cancelContext) {
        if (cancelContext == null) {
            return null;
        }
        String oldVehicleVin = cancelContext.getString("oldVehicleVin");
        if (StringUtils.isNotEmpty(oldVehicleVin)) {
            return oldVehicleVin;
        }
        Long oldVehicleId = cancelContext.getLong("oldVehicleId");
        if (Objects.isNull(oldVehicleId)) {
            return null;
        }
        R<XpVehicle> vehicleResult = xpVehicleClient.queryById(oldVehicleId);
        XpVehicle xpVehicle = vehicleResult == null ? null : vehicleResult.getData();
        return xpVehicle == null ? null : xpVehicle.getVehicleVin();
    }
    /**
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/util/XPUUtil.java
@@ -8,6 +8,7 @@
import com.xiaopeng.api.feignClient.automaticPilot.XPUClient;
import com.xiaopeng.api.vo.automaticPilot.MqttPublishVo;
import com.xiaopeng.api.vo.automaticPilot.protobuf.*;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.core.utils.uuid.UUID;
import lombok.extern.slf4j.Slf4j;
@@ -210,7 +211,7 @@
        log.info("getMqttPublishTopic vin,{},xpuIdMapping,{}", vin,  JSON.toJSONString(xpuIdMapping));
        if (xpuIdMapping == null || StringUtils.isEmpty(xpuIdMapping.get(XPU_ID_TYPE)) ) {
            log.error("根据vin查询车辆xpuid信息为空,vin={}", vin);
            throw new RuntimeException("根据vin查询车辆xpuid信息为空");
            throw new ServiceException("根据vin查询车辆xpuid信息为空");
        }
        return xpuIdMapping.get(XPU_ID_TYPE);
    }
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/job/jobs/ElectronicEffectJob.java
@@ -41,15 +41,7 @@
                xpElectronicFenceAreaService.updateBatchById(electronicFenceAreaList);
            }else{
                // 电子围栏失效
                XpElectronicFence electronicFence = xpElectronicFenceService.getById(electronicId);
                electronicFence.setStatus(3);
                xpElectronicFenceService.updateById(electronicFence);
                List<XpElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery().eq(XpElectronicFenceArea::getElectronicId, electronicId).list();
                // 设置为禁用
                for(XpElectronicFenceArea electronicFenceArea : electronicFenceAreaList){
                    electronicFenceArea.setAreaStatus(2);
                }
                xpElectronicFenceAreaService.updateBatchById(electronicFenceAreaList);
                xpElectronicFenceService.expireFence(electronicId);
            }
        }catch(Exception e){
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/schedule/ElectronicFenceFailureCompensationTask.java
New file
@@ -0,0 +1,81 @@
package com.xiaopeng.map.schedule;
import com.xiaopeng.api.model.operation.XpElectronicFence;
import com.xiaopeng.map.service.XpElectronicFenceService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
 * 电子围栏失效补偿定时任务
 */
@Slf4j
@Component
public class ElectronicFenceFailureCompensationTask {
    private static final String LOCK_KEY = "MAP:ELECTRONIC_FENCE:FAILURE_COMPENSATE";
    private static final int LOCK_WAIT_SECONDS = 0;
    private static final int LOCK_LEASE_SECONDS = 15;
    @Resource
    private XpElectronicFenceService xpElectronicFenceService;
    @Resource
    private RedissonClient redissonClient;
    @Scheduled(fixedDelay = 10 * 1000L)
    public void compensateExpiredFence() {
        RLock lock = redissonClient.getLock(LOCK_KEY);
        boolean locked = false;
        try {
            locked = lock.tryLock(LOCK_WAIT_SECONDS, LOCK_LEASE_SECONDS, TimeUnit.SECONDS);
            if (!locked) {
                return;
            }
            LocalDateTime now = LocalDateTime.now();
            List<XpElectronicFence> expiredFenceList = xpElectronicFenceService.lambdaQuery()
                    .eq(XpElectronicFence::getFailureType, 2)
                    .le(XpElectronicFence::getFailureTime, now)
                    .in(XpElectronicFence::getStatus, 1, 2)
                    .list();
            if (expiredFenceList == null || expiredFenceList.isEmpty()) {
                return;
            }
            List<Long> electronicIds = expiredFenceList.stream()
                    .map(XpElectronicFence::getId)
                    .collect(Collectors.toList());
            log.info("电子围栏失效补偿开始, count={}, ids={}", electronicIds.size(), electronicIds);
            for (Long electronicId : electronicIds) {
                try {
                    xpElectronicFenceService.expireFence(electronicId);
                } catch (Exception e) {
                    log.error("电子围栏失效补偿单条处理异常, electronicId={}", electronicId, e);
                }
            }
            log.info("电子围栏失效补偿完成, count={}, ids={}", electronicIds.size(), electronicIds);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("电子围栏失效补偿任务获取锁被中断", e);
        } catch (Exception e) {
            log.error("电子围栏失效补偿任务执行异常", e);
        } finally {
            try {
                if (locked && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            } catch (Exception e) {
                log.warn("释放电子围栏失效补偿锁失败 lockKey={}", LOCK_KEY, e);
            }
        }
    }
}
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpElectronicFenceService.java
@@ -37,9 +37,10 @@
     * @return
     */
    List<XpElectronicFence> getList();
    /**
     * 将围栏置为已停用,并同步禁用围栏区域
     * @param electronicId 围栏ID
     */
    void expireFence(Long electronicId);
}
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java
@@ -958,6 +958,36 @@
        return list();
    }
    @Override
    @Transactional
    @ShardingSphereTransactionType(TransactionType.BASE)
    public void expireFence(Long electronicId) {
        if (electronicId == null) {
            return;
        }
        XpElectronicFence electronicFence = this.getById(electronicId);
        if (electronicFence == null || electronicFence.getStatus() == null) {
            return;
        }
        Integer status = electronicFence.getStatus();
        if (status != 1 && status != 2) {
            return;
        }
        electronicFence.setStatus(3);
        this.updateById(electronicFence);
        List<XpElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery()
                .eq(XpElectronicFenceArea::getElectronicId, electronicId)
                .list();
        if (CollUtil.isEmpty(electronicFenceAreaList)) {
            return;
        }
        for (XpElectronicFenceArea electronicFenceArea : electronicFenceAreaList) {
            electronicFenceArea.setAreaStatus(2);
        }
        xpElectronicFenceAreaService.updateBatchById(electronicFenceAreaList);
    }
    /**
     * 计算围栏中心点
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/vo/XpOrderPageVO.java
@@ -1,5 +1,6 @@
package com.xiaopeng.order.model.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -59,4 +60,10 @@
    private BigDecimal orderMoney;
    @ApiModelProperty(value = "状态 0=已取消,1=待接单,2=待出发,3=接驾中,4=待客上车,5=乘客已上车,6=服务中,7=待客下车,8=待付款,9=待评价,10=已完成")
    private Integer status;
    @ApiModelProperty(value = "下单站点名称")
    private String startPointName;
    @ApiModelProperty(value = "下单终点站点名称")
    private String endPointName;
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -137,6 +137,9 @@
@RequiredArgsConstructor(onConstructor_ = @Lazy)
public class XpOrderServiceImpl extends ServiceImpl<XpOrderMapper, XpOrder> implements IXpOrderService {
    private static final String REASSIGN_CANCEL_CONTEXT_PREFIX = "REASSIGN_CANCEL_CONTEXT:";
    private static final long REASSIGN_CANCEL_CONTEXT_TTL_MINUTES = 5L;
    private final UserResourcesClient userResourcesClient;
    private final XpPointClient xpPointClient;
    private final XpVehicleClient xpVehicleClient;
@@ -969,6 +972,8 @@
                    cancelDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
                    cancelDto.setCancelScene(CancelOrderSceneEnum.REASSIGN_OLD_VEHICLE.getCode());
                    orderMissionClient.cancelOrder(cancelDto);
                    cacheReassignCancelContext(orderId, oldVehicleId, newVehicleId,
                            oldVehicle.getVehicleVin(), currentStatus);
                    log.info("已取消原车辆订单指令,订单ID: {}, VIN: {}, taskId: {}, cancelScene: {}",
                            orderId, oldVehicle.getVehicleVin(), cancelDto.getTaskId(), cancelDto.getCancelScene());
@@ -1113,6 +1118,22 @@
    }
    private void cacheReassignCancelContext(Long orderId, Long oldVehicleId, Long newVehicleId,
                                            String oldVehicleVin, Integer preReassignStatus) {
        JSONObject context = new JSONObject();
        context.put("orderId", orderId);
        context.put("oldVehicleId", oldVehicleId);
        context.put("newVehicleId", newVehicleId);
        context.put("oldVehicleVin", oldVehicleVin);
        context.put("cancelScene", CancelOrderSceneEnum.REASSIGN_OLD_VEHICLE.getCode());
        context.put("operationTime", System.currentTimeMillis());
        context.put("preReassignStatus", preReassignStatus);
        redisService.setCacheObject(REASSIGN_CANCEL_CONTEXT_PREFIX + orderId, context.toJSONString(),
                REASSIGN_CANCEL_CONTEXT_TTL_MINUTES, TimeUnit.MINUTES);
        log.info("记录改派取消上下文成功,orderId: {}, oldVehicleId: {}, newVehicleId: {}, preReassignStatus: {}",
                orderId, oldVehicleId, newVehicleId, preReassignStatus);
    }
    /**
     * 为原车辆创建返程巡游订单
     * <p>