| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | 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.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.feignClient.map.XpMapClient; |
| | | import com.xiaopeng.api.feignClient.operation.XpPointClient; |
| | | 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.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 lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | 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 GDMapGeocodingUtil gdMapGeocodingUtil; |
| | | |
| | | @Resource |
| | | private XpPointClient xpPointClient; |
| | | |
| | |
| | | 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) { |
| | | public List<RouteAndFareVo> getRouteAndFare(RouteAndFareDto routeAndFareDto, String userId) { |
| | | // // 判断是否5分钟内进行了查询 相同站点的起始点 并且是同时段 |
| | | // String key = "xp-map-query-" + routeAndFareDto.getOpenCityId() |
| | | // + "-" + routeAndFareDto.getOrigin().getLongitude() + "-" + routeAndFareDto.getOrigin().getLatitude() |
| | |
| | | // RouteAndFareVo RouteAndFareVo = JSONObject.parseObject(jsonStr, RouteAndFareVo.class); |
| | | // return (RouteAndFareVo); |
| | | // } |
| | | ArrayList<RouteAndFareVo> routeAndFareVos = new ArrayList<>(3); |
| | | |
| | | // 1. 基础数据查询 |
| | | Map<String, String> geocode = gdMapGeocodingUtil.geocode(routeAndFareDto.getOrigin().getLongitude().toString(), routeAndFareDto.getOrigin().getLatitude().toString()); |
| | |
| | | if (openCities == null ) { |
| | | 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); |
| | | } |
| | | List<XpRoutePlan> xpRoutePlan = xpRoutePlanMapper.selectList(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getAuditStatus, 2).eq(XpRoutePlan::getCityCode, 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() |
| | | ); |
| | | } |
| | | |
| | | // 优惠券: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){ |
| | | 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); |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | // 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()); |
| | | } |
| | | 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.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()); |
| | | // 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; |
| | | } |
| | | |
| | | |
| | |
| | | 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()); |
| | | |
| | | |
| | | // 当前时段 |
| | | String currentTimeSlot = DateUtils.getCurrentTimeSlot(); |
| | |
| | | xpGeoQueryEntity.setStatus(0); |
| | | xpGeoQueryEntity.setTimestamp(System.currentTimeMillis()); |
| | | geoQueryEntityService.save(xpGeoQueryEntity); |
| | | |
| | | return orderId.toString(); |
| | | } |
| | | } |
| | |
| | | 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) { |
| | |
| | | throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION); |
| | | } |
| | | |
| | | List<RouteStrategyKind> kinds = buildStrategyKindsForPlanning(userId); |
| | | StrategyPlanningConfig planningConfig = buildStrategyKindsForPlanning(userId, routeAndFareDto.getOpenCityId()); |
| | | List<RouteStrategyKind> kinds = planningConfig.getKinds(); |
| | | Map<String, RouteVo> routeById = new LinkedHashMap<>(); |
| | | Map<String, Set<RouteStrategyKind>> strategiesById = new LinkedHashMap<>(); |
| | | for (RouteStrategyKind kind : kinds) { |
| | |
| | | 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()); |
| | | } |
| | | } |
| | | List<String> labels = RouteStrategyKind.orderedOf(kindsOnRoute).stream() |
| | | .map(kind -> resolveStrategyDisplayName(kind, planningConfig.getDisplayNames())) |
| | | .collect(Collectors.toList()); |
| | | opt.setStrategyLabels(labels); |
| | | opt.setPrimaryStrategyLabel(primary.getDisplayName()); |
| | | opt.setPrimaryStrategyLabel(resolveStrategyDisplayName(primary, planningConfig.getDisplayNames())); |
| | | opt.setPrimaryRoutePlanRule(primary.getAmapStrategy()); |
| | | opt.setRoute(part.getRoute()); |
| | | opt.setFareEstimate(part.getFareEstimate()); |
| | |
| | | public List<RoutePreferenceItemVo> listRoutePreferences(Long openCityId, String userId) { |
| | | Set<String> enabledCodes = parseEnabledPreferenceCodes(); |
| | | Optional<RouteStrategyKind> savedPref = resolveUserPreferredKind(userId); |
| | | Map<RouteStrategyKind, String> displayNames = buildStrategyKindsForPlanning(userId, openCityId).getDisplayNames(); |
| | | List<RoutePreferenceItemVo> list = new ArrayList<>(); |
| | | int i = 0; |
| | | for (RouteStrategyKind k : Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE)) { |
| | | for (RouteStrategyKind k : RouteStrategyKind.orderedValues()) { |
| | | if (!enabledCodes.contains(k.name())) { |
| | | continue; |
| | | } |
| | | RoutePreferenceItemVo v = new RoutePreferenceItemVo(); |
| | | v.setCode(k.name()); |
| | | v.setName(k.getDisplayName()); |
| | | v.setName(resolveStrategyDisplayName(k, displayNames)); |
| | | v.setEnabled(true); |
| | | v.setSortOrder(i++); |
| | | v.setAmapStrategy(k.getAmapStrategy()); |
| | |
| | | } |
| | | } |
| | | 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<RouteStrategyKind> buildStrategyKindsForPlanning(String userId) { |
| | | List<RouteStrategyKind> all = new ArrayList<>(); |
| | | private StrategyPlanningConfig buildStrategyKindsForPlanning(String userId, Long openCityId) { |
| | | Set<String> enabled = parseEnabledPreferenceCodes(); |
| | | for (RouteStrategyKind k : Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE)) { |
| | | List<XpRoutePlan> xpRoutePlans = xpRoutePlanMapper.selectList(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getOpenCityId, openCityId)); |
| | | Map<RouteStrategyKind, String> displayNames = new LinkedHashMap<>(); |
| | | List<RouteStrategyKind> configuredKinds = new ArrayList<>(); |
| | | if (xpRoutePlans.isEmpty()) { |
| | | configuredKinds.addAll(RouteStrategyKind.defaultPreferenceKinds()); |
| | | } 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()); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | List<RouteStrategyKind> all = new ArrayList<>(); |
| | | for (RouteStrategyKind k : configuredKinds) { |
| | | if (enabled.contains(k.name())) { |
| | | all.add(k); |
| | | addIfAbsent(all, k); |
| | | } |
| | | } |
| | | if (all.isEmpty()) { |
| | | all.add(RouteStrategyKind.SMART); |
| | | all.add(resolveDefaultEnabledKind(enabled)); |
| | | } |
| | | Optional<RouteStrategyKind> pref = resolveUserPreferredKind(userId); |
| | | if (!pref.isPresent()) { |
| | | return all; |
| | | return new StrategyPlanningConfig(all, displayNames); |
| | | } |
| | | RouteStrategyKind p = pref.get(); |
| | | if (!all.contains(p)) { |
| | | return all; |
| | | return new StrategyPlanningConfig(all, displayNames); |
| | | } |
| | | List<RouteStrategyKind> ordered = new ArrayList<>(); |
| | | ordered.add(p); |
| | |
| | | ordered.add(k); |
| | | } |
| | | } |
| | | return ordered; |
| | | return new StrategyPlanningConfig(ordered, 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); |
| | | } |
| | | } |
| | | |
| | | private RouteStrategyKind resolveDefaultEnabledKind(Set<String> enabled) { |
| | | for (RouteStrategyKind kind : RouteStrategyKind.defaultPreferenceKinds()) { |
| | | if (enabled.contains(kind.name())) { |
| | | return kind; |
| | | } |
| | | } |
| | | 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) { |
| | | 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); |
| | |
| | | @Override |
| | | public QueueStatusVo getQueueStatus(String orderId, String uid) { |
| | | QueueStatusVo queueStatusVo = new QueueStatusVo(); |
| | | |
| | | |
| | | 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.setEstimatedWaitTime(0); |
| | | return queueStatusVo; |
| | | } |
| | | |
| | | |
| | | // 从Redis获取队列数据 |
| | | List<String> queueItems = redisService.getCacheList(queueName); |
| | | if (queueItems == null || queueItems.isEmpty()) { |
| | |
| | | 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 + "_")) { |
| | |
| | | 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); |
| | | |
| | | |
| | | } catch (Exception e) { |
| | | log.error("查询排队状态失败: {}", e.getMessage(), e); |
| | | // 异常情况下返回默认值 |
| | |
| | | queueStatusVo.setTotalCount(0); |
| | | queueStatusVo.setEstimatedWaitTime(0); |
| | | } |
| | | |
| | | |
| | | return queueStatusVo; |
| | | } |
| | | |