xuhy
2026-04-22 746517aca9d0b7ae334c70b3041a9b1de795addb
Merge branch '1.0' of http://120.76.84.145:10101/gitblit/r/java/Robotaxi into 1.0
7个文件已添加
44个文件已修改
1105 ■■■■■ 已修改文件
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpAppUserCouponController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/pushMessage/PushOrderRouteDTO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/vehicleConvoy/XpVehicleClientFallbackFactory.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/XpVehicleClient.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/operation/XpStation.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/query/account/XpAppUserCouponPageListQuery.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/query/operation/XpStationQuery.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/query/operation/XpTargetedServiceQuery.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/AppUserPosition.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/operation/RouteVo.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/vehicleConvoy/VehicleDoorStatusVo.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/vehicleConvoy/VehiclesCommandEnum.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/impl/VehicleRtInfoServiceImpl.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/ActiveOrderServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpVehicleModelServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpPointManagementController.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpStationManagementController.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/job/base/AbstractJob.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/job/jobs/ElectronicEffectJob.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpPointService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpStationService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpStationServiceImpl.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/resources/mapper/XpPointMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpOpenCityController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpTargetedServiceController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/query/WhitelistPageListQuery.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpOpenCityServiceImpl.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpTargetedServiceServiceImpl.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpTargetedServiceMapper.xml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpWhitelistMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/CustomerServiceController.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/vo/AppUserLocationVo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/vo/FenceVo.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/vo/VehicleFenceInformationVo.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/vo/VehicleSeatStatusVo.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/controller/PushOrderRouteController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/webSocket/handler/WebSocketTextFrameHandler.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/CarControlCallbackController.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/mapper/XpVehicleMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/dto/AirConditionerUnlockDTO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/query/VehicleMapQuery.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpVehicleService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/resources/mapper/vehicleConvoy/XpVehicleMapper.xml 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpAppUserCouponController.java
@@ -82,11 +82,11 @@
    @PostMapping("/v1/api/appUserCoupon/deleteByIds")
    @ApiOperation(value = "根据ids删除用户优惠券", tags = {"管理后台-白名单-删除用户优惠券"})
    public R<Boolean> deleteByIds(@RequestBody List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
    public R<Boolean> deleteByIds(@RequestBody XpAppUserCouponPageListQuery query  ) {
        if (query.getIds() == null || query.getIds().isEmpty()) {
            return R.fail("ID列表不能为空");
        }
        boolean result = xpAppUserCouponService.removeByIds(ids);
        boolean result = xpAppUserCouponService.removeByIds(query.getIds());
        return R.ok(result);
    }
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/pushMessage/PushOrderRouteDTO.java
@@ -39,4 +39,10 @@
    @ApiModelProperty(value = "消息类型")
    private String messageType;
    @ApiModelProperty("连环单上一单的终点经度")
    private Double endLongitude;
    @ApiModelProperty("连环单上一单的终点维度")
    private Double endLatitude;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/vehicleConvoy/XpVehicleClientFallbackFactory.java
@@ -7,6 +7,7 @@
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.api.vo.operation.VehiclePositionVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleDoorStatusVo;
import com.xiaopeng.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
@@ -114,6 +115,13 @@
                log.error("获取服务车辆列表失败,原因: {}", cause.getMessage());
                return R.fail("获取服务车辆列表失败:" + cause.getMessage());
            }
            @Override
            public R<VehicleDoorStatusVo> getVehicleDoorStatus(Long orderId) {
                log.error("获取车辆四门三盖状态失败,原因: {}", cause.getMessage());
                return R.fail("获取车辆四门三盖状态失败:" + cause.getMessage());
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/XpVehicleClient.java
@@ -5,13 +5,11 @@
import com.xiaopeng.api.dto.vehicleConvoy.UpdateVehicleStateDto;
import com.xiaopeng.api.factory.vehicleConvoy.XpVehicleClientFallbackFactory;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleDoorStatusVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -144,4 +142,13 @@
    @PostMapping("/logan/inner/vehicle/getServiceVehicleList")
    R<List<XpVehicle>> getServiceVehicleList(@RequestBody ServiceVehicleListDto dto);
    /**
     * 根据订单id查询四门三盖状态
     * @param orderId
     * @return
     */
    @GetMapping("/logan/inner/vehicle/getVehicleDoorStatus/{orderId}")
    R<VehicleDoorStatusVo> getVehicleDoorStatus(@PathVariable Long orderId);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/operation/XpStation.java
@@ -43,7 +43,7 @@
    @ApiModelProperty(value = "场站编号唯一 规则:城市首字母-HUB-4位流水号")
    private String stationNumber;
    @ApiModelProperty(value = "场站状态 0=草稿 1=待测试 2=测试中 3=测试不通过 4=可运营 5=待生效 6=启用中 7=已停用 8=失效")
    @ApiModelProperty(value = "场站状态 0=草稿 1=待启用 2=启用中 3=已停用 4=失效")
    private Integer status;
@@ -52,20 +52,12 @@
            if(status == 0){
                return "草稿";
            }else if(status == 1){
                return "待测试";
                return "待启用";
            } else if(status == 2){
                return "测试中";
            }else if(status == 3){
                return "测试未通过";
            }else if(status == 4){
                return "可运营";
            }else if(status == 5){
                return "待生效";
            }else if(status == 6){
                return "启用中";
            }else if(status == 7){
            }else if(status == 3){
                return "已停用";
            }else if(status == 8){
            }else if(status == 4){
                return "失效";
            }
        }
xp-robotaxi-api/src/main/java/com/xiaopeng/api/query/account/XpAppUserCouponPageListQuery.java
@@ -11,7 +11,7 @@
@Data
public class XpAppUserCouponPageListQuery extends BasePage {
    private  List<Long> ids;
    @ApiModelProperty(value = "用户id 根据手机号获取")
    private Long appUserId;
xp-robotaxi-api/src/main/java/com/xiaopeng/api/query/operation/XpStationQuery.java
@@ -14,11 +14,13 @@
    @ApiModelProperty(value = "场站名称编码")
    private String stationNameOrNumber;
    @ApiModelProperty(value = "点位经纬度")
    private String pointLonAndLat;
    @ApiModelProperty(value = "场站状态 0=草稿 1=待测试 2=测试中 3=测试不通过 4=可运营 5=待生效 6=启用中 7=已停用 8=失效")
    @ApiModelProperty(value = "场站状态 0=草稿 1=待启用 2=启用中 3=已停用 4=失效")
    private Integer status;
    @ApiModelProperty(value = "场站状态 0=草稿 1=待测试 2=测试中 3=测试不通过 4=可运营 5=待生效 6=启用中 7=已停用 8=失效")
    @ApiModelProperty(value = "场站状态 0=草稿 1=待启用 2=启用中 3=已停用 4=失效")
    private List<Integer> statusList;
    @ApiModelProperty(value = "创建人")
xp-robotaxi-api/src/main/java/com/xiaopeng/api/query/operation/XpTargetedServiceQuery.java
@@ -5,11 +5,16 @@
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(value = "定向服务 query")
public class XpTargetedServiceQuery extends BasePage {
    @ApiModelProperty(value = "ids")
    private List<Long> ids;
    @ApiModelProperty(value = "省份名称")
    private String provinceName;
    @ApiModelProperty(value = "省份code")
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/AppUserPosition.java
New file
@@ -0,0 +1,38 @@
package com.xiaopeng.api.vo.automaticPilot;
import lombok.Data;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.CompoundIndexes;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.Serializable;
/**
 * 车辆实时位置信息
 */
@Data
@Document(collection = "app_user_position")
@CompoundIndexes({
        @CompoundIndex(name = "appUserId", def = "{'appUserId': 1}"),
        @CompoundIndex(name = "location_index", def = "{'point': '2dsphere'}")
}
)
public class AppUserPosition implements Serializable {
    /**
     * 用户id
     */
    private Long appUserId;
    /**
     * 车辆实时位置信息
     */
    @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
    private GeoJsonPoint point;
    /**
     * 添加时间
     */
    private Long addTime;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/operation/RouteVo.java
@@ -30,4 +30,10 @@
    @ApiModelProperty("被截取的点位,用于前端使用")
    private List<LonLatDto> served;
    @ApiModelProperty("连环单上一单的终点经度")
    private Double endLongitude;
    @ApiModelProperty("连环单上一单的终点维度")
    private Double endLatitude;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/vehicleConvoy/VehicleDoorStatusVo.java
New file
@@ -0,0 +1,25 @@
package com.xiaopeng.api.vo.vehicleConvoy;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel
public class VehicleDoorStatusVo {
    @ApiModelProperty("前左门状态(开=1/关=0)")
    private Integer frontLeftDoor;
    @ApiModelProperty("前右门状态(开=1/关=0)")
    private Integer frontRightDoor;
    @ApiModelProperty("后左门状态(开=1/关=0)")
    private Integer rearLeftDoor;
    @ApiModelProperty("后右门状态(开=1/关=0)")
    private Integer rearRightDoor;
    @ApiModelProperty("后备箱盖状态(开=1/关=0)")
    private Integer trunk;
    @ApiModelProperty("前备箱盖状态(开=1/关=0)")
    private Integer frunk;
    @ApiModelProperty("充电口盖状态(开=1/关=0)")
    private Integer chargePort;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/vehicleConvoy/VehiclesCommandEnum.java
@@ -11,7 +11,9 @@
    DOOR_TRUNK_CLOSE("DOOR_TRUNK_CLOSE", "后备箱关闭"),
    DOOR_UNLOCK("DOOR_UNLOCK", "车门开锁"),
    HVAC_TEMP_SET_FLOAT("HVAC_TEMP_SET_FLOAT", "设定温度"),
    STATUS_REPORT_NOW("STATUS_REPORT_NOW", "上报实时车况");
    STATUS_REPORT_NOW("STATUS_REPORT_NOW", "上报实时车况"),
    HVAC_ON("HVAC_ON", "空调开启"),
    HVAC_OFF("HVAC_OFF", "空调关闭");
    /**
     * 命令
     */
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java
@@ -450,9 +450,12 @@
                    if(!order_id.contains("TEST")){
                        xpPushMessageClient.pushOrderRoute(dto);//存储历史轨迹
                        // 给连环派单的用户推送
                        Long nextOrderId = redisService.getCacheObject(CacheConstants.nextOrder(order_id));
                        if (null != nextOrderId){
                            dto.setOrderId(nextOrderId);
                        String json = redisService.getCacheObject(CacheConstants.nextOrder(order_id));
                        if (StringUtils.isNotEmpty(json)){
                            JSONObject jsonObject = JSON.parseObject(json);
                            dto.setOrderId(jsonObject.getLong("orderId"));
                            dto.setEndLongitude(jsonObject.getDouble("endLongitude"));
                            dto.setEndLatitude(jsonObject.getDouble("endLatitude"));
                            xpPushMessageClient.pushOrderRoute(dto);
                        }
                        xpOrderClient.saveVehicleLocation(orderTrajectory);
@@ -1343,8 +1346,10 @@
                                xpOrderClient.updateOrderStatus(Long.parseLong(orderId), OrderStatusEnum.SEE_OFF_GUESTS.getCode(), true);
                                redisService.setCacheObject("ORDER_STATUS:" + orderId, OrderStatusEnum.SEE_OFF_GUESTS.getCode(), 24L, TimeUnit.HOURS);
                                // 判断当前订单的车辆是否有连环派单,然后判断是都还满足连环派单的条件,不满足则需要重新派单
                                Long nextOrderId = redisService.getCacheObject(CacheConstants.nextOrder(orderId));
                                if(null != nextOrderId){
                                String json = redisService.getCacheObject(CacheConstants.nextOrder(orderId));
                                if (StringUtils.isNotEmpty(json)){
                                    JSONObject jsonObject = JSON.parseObject(json);
                                    Long nextOrderId = jsonObject.getLong("orderId");
                                    XpOrder nextOrder = xpOrderClient.queryOrderById(nextOrderId).getData();
                                    if(null != nextOrder && nextOrder.getStatus() == OrderStatusEnum.PICK_UP.getCode()){
                                        redisService.setCacheObject(CacheConstants.nextOrder(orderId),null);
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/impl/VehicleRtInfoServiceImpl.java
@@ -162,16 +162,18 @@
        if (!SmsType.ACCEPT_RIDES.name().equals(smsType)) {
            return null;
        }
//        XpVehicle vehicle = xpVehicleClient.findByVehicleVin(vin).getData();
//        if (vehicle == null) {
//            return XpCommonResponse.fail(RobotaxiErrorEnum.MOBILE_VIN_MISMATCH);
//        }
//        Integer status = vehicle.getStatus();
//        Integer operatingStatus = vehicle.getOperatingStatus();
//        if (status == null || status != 2 || operatingStatus == null || operatingStatus != 1) {
//            return XpCommonResponse.fail(RobotaxiErrorEnum.VEHICLE_NOT_READY);
//        }
        XpVehicle vehicle = xpVehicleClient.findByVehicleVin(vin).getData();
        if (vehicle == null) {
            return XpCommonResponse.fail(RobotaxiErrorEnum.MOBILE_VIN_MISMATCH);
        }
        Integer status = vehicle.getStatus();
        Integer operatingStatus = vehicle.getOperatingStatus();
        if (smsType.equals(SmsType.ACCEPT_RIDES.name()) && operatingStatus != 1) {
            return XpCommonResponse.fail(RobotaxiErrorEnum.VEHICLE_NOT_READY);
        }
        if (smsType.equals(SmsType.STOP_ACCEPT_RIDES.name()) && operatingStatus != 3) {
            return XpCommonResponse.fail(RobotaxiErrorEnum.VEHICLE_NOT_READY);
        }
        return null;
    }
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/ActiveOrderServiceImpl.java
@@ -2,6 +2,7 @@
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.xiaopeng.api.dto.automaticPilot.PushOrderDto;
import com.xiaopeng.api.dto.operation.ReassignOrderDTO;
import com.xiaopeng.api.dto.order.OrderChangeDto;
@@ -25,6 +26,7 @@
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.OrderStatusEnum;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.itinerary.constant.I18nKeyConstant;
import com.xiaopeng.itinerary.mapper.XpElectronicFenceAreaMapper;
@@ -208,8 +210,10 @@
        // 判断当前订单的车辆是否有连环派单,然后判断是都还满足连环派单的条件,不满足则需要重新派单
        Long nextOrderId = redisService.getCacheObject(CacheConstants.nextOrder(order.getId().toString()));
        if(null != nextOrderId){
        String json = redisService.getCacheObject(CacheConstants.nextOrder(order.getId().toString()));
        if (StringUtils.isNotEmpty(json)){
            JSONObject jsonObject = JSON.parseObject(json);
            Long nextOrderId = jsonObject.getLong("orderId");
            XpOrder nextOrder = xpOrderClient.queryOrderById(nextOrderId).getData();
            if(null != nextOrder && nextOrder.getStatus() == OrderStatusEnum.PICK_UP.getCode()){
                redisService.setCacheObject(CacheConstants.nextOrder(order.getId().toString()),null);
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpVehicleModelServiceImpl.java
@@ -43,12 +43,12 @@
    @Override
    public boolean logicalRemoveById(String id) {
        XpVehicleModel entity = this.getById(id);
        if (entity != null) {
            entity.setDeleted(true);
            return this.updateById(entity);
        }
        return false;
//        XpVehicleModel entity = this.getById(id);
//        if (entity != null) {
//            entity.setDeleted(true);
//            return this.updateById(entity);
//        }
        return this.removeById(id);
    }
    /**
     * 获取指定车型ID关联的车辆数量
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpPointManagementController.java
@@ -3,6 +3,10 @@
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.xiaopeng.api.dto.map.XpPointExportExcelQuery;
import com.xiaopeng.api.dto.operation.PointAddDto;
import com.xiaopeng.api.dto.operation.PointUpdateDto;
@@ -25,6 +29,7 @@
import com.xiaopeng.map.service.XpElectronicFenceService;
import com.xiaopeng.map.service.XpPointService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
@@ -70,10 +75,16 @@
    @ApiOperation(value = "获取所有运营中的区域", response = XpElectronicFenceArea.class)
    @PostMapping("/listElectronicFenceArea")
    public R<List<XpElectronicFenceArea>> listElectronicFenceArea() {
    public R<List<XpElectronicFenceArea>> listElectronicFenceArea(@RequestParam(required = false) String cityCode) {
        // 获取所有启用的电子围栏
        List<XpElectronicFence> electronicFences = xpElectronicFenceService.lambdaQuery()
                .eq(XpElectronicFence::getStatus, 2).list();
        LambdaQueryWrapper<XpElectronicFence>  queryWrapper= new LambdaQueryWrapper<>();
        queryWrapper.eq(XpElectronicFence::getStatus, 2);
        if(StrUtil.isNotBlank(cityCode)){
            queryWrapper.eq(XpElectronicFence::getCityCode, cityCode);
        }
        List<XpElectronicFence> electronicFences = xpElectronicFenceService.list(queryWrapper);
        List<Long> electronicFenceIds = electronicFences.stream().map(XpElectronicFence::getId).collect(Collectors.toList());
        if (electronicFenceIds.isEmpty()) {
            return R.ok(new ArrayList<XpElectronicFenceArea>());
@@ -224,6 +235,15 @@
            }
        }
        xpPoint.setFpElectronicAreaId(null);
        Long stationId = pointAddDto.getStationId();
        Integer stationSortNo = pointAddDto.getStationSortNo(); //点位在场站内排序
        if(stationId != null && stationId > 0){
            if(stationSortNo == null){
                stationSortNo = 0;
            }
        }
        // 使用 lambdaUpdate 确保 pointPictures 为 null 时也能将数据库字段清空
        // (updateById 默认策略 NOT_NULL 会跳过 null 值,导致删除图片后数据库仍保留旧图片)
        xpPointService.lambdaUpdate()
@@ -252,9 +272,12 @@
                .set(XpPoint::getCityName, xpPoint.getCityName())
                .set(XpPoint::getDistrictName, xpPoint.getDistrictName())
                .set(XpPoint::getDistrictCode, xpPoint.getDistrictCode())
                .set(XpPoint::getStationId, xpPoint.getStationId())
                .set(XpPoint::getStationSortNo, xpPoint.getStationSortNo())
                .set(XpPoint::getStationId, stationId)
                .set(XpPoint::getStationSortNo, stationSortNo)
                .update();
        //场站点位排序 后添加的点位 排第一个。
        xpPointService.setStationSortNo(xpPoint.getStationId(),stationSortNo);
        return R.ok();
    }
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpStationManagementController.java
@@ -2,6 +2,7 @@
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xiaopeng.api.model.operation.XpStation;
import com.xiaopeng.api.query.operation.XpStationQuery;
import com.xiaopeng.api.vo.account.LoginUser;
@@ -47,7 +48,17 @@
    private TokenService tokenService;
    @ApiOperation(value = "场站管理列表", tags = {"管理后台-运营-场站管理"})
    @ApiOperation(value = "场站管理已经启动列表", tags = {"管理后台-运营-场站管理"})
    @GetMapping("/findListEnable")
    public R<List<XpStation>> findListEnable(@RequestParam String cityCode) {
        LambdaQueryWrapper<XpStation> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(XpStation::getStatus, 2);
        queryWrapper.eq(XpStation::getCityCode, cityCode);
        queryWrapper.eq(XpStation::getDeleted, false);
        return R.ok( xpStationService.list(queryWrapper));
    }
    @ApiOperation(value = "场站管理分页列表", tags = {"管理后台-运营-场站管理"})
    @PostMapping("/pageList")
    public R<PageInfo<XpStation>> pageList(@RequestBody XpStationQuery query) {
        PageInfo<XpStation> pageList = xpStationService.pageList(query);
@@ -66,11 +77,9 @@
        LoginUser loginUser = tokenService.getLoginUser();
        vo.setCreateBy(loginUser.getUsername());
        vo.setUpdateBy(loginUser.getUsername());
        vo.setCreateTime(LocalDateTime.now());
        vo.setUpdateBy(loginUser.getUsername());
        vo.setUpdateTime(LocalDateTime.now());
        return xpStationService.add(vo);
    }
@@ -89,6 +98,10 @@
        if(bean == null){
            return R.fail("参数错误");
        }
        //场站状态 0=草稿 1=待启用 2=启用中 3=已停用 4=失效
        if(bean.getStatus() == 2){
            return R.fail("场站在启动中不能编辑");
        }
        LoginUser loginUser = tokenService.getLoginUser();
@@ -106,18 +119,22 @@
     *       - 否:子点位维持当前状态,但自动退回为“独立点位”。
     */
    @ApiOperation(value = "停用" , tags = {"管理后台-运营-场站管理"})
    @PostMapping("/updateStatus/{id}")
    public R updateStatusStop(@PathVariable Long id, @RequestParam Boolean pointStop) {
    @GetMapping("/updateStatus/{id}")
    public R updateStatusStop(@PathVariable Long id, @RequestParam Boolean isEnable, @RequestParam Boolean pointStop) {
        XpStation bean = xpStationService.getById(id);
        if (bean == null) {
            throw new ServiceException("场站不存在,请刷新重试");
        }
//        Integer status =6;
//        if(!statusEnable){
//            status = 7;
//        }
        //场站状态 0=草稿 1=待测试 2=测试中 3=测试不通过 4=可运营 5=待生效 6=启用中 7=已停用 8=失效
        xpStationService.updateStatusStop(bean,pointStop);
        if(isEnable){
            xpStationService.updateStatusEnable(bean);
        }else{
            if(bean.getStatus() == 2){//场站状态 0=草稿 1=待启用 2=启用中 3=已停用 4=失效
                return R.fail("场站状态未启动,不能停用");
            }
            //场站状态 0=草稿 1=待测试 2=测试中 3=测试不通过 4=可运营 5=待生效 6=启用中 7=已停用 8=失效
            xpStationService.updateStatusStop(bean,pointStop);
        }
        return R.ok();
    }
    /**
@@ -137,18 +154,10 @@
        if (bean == null) {
            throw new ServiceException("场站不存在,请刷新重试");
        }
        // 场站状态 0=草稿 1=待测试 2=测试中 3=测试不通过 4=可运营 5=待生效 6=启用中 7=已停用 8=失效
        // 场站状态  0=草稿 1=待启用 2=启用中 3=已停用 4=失效
        // 只有状态为已停用(7)时才能删除
        if (bean.getStatus() == 4 || bean.getStatus() == 5 || bean.getStatus() == 6) {
            String msg = "";
            if(bean.getStatus() == 4){
                msg = "可运营";
            }else if (bean.getStatus() == 5){
                msg = "待生效";
            }else {
                msg = "启用中";
            }
            throw new ServiceException("场站"+msg+",无法删除");
        if (bean.getStatus() == 2 ) {
            throw new ServiceException("场站启用中,无法删除");
        }
        boolean b = xpStationService.removeById(bean,pointDelete);
        if (b){
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/job/base/AbstractJob.java
@@ -2,10 +2,7 @@
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
import com.xiaopeng.map.job.utils.SpringContextsUtil;
import com.xiaopeng.map.service.AdcFencePushService;
import com.xiaopeng.map.service.XpElectronicFenceAreaService;
import com.xiaopeng.map.service.XpElectronicFenceService;
import com.xiaopeng.map.service.XpPointService;
import com.xiaopeng.map.service.*;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
@@ -23,6 +20,7 @@
    protected XpPointService xpPointService;
    protected XpPushMessageClient xpPushMessageClient;
    protected AdcFencePushService adcFencePushService;
    protected XpStationService xpStationService;
    public AbstractJob(){
        this.xpElectronicFenceService = SpringContextsUtil.getBean(XpElectronicFenceService.class);
@@ -30,6 +28,7 @@
        this.xpPointService = SpringContextsUtil.getBean(XpPointService.class);
        this.xpPushMessageClient = SpringContextsUtil.getBean(XpPushMessageClient.class);
        this.adcFencePushService = SpringContextsUtil.getBean(AdcFencePushService.class);
        this.xpStationService = SpringContextsUtil.getBean(XpStationService.class);
    }
     
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/job/jobs/ElectronicEffectJob.java
@@ -59,6 +59,7 @@
                }
                xpElectronicFenceAreaService.updateBatchById(electronicFenceAreaList);
                xpStationService.setStatusFailure(electronicId);//场站失效
                // 清除绑定车辆的 FBS 数据
                try {
                    adcFencePushService.deactivateFenceForVehicles(electronicId);
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpPointService.java
@@ -41,6 +41,10 @@
     */
    XpPoint addPointWithGeneratedNumber(@Valid PointAddDto pointAddDto);
    @Transactional
    void setStationSortNo(Long stationId, Integer stationSortNo);
    String getPointNumber(Long electronicAreaId);
    /**
@@ -113,5 +117,8 @@
    void statusStopByStationId(Long stationId, Boolean pointStop);
    @Transactional(rollbackFor = Exception.class)
    void statusEnableByStationId(Long stationId);
    @Transactional(rollbackFor = Exception.class)
    void pointDeleteByStationId(Long stationId, Boolean pointDelete);
}
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpStationService.java
@@ -26,6 +26,8 @@
    PageInfo<XpStation> pageList(XpStationQuery query);
    boolean isPolygonInPolygon(String innerFencePoints, String outerFencePoints);
    void validate(XpStationVo dto);
    @Transactional
@@ -37,8 +39,12 @@
    R edit(XpStationVo dto);
    void setStatusFailure(Long electronicId);
    boolean setStatus(XpStation bean, Integer status);
    boolean updateStatusEnable(XpStation bean);
    boolean updateStatusStop(XpStation bean, Boolean pointStop);
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java
@@ -828,6 +828,13 @@
        );
    }
    /**
     * 失效
     * @param electronicId
     * @param failureType
     * @param failureTime
     * @param now
     */
    private void registerFailureJobIfNeeded(Long electronicId, Integer failureType, LocalDateTime failureTime, LocalDateTime now) {
        QuartzManager.removeJob((ElectronicEffectJob.failure + electronicId).toUpperCase(), TimeJobType.ELECTRONIC_FAILURE);
        if (failureType == null || failureType != 2 || failureTime == null || failureTime.isBefore(now)) {
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java
@@ -57,6 +57,7 @@
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -104,6 +105,7 @@
    private XpElectronicFenceMapper xpElectronicFenceMapper;
    @Resource
    @Lazy
    private XpStationService xpStationService;
    @Resource
@@ -136,9 +138,40 @@
        BeanUtils.copyProperties(pointAddDto, xpPoint);
        xpPoint.setFpElectronicAreaId(pointAddDto.getElectronicAreaId());
        xpPoint.setPointNumber(pointNumber);
        xpPoint.setStationSortNo(0);
        this.save(xpPoint);
        //场站点位排序 后添加的点位 排第一个。
        this.setStationSortNo(xpPoint.getStationId(),0);
        return xpPoint;
    }
    /**
     * 场站 点位排序
     * @param stationId
     * @param stationSortNo
     */
    @Transactional
    @Override
    public void setStationSortNo(Long stationId, Integer stationSortNo){
        if(null != stationId  && stationSortNo != null && stationSortNo ==0){
            // 查询该场站下的所有点位,按 stationSortNo 排序
            List<XpPoint> points = this.lambdaQuery()
                    .eq(XpPoint::getStationId, stationId)
                    .orderByAsc(XpPoint::getStationSortNo)
                    .list();
            if (CollUtil.isNotEmpty(points)) {
                int newSortNo = 1;
                for (XpPoint point : points) {
                    point.setStationSortNo(newSortNo++);
                }
                this.updateBatchById(points);
            }
        }
    }
    @Override
    public String getPointNumber(Long electronicAreaId) {
        String cityPrefix = buildCityPrefix(electronicAreaId);
@@ -354,12 +387,12 @@
                        continue;
                    }
                    String downloadUrl ="";
                    try {
                        downloadUrl = aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true);
                        log.info("downloadUrl:{}", downloadUrl);
                    } catch (UnsupportedEncodingException e) {
                        log.error("获取图片下载地址失败", e);
                    }
//                    try { todo 20260422 yyy暂时注释
//                        downloadUrl = aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true);
//                        log.info("downloadUrl:{}", downloadUrl);
//                    } catch (UnsupportedEncodingException e) {
//                        log.error("获取图片下载地址失败", e);
//                    }
                    jsonObject.put("url", downloadUrl);
                }
                log.info("jsonArray:{}", jsonArray);
@@ -746,6 +779,18 @@
        }
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void statusEnableByStationId(Long stationId) {
        if (stationId == null) {
            return;
        }
        this.lambdaUpdate()
                .eq(XpPoint::getStationId, stationId)
                .set(XpPoint::getOperationStatus, 2)//运营状态 1=待启用 2=启用中 3=已停用 4=已失效
                .update();
    }
    /**
     * 场站删除
     *       - 删除拦截逻辑:
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpStationServiceImpl.java
@@ -7,10 +7,12 @@
import com.xiaopeng.api.model.map.XpGeoFencingPolygon;
import com.xiaopeng.api.model.operation.*;
import com.xiaopeng.api.query.operation.XpStationQuery;
import com.xiaopeng.api.vo.account.LoginUser;
import com.xiaopeng.api.vo.operation.XpStationVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.security.service.TokenService;
import com.xiaopeng.map.constant.I18nKeyConstant;
import com.xiaopeng.map.mapper.XpRegionMapper;
import com.xiaopeng.map.mapper.XpStationMapper;
@@ -24,6 +26,7 @@
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.annotation.Lazy;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.query.Criteria;
@@ -34,7 +37,6 @@
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@@ -49,6 +51,8 @@
public class XpStationServiceImpl extends ServiceImpl<XpStationMapper, XpStation> implements XpStationService {
    @Resource
    private TokenService tokenService;
    @Autowired
    private MongoTemplate mongoTemplate;
@@ -62,6 +66,7 @@
    private GDMapGeocodingUtil gdMapGeocodingUtil;
    @Autowired
    private XpRegionMapper xpRegionMapper;
    @Lazy
    @Resource
    private XpPointService xpPointService;
@@ -164,6 +169,7 @@
     * @param outerFencePoints 外部围栏点位字符串(参考围栏),格式为 "lng1_lat1,lng2_lat2,..."
     * @return 是否在内 true在内 false不在
     */
    @Override
    public boolean isPolygonInPolygon(String innerFencePoints, String outerFencePoints) {
        if (innerFencePoints == null || innerFencePoints.trim().isEmpty()
                || outerFencePoints == null || outerFencePoints.trim().isEmpty()) {
@@ -314,7 +320,10 @@
                );
            }
        }
    }
    public static String getPinyinInitials(String chinese) {
        StringBuilder pinyinInitials = new StringBuilder();
@@ -354,9 +363,11 @@
            this.updateById(dto);
            save_operatingArea(dto);
            savePoint(dto);
            saveMongo_operatingArea(dto);
            return R.ok();
        } catch (Exception e) {
            log.error("新增场站失败", e);
@@ -373,11 +384,11 @@
        try {
//            dto.setStatus(0); //status = 场站状态 0=草稿 1=待测试 2=测试中 3=测试不通过 4=可运营 5=待生效 6=启用中 7=已停用 8=失效
            this.updateById(dto);
            // 删除旧的区域数据,然后保存新的区域数据
            remove_operatingArea(dto);
            save_operatingArea(dto);
            xpPointService.clearStationInfoByStationId(dto.getId());
            savePoint(dto);
            // 删除旧的区域数据,然后保存新的区域数据
            removeMongo_operatingArea(dto);
            saveMongo_operatingArea(dto);
            return R.ok();
        } catch (Exception e) {
            log.error("新增场站失败", e);
@@ -412,45 +423,78 @@
        }
    }
    /**
     * 场站 失效-围栏失效 场站失效
     * @param electronicId
     */
    @Override
    public void setStatusFailure(Long electronicId){
        LambdaQueryWrapper<XpStation> query = new LambdaQueryWrapper<>();
        query.eq(XpStation::getElectronicId, electronicId);
        query.eq(XpStation::getDeleted, 0);
        List<XpStation> list = this.list(query);
        if(!list.isEmpty()){
            for (XpStation xpStation : list) {
                this.setStatus(xpStation,4);
            }
        }
    }
    @Override
    public boolean setStatus(XpStation bean, Integer status){
        bean.setStatus(status);
        LoginUser loginUser = tokenService.getLoginUser();
        bean.setUpdateBy(loginUser.getUsername());
        bean.setUpdateTime(LocalDateTime.now());
        bean.setStatus(status);//场站状态 0=草稿 1=待启用 2=启用中 3=已停用 4=失效
        boolean b = updateById(bean);
        if(b){
            //场站状态 0=草稿 1=待测试 2=测试中 3=测试不通过 4=可运营 5=待生效 6=启用中 7=已停用 8=失效
            List<Integer> statusList = Arrays.asList(4, 5, 6, 7) ;
            if(statusList.contains(status) ){
                save_operatingArea(bean);
            }
            if(status == 7 ){
                remove_operatingArea(bean);
            if(status == 2){
                saveMongo_operatingArea(bean);
            }else if(status == 3 || status == 4){
                removeMongo_operatingArea(bean);
            }
        }
        return b;
    }
    private void remove_operatingArea(XpStation bean) {
    private void removeMongo_operatingArea(XpStation bean) {
        // 同步删除Mongo电子围栏多边形
        Query delMongoQuery = new Query();
        delMongoQuery.addCriteria(Criteria.where("stationId").is(String.valueOf(bean.getId())));
        mongoTemplate.remove(delMongoQuery, XpGeoFencingPolygon.class);
    }
    private void save_operatingArea (XpStation bean) {
        XpGeoFencingPolygon xpGeoFencingPolygon = new XpGeoFencingPolygon();
        xpGeoFencingPolygon.setFenceId(String.valueOf(bean.getElectronicId()));
        xpGeoFencingPolygon.setName(bean.getStationName());
        xpGeoFencingPolygon.setAreaId(String.valueOf(bean.getElectronicAreaId()));
        xpGeoFencingPolygon.setCityCode(bean.getCityCode());
        xpGeoFencingPolygon.setStationId(String.valueOf(bean.getId()));
        xpGeoFencingPolygon.setOperationStrategy(null);//运营策略
        // 将围栏点转换为GeoJSON多边形
        GeoJsonPolygon geoJsonPolygon = xpElectronicFenceService.convertToGeoJsonPolygon(bean.getFencePoints());
        xpGeoFencingPolygon.setPoints(geoJsonPolygon);
        mongoTemplate.insert(xpGeoFencingPolygon);
    private void saveMongo_operatingArea (XpStation bean) {
        //场站状态 0=草稿 1=待启用 2=启用中 3=已停用 4=失效
        if(bean.getStatus() == 2){
            XpGeoFencingPolygon xpGeoFencingPolygon = new XpGeoFencingPolygon();
            xpGeoFencingPolygon.setFenceId(String.valueOf(bean.getElectronicId()));
            xpGeoFencingPolygon.setName(bean.getStationName());
            xpGeoFencingPolygon.setAreaId(String.valueOf(bean.getElectronicAreaId()));
            xpGeoFencingPolygon.setCityCode(bean.getCityCode());
            xpGeoFencingPolygon.setStationId(String.valueOf(bean.getId()));
            xpGeoFencingPolygon.setOperationStrategy(null);//运营策略
            // 将围栏点转换为GeoJSON多边形
            GeoJsonPolygon geoJsonPolygon = xpElectronicFenceService.convertToGeoJsonPolygon(bean.getFencePoints());
            xpGeoFencingPolygon.setPoints(geoJsonPolygon);
            mongoTemplate.insert(xpGeoFencingPolygon);
        }
    }
    /**
     * 启用操作
     * @param bean
     * @return
     */
    @Override
    public boolean updateStatusEnable(XpStation bean){
        xpPointService.statusEnableByStationId(bean.getId());
        return setStatus(bean, 2);
    }
    /**
     * 停用
@@ -462,7 +506,7 @@
    public boolean updateStatusStop(XpStation bean, Boolean pointStop){
        xpPointService.statusStopByStationId(bean.getId(),pointStop);
        return setStatus(bean, 7);
        return setStatus(bean, 3);
    }
    /**
@@ -478,18 +522,16 @@
    @Override
    public boolean removeById(XpStation bean, Boolean pointDelete){
        boolean b = true;
        //场站状态 0=草稿 1=待测试 2=测试中 3=测试不通过 4=可运营 5=待生效 6=启用中 7=已停用 8=失效
        if(bean.getStatus() == 1 || bean.getStatus() == 2 || bean.getStatus() == 3 || bean.getStatus() == 7 || bean.getStatus() == 8){
        //场站状态  0=草稿 1=待启用 2=启用中 3=已停用 4=失效
        if(bean.getStatus() != 2){
            b = super.removeById(bean.getId());
        }
        if(bean.getStatus() == 7 || bean.getStatus() == 8){
             xpPointService.pointDeleteByStationId(bean.getId(),pointDelete);
        }
        if(b){
            // 同步删除Mongo电子围栏多边形
            Query delMongoQuery = new Query();
            delMongoQuery.addCriteria(Criteria.where("stationId").is(String.valueOf(bean.getId())));
            mongoTemplate.remove(delMongoQuery, XpGeoFencingPolygon.class);
            xpPointService.pointDeleteByStationId(bean.getId(),pointDelete);
            if(b){
                // 同步删除Mongo电子围栏多边形
                Query delMongoQuery = new Query();
                delMongoQuery.addCriteria(Criteria.where("stationId").is(String.valueOf(bean.getId())));
                mongoTemplate.remove(delMongoQuery, XpGeoFencingPolygon.class);
            }
        }
        return b;
xp-robotaxi-map-boot/src/main/resources/mapper/XpPointMapper.xml
@@ -72,6 +72,9 @@
        select t1.*
        from xp_point t1
        where t1.is_deleted = 0
        <if test="query.cityCode != null and query.cityCode != ''">
            and t1.city_code = #{query.cityCode}
        </if>
        <if test="query.queryParams != null and query.queryParams != ''">
            and (
            t1.point_name like concat('%',#{query.queryParams},'%')
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpOpenCityController.java
@@ -47,6 +47,11 @@
        return R.ok(xpOpenCityService.citiesList());
    }
    @ApiOperation(value = "获取开通城市启用列表",tags = {"管理后台-开通城市-获取启用的数据"},response = XpOpenCityVo.class)
    @GetMapping("/api/v1/openCity/findLstEnable")
    public R<XpOpenCityVo> openCityFindList() {
        return R.ok(xpOpenCityService.citiesList());
    }
    /**
     * 获取开通城市列表(管理后台使用)
     * 用于路线规划配置的新增和编辑
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpTargetedServiceController.java
@@ -1,5 +1,6 @@
package com.xiaopeng.operation.controller;
import cn.hutool.core.collection.CollUtil;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.api.model.operation.XpTargetedService;
import com.xiaopeng.api.model.operation.XpWhitelist;
@@ -116,12 +117,14 @@
    /**
     * 批量删除
     */
    @GetMapping("/api/v1/targetedService/deleteByIds")
    @PostMapping("/api/v1/targetedService/deleteByIds")
    @ApiOperation(value = "定向服务-批量删除", tags = {"管理后台-定向服务批量删除"})
    public R deleteByIds(String ids) {
    public R deleteByIds(@RequestBody XpTargetedServiceQuery query ) {
        if(CollUtil.isEmpty(query.getIds())){
            return R.fail("ID列表不能为空");
        }
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        List<Long> whiteIds = Arrays.stream(ids.split(",")).map(Long::parseLong).collect(Collectors.toList());
        return xpTargetedServiceService.deleteByIds(whiteIds, sysUser);
        return xpTargetedServiceService.deleteByIds(query.getIds(), sysUser);
    }
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/query/WhitelistPageListQuery.java
@@ -12,6 +12,8 @@
public class WhitelistPageListQuery extends BasePage {
    @ApiModelProperty(value = "白名单账号")
    private String account;
    @ApiModelProperty(value = "用户名称")
    private String nickName;
    @ApiModelProperty(value = "状态 0=冻结 1=正常")
    private Boolean status;
    @ApiModelProperty(value = "来源 1=添加 2=邀请码")
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpOpenCityServiceImpl.java
@@ -76,11 +76,11 @@
                        .orderByAsc(XpOpenCity::getId)
        );
        List<Long> existsOpenCityIds = new ArrayList<>();
        //2.根据type过滤已配置的开通城市
        //2.根据type过滤已配置的开通城市  类型:1-路线规划配置使用 2-定价管理配置使用
        if (Integer.valueOf(1).equals(type)) {
            //路径规划
            existsOpenCityIds = xpRoutePlanService.list().stream().map(XpRoutePlan::getOpenCityId).collect(Collectors.toList());
        } else {
        } else if (Integer.valueOf(2).equals(type)){
            //定价管理
            existsOpenCityIds = xpPricingService.list().stream().map(XpPricing::getCityId).collect(Collectors.toList());
        }
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpTargetedServiceServiceImpl.java
@@ -81,7 +81,7 @@
    
    @Override
    public XpTargetedServiceVo getById(Long id) {
        XpTargetedService entity = this.getById(id);
        XpTargetedService entity = baseMapper.selectById(id);
        if (entity == null) {
            return null;
        }
@@ -170,17 +170,21 @@
    }
    @Override
    @Transactional
    public R deleteByIds(List<Long> ids, SysUser sysUser) {
        List<XpTargetedService> list = new ArrayList<>();
        for (Long id : ids) {
            list.add(new XpTargetedService(){{
                setId(id);
                setUpdateBy(sysUser.getNickName());
                setDeleted(true);
            }});
//        List<XpTargetedService> list = new ArrayList<>();
//        for (Long id : ids) {
//            list.add(new XpTargetedService(){{
//                setId(id);
//                setUpdateBy(sysUser.getNickName());
//                setUpdateTime(LocalDateTime.now());
//                setDeleted(true);
//            }});
//
//        }
//        this.updateBatchById(list);
        }
        this.updateBatchById(list);
        removeBatchByIds(ids);
        return R.ok();
    }
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java
@@ -2,6 +2,7 @@
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.xiaopeng.api.dto.automaticPilot.PushOrderDto;
import com.xiaopeng.api.dto.map.RouteAndFareDto;
import com.xiaopeng.api.dto.operation.LonLatDto;
@@ -129,6 +130,7 @@
    /**
     * 5秒执行一次
     * 播单
     */
    @Scheduled(cron = "0/5 * * * * ?")
    public void taskOrder() {
@@ -1160,7 +1162,11 @@
                pushOrderDto.setTaskId(xpOrder.getXpuTaskId());
                // 订单流转
                orderMissionClient.pushOrder(pushOrderDto);
                redisService.setCacheObject(CacheConstants.nextOrder(luckyOder.getId().toString()), xpOrder.getId());
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("orderId", xpOrder.getId());
                jsonObject.put("endLongitude", xpOrder.getEndLat());
                jsonObject.put("endLatitude", xpOrder.getEndLat());
                redisService.setCacheObject(CacheConstants.nextOrder(luckyOder.getId().toString()), jsonObject.toJSONString());
                redisService.setCacheObject(CacheConstants.navigationStart(xpOrder.getAppUserId().toString()), routeToOrigin);
                redisService.removeByPattern(quoteName,xpOrder.getOrderNumber());
                log.info("将订单 {} 从队列 {} 弹出去", orderNo, quoteName);
@@ -1306,7 +1312,11 @@
                    pushOrderDto.setVin(xpVehicle.getVehicleVin());
                    // 订单流转
                    orderMissionClient.pushOrder(pushOrderDto);
                    redisService.setCacheObject(CacheConstants.nextOrder(luckyOder.getId().toString()), data.getId());
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("orderId", data.getId());
                    jsonObject.put("endLongitude", data.getEndLat());
                    jsonObject.put("endLatitude", data.getEndLat());
                    redisService.setCacheObject(CacheConstants.nextOrder(luckyOder.getId().toString()), jsonObject.toJSONString());
                    redisService.setCacheObject(CacheConstants.navigationStart(data.getAppUserId().toString()), routeToOrigin);
                    break;
                }
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpTargetedServiceMapper.xml
@@ -5,8 +5,8 @@
    <select id="pageList" resultType="com.xiaopeng.api.vo.operation.XpTargetedServiceVo">
        select a.* ,
            (select count(1) from xp_targeted_service_vehicle where xp_targeted_service_id = a.id) as vehicleCount,
            (select count(1) from xp_targeted_service_whitelist where xp_targeted_service_id = a.id) as whitelistCount
            (select count(1) from xp_targeted_service_vehicle where targeted_service_id = a.id) as vehicleCount,
            (select count(1) from xp_targeted_service_whitelist where targeted_service_id = a.id) as whitelistCount
        from xp_targeted_service as a
        where a.is_deleted = 0
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpWhitelistMapper.xml
@@ -16,6 +16,9 @@
        <if test="query.account != null and query.account != ''">
            and account like concat('%',#{query.account},'%')
        </if>
        <if test="query.nickName != null and query.nickName != ''">
            and nick_name like concat('%',#{query.nickName},'%')
        </if>
        <if test="query.status != null">
            and status = #{query.status}
        </if>
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/CustomerServiceController.java
@@ -9,7 +9,9 @@
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.automaticPilot.AppUserPosition;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleDoorStatusVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.order.dto.UserOrderListDto;
@@ -24,6 +26,7 @@
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@@ -162,6 +165,52 @@
    /**
     * 8. 查询乘客位置
     * @param orderId
     * @return
     */
    @GetMapping("/getAppUserLocation/{orderId}")
    @ApiOperation(value = "查询乘客位置", notes = "客服系统对接")
    @ApiImplicitParam(name = "orderId", value = "订单ID", required = true, dataType = "Long", paramType = "path")
    public R<AppUserLocationVo> getAppUserLocation(@PathVariable Long orderId){
        XpOrder xpOrder = xpOrderService.getById(orderId);
        if(null == xpOrder){
            return R.ok();
        }
        AppUserLocationVo vo = new AppUserLocationVo();
        AppUserPosition userPosition = mongoTemplate.findOne(Query.query(Criteria.where("appUserId").is(xpOrder.getAppUserId())), AppUserPosition.class);
        if(null != userPosition){
            vo.setPassengerLng(userPosition.getPoint().getX());
            vo.setPassengerLat(userPosition.getPoint().getY());
            return R.ok(vo);
        }
        return R.ok();
    }
    /**
     * 9. 根据 订单查询安全带及座椅传感器状态
     * @param orderId
     * @return
     */
    @GetMapping("/getVehicleSeatStatus/{orderId}")
    @ApiOperation(value = "根据 订单查询安全带及座椅传感器状态", notes = "客服系统对接")
    @ApiImplicitParam(name = "orderId", value = "订单ID", required = true, dataType = "Long", paramType = "path")
    public R<VehicleSeatStatusVo> getVehicleSeatStatus(@PathVariable Long orderId){
        XpOrder xpOrder = xpOrderService.getById(orderId);
        if(null == xpOrder){
            return R.ok();
        }
        return R.ok();
    }
    /**
     * 10. 根据 VIN 查询剩余电量及预估续航
     * @param vin
     * @return
@@ -179,4 +228,46 @@
        vo.setEstimatedRange(Double.valueOf(vehiclePositions.getMileageRange()));
        return R.ok(vo);
    }
    /**
     * 11. 根据 订单查询四门三盖状态
     * @param orderId
     * @return
     */
    @GetMapping("/getVehicleDoorStatus/{orderId}")
    @ApiOperation(value = "根据 订单查询四门三盖状态", notes = "客服系统对接")
    @ApiImplicitParam(name = "orderId", value = "订单ID", required = true, dataType = "Long", paramType = "path")
    public R<VehicleDoorStatusVo> getVehicleDoorStatus(@PathVariable Long orderId){
        VehicleDoorStatusVo data = xpVehicleClient.getVehicleDoorStatus(orderId).getData();
        return R.ok(data);
    }
    /**
     * 12. 根据 订单查询车辆位置及电子围栏信息
     * @param orderId
     * @return
     */
    @GetMapping("/getVehicleFenceInformation/{orderId}")
    @ApiOperation(value = "根据 订单查询车辆位置及电子围栏信息", notes = "客服系统对接")
    @ApiImplicitParam(name = "orderId", value = "订单ID", required = true, dataType = "Long", paramType = "path")
    public R<VehicleFenceInformationVo> getVehicleFenceInformation(@PathVariable Long orderId){
        XpOrder xpOrder = xpOrderService.getById(orderId);
        if(null == xpOrder){
            return R.ok();
        }
        XpVehicle xpVehicle = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData();
        VehiclePosition vehiclePositions = mongoTemplate.findOne(Query.query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
        if(null == vehiclePositions){
            return R.ok();
        }
        VehicleFenceInformationVo vo = new VehicleFenceInformationVo();
        vo.setVehicleLng(vehiclePositions.getPoint().getX());
        vo.setVehicleLat(vehiclePositions.getPoint().getY());
        vo.setGeofenceBoundary(new ArrayList<>());
        vo.setIsOutOfGeofence(false);
        return R.ok(vo);
    }
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java
@@ -609,6 +609,14 @@
        XpVehicle xpVehicle = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData();
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
        routeVo.setDirection(null != vehiclePosition ? vehiclePosition.getCourse() : 90);
        Long vehicleId = xpOrder.getVehicleId();
        XpOrder one = xpOrderService.getOne(new QueryWrapper<XpOrder>().eq("vehicle_id", vehicleId).in("status", Arrays.asList(6, 7)).eq("is_deleted", 0));
        if(null != one){
            routeVo.setEndLongitude(one.getEndLon());
            routeVo.setEndLatitude(one.getEndLat());
        }
        return R.ok(routeVo);
    }
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -2090,8 +2090,10 @@
                }
                //判断是否有连环单
                Long id = redisService.getCacheObject(CacheConstants.nextOrder(xpOrder.getId().toString()));
                if(id != null){
                String json = redisService.getCacheObject(CacheConstants.nextOrder(xpOrder.getId().toString()));
                if (StringUtils.isNotEmpty(json)){
                    JSONObject jsonObject = JSON.parseObject(json);
                    Long id = jsonObject.getLong("orderId");
                    QuartzManager.addJob(
                            PushChainReactionOrderJob.class,
                            (PushChainReactionOrderJob.name + id).toUpperCase(),
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/vo/AppUserLocationVo.java
New file
@@ -0,0 +1,15 @@
package com.xiaopeng.order.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel
public class AppUserLocationVo {
    @ApiModelProperty("经度")
    private Double passengerLng;
    @ApiModelProperty("纬度")
    private Double passengerLat;
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/vo/FenceVo.java
New file
@@ -0,0 +1,14 @@
package com.xiaopeng.order.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel
public class FenceVo {
    @ApiModelProperty("经度")
    private Double lng;
    @ApiModelProperty("纬度")
    private Double lat;
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/vo/VehicleFenceInformationVo.java
New file
@@ -0,0 +1,21 @@
package com.xiaopeng.order.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel
public class VehicleFenceInformationVo {
    @ApiModelProperty("车辆经度")
    private Double vehicleLng;
    @ApiModelProperty("车辆纬度")
    private Double vehicleLat;
    @ApiModelProperty("电子围栏经纬度数组")
    private List<FenceVo> geofenceBoundary;
    @ApiModelProperty("是否超越电子围栏边界")
    private Boolean isOutOfGeofence;
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/vo/VehicleSeatStatusVo.java
New file
@@ -0,0 +1,19 @@
package com.xiaopeng.order.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel
public class VehicleSeatStatusVo {
    @ApiModelProperty("主驾")
    private String frontLeft;
    @ApiModelProperty("副驾")
    private String frontRight;
    @ApiModelProperty("左后")
    private String rearLeft;
    @ApiModelProperty("右后")
    private String rearRight;
}
xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/controller/PushOrderRouteController.java
@@ -123,6 +123,8 @@
                content.put("routeType",routeType);
                content.put("direction",dto.getDirection());
                content.put("route",JSONObject.toJSONString(route));
                content.put("endLongitude", dto.getEndLongitude());
                content.put("endLatitude", dto.getEndLatitude());
                // 封装轨迹信息
                Map<String, Object> params = new HashMap<>();
xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/webSocket/handler/WebSocketTextFrameHandler.java
@@ -2,6 +2,8 @@
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xiaopeng.api.vo.automaticPilot.AppUserPosition;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.pushMessage.webSocket.service.WebSocketUserConnectionManager;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
@@ -9,9 +11,15 @@
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
@@ -33,6 +41,9 @@
    @Autowired
    private WebSocketAuthHandler authHandler;
    @Resource
    private MongoTemplate mongoTemplate;
    Map<String, BusinessMsgHandler> businessMsgHandlers;
@@ -160,8 +171,7 @@
                log.info("收到用户 {} 的实时位置消息: {}", appUserId, messageObj.toJSONString());
                Double latitude = messageObj.getDouble("latitude");
                Double longitude = messageObj.getDouble("longitude");
                String timestamp = messageObj.getString("timestamp");
                saveAppUserLocation(Long.parseLong(appUserId), latitude, longitude);
            }
            // 回复消息
@@ -213,4 +223,21 @@
        log.error("WebSocket连接异常: {}", cause.getMessage(), cause);
        ctx.channel().close();
    }
    private void saveAppUserLocation(Long appUserId, Double latitude, Double longitude) {
        AppUserPosition userPosition = mongoTemplate.findOne(Query.query(Criteria.where("appUserId").is(appUserId)), AppUserPosition.class);
        GeoJsonPoint point = new GeoJsonPoint(longitude, latitude);
        if(null != userPosition){
            mongoTemplate.updateFirst(Query.query(Criteria.where("appUserId").is(appUserId)),
                    Update.update("point", point).set("addTime", System.currentTimeMillis()), AppUserPosition.class);
        }else{
            userPosition = new AppUserPosition();
            userPosition.setAppUserId(appUserId);
            userPosition.setPoint(point);
            userPosition.setAddTime(System.currentTimeMillis());
            mongoTemplate.save(userPosition);
        }
    }
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/CarControlCallbackController.java
@@ -6,6 +6,7 @@
import com.xiaopeng.api.vo.vehicleConvoy.VehicleStateVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.vehicleConvoy.model.dto.CarControlCallbackDTO;
import com.xiaopeng.vehicleConvoy.service.AsyncVehicleStateService;
import lombok.extern.slf4j.Slf4j;
@@ -17,6 +18,7 @@
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
 * 控车回调接口
@@ -28,6 +30,7 @@
    @Resource
    private AsyncVehicleStateService asyncVehicleStateService;
    private RedisService redisService;
    /**
     * 车控指令执行的回调接口
@@ -51,6 +54,8 @@
                    // 通知异步服务处理回调
                    asyncVehicleStateService.handleStateCallback(request.getMsgRef(), stateData);
                }
                // 尝试解析车辆空调数据
                parseAirConditioningSwitchFromCallback(request);
            }
        } catch (Exception e) {
            log.error("处理车辆状态回调异常: msgId={}", request.getMsgRef(), e);
@@ -94,6 +99,8 @@
            // 解析door信息
            Map<String, Object> door = (Map<String, Object>) instant.get("door");
            if (door != null) {
                //存入缓存用于后续使用
                redisService.setCacheObject("DOOR:" + callback.getMsgId(), JSON.toJSONString(door), 30L, TimeUnit.SECONDS);
                // 获取trunk_ajar_st_new值
                Object trunkAjarStNewObj = door.get("trunk_ajar_st_new");
                if (trunkAjarStNewObj != null) {
@@ -115,4 +122,52 @@
            return null;
        }
    }
    /**
     * 从回调消息中解析车辆空调状态数据
     */
    private VehicleTemperatureDto parseAirConditioningSwitchFromCallback(CarControlCallbackDTO callback) {
        try {
            if (callback.getMsgContent() == null) {
                return null;
            }
            log.debug("解析空调状态数据: msgId={}, content={}", callback.getMsgRef(), JSON.toJSONString(callback.getMsgContent()));
            // 解析msgContent为Map结构
            Map<String, Object> msgContent = (Map<String, Object>) callback.getMsgContent();
            // 检查返回码
            Integer code = (Integer) msgContent.get("code");
            if (code == null || code != 200) {
                log.warn("空调状态回调返回错误码: msgId={}, code={}", callback.getMsgRef(), code);
                return null;
            }
            // 获取instant数据
            Map<String, Object> instant = (Map<String, Object>) msgContent.get("instant");
            if (instant == null) {
                log.warn("回调数据中缺少instant字段: msgId={}", callback.getMsgRef());
                return null;
            }
            // 创建VehicleTemperatureDto对象
            VehicleTemperatureDto stateData = new VehicleTemperatureDto();
            Map<String, VehicleStateVo> stateMap = new HashMap<>();
            // 解析hvac信息
            Map<String, Object> hvac = (Map<String, Object>) instant.get("hvac");
            if (hvac != null) {
                log.info("解析到空调: msgId={}, hvac_on={}", callback.getMsgRef(), JSON.toJSONString(hvac));
                //存入缓存用于后续使用
                redisService.setCacheObject("HVAC:" + callback.getMsgId(), JSON.toJSONString(hvac), 30L, TimeUnit.SECONDS);
            }
            stateData.setStateMap(stateMap);
            return stateData;
        } catch (Exception e) {
            log.warn("解析空调状态数据失败: msgId={}", callback.getMsgRef(), e);
            return null;
        }
    }
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -25,6 +25,7 @@
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicleAnnualRecord;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleDoorStatusVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleStateVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandCallbackVo;
import com.xiaopeng.common.core.domain.R;
@@ -862,8 +863,8 @@
            @ApiImplicitParam(name = "orderId", value = "订单ID", required = true, dataTypeClass = Long.class),
            @ApiImplicitParam(name = "orderType", value = "订单类型(1=用户单,2=测试单)", required = true, dataTypeClass = Integer.class)
    })
    public R<List<XpVehicle>> listAvailableVehicles(@RequestParam Long orderId, Integer orderType) {
        List<XpVehicle> list = xpVehicleService.listAvailableVehicles(orderId, orderType);
    public R<List<XpVehicleVO>> listAvailableVehicles(@RequestParam Long orderId, Integer orderType, String plateNumber, String fleetName) {
        List<XpVehicleVO> list = xpVehicleService.listAvailableVehicles(orderId, orderType,plateNumber,fleetName);
        return R.ok(list);
    }
@@ -1121,4 +1122,38 @@
        XpOperationsUser xpOperationsUser = tokenService.getLoginOperationUser().getXpOperationsUser();
        return R.ok(xpVehicleService.unlockDoor(dto, xpOperationsUser));
    }
    @ApiOperation(value = "通过订单id查询当前车辆空调状态", tags = {"小程序-业务流程"})
    @GetMapping("/api/v1/mini-program/order/queryVehicleAirConditionerSwitch")
    @XpClientCheck(checkToken = false, checkOtpToken = true)
    public R<Boolean> queryVehicleAirConditionerSwitch(@RequestParam Long orderId, HttpServletRequest request) {
        String uid = request.getHeader("XP-Uid");
        Boolean hasKey = xpVehicleService.queryVehicleAirConditionerSwitch(orderId, uid);
        return R.ok(hasKey);
    }
    /**
     * 业务流程-车门解锁/后备箱解锁
     */
    @ApiOperation(value = "打开/关闭车内空调", tags = {"小程序-业务流程"})
    @PostMapping("/api/v1/mini-program/order/airConditionerUnlock")
    @XpClientCheck(checkToken = false, checkOtpToken = true)
    public R<Integer> airConditionerUnlock(@RequestBody AirConditionerUnlockDTO dto, HttpServletRequest request) {
        String uid = request.getHeader("XP-Uid");
        return R.ok(xpVehicleService.airConditionerUnlock(dto, uid));
    }
    /**
     * 根据订单id查询四门三盖状态
     * @param orderId
     * @return
     */
    @GetMapping("/logan/inner/vehicle/getVehicleDoorStatus/{orderId}")
    public R<VehicleDoorStatusVo> getVehicleDoorStatus(@PathVariable Long orderId){
        VehicleDoorStatusVo vehicleDoorStatus = xpVehicleService.getVehicleDoorStatus(orderId);
        return R.ok(vehicleDoorStatus);
    }
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/mapper/XpVehicleMapper.java
@@ -65,6 +65,8 @@
     */
    List<XpVehicleVO> pageList(@Param("pageInfo") PageInfo<XpVehicleVO> pageInfo, @Param("query") VehicleMapQuery query);
    List<XpVehicleVO> findList(@Param("query") VehicleMapQuery query);
    /**
     * 在库车辆管理分页查询
     *
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/dto/AirConditionerUnlockDTO.java
New file
@@ -0,0 +1,17 @@
package com.xiaopeng.vehicleConvoy.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel
public class AirConditionerUnlockDTO {
    @ApiModelProperty(value = "订单id")
    private Long orderId;
    @ApiModelProperty(value = "开关控制(开=true, 关=false)")
    private Boolean open;
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/query/VehicleMapQuery.java
@@ -14,6 +14,10 @@
    private String plateNumber;
    @ApiModelProperty(value = "运营状态 1运营中 2维修中 3已收车 4车辆异常")
    private String status;
    @ApiModelProperty(value = "运营状态 1运营中 2维修中 3已收车 4车辆异常")
    private Integer operatingStatus;
    @ApiModelProperty(value = "车辆接单状态 1接单中 0空闲")
    private Integer state;
    @ApiModelProperty(value = "围栏id")
    private String electronicId;
@@ -25,4 +29,11 @@
    private Long operationsUserId;
    @ApiModelProperty(value = "车辆id集合 前端忽略")
    private List<Long> vehicleIds;
    @ApiModelProperty(value = "车辆类型 1=在库 2=运营")
    private Integer vehicleType;
    @ApiModelProperty(value = "车队名称")
    private String fleetName;
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpVehicleService.java
@@ -6,11 +6,13 @@
import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto;
import com.xiaopeng.api.model.account.XpOperationsUser;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleDoorStatusVo;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.vehicleConvoy.model.dto.*;
import com.xiaopeng.vehicleConvoy.model.importExcel.XpVehicleImportExcel;
import com.xiaopeng.vehicleConvoy.model.query.*;
import com.xiaopeng.vehicleConvoy.model.vo.XpVehicleVO;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
@@ -218,4 +220,30 @@
     * @return
     */
    Integer unlockDoor(UnlockAppDTO dto, XpOperationsUser xpOperationsUser);
    /**
     * 根据订单id获取空调开关状态
     * @param orderId
     * @param uid
     * @return
     */
    Boolean queryVehicleAirConditionerSwitch(Long orderId, String uid);
    /**
     * 开关空调
     * @param dto
     * @param uid
     * @return
     */
    Integer airConditionerUnlock(AirConditionerUnlockDTO dto, String uid);
    /**
     * 获取车辆车门和后备箱状态
     * @param vehicleId
     * @return
     */
    VehicleDoorStatusVo getVehicleDoorStatus(Long orderId);
}
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -3,6 +3,8 @@
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
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;
@@ -40,6 +42,7 @@
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.exception.base.BaseException;
import com.xiaopeng.common.core.utils.GeodesyUtil;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.redis.service.RedisService;
@@ -51,6 +54,7 @@
import com.xiaopeng.vehicleConvoy.model.importExcel.XpVehicleImportExcel;
import com.xiaopeng.vehicleConvoy.model.query.*;
import com.xiaopeng.vehicleConvoy.model.vo.XpVehicleVO;
import com.xiaopeng.vehicleConvoy.service.AsyncVehicleStateService;
import com.xiaopeng.vehicleConvoy.service.IXpFleetVehicleService;
import com.xiaopeng.vehicleConvoy.service.IXpVehicleAnnualRecordService;
import com.xiaopeng.vehicleConvoy.service.IXpVehicleService;
@@ -136,6 +140,8 @@
    @Resource
    private UserResourcesClient userResourcesClient;
    @Resource
    private AsyncVehicleStateService asyncVehicleStateService;
    @Value("${vehicle.control.distance-threshold:100}")
    private int distanceThreshold;
@@ -162,7 +168,7 @@
     * @return
     */
    @Override
    public List<XpVehicle> listAvailableVehicles(Long orderId, Integer orderType) {
    public List<XpVehicleVO> listAvailableVehicles(Long orderId, Integer orderType, String plateNumber, String fleetName) {
        // 1. 通过订单ID查询订单信息,获取区域ID
        Long areaId = null;
@@ -205,16 +211,15 @@
        }
        // 3. 根据围栏ID查询车辆列表 (operatingStatus=1 且 state=0)
        LambdaQueryWrapper<XpVehicle> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(XpVehicle::getElectronicId, electronicId)
                .eq(XpVehicle::getOperatingStatus, 1)
                .eq(XpVehicle::getVehicleType, 2)
                .eq(XpVehicle::getState, 0);
        VehicleMapQuery query = new VehicleMapQuery();
        query.setState(0);
        query.setVehicleType(2);
        query.setOperatingStatus(1);
        query.setElectronicId(electronicId.toString());
        query.setFleetName(fleetName);
        query.setPlateNumber(plateNumber);
        // 排序:按电量降序、入库时间降序
        wrapper.orderByDesc(XpVehicle::getCurrentElectricity)
                .orderByDesc(XpVehicle::getWarehousingTime);
        List<XpVehicle> list = this.list(wrapper);
        List<XpVehicleVO> list = baseMapper.findList(query);
        //获取车的位置 和订单的位置 并计算 距离 和 时间
        if(!list.isEmpty()){
            for (XpVehicle xpVehicle : list) {
@@ -1329,4 +1334,116 @@
        return 0;
    }
    /**
     * 根据订单id获取空调开关状态
     * @param orderId
     * @param uid
     * @return
     */
    @Override
    public Boolean queryVehicleAirConditionerSwitch(Long orderId, String uid) {
        XpOrder xpOrder = xpOrderClient.queryOrderById(orderId).getData();
        if(null == xpOrder){
            return false;
        }
        XpVehicle vehicle = this.getById(xpOrder.getVehicleId());
        String vin = vehicle.getVehicleVin();
        // 1. 创建车辆控制指令
        VehiclesCommandVo commandVo = new VehiclesCommandVo();
        commandVo.setVin(vin);
        commandVo.setCommandCode(VehiclesCommandEnum.STATUS_REPORT_NOW.getCommand());
        // 2. 下发指令获取消息ID
        String msgId = vehicleControlUtil.vehiclesCommand(commandVo, Long.valueOf(uid));
        if (StrUtil.isEmpty(msgId)) {
            throw new BaseException("获取消息ID失败: " + msgId);
        }
        log.info("同步查询车辆空调状态成功,开始等到查询回调结果");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        String cacheObject = redisService.getCacheObject("HVAC:" + msgId);
        if(StringUtils.isEmpty(cacheObject)){
            return false;
        }
        JSONObject jsonObject = JSON.parseObject(cacheObject);
        Integer hvac_on = jsonObject.getInteger("hvac_on");
        return hvac_on == 1;
    }
    /**
     * 开关空调
     * @param dto
     * @param uid
     * @return
     */
    @Override
    public Integer airConditionerUnlock(AirConditionerUnlockDTO dto, String uid) {
        XpOrder xpOrder = xpOrderClient.queryOrderById(dto.getOrderId()).getData();
        if(null == xpOrder){
            return 0;
        }
        XpVehicle vehicle = this.getById(xpOrder.getVehicleId());
        VehiclesCommandVo vehiclesCommandVo = new VehiclesCommandVo();
        vehiclesCommandVo.setVin(vehicle.getVehicleVin());
        vehiclesCommandVo.setCommandCode(dto.getOpen() ? VehiclesCommandEnum.HVAC_ON.getCommand() : VehiclesCommandEnum.HVAC_OFF.getCommand());
        String msgId = vehicleControlUtil.vehiclesCommand(vehiclesCommandVo, Long.valueOf(uid));
        log.info("空调控制结果:{}", msgId);
        if (StringUtils.isEmpty(msgId)) {
            return 0;
        } else {
            return 1;
        }
    }
    /**
     * 获取车辆门锁状态
     * @param orderId
     * @return
     */
    @Override
    public VehicleDoorStatusVo getVehicleDoorStatus(Long orderId) {
        XpOrder xpOrder = xpOrderClient.queryOrderById(orderId).getData();
        if(null == xpOrder){
            return null;
        }
        XpVehicle vehicle = this.getById(xpOrder.getVehicleId());
        String vin = vehicle.getVehicleVin();
        // 1. 创建车辆控制指令
        VehiclesCommandVo commandVo = new VehiclesCommandVo();
        commandVo.setVin(vin);
        commandVo.setCommandCode(VehiclesCommandEnum.STATUS_REPORT_NOW.getCommand());
        // 2. 下发指令获取消息ID
        String msgId = vehicleControlUtil.vehiclesCommand(commandVo, xpOrder.getAppUserId());
        if (StrUtil.isEmpty(msgId)) {
            throw new BaseException("获取消息ID失败: " + msgId);
        }
        log.info("同步查询车辆空调状态成功,开始等到查询回调结果");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        String cacheObject = redisService.getCacheObject("DOOR:" + msgId);
        if(StringUtils.isEmpty(cacheObject)){
            return null;
        }
        // todo 待文档补充后再更新代码
        VehicleDoorStatusVo vo = new VehicleDoorStatusVo();
        JSONObject jsonObject = JSON.parseObject(cacheObject);
        Integer driver_door_ajar_st = jsonObject.getInteger("driver_door_ajar_st");
        Integer passenger_door_ajar_st = jsonObject.getInteger("passenger_door_ajar_st");
        Integer rear_left_door_ajar_st = jsonObject.getInteger("rear_left_door_ajar_st");
        Integer rear_right_door_ajar_st = jsonObject.getInteger("rear_right_door_ajar_st");
        Integer trunk_ajar_st_new = jsonObject.getInteger("trunk_ajar_st_new");
        vo.setTrunk(trunk_ajar_st_new == 5 ? 1 : 0);
        return vo;
    }
}
xp-robotaxi-vehicle-convoy-boot/src/main/resources/mapper/vehicleConvoy/XpVehicleMapper.xml
@@ -96,6 +96,58 @@
        ORDER BY create_time DESC
    </select>
    <select id="findList" resultType="com.xiaopeng.vehicleConvoy.model.vo.XpVehicleVO">
        SELECT a.*
        FROM xp_vehicle as a
        <where> a.is_deleted = 0
            <if test="query.vehicleType != null and query.vehicleType != ''">
                AND a.vehicle_type = #{query.vehicleType}
            </if>
            <if test="query.plateNumber != null and query.plateNumber != ''">
                AND a.plate_number LIKE CONCAT('%', #{query.plateNumber}, '%')
            </if>
            <!-- 运营状态 1运营中 2维修中 3已收车 4车辆异常 -->
            <if test="query.operatingStatus != null and query.operatingStatus != ''">
                AND a.operating_status = #{query.operatingStatus}
            </if>
            <!-- 车辆接单状态 1接单中 0空闲 -->
            <if test="query.state != null and query.state != ''">
                AND a.state = #{query.state}
            </if>
            <if test="query.statusList != null and query.statusList.size()>0">
                AND a.operating_status in
                <foreach item="item" collection="query.statusList" separator="," open="(" close=")">
                    #{item}
                </foreach>
            </if>
            <if test="query.electronicId != null and query.electronicId != ''">
                    AND a.electronic_id #{query.electronicId}
                </if>
            <if test="query.electronicIdList != null and query.electronicIdList.size()>0">
                AND a.electronic_id in
                <foreach item="item" collection="query.electronicIdList" separator="," open="(" close=")">
                    #{item}
                </foreach>
            </if>
            <if test="query.vehicleIds != null and query.vehicleIds.size()>0">
                AND a.id in
                <foreach item="item" collection="query.vehicleIds" separator="," open="(" close=")">
                    #{item}
                </foreach>
            </if>
            <if test="query.fleetName != null and query.fleetName != ''">
                AND EXISTS (
                    SELECT 1 FROM xp_fleet_vehicle xfv
                    LEFT JOIN xp_fleet xf ON xfv.fleet_id = xf.id
                    WHERE xfv.vehicle_id = a.id
                    AND xf.fleet_name LIKE CONCAT('%', #{query.fleetName}, '%')
                )
            </if>
        </where>
        ORDER BY a.current_electricity DESC,a.warehousing_time desc
    </select>
    <!-- 在库车辆管理分页查询条件 -->
    <sql id="Vehicle_Page_Where">
        AND vehicle_type = 1