liujie
2026-06-09 c96fbe6b0aeb413a17c429133ee6d81d138144b9
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpTestOrderServiceImpl.java
@@ -2,7 +2,7 @@
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.core.lang.Snowflake;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
@@ -10,21 +10,10 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableMap;
import com.xiaopeng.api.dto.account.GetUserCouponDto;
import com.xiaopeng.api.dto.account.UcUserInfo;
import com.xiaopeng.api.dto.account.UseUserCouponDto;
import com.xiaopeng.api.dto.automaticPilot.PushOrderDto;
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;
import com.xiaopeng.api.dto.order.HistoryStartPointsDto;
import com.xiaopeng.api.dto.order.PopOrderDto;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.dto.statistics.DailyCountDTO;
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.feignClient.account.UserResourcesClient;
import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient;
import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient;
@@ -36,66 +25,54 @@
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.maintenance.XpTaskRule;
import com.xiaopeng.api.model.map.XpTestPoint;
import com.xiaopeng.api.model.operation.*;
import com.xiaopeng.api.model.order.*;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.OrderType;
import com.xiaopeng.api.vo.SystemEventType;
import com.xiaopeng.api.vo.account.UserCouponVo;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.api.vo.operation.FareVo;
import com.xiaopeng.api.vo.operation.RouteAndFareVo;
import com.xiaopeng.api.vo.automaticPilot.protobuf.OrderOperationType;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.api.vo.order.*;
import com.xiaopeng.api.vo.vehicleConvoy.AddTaxiPassengerVo;
import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo;
import com.xiaopeng.api.vo.order.FareBreakdownDetailVo;
import com.xiaopeng.api.vo.order.XpPricingCardVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.constant.Constants;
import com.xiaopeng.common.core.constant.HttpStatus;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.OrderStatusEnum;
import com.xiaopeng.common.core.enums.PushMessageTypeEnum;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.DateUtils;
import com.xiaopeng.common.core.utils.GeodesyUtil;
import com.xiaopeng.common.core.utils.SensitiveDataCryptoUtil;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.core.web.domain.BaseModel;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.common.security.utils.SecurityUtils;
import com.xiaopeng.order.constant.I18nKeyConstant;
import com.xiaopeng.order.dto.MileageDto;
import com.xiaopeng.order.job.base.QuartzManager;
import com.xiaopeng.order.job.base.TimeJobType;
import com.xiaopeng.order.job.jobs.AddCruiseMissionJob;
import com.xiaopeng.order.job.jobs.EndTripJob;
import com.xiaopeng.order.job.jobs.OrderCancelJob;
import com.xiaopeng.order.mangodb.model.OrderTrajectory;
import com.xiaopeng.order.mangodb.service.OrderTrajectoryService;
import com.xiaopeng.order.mapper.*;
import com.xiaopeng.order.model.dto.XpOrderCommentDto;
import com.xiaopeng.order.model.dto.XpOrderPayDto;
import com.xiaopeng.order.model.query.XpHistoryOrderListQuery;
import com.xiaopeng.order.model.query.XpOrderExcelQuery;
import com.xiaopeng.order.model.query.XpOrderPageQuery;
import com.xiaopeng.order.job.jobs.TestOrderCancelJob;
import com.xiaopeng.api.model.order.OrderTrajectory;
import com.xiaopeng.order.mapper.XpCruiseOrderMapper;
import com.xiaopeng.order.mapper.XpTestOrderDetailMapper;
import com.xiaopeng.order.mapper.XpTestOrderMapper;
import com.xiaopeng.order.model.query.XpTestOrderPageQuery;
import com.xiaopeng.order.model.vo.*;
import com.xiaopeng.order.model.vo.AddTestOrderVo;
import com.xiaopeng.order.model.vo.XpTestOrderDetailVO;
import com.xiaopeng.order.model.vo.XpTestOrderPageVO;
import com.xiaopeng.order.service.*;
import com.xiaopeng.order.utils.GDMapGeocodingUtil;
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;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -109,12 +86,10 @@
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -141,7 +116,9 @@
    private final UserVehicleClient userVehicleClient;
    private final IXpOrderEvaluateService xpOrderEvaluateService;
    private final IXpTestOrderCancelService xpTestOrderCancelService;
    private final IXpOrderReassignService xpOrderReassignService;
    @Lazy
    @Autowired
    private IXpOrderReassignService xpOrderReassignService;
    private final RedisService redisService;
    private final XpPricingClient xpPricingClient;
    private final XpSystemSettingClient xpSystemSettingClient;
@@ -161,8 +138,17 @@
    private final MongoTemplate mongoTemplate;
    private final XpElectronicFenceClient xpElectronicFenceClient;
    private final XpElectronicClient xpElectronicClient;
    private final XpTestPointClient testPointClient;
    @Resource
    private BootCommonService bootCommonService;
    @Resource
    private XpTradingStrategyClient tradingStrategyClient;
    @Resource(name = "hutoolSnowflake")
    private Snowflake snowflake;
@@ -173,11 +159,10 @@
     */
    @Override
    public void addTestOrder(AddTestOrderVo query) {
        XpTestPlanPoint xpTestPlanPoint = xpTestPlanClient.getXpTestPlanPointById(query.getTestPlanPointId()).getData();
        Long startTestPointId = xpTestPlanPoint.getStartTestPointId();
        Long endTestPointId = xpTestPlanPoint.getEndTestPointId();
        XpTestPoint xpPoint = xpTestPlanClient.getXpTestPointById(startTestPointId).getData();
        XpTestPoint endXpPoint = xpTestPlanClient.getXpTestPointById(endTestPointId).getData();
        Long startTestPointId = query.getStartTestPointId();
        Long endTestPointId = query.getEndTestPointId();
        XpTestPoint xpPoint = testPointClient.queryById(startTestPointId).getData();
        XpTestPoint endXpPoint = testPointClient.queryById(endTestPointId).getData();
        if(xpPoint ==null || xpPoint.getOperationStatus()!=2){
            throw new ServiceException("起站点已停止运营", 200,I18nKeyConstant.NO_CURRENT_POINT);
        }
@@ -211,7 +196,7 @@
        if (openCity == null) {
            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
        }
        XpPricing xpPricing = xpPricingClient.getPricingByCid(openCity.getId()).getData();
        XpPricing xpPricing = bootCommonService.getXpPricing(openCity.getId(),null);
        if (xpPricing == null) {
            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
        }
@@ -241,10 +226,14 @@
        // 生成订单数据
        XpTestOrder xpOrder = new XpTestOrder();
        xpOrder.setTestPlanId(query.getTestPlanId());
        xpOrder.setOrderType(query.getOrderType());
        xpOrder.setMileageKilometers(0.0);
        xpOrder.setOrderNumber("XP" + System.currentTimeMillis());
        xpOrder.setOrderNumber(getOrderNumber());
        xpOrder.setCityId(openCity.getId());
        xpOrder.setPlacementLat(xpPoint.getPointLat());
        xpOrder.setPlacementLon(xpPoint.getPointLon());
        xpOrder.setPlacementAddress(xpPoint.getPointAddress());
        xpOrder.setStartLon(xpPoint.getPointLon());
        xpOrder.setStartLat(xpPoint.getPointLat());
        xpOrder.setBoardingLon(xpPoint.getPointLon());
@@ -265,6 +254,8 @@
        xpOrder.setStatus(1);
        xpOrder.setOrderSource(1);
        xpOrder.setOpenTrunk(2);
        xpOrder.setXpuStartTaskId(snowflake.nextId() + "");
        xpOrder.setXpuEndTaskId(snowflake.nextId() + "");
        // 当前时段
        String currentTimeSlot = DateUtils.getCurrentTimeSlot();
@@ -272,37 +263,43 @@
        xpOrder.setCreateTime(LocalDateTime.now());
        xpOrder.setCityCode(openCity.getCityCode());
        List<XpTestPlanVehicle> data = xpTestPlanClient.getXpTestPlanVehicleByTestPlanId(query.getTestPlanId()).getData();
        for (XpTestPlanVehicle datum : data) {
            XpVehicle xpVehicle = xpVehicleClient.queryById(datum.getVehicleId()).getData();
            xpOrder.setVehicleId(datum.getVehicleId());
        List<Long> vehicleIds = query.getVehicleIds();
        for (Long vehicleId : vehicleIds) {
            xpOrder.setId(null);
            XpVehicle xpVehicle = xpVehicleClient.queryById(vehicleId).getData();
            String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
            xpOrder.setVehicleId(vehicleId);
            xpOrder.setFleetId(xpVehicle.getFleetId());
            this.save(xpOrder);
            redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), 1, 24L, TimeUnit.HOURS);
            redisService.setCacheObject("TEST_ORDER_STATUS:" + xpOrder.getId(), 1, 24L, TimeUnit.HOURS);
            //缓存导航数据,用于下单后推送导航给车端和乘客端
            redisService.setCacheObject("TEST_ORDER_NAVIGATION_END_DATA:" + datum.getVehicleId(), JSON.toJSONString(routeVo), 24L, TimeUnit.HOURS);
            redisService.setCacheObject("TEST_ORDER_NAVIGATION_END_DATA:" + vehicleId, JSON.toJSONString(routeVo), 24L, TimeUnit.HOURS);
            //下发XPU消息
            PushOrderDto pushOrderDto = new PushOrderDto();
            pushOrderDto.setVin(xpVehicle.getVehicleVin());
            pushOrderDto.setStartTaskId(xpOrder.getXpuStartTaskId());
            pushOrderDto.setEndTaskId(xpOrder.getXpuEndTaskId());
            pushOrderDto.setVin(vin);
            pushOrderDto.setOrderId("TEST" + xpOrder.getId());
            pushOrderDto.setStartWaypointId(xpOrder.getStartTestPointId());
            pushOrderDto.setEndWaypointId(xpOrder.getEndTestPointId());
            pushOrderDto.setStartLatitude(xpOrder.getStartLat());
            pushOrderDto.setStartLongitude(xpOrder.getStartLon());
            pushOrderDto.setEndLatitude(xpOrder.getEndLat());
            pushOrderDto.setEndLongitude(xpOrder.getEndLon());
            pushOrderDto.setOrderType(OrderType.USER_ORDER);
            pushOrderDto.setEndPoiName(xpOrder.getEndTestPointName());
            pushOrderDto.setOrderType(xpOrder.getOrderType() == 1 ? OrderType.TEST_ORDER : OrderType.TEST_CRUISE_ORDER);
            pushOrderDto.setStage(1);
            // 调用推送服务  orderId + carVin
            orderMissionClient.pushOrder(pushOrderDto);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("event", SystemEventType.CREATE_ORDER);
            jsonObject.put("orderType", OrderType.TEST_ORDER);
            jsonObject.put("orderId", xpOrder.getId());
            jsonObject.put("vin", xpVehicle.getVehicleVin());
            redisService.setCacheObject("CREATE_ORDER:" + pushOrderDto.getOrderId(), jsonObject.toJSONString(),30L, TimeUnit.MINUTES);
            log.info("开始推送 {} 订单到XPU。", xpOrder.getOrderNumber());
        }
    }
    private String getOrderNumber(){
        int random = Double.valueOf(Math.random() * 1000).intValue();
        return "XP" + random + System.currentTimeMillis();
    }
    private Boolean isInOperationTime(String operationStrategy) {
        JSONArray jsonArray = JSONArray.parseArray(operationStrategy);
@@ -369,6 +366,7 @@
        }
        // 1. 构建查询条件
        LambdaQueryWrapper<XpTestOrder> wrapper = buildQueryWrapper(query);
        wrapper.eq(XpTestOrder::getTestPlanId, query.getTestPlanId());
        if (CollUtil.isNotEmpty(vehicleIdsFilter)) {
            wrapper.in(XpTestOrder::getVehicleId, vehicleIdsFilter);
        }
@@ -434,7 +432,7 @@
            XpVehicle vehicle = vehicleMap.get(order.getVehicleId());
            if (Objects.nonNull(vehicle)) {
                vo.setPlateNumber(vehicle.getPlateNumber());
                vo.setVehicleModel(vehicle.getVehicleModel());
                vo.setVehicleModel(vehicle.getVehicleModelName());
            }
        }
@@ -584,6 +582,7 @@
                                          Map<Long, XpFleet> fleetMap) {
        XpTestOrderPageVO vo = new XpTestOrderPageVO();
        vo.setId(order.getId());
        vo.setOrderType(order.getOrderType());
        vo.setOrderNumber(order.getOrderNumber());
        vo.setCreateTime(order.getCreateTime());
        vo.setOrderSource(order.getOrderSource());
@@ -610,7 +609,6 @@
     * 取消订单
     */
    private void cancelOrder(XpTestOrder order) {
        redisService.removeByPattern(order.getQueueName(), order.getOrderNumber());
        //判断还原车辆状态
        if (null == order.getVehicleId()) {
            Object cacheObject1 = redisService.getCacheObject("CREATE_ORDER:" + order.getId().toString());
@@ -643,6 +641,7 @@
        R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(order.getVehicleId());
        if (xpVehicleR.getCode() == HttpStatus.SUCCESS && Objects.nonNull(xpVehicleR.getData())) {
            XpVehicle xpVehicle = xpVehicleR.getData();
            String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
            //还原车辆状态
            if(1 == xpVehicle.getState()){
                XpVehicle vehicle1 = new XpVehicle();
@@ -654,14 +653,12 @@
            // 下发命令
            PushOrderDto pushOrderDto = new PushOrderDto();
            pushOrderDto.setOrderId(order.getId().toString());
            pushOrderDto.setVin(xpVehicle.getVehicleVin());
            pushOrderDto.setEndTaskId(order.getStatus() < 6 ? order.getXpuStartTaskId() : order.getXpuEndTaskId());
            pushOrderDto.setOrderId("TEST" + order.getId().toString());
            pushOrderDto.setVin(vin);
            pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
            orderMissionClient.cancelOrder(pushOrderDto);
            log.info("运营人员取消订单成功,开始执行巡游订单 {}", order.getId());
            //车辆继续巡游
            addCruiseMission(xpVehicle.getId());
        } else {
            log.info("查询车辆信息失败,订单ID: {}, 下发取消订单命令失败", order.getId());
        }
@@ -673,10 +670,11 @@
     */
    private void closeOrder(XpTestOrder order) {
        XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData();
        String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
        order.setStatus(7);
        order.setGetoffTime(LocalDateTime.now());
        // 解析下车地点
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
        if(Objects.nonNull(vehiclePosition)){
            GeoJsonPoint point = vehiclePosition.getPoint();
            if(Objects.nonNull(point)){
@@ -692,35 +690,83 @@
        order.setMileageKilometers(new BigDecimal(reduce).divide(new BigDecimal(1000)).setScale(4, RoundingMode.HALF_EVEN).doubleValue());
        long duration = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) - order.getTripBegin().toEpochSecond(ZoneOffset.of("+8"));
        order.setDuration(new BigDecimal(duration).divide(new BigDecimal(60), 2, RoundingMode.HALF_EVEN).doubleValue());
        order.setVehicleId(null);
        order.setStartTestPointId(null);
        this.updateById(order);
        redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 7, 24L, TimeUnit.HOURS);
        redisService.setCacheObject("TEST_ORDER_STATUS:" + order.getId(), 7, 24L, TimeUnit.HOURS);
        //给XPU推送订单取消的报文
        PushOrderDto pushOrderDto = new PushOrderDto();
        pushOrderDto.setOrderId(order.getId().toString());
        pushOrderDto.setVin(xpVehicle.getVehicleVin());
        pushOrderDto.setEndTaskId(order.getStatus() < 6 ? order.getXpuStartTaskId() : order.getXpuEndTaskId());
        pushOrderDto.setOrderId("TEST" + order.getId().toString());
        pushOrderDto.setVin(vin);
        pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
        orderMissionClient.cancelOrder(pushOrderDto);
        //结束行程,修改状态和计算费用
        endTrip(order.getId());
    }
    public void carAbnormalUpdateOrderStatus(Long orderId) {
        XpTestOrder order = this.getById(orderId);
        XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData();
        String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
        order.setStatus(7);
        order.setGetoffTime(LocalDateTime.now());
        // 解析下车地点
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
        if(Objects.nonNull(vehiclePosition)){
            GeoJsonPoint point = vehiclePosition.getPoint();
            if(Objects.nonNull(point)){
                double x = point.getX();
                double y = point.getY();
                Map<String, String> geocode = gdMapGeocodingUtil.geocode(String.valueOf(x), String.valueOf(y));
                order.setGetoffAddress(geocode.get("address"));
            }
        }
        //计算行驶总距离和时长
        List<OrderTrajectory> orderTrajectoryList = mongoTemplate.find(new Query().addCriteria(Criteria.where("orderId").is(order.getId())), OrderTrajectory.class);
        Double reduce = orderTrajectoryList.stream().map(OrderTrajectory::getDistance).reduce(0.0, Double::sum);
        order.setMileageKilometers(new BigDecimal(reduce).divide(new BigDecimal(1000)).setScale(4, RoundingMode.HALF_EVEN).doubleValue());
        long duration = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) - order.getTripBegin().toEpochSecond(ZoneOffset.of("+8"));
        order.setDuration(new BigDecimal(duration).divide(new BigDecimal(60), 2, RoundingMode.HALF_EVEN).doubleValue());
        order.setStartTestPointId(null);
        this.updateById(order);
        redisService.setCacheObject("TEST_ORDER_STATUS:" + order.getId(), 7, 24L, TimeUnit.HOURS);
        //给XPU推送订单取消的报文
        PushOrderDto pushOrderDto = new PushOrderDto();
        pushOrderDto.setEndTaskId(order.getStatus() < 6 ? order.getXpuStartTaskId() : order.getXpuEndTaskId());
        pushOrderDto.setOrderId("TEST" + order.getId().toString());
        pushOrderDto.setVin(vin);
        pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
        orderMissionClient.cancelOrder(pushOrderDto);
        //结束行程,修改状态和计算费用
        endTrip(order.getId());
        //创建定时任务执行延迟驶离终点和下发巡游单
        XpTestPoint point = xpTestPointClient.queryById(order.getEndTestPointId()).getData();
        Map<String, ? extends Object> jobMaps =
                new ImmutableMap.Builder<String, String>()
                        .put("id", order.getId().toString())
                        .put("appid", appid)
                        .build();
        QuartzManager.addJob(
                AddCruiseMissionJob.class,
                (AddCruiseMissionJob.name + order.getId()).toUpperCase(),
                TimeJobType.ADD_CRUISE_MISSION,
                DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(point.getParkingTimeLimit())),
                jobMaps
        );
        //将支付金额修改成0 并将状态修改成 订单状态9
        setFree(order.getId());
    }
    /**
     * 豁免
     */
    public void setFree(Long id) {
        //将支付金额修改成0 并将状态修改成 订单状态9
        XpTestOrder xpOrder1 = new XpTestOrder();
        xpOrder1.setId(id);
        xpOrder1.setIsFree(true);//豁免
        xpOrder1.setPayMoney(BigDecimal.ZERO);
        xpOrder1.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode());
        xpOrder1.setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(xpOrder1);
    }
    /**
     * 改派订单
@@ -758,9 +804,9 @@
        // 运营状态:1=运营中 2=维修中 3=已收车 4=车辆异常
        // 当前模式:1=接单 2=测试
        // 模式状态:1=开始接单/开始测试 2=停止接单/停止测试
        if (Objects.isNull(newVehicle.getStatus()) || newVehicle.getStatus() == 3) {
            throw new ServiceException("新车辆保险或年检已过期,无法接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_STATUS_INVALID);
        }
//        if (Objects.isNull(newVehicle.getStatus()) || newVehicle.getStatus() == 3) {
//            throw new ServiceException("新车辆保险或年检已过期,无法接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_STATUS_INVALID);
//        }
        if (Objects.isNull(newVehicle.getOperatingStatus()) || newVehicle.getOperatingStatus() != 1) {
            throw new ServiceException("新车辆运营状态异常,无法接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_OPERATING_STATUS_INVALID);
        }
@@ -784,14 +830,15 @@
                R<XpVehicle> oldVehicleResult = xpVehicleClient.queryById(oldVehicleId);
                if (oldVehicleResult != null && oldVehicleResult.getCode() == HttpStatus.SUCCESS && oldVehicleResult.getData() != null) {
                    XpVehicle oldVehicle = oldVehicleResult.getData();
                    String vin = SensitiveDataCryptoUtil.decryptIfPossible(oldVehicle.getVehicleVin());
                    // 6.1.1 取消原订单指令
                    PushOrderDto cancelDto = new PushOrderDto();
                    cancelDto.setOrderId(order.getId().toString());
                    cancelDto.setVin(oldVehicle.getVehicleVin());
                    cancelDto.setOrderId("TEST" + order.getId().toString());
                    cancelDto.setVin(vin);
                    cancelDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
                    orderMissionClient.cancelOrder(cancelDto);
                    log.info("已取消原车辆订单指令,订单ID: {}, VIN: {}", orderId, oldVehicle.getVehicleVin());
                    log.info("已取消原车辆订单指令,订单ID: {}, VIN: {}", orderId, vin);
                    // 6.1.3 修改原车辆状态为空闲
                    XpVehicle vehicle1 = new XpVehicle();
@@ -843,6 +890,7 @@
        if (Objects.nonNull(newFleetId)) {
            order.setFleetId(newFleetId);
        }
        order.setStartTestPointId(null);
        this.updateById(order);
        // 10. 保存改派记录
@@ -865,20 +913,26 @@
        // 11. 处理新车辆:绑定乘客、下发订单、更新状态
        try {
            String vin = SensitiveDataCryptoUtil.decryptIfPossible(newVehicle.getVehicleVin());
            // 11.2 下发订单到新车辆
            PushOrderDto pushOrderDto = new PushOrderDto();
            pushOrderDto.setVin(newVehicle.getVehicleVin());
            pushOrderDto.setOrderId(order.getId().toString());
            pushOrderDto.setStartTaskId(order.getXpuStartTaskId());
            pushOrderDto.setEndTaskId(order.getXpuEndTaskId());
            pushOrderDto.setVin(vin);
            pushOrderDto.setOrderId("TEST" + order.getId());
            pushOrderDto.setStartWaypointId(order.getStartTestPointId());
            pushOrderDto.setEndWaypointId(order.getEndTestPointId());
            pushOrderDto.setStartLatitude(order.getStartLat());
            pushOrderDto.setStartLongitude(order.getStartLon());
            pushOrderDto.setEndLatitude(order.getEndLat());
            pushOrderDto.setEndLongitude(order.getEndLon());
            pushOrderDto.setEndPoiName(order.getEndTestPointName());
            pushOrderDto.setOrderType(OrderType.USER_ORDER);
            pushOrderDto.setStage(1);
            // 调用推送服务  orderId + carVin
            orderMissionClient.pushOrder(pushOrderDto);
            log.info("已下发订单到新车辆,订单ID: {}, VIN: {}", orderId, newVehicle.getVehicleVin());
            log.info("已下发订单到新车辆,订单ID: {}, VIN: {}", orderId, vin);
            // 11.3 更新新车辆状态为已接单
            XpVehicle vehicle1 = new XpVehicle();
@@ -929,9 +983,9 @@
                                    put("id", xpOrder.getId())
                                    .build();
                    QuartzManager.addJob(
                            OrderCancelJob.class,
                            (OrderCancelJob.name + xpOrder.getId()).toUpperCase(),
                            TimeJobType.ORDER_CANCEL,
                            TestOrderCancelJob.class,
                            (TestOrderCancelJob.name + xpOrder.getId()).toUpperCase(),
                            TimeJobType.TEST_ORDER_CANCEL,
                            DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(xpPoint.getParkingTimeLimit())),
                            cancelJobMaps
                    );
@@ -943,16 +997,44 @@
                    xpOrder.setBoardingTime(now);
                    xpOrder.setRideTime(now);
                    // 删除定时任务job
                    QuartzManager.removeJob((OrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.ORDER_CANCEL);
                    QuartzManager.removeJob((TestOrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.TEST_ORDER_CANCEL);
                    //乘客已上车,重新倒计时不开始行程取消订单
                    XpTestPoint xpPoint1 = xpTestPointClient.queryById(xpOrder.getStartTestPointId()).getData();
                    if(StringUtils.isEmpty(xpPoint1.getCityCode())){
                        Map<String, String> geocode = gdMapGeocodingUtil.geocode(xpPoint1.getPointLon().toString(), xpPoint1.getPointLat().toString());
                        if(null != geocode){
                            xpPoint1.setCityCode(geocode.get("cityCode"));
                            xpPoint1.setCityName(geocode.get("address"));
                            xpTestPointClient.editXpPoint(xpPoint1);
                        }
                    }
                    XpTradingStrategy tradingStrategy = tradingStrategyClient.getTradingStrategyByCityCode(xpPoint1.getCityCode()).getData();
                    if(null != tradingStrategy){
                        Map<String, ? extends Object> cancelJobMaps1 =
                                new ImmutableMap.Builder<String, Long>()
                                        .put("id", xpOrder.getId())
                                        .build();
                        QuartzManager.addJob(
                                TestOrderCancelJob.class,
                                (TestOrderCancelJob.name + xpOrder.getId()).toUpperCase(),
                                TimeJobType.TEST_ORDER_CANCEL,
                                DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(tradingStrategy.getBdNotBoardedTime())),
                                cancelJobMaps1
                        );
                    }
                    break;
                case 6:
                    // 删除定时任务job
                    QuartzManager.removeJob((TestOrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.TEST_ORDER_CANCEL);
                    // 行程开始时间
                    xpOrder.setTripBegin(LocalDateTime.now());
                    redisService.deleteObject("TEST_ORDER_NAVIGATION_START_DATA:" + xpOrder.getVehicleId());
                    break;
                case 7:
                    String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
                    xpOrder.setGetoffTime(LocalDateTime.now());
                    // 解析下车地点
                    VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
                    VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
                    if(Objects.nonNull(vehiclePosition)){
                        GeoJsonPoint point = vehiclePosition.getPoint();
                        if(Objects.nonNull(point)){
@@ -975,7 +1057,7 @@
                    Map<String, ? extends Object> endTripJobMaps =
                            new ImmutableMap.Builder<String, String>()
                                    .put("id", xpOrder.getId().toString())
                                    .put("appid", appid)
                                    .put("orderType", OrderType.TEST_ORDER)
                                    .build();
                    QuartzManager.addJob(
                            EndTripJob.class,
@@ -984,34 +1066,20 @@
                            DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(arriveTime)),
                            endTripJobMaps
                    );
                    // 添加车辆到达终点后限停时间到后驶离的定时任务
                    //时间到后将车辆状态回滚到空闲、添加巡游订单,解绑用户和车辆关系,车辆驶离点位
                    XpTestPoint point = xpTestPointClient.queryById(xpOrder.getEndTestPointId()).getData();
                    Map<String, ? extends Object> jobMaps =
                            new ImmutableMap.Builder<String, String>()
                                    .put("id", xpOrder.getId().toString())
                                    .put("appid", appid)
                                    .build();
                    QuartzManager.addJob(
                            AddCruiseMissionJob.class,
                            (AddCruiseMissionJob.name + xpOrder.getId()).toUpperCase(),
                            TimeJobType.ADD_CRUISE_MISSION,
                            DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(point.getParkingTimeLimit())),
                            jobMaps
                    );
                    break;
            }
            xpOrder.setStatus(status);
            xpOrder.setVehicleId(null);
            xpOrder.setStartTestPointId(null);
            this.updateById(xpOrder);
            redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), status, 24L, TimeUnit.HOURS);
            redisService.setCacheObject("TEST_ORDER_STATUS:" + xpOrder.getId(), status, 24L, TimeUnit.HOURS);
        }else{
            //测试巡游单,只需要修改状态
            if(status == 10){
                xpOrder.setStatus(status);
                xpOrder.setVehicleId(null);
                xpOrder.setStartTestPointId(null);
                this.updateById(xpOrder);
                redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), status, 24L, TimeUnit.HOURS);
                redisService.setCacheObject("TEST_ORDER_STATUS:" + xpOrder.getId(), status, 24L, TimeUnit.HOURS);
            }
        }
        return R.ok();
@@ -1037,14 +1105,13 @@
            throw new RuntimeException("订单状态错误");
        }
        order.setStatus(8);
        order.setVehicleId(null);
        order.setStartTestPointId(null);
        order.setTripEnd(LocalDateTime.now());
        baseMapper.updateById(order);
        redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 8, 24L, TimeUnit.HOURS);
        redisService.setCacheObject("TEST_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);
        }
@@ -1079,7 +1146,7 @@
            JSONObject basicPricingJson = JSONObject.parseObject(pricing.getBasicPricingJson());
            XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationFixedPrice(uid, basicPricingJson, null, mileageKilometers, duration1, 0, 0.0, pricing.getIsDynamicPrice(), order.getCityId(), pricing.getDynamicPricingJson());
            XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationFixedPrice(order.getVehicleId().toString(), basicPricingJson, null, mileageKilometers, duration1, 0, 0.0, pricing.getIsDynamicPrice(), order.getCityId(), pricing.getDynamicPricingJson());
            XpTestOrderDetail xpOrderDetail = new XpTestOrderDetail();
            xpOrderDetail.setDetailType(1);
            xpOrderDetail.setOrderId(orderId);
@@ -1109,7 +1176,7 @@
                xpTestOrderDetailMapper.insert(xpOrderDetail1);
            }
            order.setOrderMoney(totalPrice);
            order.setVehicleId(null);
            order.setStartTestPointId(null);
            baseMapper.updateById(order);
        } else {
            //动态计费
@@ -1174,7 +1241,7 @@
            double priceRate = 1;
            if (pricing.getIsDynamicPrice() == 1) {
                // 先判断是否已经调价过了
                Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid);
                Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + order.getVehicleId());
                if (userDynamicPriceObj != null) {
                    // 是否调价过了
                    priceRate = Double.parseDouble(userDynamicPriceObj.toString());
@@ -1204,7 +1271,7 @@
                            // 冷却时间
                            Integer time = getOrderNum(jsonObject, 6);
                            // 触发调价了
                            redisService.setCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid, priceRate, time.longValue(), TimeUnit.MINUTES);
                            redisService.setCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + order.getVehicleId(), priceRate, time.longValue(), TimeUnit.MINUTES);
                        }
                    }
@@ -1367,41 +1434,13 @@
                    .add(order.getLongDistancemoney() != null ? order.getLongDistancemoney() : BigDecimal.ZERO)
                    .add(order.getWaitMoney() != null ? order.getWaitMoney() : BigDecimal.ZERO);
            order.setOrderMoney(orderMoney);
            order.setVehicleId(null);
            order.setStartTestPointId(null);
            baseMapper.updateById(order);
        }
        // 计算完价格后 挑选优惠券
        GetUserCouponDto getUserCouponDto = new GetUserCouponDto();
        getUserCouponDto.setUserId(Long.valueOf(uid));
        getUserCouponDto.setTotalAmount(order.getOrderMoney().doubleValue());
        getUserCouponDto.setUserCouponId(order.getCouponId());
        getUserCouponDto.setOpenCityId(order.getCityId());
        R<UserCouponVo> userCouponVoR = xpAppUserCouponClient.getUserCoupon(getUserCouponDto);
        UserCouponVo userCouponVo = userCouponVoR.getData();
        // 提示是否换券
        boolean tip =false;
        if (userCouponVo != null) {
            // 重新选券
            if(userCouponVo.getStatus()==2){
                getUserCouponDto.setUserId(Long.valueOf(uid));
                getUserCouponDto.setTotalAmount(order.getOrderMoney().doubleValue());
                getUserCouponDto.setUserCouponId(order.getCouponId());
                getUserCouponDto.setOpenCityId(order.getCityId());
                userCouponVoR = xpAppUserCouponClient.getUserCoupon(getUserCouponDto);
                userCouponVo = userCouponVoR.getData();
                tip=true;
            }
            order.setCouponId(userCouponVo.getId());
            order.setCouponMoney(BigDecimal.valueOf(userCouponVo.getMoney()));
        }
        order.setVehicleId(null);
        order.setStartTestPointId(null);
        baseMapper.updateById(order);
        return tip;
        return true;
    }
@@ -1436,7 +1475,7 @@
    @Override
    public void saveVehicleLocation(OrderTrajectory orderTrajectory) {
        // 查询订单信息
        Integer status = redisService.getCacheObject("ORDER_STATUS:" + orderTrajectory.getOrderId());
        Integer status = redisService.getCacheObject("TEST_ORDER_STATUS:" + orderTrajectory.getOrderId());
        if(status < 6 || status > 8){
            return;
        }
@@ -1545,66 +1584,6 @@
    /**
     * 发送乘客导航路径数据
     *
     * @param appUserId
     * @param keyStr
     */
    public void sendRoute(String appUserId, Long orderId, String keyStr, String vin) {
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
        Object cacheObject = redisService.getCacheObject(keyStr + ":" + appUserId);
        if (null != cacheObject) {
            RouteVo routeVo = JSON.parseObject(cacheObject.toString(), RouteVo.class);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("type", "ORDER_ROUTE");
            jsonObject.put("timestamp", System.currentTimeMillis());
            JSONObject content = new JSONObject();
            content.put("orderId", orderId.toString());
            content.put("routeType", "ORDER_NAVIGATION_START_DATA".equals(keyStr) ? 1 : 2);
            content.put("direction", null != vehiclePosition ? vehiclePosition.getCourse() : 90);
            JSONObject route = new JSONObject();
            route.put("distance", routeVo.getDistance());
            route.put("duration", routeVo.getDuration());
            route.put("routeID", routeVo.getRouteID());
            List<JSONObject> collect = routeVo.getPolyline().stream().map(item -> {
                JSONObject point = new JSONObject();
                point.put("latitude", item.getLatitude());
                point.put("longitude", item.getLongitude());
                return point;
            }).collect(Collectors.toList());
            route.put("polyline", JSON.toJSONString(collect));
            content.put("route", route.toJSONString());
            jsonObject.put("content", content.toJSONString());
            SendMessageDTO messageDTO = new SendMessageDTO();
            messageDTO.setAppUserId(appUserId);
            messageDTO.setMessage(jsonObject.toJSONString());
            R<String> stringR = xpPushMessageClient.sendRoute(messageDTO);
            XpTestOrder xpOrder = this.getById(orderId);
            //推送失败,重试3次
            if(stringR.getCode() != Constants.SUCCESS){
                ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
                executor.execute(() -> {
                    for (int i = 0; i < 3; i++) {
                        R<String> stringR1 = xpPushMessageClient.sendStatus(messageDTO);
                        if(stringR1.getCode() == Constants.SUCCESS){
                            break;
                        }
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        log.info("推送乘客端导航数据消息失败:{} {} {}", stringR1.getMsg(), JSON.toJSONString(messageDTO), xpOrder.getOrderNumber());
                    }
                });
            }
        }
    }
    @Override
    public void changeAmount(Long id, BigDecimal amount) {
@@ -1643,4 +1622,28 @@
            throw new ServiceException("订单改价失败,请稍后重试", I18nKeyConstant.ORDER_CHANGE_AMOUNT_FAILED);
        }
    }
    @Override
    public R<?> cancelTestOrderById(Long orderId) {
        XpTestOrder xpTestOrder = this.getById(orderId);
        if (xpTestOrder != null) {
            xpTestOrder.setStatus(0);
            xpTestOrder.setStartTestPointId(null);
            this.updateById(xpTestOrder);
            XpVehicle xpVehicle = xpVehicleClient.queryById(xpTestOrder.getVehicleId()).getData();
            String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
            PushOrderDto pushOrderDto = new PushOrderDto();
            pushOrderDto.setEndTaskId(xpTestOrder.getStatus() < 6 ? xpTestOrder.getXpuStartTaskId() : xpTestOrder.getXpuEndTaskId());
            pushOrderDto.setVin(vin);
            pushOrderDto.setOrderId("TEST" + xpTestOrder.getId());
            pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
            R<Boolean> booleanR = orderMissionClient.cancelOrder(pushOrderDto);
            if(booleanR==null || !booleanR.getData()){
                log.error("XPU取消测试巡游订单失败");
                return R.fail("XPU取消测试巡游订单失败");
            }
            return R.ok();
        }
        return R.fail("获取订单失败");
    }
}