“mitao”
2026-02-28 eecc2a5240a4b9389d8396d36624055da808ad9b
feat(management):订单改派逻辑完善、收车定时任务逻辑完善
11个文件已修改
326 ■■■■■ 已修改文件
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpElectronicClientFallbackFactory.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/XpElectronicClient.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/schedule/VehicleCollectionScheduleTask.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpElectronicFenceAreaController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpVehicleService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages.properties 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_zh_CN.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpElectronicClientFallbackFactory.java
@@ -5,6 +5,7 @@
import com.xiaopeng.api.dto.operation.ElectronicFenceUpdateDto;
import com.xiaopeng.api.feignClient.operation.XpElectronicClient;
import com.xiaopeng.api.model.operation.XpElectronicFence;
import com.xiaopeng.api.model.operation.XpElectronicFenceArea;
import com.xiaopeng.api.query.operation.ElectronicPageListQuery;
import com.xiaopeng.api.vo.operation.ElectronicFenceDetailVo;
import com.xiaopeng.common.core.domain.R;
@@ -67,6 +68,12 @@
                log.error("根据ID列表批量查询电子围栏失败:{}", cause.getMessage());
                return R.fail("根据ID列表批量查询电子围栏失败");
            }
            @Override
            public R<XpElectronicFenceArea> queryElectronicFenceAreaById(Long id) {
                log.error("通过区域ID查询围栏失败:{}", cause.getMessage());
                return R.fail("通过区域ID查询围栏失败");
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/XpElectronicClient.java
@@ -5,6 +5,7 @@
import com.xiaopeng.api.dto.operation.ElectronicFenceUpdateDto;
import com.xiaopeng.api.factory.operation.XpPointClientFallbackFactory;
import com.xiaopeng.api.model.operation.XpElectronicFence;
import com.xiaopeng.api.model.operation.XpElectronicFenceArea;
import com.xiaopeng.api.query.operation.ElectronicPageListQuery;
import com.xiaopeng.api.vo.operation.ElectronicFenceDetailVo;
import com.xiaopeng.common.core.domain.R;
@@ -57,4 +58,13 @@
     */
    @PostMapping("/api/v1/electronicFence/listByIds")
    R<List<XpElectronicFence>> listByIds(@RequestBody Collection<Long> electronicIdSet);
    /**
     * 根据区域ID查询电子围栏区域信息
     *
     * @param id 区域ID
     * @return 区域信息
     */
    @PostMapping("/inner/v1/xp-electronic-fence-area/queryById")
    R<XpElectronicFenceArea> queryElectronicFenceAreaById(@RequestParam("id") Long id);
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/schedule/VehicleCollectionScheduleTask.java
@@ -1,14 +1,24 @@
package com.xiaopeng.maintenance.schedule;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xiaopeng.api.dto.automaticPilot.PushOrderDto;
import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient;
import com.xiaopeng.api.feignClient.operation.XpCruisePlanClient;
import com.xiaopeng.api.feignClient.order.XpCruiseOrderClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.maintenance.XpTaskRule;
import com.xiaopeng.api.model.order.XpCruiseOrder;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.maintenance.mapper.XpTaskRuleMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@@ -42,6 +52,18 @@
    @Autowired
    private XpVehicleClient xpVehicleClient;
    @Autowired
    private XpCruiseOrderClient xpCruiseOrderClient;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private OrderMissionClient orderMissionClient;
    @Autowired
    private XpCruisePlanClient xpCruisePlanClient;
    /**
     * 每分钟检查一次是否需要执行收车任务
@@ -162,6 +184,7 @@
                return result.getData().stream()
                        .filter(v -> v.getVehicleType() != null && v.getVehicleType() == 2)
                        .filter(v -> v.getOperatingStatus() != null && v.getOperatingStatus() == 1)
                        .filter(v -> v.getState() != null && v.getState() == 0)
                        .collect(Collectors.toList());
            }
        } catch (Exception e) {
@@ -179,10 +202,37 @@
     * 2. 更新车辆状态为"已收车"
     * </p>
     *
     * @param vehicle 车辆信息
     * @param xpVehicle 车辆信息
     */
    private void sendCollectionCommand(XpVehicle vehicle) {
        // TODO: 具体发送收车指令的实现
        log.info("向车辆[{}]发送收车指令", vehicle.getPlateNumber());
    private void sendCollectionCommand(XpVehicle xpVehicle) {
        // 收车 关闭当前巡游单子 生成新的返程单子  找到收车站点
        R<String> r = xpCruiseOrderClient.updateCruiseOrder(xpVehicle.getVehicleVin());
        // redis 获取当前最新经纬度位置
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
        XpCruiseOrder xpCruiseOrder = new XpCruiseOrder();
        if(vehiclePosition!=null){
            double x = vehiclePosition.getPoint().getX();
            double y = vehiclePosition.getPoint().getY();
            xpCruiseOrder.setStartLon(x);
            xpCruiseOrder.setStartLat(y);
        }
        xpCruiseOrder.setId("XU"+ IdUtil.fastUUID());
        xpCruiseOrder.setVehicleId(xpVehicle.getId());
        xpCruiseOrder.setFleetId(xpVehicle.getFleetId());
        xpCruiseOrder.setOrderType(2);
        xpCruiseOrder.setStatus(1);
        xpCruiseOrder.setOrderReverse(1);
        xpCruiseOrder.setLastOrderId(r.getData());
        // 找到收车站点
        R<XpCruiseOrder> returnCruisePoint = xpCruisePlanClient.findReturnCruisePoint(xpCruiseOrder);
        if(returnCruisePoint!=null){
            xpCruiseOrderClient.addCruiseOrder(returnCruisePoint.getData());
            // 下发命令
            orderMissionClient.pushOrder(new PushOrderDto( xpCruiseOrder.getVehicleVin(),xpCruiseOrder.getId(),null));
        }
        log.info("向车辆[{}]发送收车指令", xpVehicle.getPlateNumber());
    }
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpElectronicFenceAreaController.java
@@ -4,11 +4,12 @@
import com.xiaopeng.api.model.operation.XpElectronicFenceArea;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.operation.service.IXpElectronicFenceAreaService;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@@ -20,8 +21,21 @@
 * @author mitao
 * @since 2026-02-03
 */
@Controller
@RequestMapping("/xp-electronic-fence-area")
@RestController
public class XpElectronicFenceAreaController {
    @Resource
    private IXpElectronicFenceAreaService xpElectronicFenceAreaService;
    /**
     * 根据区域ID查询区域信息
     *
     * @param id 区域ID
     * @return 区域信息
     */
    @ApiOperation(value = "根据区域ID查询区域信息", notes = "根据区域ID查询区域信息", tags = {"内部接口"})
    @PostMapping("/inner/v1/xp-electronic-fence-area/queryById/queryById")
    public R<XpElectronicFenceArea> queryById(@RequestParam("id") Long id) {
        return R.ok(xpElectronicFenceAreaService.getById(id));
    }
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java
@@ -270,7 +270,7 @@
     * @param dto 改派请求参数
     * @return 操作结果
     */
    @ApiOperation(value = "改派订单", tags = {"管理后台-交易管理-订单管理"},notes = "车辆列表在maintenance服务")
    @ApiOperation(value = "改派订单", tags = {"管理后台-交易管理-订单管理"},notes = "车辆列表在vehicleConvoy服务")
    @PostMapping("/api/v1/order/reassignById")
    public R<?> reassignById(@Validated @RequestBody XpOrderReassignDTO dto) {
        xpOrderService.reassignOrder(
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -511,7 +511,20 @@
        orderCancel.setCancelReason("运营人员取消订单");
        orderCancel.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName());
        xpOrderCancelService.save(orderCancel);
        //查询车辆信息
        R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(order.getId());
        if (xpVehicleR.getCode() == HttpStatus.SUCCESS && Objects.nonNull(xpVehicleR.getData())) {
            // 下发命令
            if (order.getStatus() > 1) {
                PushOrderDto pushOrderDto = new PushOrderDto();
                pushOrderDto.setOrderId(order.getId().toString());
                pushOrderDto.setVin(xpVehicleR.getData().getVehicleVin());
                pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
                orderMissionClient.cancelOrder(pushOrderDto);
            }
        } else {
            log.info("查询车辆信息失败,订单ID: {}, 下发取消订单命令失败", order.getId());
        }
        log.info("订单取消成功,订单ID: {}, 操作人: {}", order.getId(), SecurityUtils.getUsername());
    }
@@ -538,10 +551,10 @@
            throw new ServiceException("订单不存在");
        }
        // 2. 校验订单状态(只有待接单、待出发、接驾中状态可以改派)
        // 2. 校验订单状态(只有待出发、接驾中、待乘客上车状态可以改派)
        Integer currentStatus = order.getStatus();
        if (currentStatus != 1 && currentStatus != 2 && currentStatus != 3) {
            throw new ServiceException("当前订单状态不允许改派,只有待接单、待出发、接驾中状态的订单可以改派", I18nKeyConstant.ORDER_REASSIGN_FAILED);
        if (currentStatus != 2 && currentStatus != 3 && currentStatus != 4) {
            throw new ServiceException("当前订单状态不允许改派,只有待出发、接驾中、待乘客上车状态的订单可以改派", I18nKeyConstant.ORDER_REASSIGN_FAILED);
        }
        // 3. 校验新旧车辆是否相同
@@ -580,6 +593,47 @@
        // 6. 保存原车辆和车队信息
        Long oldVehicleId = order.getVehicleId();
        Long oldFleetId = order.getFleetId();
        // 6.1 处理原车辆:取消订单指令、解绑乘客、更新状态、创建返程巡游订单
        if (Objects.nonNull(oldVehicleId)) {
            try {
                R<XpVehicle> oldVehicleResult = xpVehicleClient.queryById(oldVehicleId);
                if (oldVehicleResult != null && oldVehicleResult.getCode() == HttpStatus.SUCCESS && oldVehicleResult.getData() != null) {
                    XpVehicle oldVehicle = oldVehicleResult.getData();
                    // 6.1.1 取消原订单指令
                    PushOrderDto cancelDto = new PushOrderDto();
                    cancelDto.setOrderId(order.getId().toString());
                    cancelDto.setVin(oldVehicle.getVehicleVin());
                    cancelDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
                    orderMissionClient.cancelOrder(cancelDto);
                    log.info("已取消原车辆订单指令,订单ID: {}, VIN: {}", orderId, oldVehicle.getVehicleVin());
                    // 6.1.2 解绑乘客与原车辆关系
                    if (Objects.nonNull(order.getAppUserId())) {
                        CancelTaxiPassengerVo cancelVo = new CancelTaxiPassengerVo();
                        cancelVo.setVin(oldVehicle.getVehicleVin());
                        cancelVo.setUid(order.getAppUserId().toString());
                        cancelVo.setOrderNo(order.getOrderNumber());
                        cancelVo.setAppId("");
                        userVehicleClient.cancelTaxiPassenger(cancelVo);
                        log.info("已解绑乘客与原车辆关系,用户ID: {}, VIN: {}", order.getAppUserId(), oldVehicle.getVehicleVin());
                    }
                    // 6.1.3 修改原车辆状态为空闲
                    oldVehicle.setState(0);
                    xpVehicleClient.updateVehicleById(oldVehicle);
                    log.info("已将原车辆状态设为空闲,车辆ID: {}", oldVehicleId);
                    // 6.1.4 为原车辆创建返程巡游订单
                    createReturnCruiseOrder(oldVehicle, order);
                } else {
                    log.warn("查询原车辆信息失败,车辆ID: {}", oldVehicleId);
                }
            } catch (Exception e) {
                log.error("处理原车辆失败,车辆ID: {},错误: {}", oldVehicleId, e.getMessage());
            }
        }
        // 7. 查询新车辆关联的车队ID
        Long newFleetId = null;
@@ -634,7 +688,118 @@
        log.info("订单改派成功,订单ID: {}, 原车辆ID: {}, 新车辆ID: {}, 原车队ID: {}, 新车队ID: {}, 操作人: {}",
                orderId, oldVehicleId, newVehicleId, oldFleetId, newFleetId, SecurityUtils.getUsername());
        //推送消息
        // 11. 处理新车辆:绑定乘客、下发订单、更新状态
        try {
            // 11.1 绑定乘客与新车辆关系
            if (Objects.nonNull(order.getAppUserId())) {
                AddTaxiPassengerVo addVo = new AddTaxiPassengerVo();
                addVo.setVin(newVehicle.getVehicleVin());
                addVo.setUid(order.getAppUserId().toString());
                addVo.setOrderNo(order.getOrderNumber());
                addVo.setAppId(""); // TODO 获取appId
                userVehicleClient.addTaxiPassenger(addVo);
                log.info("已绑定乘客与新车辆关系,用户ID: {}, VIN: {}", order.getAppUserId(), newVehicle.getVehicleVin());
            }
            // 11.2 下发订单到新车辆
            PushOrderDto pushDto = new PushOrderDto();
            pushDto.setOrderId(order.getId().toString());
            pushDto.setVin(newVehicle.getVehicleVin());
            orderMissionClient.pushOrder(pushDto);
            log.info("已下发订单到新车辆,订单ID: {}, VIN: {}", orderId, newVehicle.getVehicleVin());
            // 11.3 更新新车辆状态为已接单
            newVehicle.setState(1);
            xpVehicleClient.updateVehicleById(newVehicle);
            log.info("已将新车辆状态设为已接单,车辆ID: {}", newVehicleId);
        } catch (Exception e) {
            log.error("处理新车辆失败,车辆ID: {},错误: {}", newVehicleId, e.getMessage());
        }
    }
    /**
     * 为原车辆创建返程巡游订单
     * <p>
     * 改派时,原车辆需要返回巡游路线:
     * - 若最新巡游订单状态为已终止(status=2),返回原出发点
     * - 若最新巡游订单状态为已完成(status=3),返回原终点
     * - 其他状态,默认返回原出发点
     * </p>
     *
     * @param oldVehicle 原车辆信息
     * @param order      当前订单信息
     */
    private void createReturnCruiseOrder(XpVehicle oldVehicle, XpOrder order) {
        try {
            // 查询该车辆最新的一条巡游订单
            XpCruiseOrder lastCruise = xpCruiseOrderMapper.selectOne(
                    new LambdaQueryWrapper<XpCruiseOrder>()
                            .eq(XpCruiseOrder::getVehicleId, oldVehicle.getId())
                            .orderByDesc(BaseModel::getCreateTime)
                            .last("limit 1")
            );
            if (lastCruise == null) {
                log.warn("原车辆没有巡游订单记录,跳过创建返程订单,车辆ID: {}", oldVehicle.getId());
                return;
            }
            XpCruiseOrder returnOrder = new XpCruiseOrder();
            returnOrder.setId("XU" + IdUtil.fastUUID());
            returnOrder.setVehicleId(oldVehicle.getId());
            returnOrder.setVehicleVin(oldVehicle.getVehicleVin());
            returnOrder.setFleetId(oldVehicle.getFleetId());
            returnOrder.setOrderType(2); // 返程单
            returnOrder.setStatus(1);    // 进行中
            returnOrder.setLastOrderId(lastCruise.getId());
            returnOrder.setCreateTime(LocalDateTime.now());
            // 返程起点 = 当前订单的终点(车辆当前位置附近)
            returnOrder.setStartPointId(order.getEndPointId());
            returnOrder.setStartPointName(order.getEndPointName());
            returnOrder.setStartLon(order.getEndLon());
            returnOrder.setStartLat(order.getEndLat());
            // 根据最新巡游订单状态决定返程终点
            if (lastCruise.getStatus() != null && lastCruise.getStatus() == 2) {
                // 已终止(status=2) -> 返回原出发点
                returnOrder.setEndPointId(lastCruise.getStartPointId());
                returnOrder.setEndPointName(lastCruise.getStartPointName());
                returnOrder.setEndLon(lastCruise.getStartLon());
                returnOrder.setEndLat(lastCruise.getStartLat());
                log.info("巡游订单状态为已终止,返回原出发点");
            } else if (lastCruise.getStatus() != null && lastCruise.getStatus() == 3) {
                // 已完成(status=3) -> 返回原终点
                returnOrder.setEndPointId(lastCruise.getEndPointId());
                returnOrder.setEndPointName(lastCruise.getEndPointName());
                returnOrder.setEndLon(lastCruise.getEndLon());
                returnOrder.setEndLat(lastCruise.getEndLat());
                log.info("巡游订单状态为已完成,返回原终点");
            } else {
                // 其他状态(进行中等),默认返回原出发点
                returnOrder.setEndPointId(lastCruise.getStartPointId());
                returnOrder.setEndPointName(lastCruise.getStartPointName());
                returnOrder.setEndLon(lastCruise.getStartLon());
                returnOrder.setEndLat(lastCruise.getStartLat());
                log.info("巡游订单状态为{},默认返回原出发点", lastCruise.getStatus());
            }
            xpCruiseOrderMapper.insert(returnOrder);
            // 下发巡游订单到原车辆
            PushOrderDto pushDto = new PushOrderDto();
            pushDto.setVin(oldVehicle.getVehicleVin());
            pushDto.setOrderId(returnOrder.getId());
            orderMissionClient.pushOrder(pushDto);
            log.info("为原车辆创建返程巡游订单成功,订单ID: {}, 车辆ID: {}", returnOrder.getId(), oldVehicle.getId());
        } catch (Exception e) {
            log.error("创建返程巡游订单失败,车辆ID: {},错误: {}", oldVehicle.getId(), e.getMessage());
        }
    }
    @Override
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -479,8 +479,9 @@
     */
    @ApiOperation(value = "查询可改派的车辆列表", tags = {"管理后台-订单管理"})
    @GetMapping("/api/v1/vehicle/availableList")
    public R<List<XpVehicle>> listAvailableVehicles() {
        List<XpVehicle> list = xpVehicleService.listAvailableVehicles();
    @ApiImplicitParam(name = "orderId", value = "订单ID", required = true, dataTypeClass = Long.class)
    public R<List<XpVehicle>> listAvailableVehicles(@RequestParam Long orderId) {
        List<XpVehicle> list = xpVehicleService.listAvailableVehicles(orderId);
        return R.ok(list);
    }
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpVehicleService.java
@@ -33,7 +33,7 @@
     *
     * @return 车辆列表
     */
    List<XpVehicle> listAvailableVehicles();
    List<XpVehicle> listAvailableVehicles(Long orderId);
    /**
     * 查询车辆列表接口-地图
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -11,6 +11,7 @@
import com.xiaopeng.api.feignClient.operation.XpElectronicClient;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.model.operation.XpElectronicFence;
import com.xiaopeng.api.model.operation.XpElectronicFenceArea;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicleAnnualRecord;
@@ -83,22 +84,45 @@
    }
    @Override
    public List<XpVehicle> listAvailableVehicles() {
        // 构建查询条件
        LambdaQueryWrapper<XpVehicle> wrapper = new LambdaQueryWrapper<>();
    public List<XpVehicle> listAvailableVehicles(Long orderId) {
        // 1. 通过订单ID查询订单信息,获取区域ID
        R<XpOrder> orderResult = xpOrderClient.queryOrderById(orderId);
        if (orderResult == null || orderResult.getData() == null) {
            log.warn("查询订单失败,orderId: {}", orderId);
            return Collections.emptyList();
        }
        // 必要条件:可改派车辆的4个核心条件
        wrapper.ne(XpVehicle::getStatus, 3)  // 状态不是保险/年检过期
               .isNotNull(XpVehicle::getStatus)
               .eq(XpVehicle::getOperatingStatus, 1)  // 运营中
               .eq(XpVehicle::getAcceptOrTest, 1)  // 接单模式
               .eq(XpVehicle::getAcceptOrTestStatus, 1);  // 开始接单
        XpOrder order = orderResult.getData();
        Long areaId = order.getXpElectronicFenceAreaId();
        if (areaId == null) {
            log.warn("订单区域ID为空,orderId: {}", orderId);
            return Collections.emptyList();
        }
        // 2. 通过区域ID查询围栏ID
        R<XpElectronicFenceArea> areaResult = xpElectronicClient.queryElectronicFenceAreaById(areaId);
        if (areaResult == null || areaResult.getData() == null) {
            log.warn("查询围栏区域失败,areaId: {}", areaId);
            return Collections.emptyList();
        }
        Long electronicId = areaResult.getData().getElectronicId();
        if (electronicId == null) {
            log.warn("围栏ID为空,areaId: {}", areaId);
            return Collections.emptyList();
        }
        // 3. 根据围栏ID查询车辆列表 (operatingStatus=1 且 state=0)
        LambdaQueryWrapper<XpVehicle> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(XpVehicle::getElectronicId, electronicId)
               .eq(XpVehicle::getOperatingStatus, 1)
               .eq(XpVehicle::getState, 0);
        // 排序:按电量降序、入库时间降序
        wrapper.orderByDesc(XpVehicle::getCurrentElectricity)
               .orderByDesc(XpVehicle::getWarehousingTime);
        // 执行查询
        return this.list(wrapper);
    }
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages.properties
@@ -1,7 +1,7 @@
# 中文
fleet.operation.user.bound.v2=当前车队已和该人员关联
policy.application.audit.ids.notEmpty=保单申请ID列表不能为空
fleet.user.bound=运维人员[{0}]已绑定车队[{1}]
fleet.operation.user.bound.v1=运维人员[{0}]已绑定车队[{1}]
fleet.vehicle.bound.v2=当前车队已和该车辆关联
fleet.vehicle.bound.v1=车辆[{0}]已绑定车队[{1}]
policy.application.audit.status.notNull=保单审核状态不能为空
xp-robotaxi-vehicle-convoy-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -2,6 +2,7 @@
fleet.operation.user.bound.v2=当前车队已和该人员关联
policy.application.audit.ids.notEmpty=保单申请ID列表不能为空
fleet.user.bound=运维人员[{0}]已绑定车队[{1}]
fleet.operation.user.bound.v1=运维人员[{0}]已绑定车队[{1}]
fleet.vehicle.bound.v2=当前车队已和该车辆关联
policy.application.audit.status.notNull=保单审核状态不能为空
welcome.message=欢迎来到我们的应用程序!