| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | private XpMapClient xpMapClient; |
| | | |
| | | @Resource |
| | | private BootCommonService bootCommonService; |
| | | @Resource |
| | | private XpOpenCityMapper xpOpenCityMapper; |
| | | |
| | | @Resource |
| | |
| | | |
| | | @Resource |
| | | private XpOrderClient xpOrderClient; |
| | | @Resource |
| | | private XpVehicleClient xpVehicleClient; |
| | | |
| | | @Resource |
| | | private XpTradingStrategyMapper xpTradingStrategyMapper; |
| | | |
| | | @Resource |
| | | private XpTradingStrategyPushMapper xpTradingStrategyPushMapper; |
| | | |
| | | @Resource |
| | | private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper; |
| | |
| | | |
| | | @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; |
| | |
| | | private double navigationPriceWavePercent; |
| | | |
| | | /** |
| | | * 后台可配置的偏好项编码,逗号分隔,如 SMART,TIME,PRICE |
| | | * 后台可配置的偏好项编码,逗号分隔,如 SMART,TIME,PRICE,AVOID_CONGESTION |
| | | */ |
| | | @Value("${navigation.route.preference.enabled-codes:SMART,TIME,PRICE}") |
| | | private String navigationPreferenceEnabledCodes; |
| | |
| | | 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) { |
| | |
| | | // } |
| | | // 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; |
| | | } |
| | | |
| | | |
| | |
| | | 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; |
| | |
| | | |
| | | @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()); |
| | |
| | | // 判断是否有进行的订单 |
| | | R<Long> r = xpOrderClient.hasUnPaodOrder(uid); |
| | | if (r.getData() > 0) { |
| | | throw new RuntimeException("有正在进行的订单"); |
| | | throw new ServiceException("有正在进行的订单"); |
| | | } |
| | | |
| | | // 获取起点经纬度 |
| | |
| | | // } |
| | | |
| | | |
| | | // 根据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(); |
| | |
| | | |
| | | 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; |
| | |
| | | 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(); |
| | |
| | | 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) { |
| | |
| | | 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); |
| | |
| | | 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); |
| | |
| | | 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; |
| | | } |
| | |
| | | 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); |
| | |
| | | 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); |
| | |
| | | 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()); |
| | |
| | | @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; |
| | | } |
| | |
| | | } |
| | | } |
| | | 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) { |
| | |
| | | } |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | |
| | | Integer waitTimeTimeSlot = getWaitTimeTimeSlot(xpOrder.getStartLon(), xpOrder.getStartLat(), xpOrder.getTimeSlot()); |
| | | queueStatusVo.setStatus(0); // 0-排队中 |
| | | queueStatusVo.setCurrentPosition(currentPosition); |
| | | queueStatusVo.setTotalCount(totalCount); |
| | | queueStatusVo.setEstimatedWaitTime((waitTimeTimeSlot*currentPosition)/60); |
| | | |
| | | } 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; |
| | | } |
| | | |