package com.xiaopeng.itinerary.service.impl;
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.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.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.order.XpPricingCardVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.DateUtils;
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.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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
*
* 定价管理 服务实现类
*
*
* @author liu
* @since 2026-02-03
*/
@Slf4j
@Service
public class XpPricingServiceImpl extends ServiceImpl implements XpPricingService {
@Resource
private XpPricingUtil xpPricingUtil;
@Resource
private XpMapClient xpMapClient;
@Resource
private XpOpenCityMapper xpOpenCityMapper;
@Resource
private XpRoutePlanMapper xpRoutePlanMapper;
@Resource
private RedisService redisService;
@Resource
private XpSystemSettingMapper systemSettingMapper;
@Resource
private XpAppUserCouponClient xpAppUserCouponClient;
@Resource
private XpOrderClient xpOrderClient;
@Resource
private XpTradingStrategyMapper xpTradingStrategyMapper;
@Resource
private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper;
@Resource
private XpWhitelistMapper xpWhitelistMapper;
@Resource
private XpAppUserClient xpAppUserClient;
@Resource
private HolidayUtil holidayUtil;
@Autowired
private IXpGeoQueryEntityService geoQueryEntityService;
@Autowired
private XpElectronicFenceMapper xpElectronicFenceMapper;
@Autowired
private XpPointService xpPointService;
@Resource
private XpPushMessageClient xpPushMessageClient;
@Resource
private GDMapGeocodingUtil gdMapGeocodingUtil;
@Resource
private XpPointClient xpPointClient;
@Value("${navigation.route.stale-seconds:60}")
private int navigationStaleSeconds;
@Value("${navigation.route.price-wave-percent:5}")
private double navigationPriceWavePercent;
/**
* 后台可配置的偏好项编码,逗号分隔,如 SMART,TIME,PRICE
*/
@Value("${navigation.route.preference.enabled-codes:SMART,TIME,PRICE}")
private String navigationPreferenceEnabledCodes;
// 自定义线程池用于并行执行任务
private final ExecutorService executorService;
// 初始化自定义线程池
public XpPricingServiceImpl() {
this.executorService = new ThreadPoolExecutor(
40, // 核心线程数
100, // 最大线程数
60, // 线程空闲时间(秒)
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(400), // 工作队列
new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("pricing-service-thread-" + threadNumber.getAndIncrement());
thread.setDaemon(false);
return thread;
}
}, // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
}
// 关闭线程池
@PreDestroy
public void destroy() {
if (executorService != null && !executorService.isShutdown()) {
executorService.shutdown();
try {
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
/**
* 根据当前时间获取时段
* 例如:15:50 → 15-16
* @return 时段字符串,格式:HH-HH
*/
private String getCurrentTimeSlot() {
Calendar calendar = Calendar.getInstance();
int hour = calendar.get(Calendar.HOUR_OF_DAY);
return hour + "-" + (hour + 1);
}
@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);
// }
// 1. 基础数据查询
Map 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);
}
XpOpenCity xpOpenCity = openCities;
routeAndFareDto.setOpenCityId(xpOpenCity.getId());
XpRoutePlan xpRoutePlan = xpRoutePlanMapper.selectOne(new LambdaQueryWrapper().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().eq(XpPricing::getCityId, routeAndFareDto.getOpenCityId()));
if (xpPricing == null) {
throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
}
// 算距离
R 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 coupon = null;
CompletableFuture> 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> diagnosisAreaFuture = CompletableFuture.supplyAsync(
() -> xpMapClient.diagnosisArea(routeAndFareDto),
executorService
);
// 4. 等待所有并行任务完成并获取结果
R diagnosisResult = diagnosisAreaFuture.join();
R 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);
}
// // 6. 处理路线结果
// if (routeResult.getCode() != 200) {
// throw new RuntimeException(routeResult.getMsg());
// }
// 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);
}
}
// 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);
}
// 10. 检查等待时间
Integer waitTime = getWaitTime(routeAndFareDto.getOrigin().getLongitude(), routeAndFareDto.getOrigin().getLatitude());
// if (waitTime > 3) {
// throw new ServiceException("当前区域正在等待中,请耐心等待", I18nKeyConstant.WAITING_TIME);
// }
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);
// 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());
// 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;
}
/**
* 获取等待时间
* @param lon
* @param lat
* @return
*/
@Override
public Integer getWaitTime(double lon, double lat) {
List entities = geoQueryEntityService.findByLocationWithin500MetersAndLastWeekAndStatusGreater(
lon,
lat,
getCurrentTimeSlot()
);
if (entities.isEmpty()) {
return 1;
} else {
// 已完成接单的数据
List validEntities = entities.stream().filter(e -> e.getStatus() == 1).collect(Collectors.toList());
long waitNum = entities.stream().filter(e -> e.getStatus() == 0).count();
// 计算平均等待时间
OptionalDouble averageOptional = validEntities.stream().mapToDouble(XpGeoQueryEntity::getSeconds).average();
if (averageOptional.isPresent()) {
double average = averageOptional.getAsDouble();
// 未完成接单的数量
int count = (int) waitNum;
if (count == 0) {
return 1;
} else {
double waitMinutes = (average * count) / 60;
if (waitMinutes > 3) {
// throw new ServiceException("当前区域正在等待中,请耐心等待", I18nKeyConstant.WAITING_TIME);
} else if (waitMinutes == 0) {
return 1;
}else if(waitMinutes>0 && waitMinutes<1){
return 1;
}
return (int) waitMinutes;
}
} else {
return 1;
}
}
}
/**
* 获取等待时间
* @param lon
* @param lat
* @return
*/
@Override
public Integer getWaitTimeTimeSlot(double lon, double lat,String timeSlot) {
List entities = geoQueryEntityService.findByLocationWithin500MetersAndLastWeekAndStatusGreater(
lon,
lat,
timeSlot
);
if (entities.isEmpty()) {
return 1;
} else {
// 已完成接单的数据
List validEntities = entities.stream().filter(e -> e.getStatus() == 1).collect(Collectors.toList());
// 计算平均等待时间
OptionalDouble averageOptional = validEntities.stream().mapToDouble(XpGeoQueryEntity::getSeconds).average();
if (averageOptional.isPresent()) {
double average = averageOptional.getAsDouble();
return (int) average;
} else {
return 1;
}
}
}
@Override
public String saveOrder(XpOrderDto dto, String uid) {
// 是否在运营区域时间内
XpElectronicFenceArea xpElectronicFenceArea = xpElectronicFenceAreaMapper.selectById(dto.getXpElectronicFenceAreaId());
XpElectronicFence xpElectronicFence = xpElectronicFenceMapper.selectById(xpElectronicFenceArea.getElectronicId());
String operationStrategy = xpElectronicFence.getOperationStrategy();
Boolean inOperationTime = isInOperationTime(operationStrategy);
if (!inOperationTime && xpElectronicFence.getFailureType()!=1) {
log.info("当前区域暂未开通,下单失败 {}", uid);
throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
}
Integer cancelCount = xpTradingStrategyMapper.selectOne(null).getCancleCount();
// 判断是否有进行的订单
R r = xpOrderClient.hasUnPaodOrder(uid);
if (r.getData() > 0) {
throw new RuntimeException("有正在进行的订单");
}
// 获取起点经纬度
XpPoint xpPoint = xpPointService.getById(dto.getStartPointId());
if(xpPoint ==null || xpPoint.getOperationStatus()!=2){
return null;
}
// 终点
XpPoint endXpPoint = xpPointService.getById(dto.getEndPointId());
if(endXpPoint ==null || endXpPoint.getOperationStatus()!=2){
// throw new ServiceException("该站点已停止运营", 200,I18nKeyConstant.NO_CURRENT_POINT);
return null;
}
// 判断点位是否在运营区域内
boolean inTime = xpPointClient.nowInTimePoint(dto.getStartPointId()).getData();
if (!inTime) {
throw new ServiceException("当前点位未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
}
inTime = xpPointClient.nowInTimePoint(dto.getEndPointId()).getData();
if (!inTime) {
throw new ServiceException("当前点位未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
}
// 判断当天取消的次数 后台配置 redis 查询
// 静默处理
boolean isSilent = false;
Integer num = xpOrderClient.todayCancellationCount(Long.valueOf(uid)).getData();
if (null != num && num >= cancelCount) {
isSilent = true;
}
// 获取起步里程
// XpPricing xpPricing = this.baseMapper.selectOne(new LambdaQueryWrapper().eq(XpPricing::getCityId, dto.getOpenCityId()));
// Integer startMileage = XpPricingUtil.getStartMileage(xpPricing.getStartingFareJson());
// if (startMileage == null) {
// throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
// }
// 根据id获取手机号
Boolean isPriorityDispatch = true;
// R xpAppUserR = xpAppUserClient.queryOperationsUserById(Long.valueOf(uid));
// if (xpAppUserR.getCode() == R.SUCCESS) {
// Long count = xpWhitelistMapper.selectCount(new LambdaQueryWrapper().eq(XpWhitelist::getAccount, xpAppUserR.getData().getPhoneNumber()).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.setPlacementLon(dto.getPlacementLon());
xpOrder.setPlacementLat(dto.getPlacementLat());
xpOrder.setPlacementAddress(dto.getPlacementAddress());
xpOrder.setCityId(dto.getOpenCityId());
xpOrder.setStartLon(xpPoint.getPointLon());
xpOrder.setStartLat(xpPoint.getPointLat());
xpOrder.setBoardingLon(xpPoint.getPointLon());
xpOrder.setBoardingLat(xpPoint.getPointLat());
xpOrder.setBoardingAddress(xpPoint.getPointAddress());
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();
xpOrder.setTimeSlot(currentTimeSlot);
xpOrder.setCreateTime(LocalDateTime.now());
XpOpenCity openCities = xpOpenCityMapper.selectById(dto.getOpenCityId());
if (openCities == null) {
throw new ServiceException("获取城市信息失败", I18nKeyConstant.CITY_NOT_FOUND);
}
XpOpenCity xpOpenCity = openCities;
dto.setOpenCityId(xpOpenCity.getId());
xpOrder.setCityCode(xpOpenCity.getCityCode());
XpRoutePlan xpRoutePlan = xpRoutePlanMapper.selectOne(new LambdaQueryWrapper().eq(XpRoutePlan::getAuditStatus, 2).eq(XpRoutePlan::getCityCode, xpOpenCity.getCityCode()));
Integer distance = 0;
Integer duration = 0;
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);
Long couponRef = dto.getUserCouponId();
xpOrder.setCouponId(couponRef != null && couponRef > 0L ? couponRef : null);
// 产生订单
R addOrderR = xpOrderClient.addOrder(xpOrder);
if (addOrderR.getCode() != R.SUCCESS) {
throw new ServiceException("订单生成失败", I18nKeyConstant.ORDER_CREATE_FAIL);
}
Long orderId = addOrderR.getData();
xpOrder.setId(orderId);
// 静默处理 不推单
if (isSilent) {
log.info("订单取消次数达到上限,执行静默处理,不推单。");
return orderId.toString();
}
// 最大距离
Object maxPushDistanceObj = redisService.getCacheObject(CacheConstants.MAX_PUSH_DISTANCE);
Double maxPushDistance = 0.0;
if (maxPushDistanceObj != null) {
maxPushDistance = Double.valueOf(maxPushDistanceObj.toString());
} else {
maxPushDistance = xpTradingStrategyMapper.selectOne(null).getThreePushDistance();
}
// 找出现在所有的队列 通过计算距离 算出目前应该放在那个队列 如果没有符合的新增一个队列 符合后看是否白名单账号 是则直接放在队列前端
Set 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];
// 和上车点进行判断是否 coupon = null;
CompletableFuture> 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), xpPricingCardVo.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), xpPricingCardVo.getTotalPrice(), routeAndFareDto.getOpenCityId(), null)),
executorService);
}
R couponResult;
if (Long.valueOf(0L).equals(ucId) || (ucId != null && ucId > 0L)) {
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 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().eq(XpPricing::getCityId, routeAndFareDto.getOpenCityId()));
if (xpPricing == null) {
throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
}
R 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 kinds = buildStrategyKindsForPlanning(userId);
Map routeById = new LinkedHashMap<>();
Map> 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 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 options = new ArrayList<>();
for (Map.Entry e : routeById.entrySet()) {
String fid = e.getKey();
RouteVo rv = e.getValue();
Set 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 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);
}
RouteStrategyKind userPref = resolveUserPreferredKind(userId).orElse(RouteStrategyKind.SMART);
options.sort((a, b) -> compareRouteOptionByUserPreference(a, b, userPref, strategiesById));
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(), 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);
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 listRoutePreferences(Long openCityId, String userId) {
Set enabledCodes = parseEnabledPreferenceCodes();
Optional savedPref = resolveUserPreferredKind(userId);
List list = new ArrayList<>();
int i = 0;
for (RouteStrategyKind k : Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE)) {
if (!enabledCodes.contains(k.name())) {
continue;
}
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;
}
@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);
}
private Set parseEnabledPreferenceCodes() {
Set set = new LinkedHashSet<>();
for (String part : navigationPreferenceEnabledCodes.split(",")) {
String p = part.trim().toUpperCase(Locale.ROOT);
if (!p.isEmpty()) {
set.add(p);
}
}
if (set.isEmpty()) {
set.add(RouteStrategyKind.SMART.name());
set.add(RouteStrategyKind.TIME.name());
set.add(RouteStrategyKind.PRICE.name());
}
return set;
}
/**
* 用户偏好策略优先请求,其余策略仍请求以便合并去重。
*/
private List buildStrategyKindsForPlanning(String userId) {
List all = new ArrayList<>();
Set enabled = parseEnabledPreferenceCodes();
for (RouteStrategyKind k : Arrays.asList(RouteStrategyKind.SMART, RouteStrategyKind.TIME, RouteStrategyKind.PRICE)) {
if (enabled.contains(k.name())) {
all.add(k);
}
}
if (all.isEmpty()) {
all.add(RouteStrategyKind.SMART);
}
Optional pref = resolveUserPreferredKind(userId);
if (!pref.isPresent()) {
return all;
}
RouteStrategyKind p = pref.get();
if (!all.contains(p)) {
return all;
}
List ordered = new ArrayList<>();
ordered.add(p);
for (RouteStrategyKind k : all) {
if (k != p) {
ordered.add(k);
}
}
return ordered;
}
private Optional resolveUserPreferredKind(String userId) {
if (userId == null || userId.isEmpty()) {
return Optional.empty();
}
Object raw = redisService.getCacheObject(CacheConstants.USER_ROUTE_PREFERENCE + userId);
if (raw == null) {
return Optional.empty();
}
try {
int strategy = Integer.parseInt(raw.toString());
return RouteStrategyKind.fromAmapStrategy(strategy);
} catch (Exception e) {
return Optional.empty();
}
}
private int compareRouteOptionByUserPreference(RouteOptionVo a, RouteOptionVo b, RouteStrategyKind pref,
Map> strategiesById) {
Set sa = strategiesById.getOrDefault(a.getPathFingerprint(), Collections.emptySet());
Set sb = strategiesById.getOrDefault(b.getPathFingerprint(), Collections.emptySet());
boolean aHas = sa.contains(pref);
boolean bHas = sb.contains(pref);
if (aHas != bHas) {
return bHas ? 1 : -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);
}
@Override
public QueueStatusVo getQueueStatus(String orderId, String uid) {
QueueStatusVo queueStatusVo = new QueueStatusVo();
try {
// 从订单服务获取订单信息,获取queueName
R 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 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;
}
}
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.setStatus(0);
queueStatusVo.setCurrentPosition(0);
queueStatusVo.setTotalCount(0);
queueStatusVo.setEstimatedWaitTime(0);
}
return queueStatusVo;
}
}