From 46342ddcf5d52ccbf2ae8d8a9a0b14cc0d29b250 Mon Sep 17 00:00:00 2001
From: puzhibing <393733352@qq.com>
Date: 星期四, 16 四月 2026 22:14:20 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/1.0' into 1.0

---
 xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java |  238 ++++++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 177 insertions(+), 61 deletions(-)

diff --git a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
index 2edff13..5b8dd17 100644
--- a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
+++ b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -16,7 +16,7 @@
 import com.xiaopeng.api.dto.automaticPilot.PushOrderDto;
 import com.xiaopeng.api.dto.automaticPilot.VehicleEventDTO;
 import com.xiaopeng.api.dto.map.OrderRouteAndFareDto;
-import com.xiaopeng.api.dto.operation.CruisePointDto;
+import com.xiaopeng.api.dto.map.RouteAndFareDto;
 import com.xiaopeng.api.dto.operation.LonLatDto;
 import com.xiaopeng.api.dto.order.GetOrderNumDto;
 import com.xiaopeng.api.dto.order.HistoryEndPointsDto;
@@ -27,18 +27,13 @@
 import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO;
 import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
 import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto;
-import com.xiaopeng.api.dto.vehicleConvoy.UpdateVehicleStateDto;
-import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto;
 import com.xiaopeng.api.feignClient.account.UserResourcesClient;
 import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient;
 import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient;
 import com.xiaopeng.api.feignClient.automaticPilot.VehicleBuriedPointClient;
 import com.xiaopeng.api.feignClient.maintenance.XpTaskRuleClient;
 import com.xiaopeng.api.feignClient.map.XpMapClient;
-import com.xiaopeng.api.feignClient.operation.XpCruisePlanClient;
-import com.xiaopeng.api.feignClient.operation.XpPointClient;
-import com.xiaopeng.api.feignClient.operation.XpPricingClient;
-import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient;
+import com.xiaopeng.api.feignClient.operation.*;
 import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
 import com.xiaopeng.api.feignClient.vehicleConvoy.*;
 import com.xiaopeng.api.model.maintenance.XpTaskRule;
@@ -46,6 +41,7 @@
 import com.xiaopeng.api.model.order.*;
 import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
 import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
+import com.xiaopeng.api.service.BootCommonService;
 import com.xiaopeng.api.vo.OrderType;
 import com.xiaopeng.api.vo.account.UserCouponVo;
 import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
@@ -92,7 +88,6 @@
 import com.xiaopeng.order.utils.GDMapGeocodingUtil;
 import com.xiaopeng.order.utils.TaxiPricingDemoService;
 import com.xiaopeng.order.utils.RegionCodeFilterUtil;
-import com.xiaopeng.order.utils.TaxiPricingDemoService;
 import com.xiaopeng.order.utils.XpPricingUtil;
 import com.xiaopeng.order.utils.holiday.HolidayUtil;
 import com.xiaopeng.order.vo.OrderDetailVo;
@@ -163,6 +158,10 @@
     private final VehicleBuriedPointClient vehicleBuriedPointClient;
     private final IXpTestOrderService xpTestOrderService;
     private final XpMapClient xpMapClient;
+    @Resource
+    private  XpRoutePlanClient xpRoutePlanClient;
+    @Resource
+    private BootCommonService bootCommonService;
 
 
     @Autowired
@@ -1591,11 +1590,11 @@
     @ShardingSphereTransactionType(TransactionType.BASE)
     public void cancelOrder(Long orderId, String uid) {
         XpOrder order = this.getById(orderId);
-        if (order.getStatus() == 0) {
-            return;
-        }
         if (order == null) {
             throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS);
+        }
+        if (order.getStatus() == 0) {
+            return;
         }
         if (!order.getAppUserId().toString().equals(uid)) {
             throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS);
@@ -1603,7 +1602,6 @@
         if (order.getStatus() > 5 || order.getStatus() < 1) {
             throw new ServiceException("订单状态错误", I18nKeyConstant.ORDER_STATUS_ERROR);
         }
-
         try {
             redisService.removeByPattern(order.getQueueName(), order.getOrderNumber());
         } catch (Exception e) {
@@ -1625,39 +1623,20 @@
                 }
             }
         }
-
-        if (order.getStatus() == 1) {
-            order.setStatus(0);
-            order.setAppUserId(null);
-            baseMapper.updateById(order);
-            redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS);
-            return;
-        }
-        /*
-        order.setStatus(0);
-        order.setAppUserId(null);
-        baseMapper.updateById(order);
-        redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS);
-        XpOrderCancel xpOrderCancel = new XpOrderCancel();
-        xpOrderCancel.setOrderId(orderId);
-        xpOrderCancel.setUserId(Long.valueOf(uid));
-        xpOrderCancel.setCancelSource(1);
-        xpOrderCancel.setCancelReason("用户取消订单");
-        xpOrderCancelService.save(xpOrderCancel);
-        */
-
-        // 移除mongo数据
-        xpPricingClient.deleteXpGeoQueryEntity(order.getId().toString());
-
-        // 下发命令
-        if (null != order.getVehicleId()) {
+        else{// 下发命令
             R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(order.getVehicleId());
-            // 组装调用车机参数
-            CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo();
             XpVehicle xpVehicle = xpVehicleR.getData();
             if (xpVehicle == null) {
                 throw new ServiceException("车辆不存在", I18nKeyConstant.VEHICLE_NOT_EXISTS);
             }
+            //是否能取消行程
+            boolean cancelShow = this.isCancelShow(order,xpVehicle.getVehicleVin());
+            if(cancelShow){
+                throw new ServiceException("当前订单状态不允许取消,行程即将完成", I18nKeyConstant.ORDER_CANCEL_FAILED);
+            }
+
+            // 组装调用车机参数
+            CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo();
             cancelTaxiPassengerVo.setVin(xpVehicle.getVehicleVin());
             cancelTaxiPassengerVo.setUid(uid);
             cancelTaxiPassengerVo.setOrderNo(order.getOrderNumber());
@@ -1688,7 +1667,19 @@
             //车辆继续巡游
             addCruiseMission(xpVehicle.getId());
         }
+        order.setStatus(0);
+        order.setAppUserId(null);
+        baseMapper.updateById(order);
+        redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS);
 
+        XpOrderCancel xpOrderCancel = new XpOrderCancel();
+        xpOrderCancel.setOrderId(orderId);
+        xpOrderCancel.setUserId(Long.valueOf(uid));
+        xpOrderCancel.setCancelSource(1);
+        xpOrderCancel.setCancelReason("用户取消订单");
+        xpOrderCancelService.save(xpOrderCancel);
+        // 移除mongo数据
+        xpPricingClient.deleteXpGeoQueryEntity(order.getId().toString());
     }
 
     @Override
@@ -1918,6 +1909,8 @@
                         DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(arriveTime)),
                         endTripJobMaps
                 );
+
+
                 // 添加车辆到达终点后限停时间到后驶离的定时任务
                 //时间到后将车辆状态回滚到空闲、添加巡游订单,解绑用户和车辆关系,车辆驶离点位
                 XpPoint point = xpPointClient.queryById(xpOrder.getEndPointId()).getData();
@@ -1927,13 +1920,38 @@
                                 .put("appid", appid)
                                 .put("orderType", OrderType.USER_ORDER)
                                 .build();
-                QuartzManager.addJob(
-                        AddCruiseMissionJob.class,
-                        (AddCruiseMissionJob.name + xpOrder.getId()).toUpperCase(),
-                        TimeJobType.ADD_CRUISE_MISSION,
-                        DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(point.getParkingTimeLimit())),
-                        jobMaps
-                );
+
+                Long id = redisService.getCacheObject(CacheConstants.nextOrder(xpOrder.getId().toString()));
+                if(id != null){
+                    XpOrder nextOrder = getById(id);
+                    XpVehicle nextVehicle = xpVehicleClient.queryById(nextOrder.getVehicleId()).getData();
+                    PushOrderDto pushOrderDto = new PushOrderDto();
+                    pushOrderDto.setOrderId(nextOrder.getId().toString());
+                    pushOrderDto.setStartLatitude(nextOrder.getStartLat());
+                    pushOrderDto.setStartLongitude(nextOrder.getStartLon());
+                    pushOrderDto.setEndLatitude(nextOrder.getEndLat());
+                    pushOrderDto.setEndLongitude(nextOrder.getEndLon());
+                    pushOrderDto.setEndPoiName(nextOrder.getEndPointName());
+                    pushOrderDto.setOrderType(OrderType.USER_ORDER);
+                    pushOrderDto.setStage(1);
+                    pushOrderDto.setVin(nextVehicle.getVehicleVin());
+                    orderMissionClient.pushOrder(pushOrderDto);
+                    QuartzManager.addJob(
+                            AddCruiseMissionJob.class,
+                            (AddCruiseMissionJob.name + nextOrder.getId()).toUpperCase(),
+                            TimeJobType.ADD_CRUISE_MISSION,
+                            DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(point.getParkingTimeLimit())),
+                            jobMaps
+                    );
+                } else {
+                    QuartzManager.addJob(
+                            AddCruiseMissionJob.class,
+                            (AddCruiseMissionJob.name + xpOrder.getId()).toUpperCase(),
+                            TimeJobType.ADD_CRUISE_MISSION,
+                            DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(point.getParkingTimeLimit())),
+                            jobMaps
+                    );
+                }
                 break;
             default:
                 break;
@@ -2031,11 +2049,13 @@
         redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 8, 24L, TimeUnit.HOURS);
 
         // 根据订单所属开通城市id 查询定价
-        R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCid(order.getCityId());
-        XpPricing pricing = pricingByCityId.getData();
+        XpPricing pricing = bootCommonService.getXpPricing(order.getCityId(),order.getVehicleId());
         if (pricing == null) {
             throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS);
         }
+
+
+
         // 费用
         // 查询订单轨迹
         List<OrderTrajectory> trajectories = mongoTemplate.find(
@@ -2407,7 +2427,7 @@
      *
      * @param order
      */
-
+    @Override
     public void addCruiseMission(Long vehicleId, XpOrder order) {
         XpVehicle vehicle = xpVehicleClient.queryById(vehicleId).getData();
         log.info("添加巡游订单 {}", vehicle.getPlateNumber());
@@ -3109,12 +3129,11 @@
     @Override
     public R<String> feeDescription(Long orderId, String uid) {
         XpOrder order = this.getById(orderId);
-        R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCid(order.getCityId());
-        XpPricing pricing = pricingByCityId.getData();
-        if (pricing == null) {
+        XpPricing xpPricing = bootCommonService.getXpPricing(order.getCityId(), order.getVehicleId());
+        if (xpPricing == null) {
             throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS);
         } else {
-            return R.ok(pricing.getPriceRule());
+            return R.ok(xpPricing.getPriceRule());
         }
     }
 
@@ -3451,6 +3470,9 @@
         if (xpOrder.getVehicleId() != null) {
             R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(xpOrder.getVehicleId());
             XpVehicle vehicle = xpVehicleR.getData();
+            xpOrderDetailVO.setVehicleVin(vehicle.getVehicleVin());
+            xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModel());
+            xpOrderDetailVO.setVehicleColor(vehicle.getVehicleColor());
             xpOrderDetailVO.setPlateNumber(vehicle.getPlateNumber());
             xpOrderDetailVO.setVehiclePictures(vehicle.getVehiclePictures());
         }
@@ -3462,24 +3484,118 @@
             xpOrder.setAppUserId(null);
             this.updateById(xpOrder);
         }
-        Boolean cancelShow = true; //是否能取消行程
-        xpOrderDetailVO.setCancelShow(cancelShow);
-
-
-
         return xpOrderDetailVO;
     }
 
+    @Override
+    public XpOrderDetailVO getActiveOrder_itineraryManage(String uid){
+        XpOrderDetailVO xpOrderDetailVO = new XpOrderDetailVO();
+        XpOrder xpOrder = this.baseMapper.selectOne(new LambdaQueryWrapper<XpOrder>().eq(XpOrder::getAppUserId, Long.parseLong(uid)).in(XpOrder::getStatus, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8)).orderByDesc(BaseModel::getCreateTime).last("LIMIT 1"));
+        if (xpOrder == null) {
+            return null;
+        }
+        BeanUtils.copyProperties(xpOrder, xpOrderDetailVO);
+        if (xpOrder.getVehicleId() != null) {
+            R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(xpOrder.getVehicleId());
+            XpVehicle vehicle = xpVehicleR.getData();
+            xpOrderDetailVO.setVehicleVin(vehicle.getVehicleVin());
+            xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModel());
+            xpOrderDetailVO.setVehicleColor(vehicle.getVehicleColor());
+            xpOrderDetailVO.setPlateNumber(vehicle.getPlateNumber());
+
+            xpOrderDetailVO.setVehiclePictures(vehicle.getVehiclePictures());
+        }
+        Boolean cancelShow = isCancelShow(xpOrder,xpOrderDetailVO.getVehicleVin()); //是否能取消行程
+        xpOrderDetailVO.setCancelShow(cancelShow);
+        return xpOrderDetailVO;
+    }
     /**
      * 取消行程:若处于“送驾中”且离目的地小于10分钟路程(后台配置),【取消行程】按钮应置灰;
      * 修改行程:
      * 1. 当前剩余里程 < 800米 (后台配置)或 剩余时间 < 5分钟(后台配置)(以先到者为准),禁止修改,按钮置灰。
      * 2. 单次行程最多允许修改 2次 (后台配置)目的地,防恶意高频调用
+     * 特殊豁免:若系统检测到车辆在当前坐标时速 < 3km/h 且持续时间 > 3分钟(后台配置)(判定为极度拥堵或异常停滞),此时解除限制,允许用户修改目的地或新增途径点以寻找更近的下车位置。
      * @return
      */
-    private Boolean isCancelShow(){
+    private boolean isCancelShow(XpOrder xpOrder,String vehicleVin) {
+        boolean isCancel = false;
+        //获取配置文件
+        int sylc = 800;//单位米 todo yyy 需要读取后台配置文件
+        int sysj = 5;//单位分钟 todo yyy 需要读取后台配置文件
+        // 减少3分钟 获取3分账内的 行程数据
+        long minute =3;//todo yyy 需要读取后台配置文件
 
-        return true;
+
+        List<OrderTrajectory> orderTrajectories = queryOrderTrajectories(xpOrder.getId());
+        if(!orderTrajectories.isEmpty()){// 减少3分钟 获取3分账内的 行程数据
+            //3千米/小时 转换成分钟 50米/分钟
+            BigDecimal suduDefault = new BigDecimal("3");//3千米/小时
+            BigDecimal suduDefault1 = suduDefault.multiply(BigDecimal.valueOf(1000)) ;
+            BigDecimal suduDefaultNew =suduDefault1.divide(BigDecimal.valueOf(60),2,RoundingMode.HALF_EVEN);
+
+            LocalDateTime minutesBefore = LocalDateTime.now().minusMinutes(minute);
+            long milliseconds = DateUtils.getMilliseconds(minutesBefore);
+            List<OrderTrajectory> orderTrajectoriesNew = new ArrayList<>();
+            for (OrderTrajectory orderTrajectory : orderTrajectories) {
+                if(orderTrajectory.getLocatetime() > milliseconds){
+                    orderTrajectoriesNew.add(orderTrajectory);
+                }
+            }
+            /*
+            获取最近3分钟的 数据 速度、平均
+             */
+            //计算行驶总距离和时长
+            Double reduce = orderTrajectoriesNew.stream().map(OrderTrajectory::getDistance).reduce(0.0, Double::sum);
+            BigDecimal xcsudu = new BigDecimal(reduce).divide(new BigDecimal(minute),2, RoundingMode.HALF_EVEN);//米/分钟
+
+            //特殊豁免:若系统检测到车辆在当前坐标时速 < 3km/h 且持续时间 > 3分钟(后台配置)(判定为极度拥堵或异常停滞),此时解除限制,允许用户修改目的地或新增途径点以寻找更近的下车位置。
+            if(xcsudu.compareTo(suduDefaultNew)<0){
+                return true;
+            }
+        }
+        RouteVo routeVo = this.getRouteVo(xpOrder, vehicleVin);
+        if(routeVo.getDistance() <sylc){
+            isCancel = true;
+        }
+        if(routeVo.getDuration() < sysj){
+            isCancel = true;
+        }
+
+        return isCancel;
+    }
+
+    /**
+     * 获取两个点位车的距离 和时间
+     * @param xpOrder
+     * @param vehicleVin
+     * @return
+     */
+    private RouteVo getRouteVo(XpOrder xpOrder,String vehicleVin){
+
+        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vehicleVin)), VehiclePosition.class);
+        // 计算距离 和 时间
+        RouteAndFareDto routeAndFareDto = new RouteAndFareDto();
+        LonLatDto lonLatDto = new LonLatDto();
+        lonLatDto.setLongitude(vehiclePosition.getPoint().getX());//经度
+        lonLatDto.setLatitude(vehiclePosition.getPoint().getY());
+        routeAndFareDto.setOrigin(lonLatDto);
+
+        LonLatDto lonLatDto1 = new LonLatDto();
+        lonLatDto1.setLongitude(xpOrder.getEndLon());
+        lonLatDto1.setLatitude(xpOrder.getEndLat());
+        routeAndFareDto.setDestination(lonLatDto1);
+
+//        XpOpenCity xpOpenCity = xpOpenCityMapper.selectById(dto.getOpenCityId());
+//        xpRoutePlanClient.
+        XpRoutePlan xpRoutePlan = xpRoutePlanClient.getXpRoutePlanOpenCityId(xpOrder.getCityId()).getData();
+        if (xpRoutePlan != null) {
+            routeAndFareDto.setRoutePlanRule(xpRoutePlan.getRoutePlanRule());
+        }
+        R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto);
+        if(distanceAndDuration.getCode()!=200){
+            throw new ServiceException("未找到路径", I18nKeyConstant.PATH_NOT_FOUND);
+        }
+        return distanceAndDuration.getData();
     }
 
 

--
Gitblit v1.7.1