puzhibing
2026-03-26 5f7a82bca016b27f8e05d2892ca7b64b0d7d9797
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -23,7 +23,7 @@
import com.xiaopeng.api.model.vehicleConvoy.*;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.api.vo.operation.ElectronicFenceDetailVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandEnum;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleStateVo;import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandEnum;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandParamVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandVo;
import com.xiaopeng.common.core.constant.CacheConstants;
@@ -35,6 +35,7 @@
import com.xiaopeng.common.core.utils.GeodesyUtil;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.evi.oss.service.AliOssService;
import com.xiaopeng.vehicleConvoy.constant.I18nKeyConstant;
import com.xiaopeng.vehicleConvoy.mapper.XpFleetMapper;
import com.xiaopeng.vehicleConvoy.mapper.XpFleetOperationsMapper;
@@ -50,6 +51,8 @@
import com.xiaopeng.vehicleConvoy.util.VehicleControlUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.geo.Point;
@@ -106,6 +109,8 @@
    private RedisService redisService;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private AliOssService aliOssService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
@@ -180,7 +185,7 @@
            return pageInfo;
        }
        List<Long> vehicleIds = xpFleetVehicles.stream().map(XpFleetVehicle::getVehicleId).collect(Collectors.toList());
        List<Long> vehicleIds = xpFleetVehicles.stream().map(XpFleetVehicle::getVehicleId).distinct().collect(Collectors.toList());
        query.setVehicleIds(vehicleIds);
@@ -196,13 +201,6 @@
        }
        for (XpVehicleVO xpVehicleVO : list) {
//            List<Point> positionList = redisTemplate.opsForGeo().position("vehiclePosition:" + xpVehicleVO.getVehicleVin(), xpVehicleVO.getVehicleVin());
//            if (!CollectionUtils.isEmpty(positionList)) {
//                Point geoJsonPoint = positionList.get(0);
//                // 获取经纬度
//                xpVehicleVO.setLongitude(geoJsonPoint.getX());
//                xpVehicleVO.setLatitude(geoJsonPoint.getY());
//            }
            // 查询车辆的续航里程
            VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicleVO.getVehicleVin())), VehiclePosition.class);
            if (Objects.nonNull(vehiclePosition)) {
@@ -216,6 +214,22 @@
            } else {
                xpVehicleVO.setMileageRange(0F);
                xpVehicleVO.setCurrentElectricity(0);
                xpVehicleVO.setLongitude(104.056302);
                xpVehicleVO.setLatitude(30.586688);
            }
            if (org.springframework.util.StringUtils.hasLength(xpVehicleVO.getVehiclePictures())) {
                JSONObject jsonObject = JSONObject.parseObject(xpVehicleVO.getVehiclePictures());
                if(jsonObject!=null){
                    String downloadUrl = null;
                    try {
                        // 1
                        downloadUrl= aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    jsonObject.put("url", downloadUrl);
                    xpVehicleVO.setVehiclePictures(jsonObject.toJSONString());
                }
            }
        }
@@ -251,8 +265,7 @@
        if (Objects.isNull(xpVehicle)) {
            throw new ServiceException("未查询到有效车辆信息", I18nKeyConstant.VEHICLE_NOT_EXISTS);
        }
        // 判断距离是否在范围之内 4米
//        List<Point> positionList = redisTemplate.opsForGeo().position("vehiclePosition:" + xpVehicle.getVehicleVin(), xpVehicle.getVehicleVin());
        // 判断距离是否在范围之内 15米
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
        if (Objects.nonNull(vehiclePosition)) {
            GeoJsonPoint point = vehiclePosition.getPoint();
@@ -260,14 +273,14 @@
                Map<String, Double> distance = GeodesyUtil.getDistance(dto.getLongitude()+ "," + dto.getLatitude(), point.getX()+","+point.getY());
                if (null != distance) {
                    Double distance1 = distance.get("WGS84");
                    if (distance1 > 4) {
                        throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
                    if (distance1 > 50) {
                        throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
                    }
                }else {
                    throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
                    throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
                }
            }else {
                throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
                throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
            }
        }
        // 车门解锁、后备箱解锁
@@ -275,7 +288,7 @@
        vehiclesCommandVo.setVin(xpVehicle.getVehicleVin());
        if ("door".equals(dto.getDoorType())) {
            // 开启车门 判断是否为P档
            Integer gearLevel = redisService.getCacheObject(CacheConstants.GEAR_LEVEL + xpVehicle.getVehicleVin());
            Integer gearLevel = redisService.getCacheObject(CacheConstants.GEAR_LEVEL + xpVehicle.getXpuId());
            if (gearLevel == null || gearLevel != 4) {
                throw new ServiceException("车辆非P档,无法开启车门", I18nKeyConstant.VEHICLE_NOT_P_GEAR);
            }
@@ -284,36 +297,38 @@
            // 开启后备箱 判断是否为P档
            Integer gearLevel = redisService.getCacheObject(CacheConstants.GEAR_LEVEL + xpVehicle.getXpuId());
            if (gearLevel == null || gearLevel != 4) {
                throw new ServiceException("车辆非P档,无法开启后备箱", I18nKeyConstant.VEHICLE_NOT_P_GEAR);
                throw new ServiceException("车辆非P档,无法操作后备箱", I18nKeyConstant.VEHICLE_NOT_P_GEAR);
            }
            vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_OPEN.getCommand());
            if(xpOrder.getOpenTrunk()==0){
                xpOrder.setOpenTrunk(1);
            }
            if(xpOrder.getOpenTrunk()==1){
            //查询后备箱开关状态
            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);
                // 添加定时任务
                xpOrderClient.updateOrderStatus(xpOrder.getId(),7);
                // 推送倒计时消息 消息推送
                Map<String, Object> params = new HashMap<>();
                Map<String, Object> content = new HashMap<>();
                content.put("orderId", xpOrder.getId().toString());
                content.put("isOpen", 1);
                params.put("type", PushMessageTypeEnum.OPEN_TRUNK.getDesc());
                params.put("content", JSONObject.toJSONString(content));
                params.put("timestamp", new Date().getTime() + "");
                String message = JSONObject.toJSONString(params);
                SendMessageDTO sendMessageDTO = new SendMessageDTO();
                sendMessageDTO.setMessage(message);
                sendMessageDTO.setAppUserId(String.valueOf(xpOrder.getAppUserId()));
                xpPushMessageClient.sendStatus(sendMessageDTO);
                vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_CLOSE.getCommand());
            }else{
                //关闭状态
                xpOrder.setOpenTrunk(1);
                vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_OPEN.getCommand());
            }
        }
        try {
            // {
            //    "code": 0,
            //    "data": {
            //        "msgId": "1234556"
            //    },
            //    "msg": "",
            //    "desc": "成功"
            //}
            String result = vehicleControlUtil.vehiclesCommand(vehiclesCommandVo, xpOrder.getAppUserId());
            log.info("车辆控制结果:{}", result);
            xpOrderClient.updateOrder(xpOrder);
            return 1;
            if (!result.contains("msgId")) {
                return 0;
            }else {
                xpOrderClient.updateOrder(xpOrder);
                return 1;
            }
        } catch (Exception e) {
            log.error("车辆控制异常:{}", e.getMessage());
            return 0;
@@ -346,16 +361,19 @@
        vehiclesCommandVo.setExtParam(vehiclesCommandParamVo);
        try {
            String result = vehicleControlUtil.vehiclesCommand(vehiclesCommandVo, xpOrder.getAppUserId());
            log.info("车辆控制结果:{}", result);
            return 1;
            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;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addVehicle(VehicleAddDTO dto) {
        String normalizedVehicleCode = trimToNull(dto.getVehicleCode());
        String normalizedVin = normalizeVin(dto.getVehicleVin());
@@ -377,24 +395,42 @@
        xpVehicle.setVehicleType(1);
        // 新增时状态为待完善
        xpVehicle.setStatus(1);
        // 设置入库时间
        xpVehicle.setWarehousingTime(LocalDateTime.now());
        //绑定XPUID
        String vin = xpVehicle.getVehicleVin();
        String xpuid = xpuVinClient.getXpuIdByVin(vin).getData();
        R<String> xpuIdByVin = xpuVinClient.getXpuIdByVin(vin);
        if(200 != xpuIdByVin.getCode()){
            throw new RuntimeException(xpuIdByVin.getMsg());
        }
        String xpuid = xpuIdByVin.getData();
        xpVehicle.setXpuId(xpuid);
        this.save(xpVehicle);
        if (isInfoComplete(xpVehicle)) {
            //如果信息完善则状态改为待运营
            xpVehicle.setStatus(2);
            this.updateById(xpVehicle);
            //保存年检记录
            XpVehicleAnnualRecord xpVehicleAnnualRecord = BeanUtil.copyProperties(dto, XpVehicleAnnualRecord.class, "id");
            xpVehicleAnnualRecord.setVehicleId(xpVehicle.getId());
            xpVehicleAnnualRecordService.save(xpVehicleAnnualRecord);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editVehicle(VehicleEditDTO dto) {
        XpVehicle existingVehicle = this.getById(dto.getId());
        String normalizedVehicleCode = trimToNull(dto.getVehicleCode());
        String normalizedVin = normalizeVin(dto.getVehicleVin());
        String normalizedPlateNumber = trimToNull(dto.getPlateNumber());
        String normalizedVehicleColor = trimToNull(dto.getVehicleColor());
        validateAndNormalizeIdentityFields(normalizedVehicleCode, normalizedVin, normalizedPlateNumber, normalizedVehicleColor, dto.getId());
        validateElectronicFenceExists(dto.getElectronicId());
        boolean electronicIdChanged = existingVehicle == null
                || !Objects.equals(existingVehicle.getElectronicId(), dto.getElectronicId());
        if (electronicIdChanged) {
            validateElectronicFenceExists(dto.getElectronicId());
        }
        dto.setVehicleCode(normalizedVehicleCode);
        dto.setVehicleVin(normalizedVin);
@@ -404,10 +440,20 @@
        // DTO转实体
        XpVehicle xpVehicle = new XpVehicle();
        BeanUtil.copyProperties(dto, xpVehicle);
        //绑定XPUID
        String vin = xpVehicle.getVehicleVin();
        String xpuid = xpuVinClient.getXpuIdByVin(vin).getData();
        xpVehicle.setXpuId(xpuid);
        boolean vinChanged = existingVehicle == null
                || !Objects.equals(existingVehicle.getVehicleVin(), xpVehicle.getVehicleVin());
        if (vinChanged || existingVehicle == null || StringUtils.isBlank(existingVehicle.getXpuId())) {
            // 仅在 VIN 变更或原 XPUID 为空时才重新绑定 XPUID,避免不必要的远程调用
            String vin = xpVehicle.getVehicleVin();
            R<String> xpuIdByVin = xpuVinClient.getXpuIdByVin(vin);
            if(200 != xpuIdByVin.getCode()){
                throw new RuntimeException(xpuIdByVin.getMsg());
            }
            String xpuid = xpuIdByVin.getData();
            xpVehicle.setXpuId(xpuid);
        } else {
            xpVehicle.setXpuId(existingVehicle.getXpuId());
        }
        this.updateById(xpVehicle);
    }
@@ -424,30 +470,30 @@
        if (StringUtils.isNotBlank(vehicleColor) && vehicleColor.length() > 2) {
            throw new ServiceException("车辆颜色长度不能超过2", I18nKeyConstant.VEHICLE_COLOR_INVALID, new Object[]{vehicleColor});
        }
        boolean vehicleCodeExists = lambdaQuery()
                .eq(XpVehicle::getVehicleCode, vehicleCode)
        List<XpVehicle> duplicates = lambdaQuery()
                .ne(excludeId != null, XpVehicle::getId, excludeId)
                .last("limit 1")
                .one() != null;
                .and(w -> w.eq(XpVehicle::getVehicleCode, vehicleCode)
                        .or()
                        .eq(XpVehicle::getVehicleVin, vehicleVin)
                        .or()
                        .eq(XpVehicle::getPlateNumber, plateNumber))
                .last("limit 3")
                .list();
        if (CollectionUtils.isEmpty(duplicates)) {
            return;
        }
        boolean vehicleCodeExists = duplicates.stream().anyMatch(v -> vehicleCode.equals(v.getVehicleCode()));
        if (vehicleCodeExists) {
            throw new ServiceException("车辆编码已存在", I18nKeyConstant.VEHICLE_CODE_EXISTS, new Object[]{vehicleCode});
        }
        boolean vehicleVinExists = lambdaQuery()
                .eq(XpVehicle::getVehicleVin, vehicleVin)
                .ne(excludeId != null, XpVehicle::getId, excludeId)
                .last("limit 1")
                .one() != null;
        boolean vehicleVinExists = duplicates.stream().anyMatch(v -> vehicleVin.equals(v.getVehicleVin()));
        if (vehicleVinExists) {
            throw new ServiceException("车辆VIN码已存在", I18nKeyConstant.VEHICLE_VIN_EXISTS, new Object[]{vehicleVin});
        }
        boolean plateNumberExists = lambdaQuery()
                .eq(XpVehicle::getPlateNumber, plateNumber)
                .ne(excludeId != null, XpVehicle::getId, excludeId)
                .last("limit 1")
                .one() != null;
        boolean plateNumberExists = duplicates.stream().anyMatch(v -> plateNumber.equals(v.getPlateNumber()));
        if (plateNumberExists) {
            throw new ServiceException("车牌号已存在", I18nKeyConstant.VEHICLE_PLATE_EXISTS, new Object[]{plateNumber});
        }
@@ -480,7 +526,8 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Transactional
    @ShardingSphereTransactionType(TransactionType.BASE)
    public void improveInfo(VehicleImproveInfoDTO dto) {
        // 校验已填写的时间字段是否在有效范围内
        validateTimeFields(dto);
@@ -662,7 +709,6 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchOperation(String idStr) {
        if (StringUtils.isBlank(idStr)) {
            return;
@@ -755,7 +801,6 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchStock(String idStr) {
        if (StringUtils.isBlank(idStr)) {
            return;
@@ -769,7 +814,6 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchSetElectronic(List<Long> ids, Long electronicId) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
@@ -808,6 +852,7 @@
        if (CollUtil.isNotEmpty(list)) {
            LocalDate today = LocalDate.now();
            List<Long> expiredVehicleIds = new ArrayList<>();
            List<Long> VehicleIds = new ArrayList<>();
            for (XpVehicle vehicle : list) {
                // 检查是否有任意一项过期(精确到 yyyy-MM-dd)
@@ -840,7 +885,6 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importVehicles(List<XpVehicleImportExcel> importList) {
        if (CollectionUtils.isEmpty(importList)) {
            return;
@@ -889,7 +933,7 @@
                .like(StringUtils.isNotBlank(query.getVehicleVin()), XpVehicle::getVehicleVin, query.getVehicleVin())
                .like(StringUtils.isNotBlank(query.getPlateNumber()), XpVehicle::getPlateNumber, query.getPlateNumber())
                .eq(Objects.nonNull(query.getOperatingStatus()), XpVehicle::getOperatingStatus, query.getOperatingStatus())
                .eq(Objects.nonNull(query.getElectronicId()), XpVehicle::getElectronicId, query.getOperatingStatus());
                .eq(Objects.nonNull(query.getElectronicId()), XpVehicle::getElectronicId, query.getElectronicId());
        if (Objects.nonNull(query.getStartTime())) {
            wrapper.ge(XpVehicle::getAnnualInspectionDate, LocalDateTime.of(query.getStartTime(), LocalTime.MIN));
        }
@@ -960,7 +1004,7 @@
            throw new ServiceException("未查询到有效订单信息", I18nKeyConstant.ORDER_NOT_EXISTS);
        }
        XpVehicle xpVehicle = this.getById(xpOrder.getVehicleId());
        if (Objects.isNull(xpVehicle)) {
        if (Objects.isNull(xpVehicle) || Objects.isNull(xpVehicle.getVehicleVin())) {
            throw new ServiceException("未查询到有效车辆信息", I18nKeyConstant.VEHICLE_NOT_EXISTS);
        }
        try {
@@ -982,4 +1026,22 @@
        pageInfo.setRecords(page.getRecords());
        return pageInfo;
    }
    @Override
    public Boolean getVehicleTrunkOpenStatus(Long uid, Long id) {
        XpVehicle xpVehicle = this.getById(id);
        try {
            VehicleTemperatureDto vehicleTemperatureDto = vehicleControlUtil.queryVehicleTemperature(String.valueOf(uid), xpVehicle.getVehicleVin());
            log.info("查询车辆温度成功:{}", vehicleTemperatureDto);
            VehicleStateVo trunkAjarCloseSt = vehicleTemperatureDto.getStateMap().get("trunkAjarCloseSt");
            if(null == trunkAjarCloseSt){
                return false;
            }
            return trunkAjarCloseSt.getValue().equals("1");
        } catch (Exception e) {
            log.error("查询车辆温度失败", e);
            return false;
        }
    }
}