liujie
2026-06-09 c96fbe6b0aeb413a17c429133ee6d81d138144b9
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/XpPricingServiceImpl.java
@@ -1,35 +1,38 @@
package com.xiaopeng.itinerary.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Snowflake;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.dto.account.GetUserCouponDto;
import com.xiaopeng.api.dto.map.OrderRouteAndFareDto;
import com.xiaopeng.api.dto.account.UcUserInfo;
import com.xiaopeng.api.dto.map.RouteAndFareDto;
import com.xiaopeng.api.dto.map.RouteStaleCheckDto;
import com.xiaopeng.api.dto.map.SelectNavigationRouteDto;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.enums.RouteStrategyKind;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.feignClient.account.UserResourcesClient;
import com.xiaopeng.api.feignClient.account.UserRoutePlanRuleClient;
import com.xiaopeng.api.feignClient.account.XpAppUserClient;
import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient;
import com.xiaopeng.api.feignClient.map.XpMapClient;
import com.xiaopeng.api.feignClient.operation.XpPointClient;
import com.xiaopeng.api.feignClient.operation.XpTargetedServiceClient;
import com.xiaopeng.api.feignClient.operation.XpTradingStrategyClient;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.account.XpUserRoutePlanRule;
import com.xiaopeng.api.model.operation.*;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.api.vo.account.UserCouponVo;
import com.xiaopeng.api.vo.operation.FareVo;
import com.xiaopeng.api.vo.operation.MultiRouteAndFareVo;
import com.xiaopeng.api.vo.operation.RouteAndFareVo;
import com.xiaopeng.api.vo.operation.RouteOptionVo;
import com.xiaopeng.api.vo.operation.RoutePreferenceItemVo;
import com.xiaopeng.api.vo.operation.RouteStaleCheckVo;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.api.vo.operation.*;
import com.xiaopeng.api.vo.order.XpPricingCardVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
@@ -38,18 +41,22 @@
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.itinerary.constant.I18nKeyConstant;
import com.xiaopeng.itinerary.dto.XpOrderDto;
import com.xiaopeng.itinerary.navigation.MultiRouteSnapshotPayload;
import com.xiaopeng.itinerary.mapper.*;
import com.xiaopeng.itinerary.navigation.MultiRouteSnapshotPayload;
import com.xiaopeng.itinerary.service.BootCommonService;
import com.xiaopeng.itinerary.service.IXpGeoQueryEntityService;
import com.xiaopeng.itinerary.service.XpPointService;
import com.xiaopeng.itinerary.service.XpPricingService;
import com.xiaopeng.itinerary.vo.QueueStatusVo;
import com.xiaopeng.itinerary.utils.XpPricingUtil;
import com.xiaopeng.itinerary.utils.holiday.HolidayUtil;
import com.xiaopeng.itinerary.utils.map.FastDistanceCalculator;
import com.xiaopeng.itinerary.utils.map.GDMapGeocodingUtil;
import com.xiaopeng.itinerary.vo.QueueStatusVo;
import com.xiaopeng.itinerary.vo.RedisQueuePointIdVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
@@ -60,6 +67,7 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
@@ -85,6 +93,8 @@
    private XpMapClient xpMapClient;
    @Resource
    private BootCommonService bootCommonService;
    @Resource
    private XpOpenCityMapper xpOpenCityMapper;
    @Resource
@@ -101,9 +111,14 @@
    @Resource
    private XpOrderClient xpOrderClient;
    @Resource
    private XpVehicleClient xpVehicleClient;
    @Resource
    private XpTradingStrategyMapper xpTradingStrategyMapper;
    @Resource
    private XpTradingStrategyPushMapper xpTradingStrategyPushMapper;
    @Resource
    private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper;
@@ -131,9 +146,22 @@
    @Resource
    private GDMapGeocodingUtil gdMapGeocodingUtil;
    @Resource
    private XpPointClient xpPointClient;
    @Resource
    private UserRoutePlanRuleClient userRoutePlanRuleClient;
    @Resource
    private XpTargetedServiceClient xpTargetedServiceClient;
    @Resource
    private XpTradingStrategyClient xpTradingStrategyClient;
    @Resource
    private UserResourcesClient userResourcesClient;
    @Resource
    private RedissonClient redissonClient;
    @Resource(name = "hutoolSnowflake")
    private Snowflake snowflake;
    @Value("${navigation.route.stale-seconds:60}")
    private int navigationStaleSeconds;
@@ -142,7 +170,7 @@
    private double navigationPriceWavePercent;
    /**
     * 后台可配置的偏好项编码,逗号分隔,如 SMART,TIME,PRICE
     * 后台可配置的偏好项编码,逗号分隔,如 SMART,TIME,PRICE,AVOID_CONGESTION
     */
    @Value("${navigation.route.preference.enabled-codes:SMART,TIME,PRICE}")
    private String navigationPreferenceEnabledCodes;
@@ -201,121 +229,129 @@
        return hour + "-" + (hour + 1);
    }
    public XpPricing getInfo(Long cityId,String vehicleModel){
        LambdaQueryWrapper<XpPricing> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(XpPricing::getDeleted, 0);
        queryWrapper.eq(XpPricing::getCityId, cityId);
        if(vehicleModel != null){
            queryWrapper.eq(XpPricing::getVehicleModel, vehicleModel);
        }
        return super.getOne(queryWrapper);
    }
    @Override
    public RouteAndFareVo getRouteAndFare(RouteAndFareDto routeAndFareDto, String userId) {
//        // 判断是否5分钟内进行了查询 相同站点的起始点  并且是同时段
//        String key = "xp-map-query-" + routeAndFareDto.getOpenCityId()
//                + "-" + routeAndFareDto.getOrigin().getLongitude() + "-" + routeAndFareDto.getOrigin().getLatitude()
//                + "-" + routeAndFareDto.getDestination().getLongitude() + "-" + routeAndFareDto.getDestination().getLatitude() + "-" + getCurrentTimeSlot() + "-" + userId+routeAndFareDto.getUserCouponId();
//
//        if (redisService.hasKey(key)) {
//            String jsonStr = Objects.requireNonNull(redisService.getCacheObject(key)).toString();
//            RouteAndFareVo RouteAndFareVo = JSONObject.parseObject(jsonStr, RouteAndFareVo.class);
//            return (RouteAndFareVo);
//        }
    public List<RouteAndFareVo> getRouteAndFare(RouteAndFareDto routeAndFareDto, String userId) {
        ArrayList<RouteAndFareVo> routeAndFareVos = new ArrayList<>(3);
        // 1. 基础数据查询
        Map<String, String> geocode = gdMapGeocodingUtil.geocode(routeAndFareDto.getOrigin().getLongitude().toString(), routeAndFareDto.getOrigin().getLatitude().toString());
        if(null == geocode){
            throw new ServiceException("获取城市信息失败", I18nKeyConstant.CITY_NOT_FOUND);
            throw new ServiceException("城市暂未开通服务", I18nKeyConstant.CITY_NOT_FOUND);
        }
        Long cityCode = routeAndFareDto.getOpenCityId();
        XpOpenCity openCities = xpOpenCityMapper.selectById(cityCode);
        if (openCities == null ) {
            throw new ServiceException("获取城市信息失败", I18nKeyConstant.CITY_NOT_FOUND);
            throw new ServiceException("城市暂未开通服务", I18nKeyConstant.CITY_NOT_FOUND);
        }
        XpOpenCity xpOpenCity = openCities;
        routeAndFareDto.setOpenCityId(xpOpenCity.getId());
        XpRoutePlan xpRoutePlan = xpRoutePlanMapper.selectOne(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getAuditStatus, 2).eq(XpRoutePlan::getCityCode, cityCode));
        if (xpRoutePlan != null) {
            routeAndFareDto.setRoutePlanRule(xpRoutePlan.getRoutePlanRule());
        }
        Object userPref = redisService.getCacheObject(CacheConstants.USER_ROUTE_PREFERENCE + userId);
        if (userPref != null) {
            try {
                routeAndFareDto.setRoutePlanRule(Integer.valueOf(userPref.toString()));
            } catch (Exception ignored) {
                // keep city default
            }
        }
        XpPricing xpPricing = this.baseMapper.selectOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, routeAndFareDto.getOpenCityId()));
        routeAndFareDto.setOpenCityId(openCities.getId());
        //根据订单获取车型 获取定价策略
        XpPricing xpPricing = bootCommonService.getXpPricing(routeAndFareDto.getOpenCityId(), routeAndFareDto.getOrderId(), null);
        if (xpPricing == null) {
            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
        }
        routeAndFareDto.setOpenCityId(openCities.getId());
        List<XpRoutePlan> xpRoutePlan = xpRoutePlanMapper.selectList(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getAuditStatus, 2).eq(XpRoutePlan::getOpenCityId, cityCode));
        // 找出用户偏好
        Object cacheObject = redisService.getCacheObject(CacheConstants.USER_ROUTE_PREFERENCE + userId);
        for (XpRoutePlan routePlan : xpRoutePlan) {
        // 算距离
        R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto);
        if (distanceAndDuration.getCode() != 200) {
            throw new ServiceException("获取导肮信息失败", I18nKeyConstant.PATH_NOT_FOUND);
        }
        RouteVo routeVo = distanceAndDuration.getData();
        // 2. 计算价格(需要在并行任务前完成,因为优惠券查询需要价格)
        XpPricingCardVo xpPricingCardVo;
        if (xpPricing.getPricingType() == 1) {
            xpPricingCardVo = xpPricingUtil.calculationPrice(userId,
                    JSON.parseObject(xpPricing.getBasicPricingJson()),
                    JSON.parseArray(xpPricing.getStartingFareJson()),
                    routeVo.getDistance(), routeVo.getDuration(), 0, 0.0, xpPricing.getIsDynamicPrice(), routeAndFareDto.getOpenCityId(), xpPricing.getDynamicPricingJson()
            );
        } else {
            xpPricingCardVo = xpPricingUtil.calculationFixedPrice(userId,
                    JSON.parseObject(xpPricing.getBasicPricingJson()),
                    JSON.parseArray(xpPricing.getStartingFareJson()),
                    routeVo.getDistance(), routeVo.getDuration(), 0, 0.0, xpPricing.getIsDynamicPrice(), routeAndFareDto.getOpenCityId(), xpPricing.getDynamicPricingJson()
            );
        }
        // 优惠券:null=自动最佳;0=不使用;>0=指定券
        XpPricingCardVo finalXpPricingCardVo = xpPricingCardVo;
        R<UserCouponVo> coupon = null;
        CompletableFuture<R<UserCouponVo>> couponFuture=null;
        Long ucId = routeAndFareDto.getUserCouponId();
        if (Long.valueOf(0L).equals(ucId)) {
            coupon = R.ok(new UserCouponVo());
        } else if (ucId != null && ucId > 0L) {
            coupon = xpAppUserCouponClient.getUserCoupon(new GetUserCouponDto(Long.valueOf(userId), finalXpPricingCardVo.getTotalPrice(), routeAndFareDto.getOpenCityId(), ucId));
            if(coupon.getCode()==200 && coupon.getData().getStatus()==2){
                throw new ServiceException("优惠券不满足条件", I18nKeyConstant.COUPON_NOT_SATISFY);
            if (xpRoutePlan != null) {
                routeAndFareDto.setRoutePlanRule(routePlan.getRoutePlanRule());
            }
        } else {
            couponFuture= CompletableFuture.supplyAsync(
                    () -> xpAppUserCouponClient.getUserCoupon(
                            new GetUserCouponDto(Long.valueOf(userId), finalXpPricingCardVo.getTotalPrice(), routeAndFareDto.getOpenCityId(),null)
            // Object userPref = redisService.getCacheObject(CacheConstants.USER_ROUTE_PREFERENCE + userId);
            // if (userPref != null) {
            //     try {
            //         routeAndFareDto.setRoutePlanRule(Integer.valueOf(userPref.toString()));
            //     } catch (Exception ignored) {
            //         // keep city default
            //     }
            // }
            // 算距离
            R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto);
            if (distanceAndDuration.getCode() != 200) {
//                throw new ServiceException("获取导肮信息失败", I18nKeyConstant.PATH_NOT_FOUND);
                continue;
            }
            RouteVo routeVo = distanceAndDuration.getData();
            // 2. 计算价格(需要在并行任务前完成,因为优惠券查询需要价格)
            XpPricingCardVo xpPricingCardVo;
            if (xpPricing.getPricingType() == 1) {
                xpPricingCardVo = xpPricingUtil.calculationPrice(userId,
                        JSON.parseObject(xpPricing.getBasicPricingJson()),
                        JSON.parseArray(xpPricing.getStartingFareJson()),
                        routeVo.getDistance(), routeVo.getDuration(), 0, 0.0, xpPricing.getIsDynamicPrice(),
                        routeAndFareDto.getOpenCityId(), xpPricing.getDynamicPricingJson()
                );
            } else {
                xpPricingCardVo = xpPricingUtil.calculationFixedPrice(userId,
                        JSON.parseObject(xpPricing.getBasicPricingJson()),
                        JSON.parseArray(xpPricing.getStartingFareJson()),
                        routeVo.getDistance(), routeVo.getDuration(), 0, 0.0, xpPricing.getIsDynamicPrice(), routeAndFareDto.getOpenCityId(), xpPricing.getDynamicPricingJson()
                );
            }
            log.info("计算费用明细 {}", JSON.toJSONString(xpPricingCardVo));
            // 优惠券:null=自动最佳;0=不使用;>0=指定券
            XpPricingCardVo finalXpPricingCardVo = xpPricingCardVo;
            R<UserCouponVo> coupon = null;
            CompletableFuture<R<UserCouponVo>> couponFuture=null;
            Long ucId = routeAndFareDto.getUserCouponId();
            if (Long.valueOf(0L).equals(ucId)) {
                coupon = R.ok(new UserCouponVo());
            } else if (ucId != null && ucId > 0L) {
                coupon = xpAppUserCouponClient.getUserCoupon(new GetUserCouponDto(Long.valueOf(userId), finalXpPricingCardVo.getTotalPrice(), routeAndFareDto.getOpenCityId(), ucId));
                if(coupon.getCode()==200 && coupon.getData().getStatus()==2){
                    throw new ServiceException("优惠券不满足条件", I18nKeyConstant.COUPON_NOT_SATISFY);
                }
            } else {
                couponFuture= CompletableFuture.supplyAsync(
                        () -> xpAppUserCouponClient.getUserCoupon(
                                new GetUserCouponDto(Long.valueOf(userId), finalXpPricingCardVo.getTotalPrice(), routeAndFareDto.getOpenCityId(),null)
                        ),
                        executorService
                );
            }
            // 3. 并行执行远程服务调用 判断是否同一经营区域
            CompletableFuture<R<Boolean>> diagnosisAreaFuture = CompletableFuture.supplyAsync(
                    () -> xpMapClient.diagnosisArea(routeAndFareDto),
                    executorService
            );
        }
            // 4. 等待所有并行任务完成并获取结果
            R<Boolean> diagnosisResult = diagnosisAreaFuture.join();
            R<UserCouponVo> couponResult =null;
            if (Long.valueOf(0L).equals(ucId) || (ucId != null && ucId > 0L)) {
                couponResult = coupon;
            } else {
                couponResult = couponFuture.join();
            }
        // 3. 并行执行远程服务调用 判断是否同一经营区域
        CompletableFuture<R<Boolean>> diagnosisAreaFuture = CompletableFuture.supplyAsync(
                () -> xpMapClient.diagnosisArea(routeAndFareDto),
                executorService
        );
        // 4. 等待所有并行任务完成并获取结果
        R<Boolean> diagnosisResult = diagnosisAreaFuture.join();
        R<UserCouponVo> couponResult =null;
        if (Long.valueOf(0L).equals(ucId) || (ucId != null && ucId > 0L)) {
            couponResult = coupon;
        } else {
            couponResult = couponFuture.join();
        }
        // 5. 处理诊断结果
        if (diagnosisResult.getCode() != R.SUCCESS) {
            throw new RuntimeException(diagnosisResult.getMsg());
        }
        if (!diagnosisResult.getData()) {
            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
        }
            // 5. 处理诊断结果
            if (diagnosisResult.getCode() != R.SUCCESS) {
                throw new RuntimeException(diagnosisResult.getMsg());
            }
            if (!diagnosisResult.getData()) {
                throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
            }
//        // 6. 处理路线结果
//        if (routeResult.getCode() != 200) {
@@ -323,102 +359,123 @@
//        }
//        RouteVo routeVo = routeResult.getData();
        // 起终点最小距离 走缓存
        Integer minOrderDistance;
        Object minOrderDistanceObj = redisService.getCacheObject(CacheConstants.MIN_ORDER_DISTANCE);
        if (minOrderDistanceObj != null) {
            minOrderDistance = Integer.valueOf(minOrderDistanceObj.toString());
        } else {
            minOrderDistance = systemSettingMapper.selectOne(null).getMinOrderDistance();
        }
        if (routeVo.getDistance() < Integer.valueOf(minOrderDistance)) {
            throw new ServiceException("当前上下车点距离过近,建议步行前往", I18nKeyConstant.TO_WORK);
        }
        // 7. 处理优惠券结果
        if (couponResult.getCode() != R.SUCCESS) {
            throw new RuntimeException(couponResult.getMsg());
        }
        UserCouponVo userCouponVo = couponResult.getData();
        if (userCouponVo != null) {
            Double money = userCouponVo.getMoney();
            money = new BigDecimal(money).setScale(2, RoundingMode.HALF_UP).doubleValue();
            xpPricingCardVo.setCoupon(String.valueOf(money));
            if (xpPricingCardVo.getTotalPrice() > money) {
                xpPricingCardVo.setSystemDiscount(xpPricingCardVo.getTotalPrice() - money);
            // 起终点最小距离 走缓存
            Integer minOrderDistance;
            Object minOrderDistanceObj = redisService.getCacheObject(CacheConstants.MIN_ORDER_DISTANCE);
            if (minOrderDistanceObj != null) {
                minOrderDistance = Integer.valueOf(minOrderDistanceObj.toString());
            } else {
                minOrderDistance = systemSettingMapper.selectOne(null).getMinOrderDistance();
            }
            if (routeVo.getDistance() < Integer.valueOf(minOrderDistance)) {
                throw new ServiceException("当前上下车点距离过近,建议步行前往", I18nKeyConstant.TO_WORK);
            }
        }
        // 8. 构建响应对象
        RouteAndFareVo routeAndFareVo = new RouteAndFareVo();
        routeAndFareVo.setRoute(routeVo);
        // 转2位小数处理
        xpPricingCardVo.setTotalPriceStr(new BigDecimal(xpPricingCardVo.getTotalPrice()).setScale(2, RoundingMode.HALF_UP).toString());
        xpPricingCardVo.setStartPriceStr(new BigDecimal(xpPricingCardVo.getStartPrice()).setScale(2, RoundingMode.HALF_UP).toString());
        xpPricingCardVo.setDistancePriceStr(new BigDecimal(xpPricingCardVo.getDistancePrice()).setScale(2, RoundingMode.HALF_UP).toString());
        xpPricingCardVo.setDurationPriceStr(new BigDecimal(xpPricingCardVo.getDurationPrice()).setScale(2, RoundingMode.HALF_UP).toString());
        xpPricingCardVo.setSystemDiscountStr(new BigDecimal(xpPricingCardVo.getSystemDiscount()).setScale(2, RoundingMode.HALF_UP).toString());
        routeAndFareVo.setXpPricingCard(xpPricingCardVo);
        FareVo fareVo = new FareVo();
        fareVo.setEstimatedDistanceKm(routeVo.getDistance().doubleValue() / 1000);
        fareVo.setEstimatedDurationMin(routeVo.getDuration() / 60);
        // 9. 设置费用信息
        fareVo.setOriginalFare(xpPricingCardVo.getTotalPrice());
        fareVo.setOriginalFareStr(new BigDecimal(fareVo.getOriginalFare()).setScale(2, RoundingMode.HALF_UP).toString());
        if (userCouponVo != null) {
            if(userCouponVo.getMoney()>fareVo.getOriginalFare()){
                fareVo.setDiscountAmount(fareVo.getOriginalFare());
                fareVo.setFinalFare(0.00);
            }else {
                fareVo.setDiscountAmount(userCouponVo.getMoney());
                fareVo.setFinalFare(fareVo.getOriginalFare() - fareVo.getDiscountAmount());
            // 7. 处理优惠券结果
            if (couponResult.getCode() != R.SUCCESS) {
                throw new RuntimeException(couponResult.getMsg());
            }
            if(fareVo.getFinalFare()>0){
                fareVo.setSystemDiscount(fareVo.getFinalFare());
                fareVo.setFinalFare(0.00);
            UserCouponVo userCouponVo = couponResult.getData();
            if (userCouponVo != null) {
                Double money = userCouponVo.getMoney();
                money = new BigDecimal(money).setScale(2, RoundingMode.HALF_UP).doubleValue();
                xpPricingCardVo.setCoupon(String.valueOf(money));
//                if (xpPricingCardVo.getTotalPrice() > money) {
//                    xpPricingCardVo.setSystemDiscount(xpPricingCardVo.getTotalPrice() - money);
//                }
            }
            fareVo.setCouponId(userCouponVo.getId());
        } else {
            fareVo.setDiscountAmount(0.00);
            fareVo.setFinalFare(fareVo.getOriginalFare());
            fareVo.setSystemDiscount(fareVo.getFinalFare());
            fareVo.setFinalFare(0.00);
        }
        // 10. 检查等待时间
        Integer waitTime = getWaitTime(routeAndFareDto.getOrigin().getLongitude(), routeAndFareDto.getOrigin().getLatitude());
            // 8. 构建响应对象
            RouteAndFareVo routeAndFareVo = new RouteAndFareVo();
            routeAndFareVo.setRoute(routeVo);
            // 转2位小数处理
            xpPricingCardVo.setTotalPriceStr(new BigDecimal(xpPricingCardVo.getTotalPrice()).setScale(2, RoundingMode.HALF_UP).toString());
            xpPricingCardVo.setStartPriceStr(new BigDecimal(xpPricingCardVo.getStartPrice()).setScale(2, RoundingMode.HALF_UP).toString());
            xpPricingCardVo.setDistancePriceStr(new BigDecimal(xpPricingCardVo.getDistancePrice()).setScale(2, RoundingMode.HALF_UP).toString());
            xpPricingCardVo.setDurationPriceStr(new BigDecimal(xpPricingCardVo.getDurationPrice()).setScale(2, RoundingMode.HALF_UP).toString());
//            xpPricingCardVo.setSystemDiscountStr(new BigDecimal(xpPricingCardVo.getSystemDiscount()).setScale(2, RoundingMode.HALF_UP).toString());
            routeAndFareVo.setXpPricingCard(xpPricingCardVo);
            FareVo fareVo = new FareVo();
            fareVo.setEstimatedDistanceKm(routeVo.getDistance().doubleValue() / 1000);
            fareVo.setEstimatedDurationMin(routeVo.getDuration() / 60);
            // 9. 设置费用信息
            fareVo.setOriginalFare(xpPricingCardVo.getTotalPrice());
            fareVo.setOriginalFareStr(new BigDecimal(xpPricingCardVo.getTotalPrice()).setScale(2, RoundingMode.HALF_UP).toString());
            if (userCouponVo != null) {
                if(userCouponVo.getMoney()>fareVo.getOriginalFare()){
                    fareVo.setCouponType(userCouponVo.getCouponType());
                    fareVo.setDiscountAmount(fareVo.getOriginalFare());
                    fareVo.setFinalFare(0.00);
                }else {
                    fareVo.setDiscountAmount(userCouponVo.getMoney());
                    fareVo.setCouponType(userCouponVo.getCouponType());
                    fareVo.setFinalFare(fareVo.getOriginalFare() - fareVo.getDiscountAmount());
                }
//                if(fareVo.getFinalFare()>0){
//                    fareVo.setSystemDiscount(fareVo.getFinalFare());
//                    fareVo.setFinalFare(0.00);
//                }
                fareVo.setCouponId(userCouponVo.getId());
            } else {
                fareVo.setDiscountAmount(0.00);
                fareVo.setFinalFare(fareVo.getOriginalFare());
//                fareVo.setSystemDiscount(fareVo.getFinalFare());
//                fareVo.setFinalFare(0.00);
            }
            // 10. 检查等待时间
            Integer waitTime = getWaitTime(routeAndFareDto.getOrigin().getLongitude(), routeAndFareDto.getOrigin().getLatitude());
//        if (waitTime > 3) {
//            throw new ServiceException("当前区域正在等待中,请耐心等待", I18nKeyConstant.WAITING_TIME);
//        }
        fareVo.setWaitingTime(waitTime);
            fareVo.setWaitingTime(waitTime);
        // 11. 生成订单ID并缓存
        String id = CacheConstants.ORD + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
        redisService.setCacheObject(CacheConstants.ORD + id + userId, xpPricingCardVo, 1l, TimeUnit.DAYS);
        fareVo.setId(id);
            // 11. 生成订单ID并缓存
            String id = CacheConstants.ORD + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
            redisService.setCacheObject(CacheConstants.ORD + id + userId, xpPricingCardVo, 1L, TimeUnit.DAYS);
            fareVo.setId(id);
        // 12. 设置费用信息
        fareVo.setFinalFareStr(new BigDecimal(fareVo.getFinalFare()).setScale(2, RoundingMode.HALF_UP).toString());
        fareVo.setOriginalFare(new BigDecimal(fareVo.getOriginalFare()).setScale(2,RoundingMode.HALF_UP).doubleValue());
        fareVo.setSystemDiscount(new BigDecimal(fareVo.getSystemDiscount() == null ? 0 : fareVo.getSystemDiscount()).setScale(2,RoundingMode.HALF_UP).doubleValue());
        routeAndFareVo.setFareEstimate(fareVo);
            // 12. 设置费用信息
            fareVo.setFinalFareStr(new BigDecimal(fareVo.getFinalFare()).setScale(2, RoundingMode.HALF_UP).toString());
            fareVo.setOriginalFare(new BigDecimal(fareVo.getOriginalFare()).setScale(2,RoundingMode.HALF_UP).doubleValue());
//            fareVo.setSystemDiscount(new BigDecimal(fareVo.getSystemDiscount() == null ? 0 : fareVo.getSystemDiscount()).setScale(2,RoundingMode.HALF_UP).doubleValue());
            routeAndFareVo.setFareEstimate(fareVo);
        routeAndFareVo.getXpPricingCard().setFinalFareStr(fareVo.getFinalFareStr());
            routeAndFareVo.getXpPricingCard().setFinalFareStr(fareVo.getFinalFareStr());
//        String voJsonStr = JSONObject.toJSONString(routeAndFareVo);
//         进行缓存
//        redisService.setCacheObject(key, voJsonStr, 5l, TimeUnit.MINUTES);
        //缓存导航数据,用于下单后推送导航给车端和乘客端
        redisService.setCacheObject("ORDER_NAVIGATION_END_DATA:" + userId, JSON.toJSONString(routeVo), 24L, TimeUnit.HOURS);
        return routeAndFareVo;
            //缓存导航数据,用于下单后推送导航给车端和乘客端
            redisService.setCacheObject("ORDER_NAVIGATION_END_DATA:" + userId, JSON.toJSONString(routeVo), 24L, TimeUnit.HOURS);
            if(cacheObject!=null){
                if (cacheObject.toString().equals(routePlan.getRoutePlanRule().toString())) {
                    routeAndFareVo.setPreference(1);
                }
            }
            routeAndFareVo.setRoutePlanName(routePlan.getPlanName());
            routeAndFareVo.setAmapStrategy(routePlan.getRoutePlanRule());
            routeAndFareVos.add(routeAndFareVo);
        }
        if(routeAndFareVos.isEmpty()){
            throw new ServiceException("获取导肮信息失败", I18nKeyConstant.PATH_NOT_FOUND);
        }
        XpSystemSetting xpSystemSetting = systemSettingMapper.selectOne(null);
        if(null != xpSystemSetting){
            routeAndFareVos.forEach(routeAndFareVo -> {
                routeAndFareVo.setOperateFree(xpSystemSetting.getOperateFree());
            });
        }
        return routeAndFareVos;
    }
@@ -436,6 +493,14 @@
                getCurrentTimeSlot()
        );
        log.info("等待时间查询参数:{}", lon + ":" + lat);
        log.info("等待时间查询结果:{}", entities.toString());
        for (XpGeoQueryEntity entity : entities) {
            log.info("等待时间查询结果:{}", entity.toString());
            log.info("等待时间查询结果 秒数:{}", entity.getSeconds());
            log.info("等待时间查询结果 状态:{}", entity.getStatus());
        }
        if (entities.isEmpty()) {
            return 1;
@@ -470,9 +535,55 @@
        }
    }
    /**
     * 获取等待时间
     * @param lon
     * @param lat
     * @return
     */
    @Override
    public Integer getWaitTimeTimeSlot(double lon, double lat,String timeSlot) {
        List<XpGeoQueryEntity> entities = geoQueryEntityService.findByLocationWithin500MetersAndLastWeekAndStatusGreater(
                lon,
                lat,
                timeSlot
        );
        if (entities.isEmpty()) {
            return 1;
        } else {
            // 已完成接单的数据
            List<XpGeoQueryEntity> validEntities = entities.stream().filter(e -> e.getStatus() == 1).collect(Collectors.toList());
            // 计算平均等待时间
            OptionalDouble averageOptional = validEntities.stream().mapToDouble(XpGeoQueryEntity::getSeconds).average();
            if (averageOptional.isPresent()) {
                double average = averageOptional.getAsDouble();
                return (int) average;
            } else {
                return 1;
            }
        }
    }
    @Override
    public String saveOrder(XpOrderDto dto, String uid) {
        //校验白名单用户
        List<XpWhitelist> xpWhitelists = xpWhitelistMapper.selectList(new QueryWrapper<XpWhitelist>().eq("app_user_id", uid)
                .eq("status", 1).eq("is_deleted", 0).last(" and (expire_time >= DATE(NOW()) or expire_time is null)"));
        if(xpWhitelists.isEmpty()){
            throw new ServiceException("未授权用户,不能使用该功能", I18nKeyConstant.NO_PERMISSION);
        }
        XpWhitelist xpWhitelist = xpWhitelists.get(0);
        //判断最大下单数
        if(null != xpWhitelist.getMaxOrderNum() && xpWhitelist.getMaxOrderNum()>0){
            Long count = xpOrderClient.getUserOrderNum(Long.valueOf(uid)).getData();
            if(xpWhitelist.getMaxOrderNum() <= count){
                throw new ServiceException("已达到最大下单数量,不能使用该功能", I18nKeyConstant.MAX_ORDER_NUM);
            }
        }
        // 是否在运营区域时间内
        XpElectronicFenceArea xpElectronicFenceArea = xpElectronicFenceAreaMapper.selectById(dto.getXpElectronicFenceAreaId());
        XpElectronicFence xpElectronicFence = xpElectronicFenceMapper.selectById(xpElectronicFenceArea.getElectronicId());
@@ -488,7 +599,7 @@
        // 判断是否有进行的订单
        R<Long> r = xpOrderClient.hasUnPaodOrder(uid);
        if (r.getData() > 0) {
            throw new RuntimeException("有正在进行的订单");
            throw new ServiceException("有正在进行的订单");
        }
        // 获取起点经纬度
@@ -531,45 +642,45 @@
//        }
        // 根据id获取手机号
        Boolean isPriorityDispatch = true;
//        R<XpAppUser> xpAppUserR = xpAppUserClient.queryOperationsUserById(Long.valueOf(uid));
//        if (xpAppUserR.getCode() == R.SUCCESS) {
//            Long count = xpWhitelistMapper.selectCount(new LambdaQueryWrapper<XpWhitelist>().eq(XpWhitelist::getAccount, xpAppUserR.getData().getPhoneNumber()).eq(XpWhitelist::getIsPriorityDispatch, 1));
//            if (count > 0) {
//                isPriorityDispatch = true;
//            }
//        }
        //判断是否在白名单且优先派单
        boolean isPriorityDispatch = false;
        UcUserInfo ucUserInfo = userResourcesClient.getBaseUserByUid(uid);
        if (null != ucUserInfo) {
            Long count = xpWhitelistMapper.selectCount(new LambdaQueryWrapper<XpWhitelist>().eq(XpWhitelist::getAccount, ucUserInfo.getMobile()).eq(XpWhitelist::getIsPriorityDispatch, 1));
            if (count > 0) {
                isPriorityDispatch = true;
            }
        }
        // 生成订单数据
        XpOrder xpOrder = new XpOrder();
        xpOrder.setMileageKilometers(0.0);
        xpOrder.setAppUserId(Long.valueOf(uid));
        xpOrder.setOrderNumber("XP" + System.currentTimeMillis());
        xpOrder.setOrderNumber(getOrderNumber());
        xpOrder.setPlacementLon(dto.getPlacementLon());
        xpOrder.setPlacementLat(dto.getPlacementLat());
        xpOrder.setPlacementAddress(dto.getPlacementAddress());
        xpOrder.setCityId(dto.getOpenCityId());
        xpOrder.setXpElectronicFenceAreaId(dto.getXpElectronicFenceAreaId());
        xpOrder.setStartPointId(dto.getStartPointId());
        xpOrder.setStartLon(xpPoint.getPointLon());
        xpOrder.setStartLat(xpPoint.getPointLat());
        xpOrder.setBoardingLon(xpPoint.getPointLon());
        xpOrder.setBoardingLat(xpPoint.getPointLat());
        xpOrder.setBoardingAddress(xpPoint.getPointAddress());
        xpOrder.setStartAddress(xpPoint.getPointAddress());
        xpOrder.setStartPointName(xpPoint.getPointName());
        xpOrder.setEndPointId(dto.getEndPointId());
        xpOrder.setEndLon(endXpPoint.getPointLon());
        xpOrder.setEndLat(endXpPoint.getPointLat());
        xpOrder.setStartAddress(xpPoint.getPointAddress());
        xpOrder.setEndAddress(endXpPoint.getPointAddress());
        xpOrder.setXpElectronicFenceAreaId(dto.getXpElectronicFenceAreaId());
        xpOrder.setStartPointId(dto.getStartPointId());
        xpOrder.setEndPointId(dto.getEndPointId());
        xpOrder.setStartPointName(xpPoint.getPointName());
        xpOrder.setEndPointName(endXpPoint.getPointName());
        xpOrder.setXpuStartTaskId(snowflake.nextId() + "");
        xpOrder.setXpuEndTaskId(snowflake.nextId() + "");
        // 当前时段
        String currentTimeSlot = DateUtils.getCurrentTimeSlot();
@@ -579,12 +690,21 @@
        XpOpenCity openCities = xpOpenCityMapper.selectById(dto.getOpenCityId());
        if (openCities == null) {
            throw new ServiceException("获取城市信息失败", I18nKeyConstant.CITY_NOT_FOUND);
            throw new ServiceException("城市暂未开通服务", I18nKeyConstant.CITY_NOT_FOUND);
        }
        XpOpenCity xpOpenCity = openCities;
        dto.setOpenCityId(xpOpenCity.getId());
        xpOrder.setCityCode(xpOpenCity.getCityCode());
        XpRoutePlan xpRoutePlan = xpRoutePlanMapper.selectOne(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getAuditStatus, 2).eq(XpRoutePlan::getCityCode, xpOpenCity.getCityCode()));
        XpUserRoutePlanRule xpUserRoutePlanRule = userRoutePlanRuleClient.getXpUserRoutePlanRule(uid).getData();
        XpRoutePlan xpRoutePlan = null;
        if(null != xpUserRoutePlanRule){
            xpRoutePlan = xpRoutePlanMapper.selectOne(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getAuditStatus, 2)
                    .eq(XpRoutePlan::getCityCode, xpOpenCity.getCityCode()).eq(XpRoutePlan::getRoutePlanRule, xpUserRoutePlanRule.getRoutePlanRule()));
            xpRoutePlanMapper.selectById(xpUserRoutePlanRule.getRoutePlanRule());
        }else{
            xpRoutePlan = xpRoutePlanMapper.selectOne(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getAuditStatus, 2)
                    .eq(XpRoutePlan::getCityCode, xpOpenCity.getCityCode()).last(" limit 1"));
        }
        Integer distance = 0;
        Integer duration = 0;
@@ -638,30 +758,101 @@
        if (maxPushDistanceObj != null) {
            maxPushDistance = Double.valueOf(maxPushDistanceObj.toString());
        } else {
            maxPushDistance = xpTradingStrategyMapper.selectOne(null).getThreePushDistance();
            XpTradingStrategy xpTradingStrategy = xpTradingStrategyMapper.selectOne(null);
            List<XpTradingStrategyPush> xpTradingStrategyPushes = xpTradingStrategyPushMapper.selectList(new QueryWrapper<XpTradingStrategyPush>().eq("trading_strategy_id", xpTradingStrategy.getId()));
            long now = Long.parseLong(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HHmm")));
            XpTradingStrategyPush xpTradingStrategyPush = xpTradingStrategyPushes.stream().filter(item -> {
                long start = Long.parseLong(item.getStartTime().replace(":", ""));
                long end = Long.parseLong(item.getEndTime().replace(":", ""));
                return now >= start && now < end;
            }).findFirst().orElse(null);
            maxPushDistance = null != xpTradingStrategyPush ? xpTradingStrategyPush.getFivePushDistance() : xpTradingStrategy.getFivePushDistance();
        }
        // 找出现在所有的队列 通过计算距离 算出目前应该放在那个队列  如果没有符合的新增一个队列  符合后看是否白名单账号 是则直接放在队列前端
        Set<String> quoteMatchName = redisService.getQuoteMatchName(CacheConstants.QUEUE_POINT_ID);
        log.info("当前队列:{}", quoteMatchName);
        for (String s : quoteMatchName) {
        //获取定向服务配置
        R<Integer> targetedServiceWeight = xpTargetedServiceClient.getTargetedServiceWeight(xpOrder.getAppUserId(), xpOrder.getCityCode());
        if(targetedServiceWeight.getCode() == R.SUCCESS && null != targetedServiceWeight.getData()){
            log.info("添加定向服务订单:{}", xpOrder.getOrderNumber());
            return addSpOrderPush(xpOrder, xpPoint, xpRoutePlan, maxPushDistance, targetedServiceWeight.getData(), orderId, currentTimeSlot, distance, duration, uid);
        }else{
            log.info("添加普通订单:{}", xpOrder.getOrderNumber());
            return addOrderPush(xpOrder, xpPoint, xpRoutePlan, maxPushDistance, isPriorityDispatch, orderId, currentTimeSlot, distance, duration, uid);
        }
    }
    private String getOrderNumber(){
        int random = Double.valueOf(Math.random() * 1000).intValue();
        return "XP" + random + System.currentTimeMillis();
    }
    /**
     * 构建普通订单推送数据
     * @param xpOrder
     * @param xpPoint
     * @param xpRoutePlan
     * @param maxPushDistance
     * @param isPriorityDispatch
     * @param orderId
     * @param currentTimeSlot
     * @param distance
     * @param duration
     * @param uid
     * @return
     */
    private String addOrderPush(XpOrder xpOrder, XpPoint xpPoint, XpRoutePlan xpRoutePlan, double maxPushDistance,
                                Boolean isPriorityDispatch, Long orderId, String currentTimeSlot, Integer distance, Integer duration, String uid){
        RLock lock = redissonClient.getLock("ADD_ORDER_PUSH");
        try {
            if(lock.tryLock(2, 5, TimeUnit.SECONDS)){
                // 找出现在所有的队列 通过计算距离 算出目前应该放在那个队列  如果没有符合的新增一个队列  符合后看是否白名单账号 是则直接放在队列前端
                Set<String> quoteMatchName = redisService.getQuoteMatchName(CacheConstants.QUEUE_POINT_ID);
                log.info("当前队列:{}", quoteMatchName);
                for (String s : quoteMatchName) {
//            QUEUE_POINT_ID-1-30.233,120.233
            String lonLat = s.split("-")[2];
            // 和上车点进行判断是否<maxPushDistance
            double v = FastDistanceCalculator.distanceFast(Double.parseDouble(lonLat.split(",")[1]), Double.parseDouble(lonLat.split(",")[0]), xpPoint.getPointLat(), xpPoint.getPointLon());
            if (v < maxPushDistance) {
                String value = orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + planRuleForQueue;
                if (isPriorityDispatch) {
                    // 头部  value 订单_经纬度_状态_用户id_订单编号_距离_时间
                    redisService.addToHead(s, value);
                    log.info("添加到队列头部:{} {}", s, value);
                } else {
                    // 尾部
                    redisService.addToTail(s, value);
                    log.info("添加到队列尾部:{} {}", s, value);
                    String lonLat = s.split("-")[2];
                    // 和上车点进行判断是否<maxPushDistance
                    double v = FastDistanceCalculator.distanceFast(Double.parseDouble(lonLat.split(",")[1]), Double.parseDouble(lonLat.split(",")[0]), xpPoint.getPointLat(), xpPoint.getPointLon());
                    if (v < maxPushDistance) {
                        String value = orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + xpRoutePlan.getRoutePlanRule();
                        if (isPriorityDispatch) {
                            // 头部  value 订单_经纬度_状态_用户id_订单编号_距离_时间
                            redisService.addToHead(s, value);
                            log.info("添加到队列头部:{} {}", s, value);
                        } else {
                            // 尾部
                            redisService.addToTail(s, value);
                            log.info("添加到队列尾部:{} {}", s, value);
                        }
                        xpOrder.setQueueName(s);
                        xpOrder.setQueueValue(value);
                        xpOrderClient.updateOrder(xpOrder);
                        // 存储mongodb 数据
                        XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity();
                        xpGeoQueryEntity.setOrderId(orderId.toString());
                        xpGeoQueryEntity.setLocation(new GeoJsonPoint(xpPoint.getPointLon(), xpPoint.getPointLat()));
                        // 计算2个时间的秒数
                        xpGeoQueryEntity.setTime(currentTimeSlot);
                        xpGeoQueryEntity.setStatus(0);
                        xpGeoQueryEntity.setTimestamp(System.currentTimeMillis());
                        geoQueryEntityService.save(xpGeoQueryEntity);
                        return orderId.toString();
                    }
                }
                xpOrder.setQueueName(s);
                String key = CacheConstants.QUEUE_POINT_ID + "-" + xpPoint.getId() + "-" + xpPoint.getPointLon() + "," + xpPoint.getPointLat();
                if(xpRoutePlan==null){
                    redisService.setCacheList(key, Arrays.asList(orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + null));
                }else {
                    redisService.setCacheList(key, Arrays.asList(orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + xpRoutePlan.getRoutePlanRule()));
                }
                log.info("订单推单:{}   {}", key, redisService.getCacheList(key));
                xpOrder.setQueueName(key);
                xpOrderClient.updateOrder(xpOrder);
                // 存储mongodb 数据
                XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity();
@@ -672,36 +863,149 @@
                xpGeoQueryEntity.setStatus(0);
                xpGeoQueryEntity.setTimestamp(System.currentTimeMillis());
                geoQueryEntityService.save(xpGeoQueryEntity);
                return orderId.toString();
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        return orderId.toString();
    }
        String key = CacheConstants.QUEUE_POINT_ID + "-" + xpPoint.getId() + "-" + xpPoint.getPointLon() + "," + xpPoint.getPointLat();
        if (planRuleForQueue == null) {
            redisService.setCacheList(key, Arrays.asList(orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + null));
        } else {
            redisService.setCacheList(key, Arrays.asList(orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + planRuleForQueue));
    /**
     * 构建定向服务订单推送数据
     * @param xpOrder
     * @param xpPoint
     * @param xpRoutePlan
     * @param maxPushDistance
     * @param weight
     * @param orderId
     * @param currentTimeSlot
     * @param distance
     * @param duration
     * @param uid
     * @return
     */
    private String addSpOrderPush(XpOrder xpOrder, XpPoint xpPoint, XpRoutePlan xpRoutePlan, double maxPushDistance,
                                  Integer weight, Long orderId, String currentTimeSlot, Integer distance, Integer duration, String uid){
        RLock lock = redissonClient.getLock("ADD_SP_ORDER_PUSH");
        try {
            if(lock.tryLock(2, 5, TimeUnit.SECONDS)){
                // 找出现在所有的队列 通过计算距离 算出目前应该放在那个队列  如果没有符合的新增一个队列  符合后看是否白名单账号 是则直接放在队列前端
                Set<String> quoteMatchName = redisService.getQuoteMatchName(CacheConstants.SP_QUEUE_POINT_ID);
                log.info("当前队列(定向服务):{}", quoteMatchName);
                for (String s : quoteMatchName) {
                    String lonLat = s.split("-")[2];
                    // 和上车点进行判断是否<maxPushDistance
                    double v = FastDistanceCalculator.distanceFast(Double.parseDouble(lonLat.split(",")[1]), Double.parseDouble(lonLat.split(",")[0]), xpPoint.getPointLat(), xpPoint.getPointLon());
                    if (v < maxPushDistance) {
                        //在范围内,将队列中的数据转换成对象进行排序
                        List<String> cacheList = redisService.getCacheList(s);
                        List<RedisQueuePointIdVo> ll = new ArrayList<>();
                        //正在派单的数据,不参与权重排序
                        String pushOrder = null;
                        for (String string : cacheList) {
                            String[] split = string.split("_");
                            if(split[2].equals("0")){
                                RedisQueuePointIdVo redisQueuePointIdVo = new RedisQueuePointIdVo();
                                redisQueuePointIdVo.setOrderId(split[0]);
                                redisQueuePointIdVo.setPointLonLat(split[1]);
                                redisQueuePointIdVo.setPushStatus(split[2]);
                                redisQueuePointIdVo.setUid(split[3]);
                                redisQueuePointIdVo.setOrderNumber(split[4]);
                                redisQueuePointIdVo.setDistance(split[5]);
                                redisQueuePointIdVo.setDuration(split[6]);
                                redisQueuePointIdVo.setPlanRuleForQueue(split[7]);
                                redisQueuePointIdVo.setTargetedVal(Integer.valueOf(split[8]));
                                ll.add(redisQueuePointIdVo);
                            }else{
                                pushOrder = string;
                            }
                        }
                        RedisQueuePointIdVo redisQueuePointIdVo = new RedisQueuePointIdVo();
                        redisQueuePointIdVo.setOrderId(orderId.toString());
                        redisQueuePointIdVo.setPointLonLat(xpPoint.getPointLon() + "," + xpPoint.getPointLat());
                        redisQueuePointIdVo.setPushStatus("0");
                        redisQueuePointIdVo.setUid(uid);
                        redisQueuePointIdVo.setOrderNumber(xpOrder.getOrderNumber());
                        redisQueuePointIdVo.setDistance(distance.toString());
                        redisQueuePointIdVo.setDuration(duration.toString());
                        redisQueuePointIdVo.setPlanRuleForQueue(xpRoutePlan.getRoutePlanRule().toString());
                        redisQueuePointIdVo.setTargetedVal(weight);
                        ll.add(redisQueuePointIdVo);
                        //排序
                        ll.sort((o1, o2) -> o2.getTargetedVal() - o1.getTargetedVal());
                        List<String> collect = ll.stream().map(item -> {
                            String value = item.getOrderId() + "_" + item.getPointLonLat() + "_" + item.getPushStatus() + "_" + item.getUid() + "_" + item.getOrderNumber()
                                    + "_" + item.getDistance() + "_" + item.getDuration() + "_" + item.getPlanRuleForQueue() + "_" + item.getTargetedVal();
                            return value;
                        }).collect(Collectors.toList());
                        //如果有正在推单的数据,则放在第一位
                        if(StringUtils.isNotEmpty(pushOrder)){
                            collect.add(0, pushOrder);
                        }
                        //先清除key的缓存数据
                        redisService.deleteObject(s);
                        //再全部重新赋值
                        redisService.setCacheList(s, collect);
                        String value = orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0_" + uid + "_" + xpOrder.getOrderNumber()
                                + "_" + distance + "_" + duration + "_" + xpRoutePlan.getRoutePlanRule() + "_" + weight;
                        xpOrder.setQueueName(s);
                        xpOrder.setQueueValue(value);
                        xpOrderClient.updateOrder(xpOrder);
                        // 存储mongodb 数据
                        XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity();
                        xpGeoQueryEntity.setOrderId(orderId.toString());
                        xpGeoQueryEntity.setLocation(new GeoJsonPoint(xpPoint.getPointLon(), xpPoint.getPointLat()));
                        // 计算2个时间的秒数
                        xpGeoQueryEntity.setTime(currentTimeSlot);
                        xpGeoQueryEntity.setStatus(0);
                        xpGeoQueryEntity.setTimestamp(System.currentTimeMillis());
                        geoQueryEntityService.save(xpGeoQueryEntity);
                        return orderId.toString();
                    }
                }
                String key = CacheConstants.SP_QUEUE_POINT_ID + "-" + xpPoint.getId() + "-" + xpPoint.getPointLon() + "," + xpPoint.getPointLat();
                if(xpRoutePlan==null){
                    redisService.setCacheList(key, Arrays.asList(orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + null + "_" + weight));
                }else {
                    redisService.setCacheList(key, Arrays.asList(orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + xpRoutePlan.getRoutePlanRule() + "_" + weight));
                }
                log.info("订单推单:{}   {}", key, redisService.getCacheList(key));
                xpOrder.setQueueName(key);
                xpOrderClient.updateOrder(xpOrder);
                // 存储mongodb 数据
                XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity();
                xpGeoQueryEntity.setOrderId(orderId.toString());
                xpGeoQueryEntity.setLocation(new GeoJsonPoint(xpPoint.getPointLon(), xpPoint.getPointLat()));
                // 计算2个时间的秒数
                xpGeoQueryEntity.setTime(currentTimeSlot);
                xpGeoQueryEntity.setStatus(0);
                xpGeoQueryEntity.setTimestamp(System.currentTimeMillis());
                geoQueryEntityService.save(xpGeoQueryEntity);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        log.info("订单推单:{}   {}", key, redisService.getCacheList(key));
        xpOrder.setQueueName(key);
        xpOrderClient.updateOrder(xpOrder);
        // 存储mongodb 数据
        XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity();
        xpGeoQueryEntity.setOrderId(orderId.toString());
        xpGeoQueryEntity.setLocation(new GeoJsonPoint(xpPoint.getPointLon(), xpPoint.getPointLat()));
        // 计算2个时间的秒数
        xpGeoQueryEntity.setTime(currentTimeSlot);
        xpGeoQueryEntity.setStatus(0);
        xpGeoQueryEntity.setTimestamp(System.currentTimeMillis());
        geoQueryEntityService.save(xpGeoQueryEntity);
        return orderId.toString();
    }
    private Boolean isInOperationTime(String operationStrategy) {
    @Override
    public Boolean isInOperationTime(String operationStrategy) {
        JSONArray jsonArray = JSONArray.parseArray(operationStrategy);
        Integer holidayToday = holidayUtil.isHolidayToday();
        for (Object o : jsonArray) {
@@ -804,9 +1108,9 @@
            Double money = userCouponVo.getMoney();
            money = new BigDecimal(money).setScale(2, RoundingMode.HALF_UP).doubleValue();
            xpPricingCardVo.setCoupon(String.valueOf(money));
            if (xpPricingCardVo.getTotalPrice() > money) {
                xpPricingCardVo.setSystemDiscount(xpPricingCardVo.getTotalPrice() - money);
            }
//            if (xpPricingCardVo.getTotalPrice() > money) {
//                xpPricingCardVo.setSystemDiscount(xpPricingCardVo.getTotalPrice() - money);
//            }
        }
        RouteAndFareVo routeAndFareVo = new RouteAndFareVo();
        routeAndFareVo.setRoute(routeVo);
@@ -814,7 +1118,7 @@
        xpPricingCardVo.setStartPriceStr(new BigDecimal(xpPricingCardVo.getStartPrice()).setScale(2, RoundingMode.HALF_UP).toString());
        xpPricingCardVo.setDistancePriceStr(new BigDecimal(xpPricingCardVo.getDistancePrice()).setScale(2, RoundingMode.HALF_UP).toString());
        xpPricingCardVo.setDurationPriceStr(new BigDecimal(xpPricingCardVo.getDurationPrice()).setScale(2, RoundingMode.HALF_UP).toString());
        xpPricingCardVo.setSystemDiscountStr(new BigDecimal(xpPricingCardVo.getSystemDiscount()).setScale(2, RoundingMode.HALF_UP).toString());
//        xpPricingCardVo.setSystemDiscountStr(new BigDecimal(xpPricingCardVo.getSystemDiscount()).setScale(2, RoundingMode.HALF_UP).toString());
        routeAndFareVo.setXpPricingCard(xpPricingCardVo);
        FareVo fareVo = new FareVo();
        fareVo.setEstimatedDistanceKm(routeVo.getDistance().doubleValue() / 1000);
@@ -824,29 +1128,27 @@
        if (userCouponVo != null) {
            if (userCouponVo.getMoney() > fareVo.getOriginalFare()) {
                fareVo.setDiscountAmount(fareVo.getOriginalFare());
                fareVo.setCouponType(userCouponVo.getCouponType());
                fareVo.setFinalFare(0.00);
            } else {
                fareVo.setDiscountAmount(userCouponVo.getMoney());
                fareVo.setCouponType(userCouponVo.getCouponType());
                fareVo.setFinalFare(fareVo.getOriginalFare() - fareVo.getDiscountAmount());
            }
            if (fareVo.getFinalFare() > 0) {
                fareVo.setSystemDiscount(fareVo.getFinalFare());
                fareVo.setFinalFare(0.00);
            }
            fareVo.setCouponId(userCouponVo.getId());
        } else {
            fareVo.setDiscountAmount(0.00);
            fareVo.setFinalFare(fareVo.getOriginalFare());
            fareVo.setSystemDiscount(fareVo.getFinalFare());
            fareVo.setFinalFare(0.00);
//            fareVo.setSystemDiscount(fareVo.getFinalFare());
        }
        Integer waitTime = getWaitTime(routeAndFareDto.getOrigin().getLongitude(), routeAndFareDto.getOrigin().getLatitude());
        fareVo.setWaitingTime(waitTime);
        fareVo.setFinalFareStr(new BigDecimal(fareVo.getFinalFare()).setScale(2, RoundingMode.HALF_UP).toString());
        fareVo.setOriginalFare(new BigDecimal(fareVo.getOriginalFare()).setScale(2, RoundingMode.HALF_UP).doubleValue());
        fareVo.setSystemDiscount(new BigDecimal(fareVo.getSystemDiscount() == null ? 0 : fareVo.getSystemDiscount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
//        fareVo.setSystemDiscount(new BigDecimal(fareVo.getSystemDiscount() == null ? 0 : fareVo.getSystemDiscount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
        routeAndFareVo.setFareEstimate(fareVo);
        routeAndFareVo.getXpPricingCard().setFinalFareStr(fareVo.getFinalFareStr());
//        routeAndFareVo.setTrafficLights();//交通信号灯 todo 20260508 红绿灯功能还没有
        fareVo.setId(null);
        return routeAndFareVo;
    }
@@ -876,67 +1178,79 @@
            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
        }
        List<RouteStrategyKind> kinds = buildStrategyKindsForPlanning(userId);
        Map<String, RouteVo> routeById = new LinkedHashMap<>();
        Map<String, Set<RouteStrategyKind>> strategiesById = new LinkedHashMap<>();
        for (RouteStrategyKind kind : kinds) {
        StrategyPlanningConfig planningConfig = this.buildStrategyKindsForPlanning(userId, routeAndFareDto.getOpenCityId());
        List<RouteStrategyKind> kinds = planningConfig.getKinds();
        Map<String, RouteStrategyKind> strategiesById = new HashMap<>();
        List<RouteVo> routePoints = new ArrayList<>();
        List<XpRoutePlan> xpRoutePlans = xpRoutePlanMapper.selectList(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getOpenCityId, routeAndFareDto.getOpenCityId())
                .eq(XpRoutePlan::getAuditStatus, 2));
        log.info("导航策略配置 {} {}", routeAndFareDto.getOpenCityId(), JSON.toJSONString(xpRoutePlans));
        for (XpRoutePlan xpRoutePlan : xpRoutePlans) {
            RouteAndFareDto copy = new RouteAndFareDto();
            copy.setOrigin(routeAndFareDto.getOrigin());
            copy.setDestination(routeAndFareDto.getDestination());
            copy.setOpenCityId(routeAndFareDto.getOpenCityId());
            copy.setUserCouponId(routeAndFareDto.getUserCouponId());
            copy.setVehicleId(routeAndFareDto.getVehicleId());
            copy.setRoutePlanRule(kind.getAmapStrategy());
            R<RouteVo> r = xpMapClient.getDistanceAndDuration(copy);
            if (r.getCode() != 200 || r.getData() == null || r.getData().getRouteID() == null) {
            copy.setRoutePlanRule(xpRoutePlan.getRoutePlanRule());
            R<List<RouteVo>> r = xpMapClient.getDistanceAndDurationList(copy);
            if (r.getCode() != 200 || r.getData() == null || r.getData().isEmpty()) {
                continue;
            }
            String fid = r.getData().getRouteID();
            routeById.putIfAbsent(fid, r.getData());
            strategiesById.computeIfAbsent(fid, k -> new LinkedHashSet<>()).add(kind);
            bb: for (RouteVo datum : r.getData()) {
                for (RouteVo routePoint : routePoints) {
                    if(CollUtil.isEqualList(routePoint.getPolyline(), datum.getPolyline())){
                        continue bb;
                    }
                }
                routePoints.add(datum);
                RouteStrategyKind routeStrategyKind = kinds.stream().filter(kind -> kind.getAmapStrategy() == xpRoutePlan.getRoutePlanRule()).findFirst().get();
                strategiesById.put(datum.getRouteID(), routeStrategyKind);
                break;
            }
        }
        if (routeById.isEmpty()) {
        if (routePoints.isEmpty()) {
            throw new ServiceException("获取导肮信息失败", I18nKeyConstant.PATH_NOT_FOUND);
        }
        XpSystemSetting xpSystemSetting = systemSettingMapper.selectOne(null);
        Integer operateFree = 0 ;
        if(null != xpSystemSetting) {
            operateFree = xpSystemSetting.getOperateFree();
        }
        String snapshotId = UUID.randomUUID().toString().replace("-", "");
        List<RouteOptionVo> options = new ArrayList<>();
        for (Map.Entry<String, RouteVo> e : routeById.entrySet()) {
            String fid = e.getKey();
            RouteVo rv = e.getValue();
            Set<RouteStrategyKind> kindsOnRoute = strategiesById.get(fid);
            RouteStrategyKind primary = RouteStrategyKind.primaryOf(kindsOnRoute);
        for (RouteVo routePoint : routePoints) {
            RouteAndFareDto pricingDto = new RouteAndFareDto();
            pricingDto.setOrigin(routeAndFareDto.getOrigin());
            pricingDto.setDestination(routeAndFareDto.getDestination());
            pricingDto.setOpenCityId(routeAndFareDto.getOpenCityId());
            pricingDto.setUserCouponId(routeAndFareDto.getUserCouponId());
            pricingDto.setVehicleId(routeAndFareDto.getVehicleId());
            pricingDto.setRoutePlanRule(primary.getAmapStrategy());
            RouteAndFareVo part = pricingForRoute(pricingDto, rv, xpPricing, userId);
            //计算导航对应的费用
            RouteAndFareVo part = pricingForRoute(pricingDto, routePoint, xpPricing, userId);
            RouteOptionVo opt = new RouteOptionVo();
            opt.setPathFingerprint(fid);
            List<String> labels = new ArrayList<>();
            for (RouteStrategyKind k : Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE)) {
                if (kindsOnRoute.contains(k)) {
                    labels.add(k.getDisplayName());
                }
            }
            opt.setStrategyLabels(labels);
            opt.setPrimaryStrategyLabel(primary.getDisplayName());
            opt.setSnapshotId(snapshotId);
            opt.setPathFingerprint(routePoint.getRouteID());
            RouteStrategyKind primary = strategiesById.get(routePoint.getRouteID());
            opt.setStrategyLabels(Arrays.asList(primary.getDisplayName()));
            opt.setPrimaryStrategyLabel(resolveStrategyDisplayName(primary, planningConfig.getDisplayNames()));
            opt.setPrimaryRoutePlanRule(primary.getAmapStrategy());
            opt.setPrimaryRoutePlanName(primary.getDisplayName());
            opt.setRoute(part.getRoute());
            opt.setFareEstimate(part.getFareEstimate());
            opt.setXpPricingCard(part.getXpPricingCard());
            if (part.getFareEstimate() != null && part.getFareEstimate().getCouponId() == null) {
                opt.setCouponMessage("当前路线不满足优惠券使用条件");
            }
            opt.setOperateFree(operateFree);
            options.add(opt);
        }
        RouteStrategyKind userPref = resolveUserPreferredKind(userId).orElse(RouteStrategyKind.SMART);
        options.sort((a, b) -> compareRouteOptionByUserPreference(a, b, userPref, strategiesById));
        options.sort((a, b) -> compareRouteOptionByUserPreference(a, b, userPref));
        String snapshotId = UUID.randomUUID().toString().replace("-", "");
        long now = System.currentTimeMillis();
        MultiRouteSnapshotPayload payload = new MultiRouteSnapshotPayload();
        payload.setSnapshotId(snapshotId);
@@ -947,7 +1261,12 @@
        payload.setDestination(routeAndFareDto.getDestination());
        payload.setUserCouponId(routeAndFareDto.getUserCouponId());
        payload.setRoutes(options);
        redisService.setCacheObject(CacheConstants.ORDER_MULTI_ROUTE_SNAPSHOT + userId + ":" + snapshotId, JSON.toJSONString(payload), 24L, TimeUnit.HOURS);
        R<XpTradingStrategyDetailVO>  strategyDetailVOR = xpTradingStrategyClient.queryById();
        XpTradingStrategyDetailVO strategyDetailVO = strategyDetailVOR.getData();
        //将多线路数据缓存,用于后续确认操作
        redisService.setCacheObject(CacheConstants.ORDER_MULTI_ROUTE_SNAPSHOT + userId + ":" + snapshotId, JSON.toJSONString(payload), Long.valueOf(strategyDetailVO.getMaxStandingTiming()), TimeUnit.SECONDS);
        MultiRouteAndFareVo vo = new MultiRouteAndFareVo();
        vo.setSnapshotId(snapshotId);
@@ -957,12 +1276,11 @@
        vo.setRoutePreferences(listRoutePreferences(routeAndFareDto.getOpenCityId(), userId));
        vo.setStaleCheckSeconds(navigationStaleSeconds);
        vo.setPriceWaveThresholdPercent(navigationPriceWavePercent);
        RouteOptionVo first = options.get(0);
        redisService.setCacheObject("ORDER_NAVIGATION_END_DATA:" + userId, JSON.toJSONString(first.getRoute()), 24L, TimeUnit.HOURS);
        vo.setOperateFree(operateFree);
        //产品喊写死
        vo.setMaxPeopleNumber(5);
        return vo;
    }
    @Override
    public void selectNavigationRoute(SelectNavigationRouteDto dto, String userId) {
        String json = redisService.getCacheObject(CacheConstants.ORDER_MULTI_ROUTE_SNAPSHOT + userId + ":" + dto.getSnapshotId());
@@ -1031,21 +1349,25 @@
    @Override
    public List<RoutePreferenceItemVo> listRoutePreferences(Long openCityId, String userId) {
        Set<String> enabledCodes = parseEnabledPreferenceCodes();
        Optional<RouteStrategyKind> savedPref = resolveUserPreferredKind(userId);
        Optional<RouteStrategyKind> savedPref = resolveUserPreferredKind(userId);//获取用户设置的路线偏好
        List<XpRoutePlan> routePlans = findRoutePlans(openCityId);
        List<RoutePreferenceItemVo> list = new ArrayList<>();
        int i = 0;
        for (RouteStrategyKind k : Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE)) {
            if (!enabledCodes.contains(k.name())) {
                continue;
        if(CollUtil.isNotEmpty(routePlans)){
            int i = 0;
            for (XpRoutePlan routePlan : routePlans) {
                RoutePreferenceItemVo v = new RoutePreferenceItemVo();
                RouteStrategyKind strategy = RouteStrategyKind.getStrategy(routePlan.getRoutePlanRule());
                if(strategy != null){
                    v.setCode(strategy.name());
                }
                v.setName(routePlan.getPlanName());
                v.setEnabled(true);
                v.setSortOrder(i++);
                v.setAmapStrategy(routePlan.getRoutePlanRule());
                v.setSelected(savedPref.isPresent() && strategy == savedPref.get());
                list.add(v);
            }
            RoutePreferenceItemVo v = new RoutePreferenceItemVo();
            v.setCode(k.name());
            v.setName(k.getDisplayName());
            v.setEnabled(true);
            v.setSortOrder(i++);
            v.setAmapStrategy(k.getAmapStrategy());
            v.setSelected(savedPref.isPresent() && k == savedPref.get());
            list.add(v);
        }
        return list;
    }
@@ -1070,43 +1392,86 @@
            }
        }
        if (set.isEmpty()) {
            set.add(RouteStrategyKind.SMART.name());
            set.add(RouteStrategyKind.TIME.name());
            set.add(RouteStrategyKind.PRICE.name());
            for (RouteStrategyKind kind : RouteStrategyKind.defaultPreferenceKinds()) {
                set.add(kind.name());
            }
        }
        return set;
    }
    private List<XpRoutePlan> findRoutePlans (Long openCityId){
        return xpRoutePlanMapper.selectList(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getOpenCityId, openCityId)
                .eq(XpRoutePlan::getAuditStatus, 2));
    }
    /**
     * 用户偏好策略优先请求,其余策略仍请求以便合并去重。
     */
    private List<RouteStrategyKind> buildStrategyKindsForPlanning(String userId) {
        List<RouteStrategyKind> all = new ArrayList<>();
        Set<String> enabled = parseEnabledPreferenceCodes();
        for (RouteStrategyKind k : Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE)) {
            if (enabled.contains(k.name())) {
                all.add(k);
    private StrategyPlanningConfig buildStrategyKindsForPlanning(String userId, Long openCityId) {
        List<XpRoutePlan> xpRoutePlans = xpRoutePlanMapper.selectList(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getOpenCityId, openCityId)
                .eq(XpRoutePlan::getAuditStatus, 2));
        Map<RouteStrategyKind, String> displayNames = new LinkedHashMap<>();
        List<RouteStrategyKind> configuredKinds = new ArrayList<>();
        if (xpRoutePlans.isEmpty()) {
            throw new ServiceException("未配置有效的导航策略", I18nKeyConstant.PATH_NOT_FOUND);
        } else {
            for (XpRoutePlan xpRoutePlan : xpRoutePlans) {
                Integer strategy = xpRoutePlan.getRoutePlanRule();
                if (strategy == null) {
                    continue;
                }
                RouteStrategyKind.fromAmapStrategy(strategy).ifPresent(kind -> {
                    configuredKinds.add(kind);
                    if (StringUtils.isNotBlank(xpRoutePlan.getPlanName())) {
                        displayNames.putIfAbsent(kind, xpRoutePlan.getPlanName().trim());
                    }
                });
            }
        }
        if (all.isEmpty()) {
            all.add(RouteStrategyKind.SMART);
        return new StrategyPlanningConfig(configuredKinds, displayNames);
    }
    private String resolveStrategyDisplayName(RouteStrategyKind kind, Map<RouteStrategyKind, String> displayNames) {
        String configuredName = displayNames.get(kind);
        return StringUtils.isNotBlank(configuredName) ? configuredName : kind.getDisplayName();
    }
    private void addIfAbsent(List<RouteStrategyKind> kinds, RouteStrategyKind kind) {
        if (!kinds.contains(kind)) {
            kinds.add(kind);
        }
        Optional<RouteStrategyKind> pref = resolveUserPreferredKind(userId);
        if (!pref.isPresent()) {
            return all;
        }
        RouteStrategyKind p = pref.get();
        if (!all.contains(p)) {
            return all;
        }
        List<RouteStrategyKind> ordered = new ArrayList<>();
        ordered.add(p);
        for (RouteStrategyKind k : all) {
            if (k != p) {
                ordered.add(k);
    }
    private RouteStrategyKind resolveDefaultEnabledKind(Set<String> enabled) {
        for (RouteStrategyKind kind : RouteStrategyKind.defaultPreferenceKinds()) {
            if (enabled.contains(kind.name())) {
                return kind;
            }
        }
        return ordered;
        for (RouteStrategyKind kind : RouteStrategyKind.orderedValues()) {
            if (enabled.contains(kind.name())) {
                return kind;
            }
        }
        return RouteStrategyKind.SMART;
    }
    private static final class StrategyPlanningConfig {
        private final List<RouteStrategyKind> kinds;
        private final Map<RouteStrategyKind, String> displayNames;
        private StrategyPlanningConfig(List<RouteStrategyKind> kinds, Map<RouteStrategyKind, String> displayNames) {
            this.kinds = kinds;
            this.displayNames = displayNames;
        }
        private List<RouteStrategyKind> getKinds() {
            return kinds;
        }
        private Map<RouteStrategyKind, String> getDisplayNames() {
            return displayNames;
        }
    }
    private Optional<RouteStrategyKind> resolveUserPreferredKind(String userId) {
@@ -1125,91 +1490,81 @@
        }
    }
    private int compareRouteOptionByUserPreference(RouteOptionVo a, RouteOptionVo b, RouteStrategyKind pref,
            Map<String, Set<RouteStrategyKind>> strategiesById) {
        Set<RouteStrategyKind> sa = strategiesById.getOrDefault(a.getPathFingerprint(), Collections.emptySet());
        Set<RouteStrategyKind> sb = strategiesById.getOrDefault(b.getPathFingerprint(), Collections.emptySet());
        boolean aHas = sa.contains(pref);
        boolean bHas = sb.contains(pref);
        if (aHas != bHas) {
            return bHas ? 1 : -1;
    private int compareRouteOptionByUserPreference(RouteOptionVo a, RouteOptionVo b, RouteStrategyKind pref) {
        Integer primaryRoutePlanRule = a.getPrimaryRoutePlanRule();
        if(primaryRoutePlanRule == pref.getAmapStrategy()){
            return 1;
        }
        boolean aPrimary = a.getPrimaryRoutePlanRule() != null && a.getPrimaryRoutePlanRule() == pref.getAmapStrategy();
        boolean bPrimary = b.getPrimaryRoutePlanRule() != null && b.getPrimaryRoutePlanRule() == pref.getAmapStrategy();
        if (aPrimary != bPrimary) {
            return bPrimary ? 1 : -1;
        }
        int da = a.getRoute() != null && a.getRoute().getDuration() != null ? a.getRoute().getDuration() : Integer.MAX_VALUE;
        int db = b.getRoute() != null && b.getRoute().getDuration() != null ? b.getRoute().getDuration() : Integer.MAX_VALUE;
        return Integer.compare(da, db);
        return -1;
    }
    @Override
    public QueueStatusVo getQueueStatus(String orderId, String uid) {
        QueueStatusVo queueStatusVo = new QueueStatusVo();
        // 从订单服务获取订单信息,获取queueName
        R<XpOrder> orderR = xpOrderClient.queryOrderById(Long.valueOf(orderId));
        if (orderR.getCode() != R.SUCCESS || orderR.getData() == null) {
            throw new ServiceException("订单不存在");
        }
        XpOrder xpOrder = orderR.getData();
        String queueName = xpOrder.getQueueName();
        RLock lock = redissonClient.getLock("queue_lock_" + queueName);
        try {
            // 从订单服务获取订单信息,获取queueName
            R<XpOrder> orderR = xpOrderClient.queryOrderById(Long.valueOf(orderId));
            if (orderR.getCode() != R.SUCCESS || orderR.getData() == null) {
                throw new ServiceException("订单不存在");
            }
            XpOrder xpOrder = orderR.getData();
            String queueName = xpOrder.getQueueName();
            if (StringUtils.isBlank(queueName)) {
                // 订单不在排队中
                queueStatusVo.setStatus(1);
                queueStatusVo.setCurrentPosition(0);
                queueStatusVo.setTotalCount(0);
                queueStatusVo.setEstimatedWaitTime(0);
                return queueStatusVo;
            }
            // 从Redis获取队列数据
            List<String> queueItems = redisService.getCacheList(queueName);
            if (queueItems == null || queueItems.isEmpty()) {
                // 队列为空,说明已分配车辆
                queueStatusVo.setStatus(1);
                queueStatusVo.setCurrentPosition(0);
                queueStatusVo.setTotalCount(0);
                queueStatusVo.setEstimatedWaitTime(0);
                return queueStatusVo;
            }
            // 遍历队列,找到当前订单的位置
            int currentPosition = 0;
            int totalCount = queueItems.size();
            for (int i = 0; i < queueItems.size(); i++) {
                String item = queueItems.get(i);
                if (item.startsWith(orderId + "_")) {
                    currentPosition = i + 1; // 位置从1开始
                    break;
            if(lock.tryLock(2, 5, TimeUnit.SECONDS)){
                if (StringUtils.isEmpty(queueName)) {
                    // 订单不在排队中
                    queueStatusVo.setStatus(1);
                    queueStatusVo.setCurrentPosition(0);
                    queueStatusVo.setTotalCount(0);
                    queueStatusVo.setEstimatedWaitTime(0);
                    log.info("订单不在排队中: {}", orderId);
                    return queueStatusVo;
                }
                // 从Redis获取队列数据
                List<String> queueItems = redisService.getCacheList(queueName);
                if (queueItems == null || queueItems.isEmpty()) {
                    log.error("列表为空: {}", orderId);
                    // 队列为空,说明已分配车辆
                    queueStatusVo.setStatus(1);
                    queueStatusVo.setCurrentPosition(0);
                    queueStatusVo.setTotalCount(0);
                    queueStatusVo.setEstimatedWaitTime(0);
                    return queueStatusVo;
                }
                log.info("排队队列数据 {}", JSON.toJSONString(queueItems));
                // 遍历队列,找到当前订单的位置
                int currentPosition = 0;
                int totalCount = queueItems.size();
                for (int i = 0; i < queueItems.size(); i++) {
                    currentPosition++;
                    String item = queueItems.get(i);
                    String[] split = item.split("_");
                    if (split[0].equals(orderId + "")) {
                        break;
                    }
                }
                Integer waitTimeTimeSlot = getWaitTimeTimeSlot(xpOrder.getStartLon(), xpOrder.getStartLat(), xpOrder.getTimeSlot());
                queueStatusVo.setStatus(0); // 0-排队中
                queueStatusVo.setCurrentPosition(currentPosition);
                queueStatusVo.setTotalCount(totalCount);
                queueStatusVo.setEstimatedWaitTime((waitTimeTimeSlot*currentPosition)/60);
            }
            // 计算预计等待时间(假设每辆车平均需要3分钟)
            int estimatedWaitTime = (currentPosition - 1) * 3;
            if (estimatedWaitTime < 0) {
                estimatedWaitTime = 0;
            }
            queueStatusVo.setStatus(0); // 0-排队中
            queueStatusVo.setCurrentPosition(currentPosition);
            queueStatusVo.setTotalCount(totalCount);
            queueStatusVo.setEstimatedWaitTime(estimatedWaitTime);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("查询排队状态失败: {}", e.getMessage(), e);
            // 异常情况下返回默认值
            queueStatusVo.setStatus(0);
            queueStatusVo.setCurrentPosition(0);
            queueStatusVo.setTotalCount(0);
            queueStatusVo.setEstimatedWaitTime(0);
        }finally {
            lock.unlock();
        }
        return queueStatusVo;
    }