puzhibing
2026-04-18 93159eb7cd97fdca5ec832a4056c0d5222536929
新增加运维APP解锁车门接口
1个文件已添加
5个文件已修改
210 ■■■■■ 已修改文件
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/BluetoothUnlockClient.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/dto/ServiceNameConstantsEnum.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/dto/UnlockAppDTO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpVehicleService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/BluetoothUnlockClient.java
@@ -13,9 +13,8 @@
/**
 * 蓝牙解锁车门
 */
// todo 需要修改蓝牙微服务调用名
@FeignClient(contextId = "BluetoothUnlockClient",
        value = ServiceNameConstantsEnum.XP_VEHICLE_USER_SERVICE,
        value = ServiceNameConstantsEnum.XP_SMART_DIGITAL_KEY_BOOT,
        fallbackFactory = UserVehicleClientFallbackFactory.class)
public interface BluetoothUnlockClient {
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/dto/ServiceNameConstantsEnum.java
@@ -69,5 +69,9 @@
     * 小鹏短信服务
     */
    String XP_NOTIFY_BOOT = "xp-notify-boot";
    /**
     * 小鹏数字秘钥服务
     */
    String XP_SMART_DIGITAL_KEY_BOOT = "xp-smart-digital-key-boot";
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -18,7 +18,7 @@
import com.xiaopeng.api.dto.vehicleConvoy.UpdateVehicleStateDto;
import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto;
import com.xiaopeng.api.feignClient.maintenance.*;
import com.xiaopeng.api.model.maintenance.XpTask;
import com.xiaopeng.api.model.account.XpOperationsUser;import com.xiaopeng.api.model.maintenance.XpTask;
import com.xiaopeng.api.model.maintenance.XpTaskRule;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.xiaopeng.api.model.vehicleConvoy.XpFleetVehicle;
@@ -1108,4 +1108,17 @@
    public R<List<XpVehicle>> getServiceVehicleList(@RequestBody ServiceVehicleListDto dto){
        return R.ok(xpVehicleService.getServiceVehicleList(dto));
    }
    /**
     * 业务流程-车门解锁/后备箱解锁
     */
    @ApiOperation(value = "打开车门解锁/后备箱开关车门", tags = {"运维APP-车辆管理"})
    @PostMapping("/maintenance-app/api/v1/vehicle/unlockDoor")
    public R<Integer> unlockDoor(@RequestBody UnlockAppDTO dto) {
        XpOperationsUser xpOperationsUser = tokenService.getLoginOperationUser().getXpOperationsUser();
        return R.ok(xpVehicleService.unlockDoor(dto, xpOperationsUser));
    }
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/dto/UnlockAppDTO.java
New file
@@ -0,0 +1,25 @@
package com.xiaopeng.vehicleConvoy.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "运维APP-车辆解锁车门、后备箱")
public class UnlockAppDTO implements Serializable {
    @ApiModelProperty("车辆id")
    private Long vehicleId;
    @ApiModelProperty(value = "要开启的车门类型,取值: \"door\" — 主副驾车门 \"trunk\" — 后备箱")
    private String doorType;
    @ApiModelProperty(value = "用户经度")
    private Double longitude;
    @ApiModelProperty(value = "用户纬度")
    private Double latitude;
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpVehicleService.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.api.dto.vehicleConvoy.ServiceVehicleListDto;
import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto;
import com.xiaopeng.api.model.account.XpOperationsUser;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.vehicleConvoy.model.dto.*;
@@ -209,4 +210,12 @@
     * @return
     */
    List<XpVehicle> getServiceVehicleList(ServiceVehicleListDto dto);
    /**
     * 运维APP解锁车门和开关后备箱
     * @param dto
     * @return
     */
    Integer unlockDoor(UnlockAppDTO dto, XpOperationsUser xpOperationsUser);
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -11,9 +11,11 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableMap;
import com.xiaopeng.api.dto.account.UcUserInfo;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.dto.vehicleConvoy.ServiceVehicleListDto;
import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto;
import com.xiaopeng.api.feignClient.account.UserResourcesClient;
import com.xiaopeng.api.feignClient.automaticPilot.XpuVinClient;
import com.xiaopeng.api.feignClient.map.AdcFencePushClient;
import com.xiaopeng.api.feignClient.operation.XpElectronicClient;
@@ -21,6 +23,8 @@
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.feignClient.order.XpTestOrderClient;
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.UserVehicleClient;
import com.xiaopeng.api.model.account.XpOperationsUser;
import com.xiaopeng.api.model.operation.XpElectronicFence;
import com.xiaopeng.api.model.operation.XpElectronicFenceArea;
import com.xiaopeng.api.model.order.XpOrder;
@@ -28,9 +32,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.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.api.vo.vehicleConvoy.*;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.OrderStatusEnum;
@@ -127,11 +129,20 @@
    @Autowired(required = false)
    private AdcFencePushClient adcFencePushClient;
    @Resource
    private UserVehicleClient userVehicleClient;
    @Resource
    private UserResourcesClient userResourcesClient;
    @Value("${vehicle.control.distance-threshold:100}")
    private int distanceThreshold;
    @Value("${vehicle.control.report.enable:true}")
    private boolean reportEnable;
    @Value("${robotaxi.appId:xp_robotaxi_api}")
    private String appid;
    @Override
    public List<XpVehicle> listByIds(List<Long> ids) {
@@ -146,7 +157,7 @@
    public List<XpVehicle> listAvailableVehicles(Long orderId, Integer orderType) {
        // 1. 通过订单ID查询订单信息,获取区域ID
        Long areaId = null;
        if(null == orderType || 1 == orderType){
        if (null == orderType || 1 == orderType) {
            R<XpOrder> orderResult = xpOrderClient.queryOrderById(orderId);
            if (orderResult == null || orderResult.getData() == null) {
                log.warn("查询订单失败,orderId: {}", orderId);
@@ -155,7 +166,7 @@
            XpOrder order = orderResult.getData();
            areaId = order.getXpElectronicFenceAreaId();
        }else{
        } else {
            XpTestOrder testOrder = xpTestOrderClient.queryOrderById(orderId).getData();
            areaId = testOrder.getXpElectronicFenceAreaId();
        }
@@ -231,7 +242,7 @@
                xpVehicleVO.setMileageRange(vehiclePosition.getMileageRange());
                xpVehicleVO.setCurrentElectricity(vehiclePosition.getCurrentElectricity());
                GeoJsonPoint point = vehiclePosition.getPoint();
                if(Objects.nonNull(point)){
                if (Objects.nonNull(point)) {
                    xpVehicleVO.setLongitude(point.getX());
                    xpVehicleVO.setLatitude(point.getY());
                }
@@ -243,11 +254,11 @@
            }
            if (org.springframework.util.StringUtils.hasLength(xpVehicleVO.getVehiclePictures())) {
                JSONObject jsonObject = JSONObject.parseObject(xpVehicleVO.getVehiclePictures());
                if(jsonObject!=null){
                if (jsonObject != null) {
                    String downloadUrl = null;
                    try {
                        // 1
                        downloadUrl= aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true);
                        downloadUrl = aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
@@ -301,19 +312,19 @@
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
        if (Objects.nonNull(vehiclePosition)) {
            GeoJsonPoint point = vehiclePosition.getPoint();
            if(Objects.nonNull(point)){
                Map<String, Double> distance = GeodesyUtil.getDistance(dto.getLongitude()+ "," + dto.getLatitude(), point.getX()+","+point.getY());
            if (Objects.nonNull(point)) {
                Map<String, Double> distance = GeodesyUtil.getDistance(dto.getLongitude() + "," + dto.getLatitude(), point.getX() + "," + point.getY());
                if (null != distance) {
                    Double distance1 = distance.get("WGS84");
                    if (distance1 > distanceThreshold) {
                        log.info("距离过远,无法打开 {} {} {}", dto.getLongitude()+ "," + dto.getLatitude(), point.getX()+","+point.getY(), distance1);
                        log.info("距离过远,无法打开 {} {} {}", dto.getLongitude() + "," + dto.getLatitude(), point.getX() + "," + point.getY(), distance1);
                        throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
                    }
                }else {
                } else {
                    log.info("计算距离失败,无法打开车门");
                    throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
                }
            }else {
            } else {
                log.info("车辆无实时定位,无法打开车门");
                throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
            }
@@ -339,10 +350,10 @@
            //查询后备箱开关状态(打开状态=true,关闭状态=false)
            Boolean hasKey = vehicleControlUtil.queryTrunkDoorStateSync(xpOrder.getAppUserId(), xpVehicle.getVehicleVin(), reportEnable);
            //打开状态
            if(hasKey){
            if (hasKey) {
                xpOrder.setOpenTrunk(2);
                vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_CLOSE.getCommand());
            }else{
            } else {
                //关闭状态
                xpOrder.setOpenTrunk(1);
                vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_OPEN.getCommand());
@@ -361,7 +372,7 @@
            log.info("车辆控制结果:{}", msgId);
            if (StringUtils.isEmpty(msgId)) {
                return 0;
            }else {
            } else {
                xpOrderClient.updateOrder(xpOrder);
                return 1;
            }
@@ -403,7 +414,7 @@
            log.info("车辆温度设置结果:{}", msgId);
            if (StringUtils.isEmpty(msgId)) {
                return 0;
            }else {
            } else {
                return 1;
            }
        } catch (Exception e) {
@@ -440,7 +451,7 @@
        //绑定XPUID
        String vin = xpVehicle.getVehicleVin();
        R<String> xpuIdByVin = xpuVinClient.getXpuIdByVin(vin);
        if(200 != xpuIdByVin.getCode()){
        if (200 != xpuIdByVin.getCode()) {
            throw new RuntimeException(xpuIdByVin.getMsg());
        }
        String xpuid = xpuIdByVin.getData();
@@ -496,7 +507,7 @@
            // 仅在 VIN 变更或原 XPUID 为空时才重新绑定 XPUID,避免不必要的远程调用
            String vin = xpVehicle.getVehicleVin();
            R<String> xpuIdByVin = xpuVinClient.getXpuIdByVin(vin);
            if(200 != xpuIdByVin.getCode()){
            if (200 != xpuIdByVin.getCode()) {
                throw new RuntimeException(xpuIdByVin.getMsg());
            }
            String xpuid = xpuIdByVin.getData();
@@ -791,7 +802,7 @@
                .collect(Collectors.toSet());
        LocalDate now = LocalDate.now();
        List<String> errorMessages = new ArrayList<>();
        for (XpVehicle vehicle : vehicles) {
            List<String> reasons = new ArrayList<>();
            Locale locale = LocaleContextHolder.getLocale();
@@ -1121,6 +1132,7 @@
    /**
     * 获取服务车辆列表
     *
     * @param dto
     * @return
     */
@@ -1135,4 +1147,106 @@
        return list;
    }
    /**
     * 运维APP解锁车门和开关后备箱
     *
     * @param dto
     * @return
     */
    @Override
    public Integer unlockDoor(UnlockAppDTO dto, XpOperationsUser xpOperationsUser) {
        XpVehicle xpVehicle = this.getById(dto.getVehicleId());
        // 判断距离是否在范围之内 15米
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
        if (Objects.nonNull(vehiclePosition)) {
            GeoJsonPoint point = vehiclePosition.getPoint();
            if (Objects.nonNull(point)) {
                Map<String, Double> distance = GeodesyUtil.getDistance(dto.getLongitude() + "," + dto.getLatitude(), point.getX() + "," + point.getY());
                if (null != distance) {
                    Double distance1 = distance.get("WGS84");
                    if (distance1 > distanceThreshold) {
                        log.info("距离过远,无法打开 {} {} {}", dto.getLongitude() + "," + dto.getLatitude(), point.getX() + "," + point.getY(), distance1);
                        throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
                    }
                } else {
                    log.info("计算距离失败,无法打开车门");
                    throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
                }
            } else {
                log.info("车辆无实时定位,无法打开车门");
                throw new ServiceException("距离过远,无法打开", I18nKeyConstant.VEHICLE_DISTANCE_TOO_LONG);
            }
        }
        //先将运维人员和车辆绑定
        String phoneNumber = xpOperationsUser.getPhoneNumber();
        UcUserInfo userByMobile = userResourcesClient.getUserByMobile(phoneNumber);
        Long uid = userByMobile.getUid();
        AddTaxiPassengerVo addVo = new AddTaxiPassengerVo();
        addVo.setVin(xpVehicle.getVehicleVin());
        addVo.setUid(userByMobile.getUid().toString());
        addVo.setOrderNo(phoneNumber);
        addVo.setAppId(appid);
        userVehicleClient.addTaxiPassenger(addVo);
        log.info("已绑定运维与新车辆关系,电话号码: {}, VIN: {}", phoneNumber, xpVehicle.getVehicleVin());
        try {
            // 车门解锁、后备箱解锁
            VehiclesCommandVo vehiclesCommandVo = new VehiclesCommandVo();
            vehiclesCommandVo.setVin(xpVehicle.getVehicleVin());
            if ("door".equals(dto.getDoorType())) {
                // 开启车门 判断是否为P档
                Integer gearLevel = redisService.getCacheObject(CacheConstants.GEAR_LEVEL + xpVehicle.getXpuId());
                log.info("开启车门当前车辆档位: {} {}", xpVehicle.getXpuId(), gearLevel);
                if (gearLevel == null || gearLevel != 4) {
                    throw new ServiceException("车辆非P档,无法开启车门", I18nKeyConstant.VEHICLE_NOT_P_GEAR);
                }
                vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_UNLOCK.getCommand());
            } else {
                // 开启后备箱 判断是否为P档
                Integer gearLevel = redisService.getCacheObject(CacheConstants.GEAR_LEVEL + xpVehicle.getXpuId());
                log.info("开启后备箱当前车辆档位: {} {}", xpVehicle.getXpuId(), gearLevel);
                if (gearLevel == null || gearLevel != 4) {
                    throw new ServiceException("车辆非P档,无法操作后备箱", I18nKeyConstant.VEHICLE_NOT_P_GEAR);
                }
                //查询后备箱开关状态(打开状态=true,关闭状态=false)
                Boolean hasKey = vehicleControlUtil.queryTrunkDoorStateSync(uid, xpVehicle.getVehicleVin(), reportEnable);
                //打开状态
                if (hasKey) {
                    vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_CLOSE.getCommand());
                } else {
                    //关闭状态
                    vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_OPEN.getCommand());
                }
            }
            // {
            //    "code": 0,
            //    "data": {
            //        "msgId": "1234556"
            //    },
            //    "msg": "",
            //    "desc": "成功"
            //}
            String msgId = vehicleControlUtil.vehiclesCommand(vehiclesCommandVo, uid);
            log.info("车辆控制结果:{}", msgId);
            if (StringUtils.isEmpty(msgId)) {
                return 0;
            } else {
                return 1;
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //解绑运维与车辆的绑定关系
            CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo();
            cancelTaxiPassengerVo.setVin(xpVehicle.getVehicleVin());
            cancelTaxiPassengerVo.setUid(userByMobile.getUid().toString());
            cancelTaxiPassengerVo.setOrderNo(phoneNumber);
            cancelTaxiPassengerVo.setAppId(appid);
            userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo);
        }
        return 0;
    }
}