| | |
| | | import com.xiaopeng.api.dto.account.GetUserCouponDto; |
| | | import com.xiaopeng.api.dto.map.OrderRouteAndFareDto; |
| | | 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.XpAppUserClient; |
| | | import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient; |
| | |
| | | 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.order.XpPricingCardVo; |
| | | import com.xiaopeng.common.core.constant.CacheConstants; |
| | |
| | | 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.service.IXpGeoQueryEntityService; |
| | | import com.xiaopeng.itinerary.service.XpPointService; |
| | |
| | | import com.xiaopeng.itinerary.utils.map.GDMapGeocodingUtil; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.data.mongodb.core.geo.GeoJsonPoint; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | |
| | | @Resource |
| | | private XpPointClient xpPointClient; |
| | | |
| | | @Value("${navigation.route.stale-seconds:60}") |
| | | private int navigationStaleSeconds; |
| | | |
| | | @Value("${navigation.route.price-wave-percent:5}") |
| | | private double navigationPriceWavePercent; |
| | | |
| | | // 自定义线程池用于并行执行任务 |
| | | private final ExecutorService executorService; |
| | |
| | | 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())); |
| | |
| | | |
| | | Integer distance = 0; |
| | | Integer duration = 0; |
| | | Object cacheObject = redisService.getCacheObject("ORDER_NAVIGATION_END_DATA:" + uid); |
| | | if (cacheObject != null) { |
| | | RouteVo routeVo = JSON.parseObject(cacheObject.toString(), RouteVo.class); |
| | | distance = routeVo.getDistance(); |
| | | duration = routeVo.getDuration(); |
| | | if (dto.getNavigationSnapshotId() != null && dto.getSelectedRouteId() != null) { |
| | | String snap = redisService.getCacheObject(CacheConstants.ORDER_MULTI_ROUTE_SNAPSHOT + uid + ":" + dto.getNavigationSnapshotId()); |
| | | if (snap != null) { |
| | | MultiRouteSnapshotPayload snapPayload = JSON.parseObject(snap, MultiRouteSnapshotPayload.class); |
| | | RouteOptionVo opt = snapPayload.getRoutes().stream() |
| | | .filter(r2 -> dto.getSelectedRouteId().equals(r2.getPathFingerprint())) |
| | | .findFirst() |
| | | .orElse(null); |
| | | if (opt != null && opt.getRoute() != null) { |
| | | distance = opt.getRoute().getDistance(); |
| | | duration = opt.getRoute().getDuration(); |
| | | redisService.setCacheObject("ORDER_NAVIGATION_END_DATA:" + uid, JSON.toJSONString(opt.getRoute()), 24L, TimeUnit.HOURS); |
| | | if (dto.getSelectedRoutePlanRule() == null) { |
| | | dto.setSelectedRoutePlanRule(opt.getPrimaryRoutePlanRule()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | if (distance == 0 && duration == 0) { |
| | | Object cacheObject = redisService.getCacheObject("ORDER_NAVIGATION_END_DATA:" + uid); |
| | | if (cacheObject != null) { |
| | | RouteVo routeVo = JSON.parseObject(cacheObject.toString(), RouteVo.class); |
| | | distance = routeVo.getDistance(); |
| | | duration = routeVo.getDuration(); |
| | | } |
| | | } |
| | | final Integer planRuleForQueue = dto.getSelectedRoutePlanRule() != null ? dto.getSelectedRoutePlanRule() |
| | | : (xpRoutePlan != null ? xpRoutePlan.getRoutePlanRule() : null); |
| | | xpOrder.setCouponId(dto.getUserCouponId()); |
| | | // 产生订单 |
| | | R<Long> addOrderR = xpOrderClient.addOrder(xpOrder); |
| | |
| | | // 和上车点进行判断是否<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(); |
| | | String value = orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + planRuleForQueue; |
| | | if (isPriorityDispatch) { |
| | | // 头部 value 订单_经纬度_状态_用户id_订单编号_距离_时间 |
| | | redisService.addToHead(s, value); |
| | |
| | | } |
| | | |
| | | String key = CacheConstants.QUEUE_POINT_ID + "-" + xpPoint.getId() + "-" + xpPoint.getPointLon() + "," + xpPoint.getPointLat(); |
| | | if(xpRoutePlan==null){ |
| | | 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 + "_" + xpRoutePlan.getRoutePlanRule())); |
| | | } else { |
| | | redisService.setCacheList(key, Arrays.asList(orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + planRuleForQueue)); |
| | | } |
| | | log.info("订单推单:{} {}", key, redisService.getCacheList(key)); |
| | | xpOrder.setQueueName(key); |
| | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 单次路径计价(不含区域诊断、不写导航缓存),供多路线合并调用。 |
| | | */ |
| | | private RouteAndFareVo pricingForRoute(RouteAndFareDto routeAndFareDto, RouteVo routeVo, XpPricing xpPricing, String userId) { |
| | | 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() |
| | | ); |
| | | } |
| | | R<UserCouponVo> coupon = null; |
| | | CompletableFuture<R<UserCouponVo>> couponFuture = null; |
| | | if (routeAndFareDto.getUserCouponId() != null) { |
| | | coupon = xpAppUserCouponClient.getUserCoupon( |
| | | new GetUserCouponDto(Long.valueOf(userId), xpPricingCardVo.getTotalPrice(), routeAndFareDto.getOpenCityId(), routeAndFareDto.getUserCouponId())); |
| | | 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), xpPricingCardVo.getTotalPrice(), routeAndFareDto.getOpenCityId(), null)), |
| | | executorService); |
| | | } |
| | | R<UserCouponVo> couponResult; |
| | | if (routeAndFareDto.getUserCouponId() != null) { |
| | | couponResult = coupon; |
| | | } else { |
| | | couponResult = couponFuture.join(); |
| | | } |
| | | 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() < minOrderDistance) { |
| | | throw new ServiceException("当前上下车点距离过近,建议步行前往", I18nKeyConstant.TO_WORK); |
| | | } |
| | | 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); |
| | | } |
| | | } |
| | | RouteAndFareVo routeAndFareVo = new RouteAndFareVo(); |
| | | routeAndFareVo.setRoute(routeVo); |
| | | 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); |
| | | 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()); |
| | | } |
| | | 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); |
| | | } |
| | | 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()); |
| | | routeAndFareVo.setFareEstimate(fareVo); |
| | | routeAndFareVo.getXpPricingCard().setFinalFareStr(fareVo.getFinalFareStr()); |
| | | fareVo.setId(null); |
| | | return routeAndFareVo; |
| | | } |
| | | |
| | | @Override |
| | | public MultiRouteAndFareVo getMultiRouteAndFare(RouteAndFareDto routeAndFareDto, String userId) { |
| | | Map<String, String> geocode = gdMapGeocodingUtil.geocode(routeAndFareDto.getOrigin().getLongitude().toString(), routeAndFareDto.getOrigin().getLatitude().toString()); |
| | | if (null == geocode) { |
| | | 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); |
| | | } |
| | | routeAndFareDto.setOpenCityId(openCities.getId()); |
| | | |
| | | XpPricing xpPricing = this.baseMapper.selectOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, routeAndFareDto.getOpenCityId())); |
| | | if (xpPricing == null) { |
| | | throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION); |
| | | } |
| | | R<Boolean> diagnosisResult = xpMapClient.diagnosisArea(routeAndFareDto); |
| | | if (diagnosisResult.getCode() != R.SUCCESS) { |
| | | throw new RuntimeException(diagnosisResult.getMsg()); |
| | | } |
| | | if (!Boolean.TRUE.equals(diagnosisResult.getData())) { |
| | | throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION); |
| | | } |
| | | |
| | | List<RouteStrategyKind> kinds = Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE); |
| | | Map<String, RouteVo> routeById = new LinkedHashMap<>(); |
| | | Map<String, Set<RouteStrategyKind>> strategiesById = new LinkedHashMap<>(); |
| | | for (RouteStrategyKind kind : kinds) { |
| | | 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) { |
| | | continue; |
| | | } |
| | | String fid = r.getData().getRouteID(); |
| | | routeById.putIfAbsent(fid, r.getData()); |
| | | strategiesById.computeIfAbsent(fid, k -> new LinkedHashSet<>()).add(kind); |
| | | } |
| | | if (routeById.isEmpty()) { |
| | | throw new ServiceException("获取导肮信息失败", I18nKeyConstant.PATH_NOT_FOUND); |
| | | } |
| | | |
| | | 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); |
| | | 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); |
| | | 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.setPrimaryRoutePlanRule(primary.getAmapStrategy()); |
| | | opt.setRoute(part.getRoute()); |
| | | opt.setFareEstimate(part.getFareEstimate()); |
| | | opt.setXpPricingCard(part.getXpPricingCard()); |
| | | if (part.getFareEstimate() != null && part.getFareEstimate().getCouponId() == null) { |
| | | opt.setCouponMessage("当前路线不满足优惠券使用条件"); |
| | | } |
| | | options.add(opt); |
| | | } |
| | | |
| | | String snapshotId = UUID.randomUUID().toString().replace("-", ""); |
| | | long now = System.currentTimeMillis(); |
| | | MultiRouteSnapshotPayload payload = new MultiRouteSnapshotPayload(); |
| | | payload.setSnapshotId(snapshotId); |
| | | payload.setGeneratedAtMillis(now); |
| | | payload.setUserId(userId); |
| | | payload.setOpenCityId(routeAndFareDto.getOpenCityId()); |
| | | payload.setOrigin(routeAndFareDto.getOrigin()); |
| | | 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); |
| | | |
| | | MultiRouteAndFareVo vo = new MultiRouteAndFareVo(); |
| | | vo.setSnapshotId(snapshotId); |
| | | vo.setGeneratedAtMillis(now); |
| | | vo.setShowRouteSelector(options.size() >= 2); |
| | | vo.setRoutes(options); |
| | | vo.setRoutePreferences(listRoutePreferences(routeAndFareDto.getOpenCityId())); |
| | | 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); |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | public void selectNavigationRoute(SelectNavigationRouteDto dto, String userId) { |
| | | String json = redisService.getCacheObject(CacheConstants.ORDER_MULTI_ROUTE_SNAPSHOT + userId + ":" + dto.getSnapshotId()); |
| | | if (json == null) { |
| | | throw new ServiceException("路线已失效,请重新规划", I18nKeyConstant.PATH_NOT_FOUND); |
| | | } |
| | | MultiRouteSnapshotPayload payload = JSON.parseObject(json, MultiRouteSnapshotPayload.class); |
| | | RouteOptionVo selected = payload.getRoutes().stream() |
| | | .filter(r -> dto.getPathFingerprint().equals(r.getPathFingerprint())) |
| | | .findFirst() |
| | | .orElseThrow(() -> new ServiceException("未找到对应路线", I18nKeyConstant.PATH_NOT_FOUND)); |
| | | redisService.setCacheObject("ORDER_NAVIGATION_END_DATA:" + userId, JSON.toJSONString(selected.getRoute()), 24L, TimeUnit.HOURS); |
| | | String id = CacheConstants.ORD + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8); |
| | | if (selected.getXpPricingCard() != null) { |
| | | redisService.setCacheObject(CacheConstants.ORD + id + userId, selected.getXpPricingCard(), 1L, TimeUnit.DAYS); |
| | | } |
| | | if (selected.getFareEstimate() != null) { |
| | | selected.getFareEstimate().setId(id); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public RouteStaleCheckVo checkRouteStale(RouteStaleCheckDto dto, String userId) { |
| | | RouteStaleCheckVo out = new RouteStaleCheckVo(); |
| | | out.setNeedRefreshNotice(false); |
| | | long elapsed = System.currentTimeMillis() - dto.getClientSnapshotMillis(); |
| | | if (elapsed <= navigationStaleSeconds * 1000L) { |
| | | return out; |
| | | } |
| | | String snapJson = redisService.getCacheObject(CacheConstants.ORDER_MULTI_ROUTE_SNAPSHOT + userId + ":" + dto.getSnapshotId()); |
| | | if (snapJson == null) { |
| | | out.setNeedRefreshNotice(true); |
| | | out.setNoticeMessage("路况已变,价格已更新"); |
| | | out.setReplacedSnapshot(true); |
| | | return out; |
| | | } |
| | | MultiRouteSnapshotPayload oldSnap = JSON.parseObject(snapJson, MultiRouteSnapshotPayload.class); |
| | | RouteAndFareDto refreshDto = new RouteAndFareDto(); |
| | | refreshDto.setOrigin(oldSnap.getOrigin()); |
| | | refreshDto.setDestination(oldSnap.getDestination()); |
| | | refreshDto.setOpenCityId(oldSnap.getOpenCityId()); |
| | | refreshDto.setUserCouponId(oldSnap.getUserCouponId()); |
| | | MultiRouteAndFareVo fresh = getMultiRouteAndFare(refreshDto, userId); |
| | | out.setLatestMultiRoute(fresh); |
| | | if (dto.getClientFinalFare() == null || fresh.getRoutes() == null) { |
| | | return out; |
| | | } |
| | | String fp = dto.getPathFingerprint(); |
| | | RouteOptionVo newOpt = fresh.getRoutes().stream() |
| | | .filter(r -> fp == null || fp.equals(r.getPathFingerprint())) |
| | | .findFirst() |
| | | .orElse(fresh.getRoutes().get(0)); |
| | | Double nf = newOpt.getFareEstimate() != null ? newOpt.getFareEstimate().getFinalFare() : null; |
| | | if (nf == null) { |
| | | return out; |
| | | } |
| | | double diff = Math.abs(nf - dto.getClientFinalFare()) / Math.max(1e-6, dto.getClientFinalFare()) * 100.0; |
| | | if (diff > navigationPriceWavePercent) { |
| | | out.setNeedRefreshNotice(true); |
| | | out.setNoticeMessage("路况已变,价格已更新"); |
| | | out.setReplacedSnapshot(true); |
| | | } |
| | | return out; |
| | | } |
| | | |
| | | @Override |
| | | public List<RoutePreferenceItemVo> listRoutePreferences(Long openCityId) { |
| | | List<RoutePreferenceItemVo> list = new ArrayList<>(); |
| | | int i = 0; |
| | | for (RouteStrategyKind k : Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE)) { |
| | | RoutePreferenceItemVo v = new RoutePreferenceItemVo(); |
| | | v.setCode(k.name()); |
| | | v.setName(k.getDisplayName()); |
| | | v.setEnabled(true); |
| | | v.setSortOrder(i++); |
| | | v.setAmapStrategy(k.getAmapStrategy()); |
| | | list.add(v); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public void saveUserRoutePreference(String code, String userId) { |
| | | RouteStrategyKind k; |
| | | try { |
| | | k = RouteStrategyKind.valueOf(code.trim().toUpperCase(Locale.ROOT)); |
| | | } catch (Exception e) { |
| | | throw new ServiceException("无效的路线偏好编码", I18nKeyConstant.PATH_NOT_FOUND); |
| | | } |
| | | redisService.setCacheObject(CacheConstants.USER_ROUTE_PREFERENCE + userId, String.valueOf(k.getAmapStrategy()), 365L, TimeUnit.DAYS); |
| | | } |
| | | |
| | | } |