package com.xiaopeng.operation.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.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.xiaopeng.api.dto.account.GetUserCouponDto;
|
import com.xiaopeng.api.dto.map.RouteAndFareDto;
|
import com.xiaopeng.api.dto.operation.LonLatDto;
|
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.order.XpOrderClient;
|
import com.xiaopeng.api.model.account.XpAppUser;
|
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.RouteAndFareVo;
|
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.core.utils.StringUtils;
|
import com.xiaopeng.common.core.utils.uuid.UUID;
|
import com.xiaopeng.common.core.web.page.PageInfo;
|
import com.xiaopeng.common.redis.service.RedisService;
|
import com.xiaopeng.operation.constant.I18nKeyConstant;
|
import com.xiaopeng.operation.dto.*;
|
import com.xiaopeng.operation.mapper.*;
|
import com.xiaopeng.operation.query.PricingPageListQuery;
|
import com.xiaopeng.operation.service.IXpGeoQueryEntityService;
|
import com.xiaopeng.operation.service.XpPointService;
|
import com.xiaopeng.operation.service.XpPricingService;
|
import com.xiaopeng.operation.job.base.QuartzManager;
|
import com.xiaopeng.operation.job.base.TimeJobType;
|
import com.xiaopeng.operation.job.jobs.DynamicPricingCheckJob;
|
import com.xiaopeng.operation.utils.XpPricingUtil;
|
import com.xiaopeng.operation.utils.holiday.HolidayUtil;
|
import com.xiaopeng.operation.utils.map.FastDistanceCalculator;
|
import com.xiaopeng.operation.vo.PricingPageListVO;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.PreDestroy;
|
import javax.annotation.Resource;
|
import java.time.LocalDateTime;
|
import java.time.LocalTime;
|
import java.util.*;
|
import java.util.concurrent.*;
|
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.stream.Collectors;
|
|
/**
|
* <p>
|
* 定价管理 服务实现类
|
* </p>
|
*
|
* @author liu
|
* @since 2026-02-03
|
*/
|
@Slf4j
|
@Service
|
public class XpPricingServiceImpl extends ServiceImpl<XpPricingMapper, XpPricing> 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;
|
|
|
// 自定义线程池用于并行执行任务
|
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
|
@Deprecated
|
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;
|
|
if (redisService.hasKey(key)) {
|
String jsonStr = Objects.requireNonNull(redisService.getCacheObject(key)).toString();
|
RouteAndFareVo RouteAndFareVo = JSONObject.parseObject(jsonStr, RouteAndFareVo.class);
|
return (RouteAndFareVo);
|
}
|
|
// 1. 基础数据查询
|
XpOpenCity xpOpenCity = xpOpenCityMapper.selectById(routeAndFareDto.getOpenCityId());
|
if (xpOpenCity == null) {
|
throw new ServiceException("未找到步行路线", I18nKeyConstant.CITY_INFORMATION_DOES_NOT_EXIST);
|
}
|
String cityCode = xpOpenCity.getCityCode();
|
|
XpRoutePlan xpRoutePlan = xpRoutePlanMapper.selectOne(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getAuditStatus, 2).eq(XpRoutePlan::getCityCode, cityCode));
|
if (xpRoutePlan != null) {
|
routeAndFareDto.setRoutePlanRule(xpRoutePlan.getRoutePlanRule());
|
}
|
|
XpPricing xpPricing = this.baseMapper.selectOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, routeAndFareDto.getOpenCityId()));
|
if (xpPricing == null) {
|
throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
|
}
|
|
|
|
// // 算距离
|
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()
|
);
|
}
|
|
|
// 3. 并行执行远程服务调用 判断是否同一经营区域
|
CompletableFuture<R<Boolean>> diagnosisAreaFuture = CompletableFuture.supplyAsync(
|
() -> xpMapClient.diagnosisArea(routeAndFareDto),
|
executorService
|
);
|
|
|
// 优惠券
|
XpPricingCardVo finalXpPricingCardVo = xpPricingCardVo;
|
CompletableFuture<R<UserCouponVo>> couponFuture = CompletableFuture.supplyAsync(
|
() -> {
|
// 优惠券
|
return xpAppUserCouponClient.getUserCoupon(
|
new GetUserCouponDto(Long.valueOf(userId), finalXpPricingCardVo.getTotalPrice(),routeAndFareDto.getOpenCityId(),null)
|
// new GetUserCouponDto(Long.valueOf(userId), 1d, routeAndFareDto.getOpenCityId())
|
);
|
},
|
executorService
|
);
|
|
// 4. 等待所有并行任务完成并获取结果
|
R<Boolean> diagnosisResult = diagnosisAreaFuture.join();
|
R<UserCouponVo> 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) {
|
xpPricingCardVo.setCoupon(String.valueOf(userCouponVo.getMoney()));
|
}
|
|
// 8. 构建响应对象
|
RouteAndFareVo routeAndFareVo = new RouteAndFareVo();
|
|
// MOCK数据
|
// RouteVo routeVo = new RouteVo();
|
// routeVo.setDistance(3000);
|
// routeVo.setDuration(5);
|
// routeVo.setRouteID("1");
|
// List<LonLatDto> routePolyline = Arrays.asList(
|
// // 起点:天府新谷
|
// new LonLatDto(104.05363, 30.58937),
|
//
|
// // 途经点1:府城大道与交子北二路交叉口附近
|
// new LonLatDto(104.05821, 30.58745),
|
//
|
// // 途经点2:交子大道与成汉南路交叉口
|
// new LonLatDto(104.06418, 30.58492),
|
//
|
// // 途经点3:交子大道与万象南路交叉口
|
// new LonLatDto(104.06975, 30.58321),
|
//
|
// // 途经点4:交子大道与益州大道交叉口
|
// new LonLatDto(104.07366, 30.58198),
|
//
|
// // 终点:金融城(地铁站附近)
|
// new LonLatDto(104.076544, 30.581039)
|
// );
|
// routeVo.setPolyline(routePolyline);
|
routeAndFareVo.setRoute(routeVo);
|
|
|
routeAndFareVo.setXpPricingCard(xpPricingCardVo);
|
|
FareVo fareVo = new FareVo();
|
fareVo.setEstimatedDistanceKm(routeVo.getDistance().doubleValue() / 1000);
|
fareVo.setEstimatedDurationMin(routeVo.getDuration() / 60);
|
|
// 9. 设置费用信息
|
fareVo.setOriginalFare(xpPricingCardVo.getTotalPrice());
|
if (userCouponVo != null) {
|
fareVo.setDiscountAmount(userCouponVo.getMoney());
|
fareVo.setFinalFare(fareVo.getOriginalFare() - fareVo.getDiscountAmount());
|
fareVo.setCouponId(userCouponVo.getId());
|
} else {
|
fareVo.setDiscountAmount(0.0);
|
fareVo.setFinalFare(fareVo.getOriginalFare());
|
}
|
|
// 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. 设置费用信息
|
routeAndFareVo.setFareEstimate(fareVo);
|
|
|
String voJsonStr = JSONObject.toJSONString(routeAndFareVo);
|
// 进行缓存
|
redisService.setCacheObject(key, voJsonStr, 5l, TimeUnit.MINUTES);
|
|
return routeAndFareVo;
|
}
|
|
|
/**
|
* 获取等待时间
|
* @param lon
|
* @param lat
|
* @return
|
*/
|
@Override
|
public Integer getWaitTime(double lon, double lat) {
|
List<XpGeoQueryEntity> entities = geoQueryEntityService.findByLocationWithin500MetersAndLastWeekAndStatusGreater(
|
lon,
|
lat,
|
getCurrentTimeSlot()
|
);
|
|
|
if (entities.isEmpty()) {
|
return 1;
|
} else {
|
// 已完成接单的数据
|
List<XpGeoQueryEntity> validEntities = entities.stream().filter(e -> e.getStatus() == 1).collect(Collectors.toList());
|
|
// 计算平均等待时间
|
OptionalDouble averageOptional = validEntities.stream().mapToDouble(XpGeoQueryEntity::getSeconds).average();
|
if (averageOptional.isPresent()) {
|
double average = averageOptional.getAsDouble();
|
// 未完成接单的数量
|
int count = entities.size() - validEntities.size();
|
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;
|
}
|
return (int) waitMinutes;
|
}
|
} else {
|
return 1;
|
}
|
}
|
}
|
|
|
@Override
|
public Long 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) {
|
throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
|
}
|
|
|
Integer cancelCount = xpTradingStrategyMapper.selectOne(null).getCancleCount();
|
// 判断是否有进行的订单
|
R<Long> r = xpOrderClient.hasUnPaodOrder(uid);
|
if (r.getData() > 0) {
|
throw new ServiceException("有正在进行的订单", I18nKeyConstant.HAS_UNPAID_ORDER);
|
}
|
|
// 获取起点经纬度
|
XpPoint xpPoint = xpPointService.getById(dto.getStartPointId());
|
|
// 终点
|
XpPoint endXpPoint = xpPointService.getById(dto.getStartPointId());
|
|
// 判断点位是否在运营区域内
|
LocalTime now = LocalTime.now();
|
boolean inTime = xpPointService.getInTime(now, xpPoint.getEffectivePeriod(), xpPoint.getEffectiveTimePeriod());
|
if (!inTime) {
|
throw new ServiceException("当前点位未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
|
}
|
inTime = xpPointService.getInTime(now, endXpPoint.getEffectivePeriod(), endXpPoint.getEffectiveTimePeriod());
|
if (!inTime) {
|
throw new ServiceException("当前点位未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
|
}
|
|
// 静默处理
|
boolean isSilent = false;
|
Integer num = xpOrderClient.todayCancellationCount(Long.valueOf(uid)).getData();
|
if (num >= cancelCount) {
|
isSilent = true;
|
}
|
|
|
// 获取起步里程
|
// XpPricing xpPricing = this.baseMapper.selectOne(new LambdaQueryWrapper<XpPricing>().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<XpAppUser> xpAppUserR = xpAppUserClient.queryOperationsUserById(Long.valueOf(uid));
|
// if (xpAppUserR.getCode() == R.SUCCESS) {
|
// Long count = xpWhitelistMapper.selectCount(new LambdaQueryWrapper<XpWhitelist>().eq(XpWhitelist::getAccount, xpAppUserR.getData().getPhoneNumber()).eq(XpWhitelist::getIsPriorityDispatch, 1));
|
// if (count > 0) {
|
// isPriorityDispatch = true;
|
// }
|
// }
|
|
// 生成订单数据
|
XpOrder xpOrder = new XpOrder();
|
|
xpOrder.setVehicleId(1l);
|
|
// xpOrder.setStartMileage(Double.valueOf(startMileage));
|
|
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());
|
|
|
// 计算距离 和 时间
|
RouteAndFareDto routeAndFareDto = new RouteAndFareDto();
|
LonLatDto lonLatDto = new LonLatDto();
|
lonLatDto.setLongitude(xpPoint.getPointLon());
|
lonLatDto.setLatitude(xpPoint.getPointLat());
|
routeAndFareDto.setOrigin(lonLatDto);
|
|
LonLatDto lonLatDto1 = new LonLatDto();
|
lonLatDto1.setLongitude(endXpPoint.getPointLon());
|
lonLatDto1.setLatitude(endXpPoint.getPointLat());
|
routeAndFareDto.setDestination(lonLatDto1);
|
|
|
XpOpenCity xpOpenCity = xpOpenCityMapper.selectById(dto.getOpenCityId());
|
XpRoutePlan xpRoutePlan = xpRoutePlanMapper.selectOne(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getAuditStatus, 2).eq(XpRoutePlan::getCityCode, xpOpenCity.getCityCode()));
|
if (xpRoutePlan != null) {
|
routeAndFareDto.setRoutePlanRule(xpRoutePlan.getRoutePlanRule());
|
}
|
R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto);
|
Integer distance = 0;
|
Integer duration = 0;
|
if (distanceAndDuration.getCode() != 200) {
|
// throw new ServiceException("订单生成失败", I18nKeyConstant.ORDER_CREATE_FAIL);
|
}
|
// distance = distanceAndDuration.getData().getDistance();
|
// duration = distanceAndDuration.getData().getDuration();
|
|
|
// 产生订单
|
R<Long> addOrderR = xpOrderClient.addOrder(xpOrder);
|
if (addOrderR.getCode() != R.SUCCESS) {
|
throw new ServiceException("订单生成失败", I18nKeyConstant.ORDER_CREATE_FAIL);
|
}
|
Long orderId = addOrderR.getData();
|
|
// 静默处理 不推单
|
if (isSilent) {
|
return orderId;
|
}
|
|
// 最大距离
|
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<String> quoteMatchName = redisService.getQuoteMatchName(CacheConstants.QUEUE_POINT_ID);
|
for (String s : quoteMatchName) {
|
// QUEUE_POINT_ID-1-30.233,120.233
|
String lonLat = s.split("-")[2];
|
// 和上车点进行判断是否<maxPushDistance
|
double v = FastDistanceCalculator.distanceFast(Double.parseDouble(lonLat.split(",")[1]), Double.parseDouble(lonLat.split(",")[0]), xpPoint.getPointLat(), xpPoint.getPointLon());
|
if (v < maxPushDistance) {
|
if (isPriorityDispatch) {
|
// 头部 value 订单_经纬度_状态_用户id_订单编号_距离_时间
|
redisService.addToTail(s, orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + routeAndFareDto.getRoutePlanRule());
|
} else {
|
// 尾部
|
redisService.addToTail(s, orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + routeAndFareDto.getRoutePlanRule());
|
}
|
xpOrder.setQueueName(s);
|
xpOrderClient.updateOrder(xpOrder);
|
|
// 存储mongodb 数据
|
XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity();
|
xpGeoQueryEntity.setOrderId(orderId.toString());
|
xpGeoQueryEntity.setLocation(new GeoJsonPoint(xpPoint.getPointLon(), xpPoint.getPointLat()));
|
// 计算2个时间的秒数
|
xpGeoQueryEntity.setTime(currentTimeSlot);
|
xpGeoQueryEntity.setStatus(0);
|
xpGeoQueryEntity.setTimestamp(System.currentTimeMillis());
|
geoQueryEntityService.save(xpGeoQueryEntity);
|
|
return orderId;
|
}
|
}
|
|
String key = CacheConstants.QUEUE_POINT_ID + "-" + xpPoint.getId() + "-" + xpPoint.getPointLon() + "," + xpPoint.getPointLat();
|
redisService.setCacheList(key, Arrays.asList(orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + routeAndFareDto.getRoutePlanRule()));
|
xpOrder.setQueueName(key);
|
xpOrderClient.updateOrder(xpOrder);
|
|
|
// 存储mongodb 数据
|
XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity();
|
xpGeoQueryEntity.setOrderId(orderId.toString());
|
xpGeoQueryEntity.setLocation(new GeoJsonPoint(xpPoint.getPointLon(), xpPoint.getPointLat()));
|
// 计算2个时间的秒数
|
xpGeoQueryEntity.setTime(currentTimeSlot);
|
xpGeoQueryEntity.setStatus(0);
|
xpGeoQueryEntity.setTimestamp(System.currentTimeMillis());
|
geoQueryEntityService.save(xpGeoQueryEntity);
|
|
return orderId;
|
|
//
|
// XpElectronicFenceArea xpElectronicFenceArea = xpElectronicFenceAreaMapper.selectById(dto.getXpElectronicFenceAreaId());
|
// // 获取电子围栏的车辆
|
// R<List<XpVehicle>> vehicles = xpVehicleClient.listByFenceId(xpElectronicFenceArea.getElectronicId());
|
//
|
|
|
// 产生订单 存订单数据 // 下发指令 消息推送用户端
|
// return null;
|
|
}
|
|
|
private Boolean isInOperationTime(String operationStrategy) {
|
JSONArray jsonArray = JSONArray.parseArray(operationStrategy);
|
Integer holidayToday = holidayUtil.isHolidayToday();
|
for (Object o : jsonArray) {
|
JSONObject jsonObject = JSONObject.parseObject(o.toString());
|
String type = jsonObject.getString("type");
|
if (type.equals(holidayToday.toString())) {
|
JSONArray time = jsonObject.getJSONArray("time");
|
// 判断当前时间 是否在time时间段中
|
for (Object object : time) {
|
String[] timeArray = object.toString().split("-");
|
String startTime = timeArray[0];
|
String endTime = timeArray[1];
|
boolean currentTimeInRange = DateUtils.isCurrentTimeInRange(startTime, endTime);
|
if (currentTimeInRange) {
|
return true;
|
}
|
}
|
} else if (type.equals("3")) {
|
// 判断是什么类型 0每天 1-7 想起
|
String typeStr = jsonObject.getString("typeStr");
|
// 查看今天周几
|
Integer week = DateUtils.getWeek();
|
// 包含每天 或者 包含当天
|
if (typeStr.contains("0") || typeStr.contains(week.toString())) {
|
JSONArray time = jsonObject.getJSONArray("time");
|
// 判断当前时间 是否在time时间段中
|
for (Object object : time) {
|
String[] timeArray = object.toString().split("-");
|
String startTime = timeArray[0];
|
String endTime = timeArray[1];
|
boolean currentTimeInRange = DateUtils.isCurrentTimeInRange(startTime, endTime);
|
if (currentTimeInRange) {
|
return true;
|
}
|
}
|
}
|
|
}
|
}
|
return false;
|
}
|
|
@Override
|
public PageInfo<PricingPageListVO> pageList(PricingPageListQuery query) {
|
PageInfo<PricingPageListVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
|
Page<PricingPageListVO> page = this.baseMapper.pageList(pageInfo, query);
|
pageInfo.setRecords(page.getRecords());
|
pageInfo.setTotal(page.getTotal());
|
return pageInfo;
|
}
|
|
@Override
|
public Long add(PricingAddDTO dto) {
|
XpOpenCity openCity = xpOpenCityMapper.selectById(dto.getCityId());
|
if (openCity == null) {
|
throw new ServiceException("开通城市不存在");
|
}
|
|
Long count = this.lambdaQuery()
|
.eq(XpPricing::getVehicleModel, dto.getVehicleModel())
|
.eq(XpPricing::getChannel, dto.getChannel())
|
.eq(XpPricing::getCityId, dto.getCityId().intValue())
|
.count();
|
if (count != null && count > 0) {
|
throw new ServiceException("该车型/渠道/城市的定价已存在", I18nKeyConstant.PRICING_EXISTS);
|
}
|
|
XpPricing pricing = new XpPricing();
|
pricing.setVehicleModel(dto.getVehicleModel());
|
pricing.setChannel(dto.getChannel());
|
pricing.setCityId(dto.getCityId());
|
pricing.setRemark(dto.getRemark());
|
pricing.setPricingType(dto.getPricingType());
|
pricing.setBasicPricingJson("{}");
|
pricing.setStartingFareJson("[]");
|
pricing.setDynamicPricingJson("{}");
|
pricing.setIsDynamicPrice(0);
|
this.save(pricing);
|
return pricing.getId();
|
}
|
|
@Override
|
public Long edit(PricingEditDTO dto) {
|
//1.查询定价
|
XpPricing pricing = getById(dto.getId());
|
if (Objects.nonNull(pricing)) {
|
LambdaUpdateWrapper<XpPricing> updateWrapper = new LambdaUpdateWrapper<>();
|
//2.判断定价类型是否改变
|
if (!pricing.getPricingType().equals(dto.getPricingType())) {
|
updateWrapper.set(XpPricing::getBasicPricingJson, null);
|
updateWrapper.set(XpPricing::getStartingFareJson, null);
|
updateWrapper.set(XpPricing::getDynamicPricingJson, null);
|
}
|
updateWrapper.set(XpPricing::getVehicleModel, dto.getVehicleModel());
|
updateWrapper.set(XpPricing::getChannel, dto.getChannel());
|
updateWrapper.set(XpPricing::getCityId, dto.getCityId());
|
updateWrapper.set(StringUtils.isNotBlank(dto.getRemark()), XpPricing::getRemark, dto.getRemark());
|
updateWrapper.set(XpPricing::getPricingType, dto.getPricingType());
|
updateWrapper.eq(XpPricing::getId, dto.getId());
|
update(updateWrapper);
|
}
|
return dto.getId();
|
}
|
|
@Override
|
public void setPrice(PricingSetPriceDTO dto) {
|
XpPricing pricing = this.getById(dto.getId());
|
if (pricing == null) {
|
throw new ServiceException("定价不存在", I18nKeyConstant.PRICING_NOT_EXIST);
|
}
|
try {
|
if (StringUtils.isNotBlank(dto.getBasicPricingJson())) {
|
JSONObject.parseObject(dto.getBasicPricingJson());
|
}
|
if (StringUtils.isNotBlank(dto.getStartingFareJson())) {
|
JSONArray.parseArray(dto.getStartingFareJson());
|
}
|
if (StringUtils.isNotBlank(dto.getDynamicPricingJson())) {
|
JSONObject.parseObject(dto.getDynamicPricingJson());
|
}
|
} catch (Exception e) {
|
throw new ServiceException("价格配置JSON格式不正确", I18nKeyConstant.PRICING_JSON_INVALID);
|
}
|
|
XpPricing update = new XpPricing();
|
update.setId(dto.getId());
|
update.setBasicPricingJson(dto.getBasicPricingJson());
|
update.setStartingFareJson(StringUtils.isNotBlank(dto.getStartingFareJson()) ? dto.getStartingFareJson() : null);
|
update.setDynamicPricingJson(dto.getDynamicPricingJson());
|
update.setIsDynamicPrice(dto.getIsDynamicPrice());
|
this.updateById(update);
|
|
// 动态调价 Quartz 任务调度
|
String jobName = DynamicPricingCheckJob.JOB_PREFIX + dto.getId();
|
if (Integer.valueOf(1).equals(dto.getIsDynamicPrice())
|
&& StringUtils.isNotBlank(dto.getDynamicPricingJson())) {
|
JSONObject dynamicJson = JSONObject.parseObject(dto.getDynamicPricingJson());
|
Integer periodMinutes = dynamicJson.getInteger("num1");
|
if (periodMinutes != null && periodMinutes > 0) {
|
// 先移除可能存在的旧任务,再注册新任务(num1 分钟后首次执行)
|
QuartzManager.removeJob(jobName, TimeJobType.DYNAMIC_PRICING_CHECK);
|
Date triggerTime = new Date(System.currentTimeMillis() + periodMinutes * 60_000L);
|
Map<String, Object> jobData = new HashMap<>();
|
jobData.put("pricingId", dto.getId());
|
QuartzManager.addJob(DynamicPricingCheckJob.class, jobName,
|
TimeJobType.DYNAMIC_PRICING_CHECK, triggerTime, jobData);
|
log.info("动态调价任务已注册 pricingId={}, 供需计算周期={}分钟", dto.getId(), periodMinutes);
|
}
|
} else {
|
// 关闭动态调价时,移除已存在的任务
|
QuartzManager.removeJob(jobName, TimeJobType.DYNAMIC_PRICING_CHECK);
|
log.info("动态调价任务已移除 pricingId={}", dto.getId());
|
}
|
}
|
|
@Override
|
public void setRule(PricingSetRuleDTO dto) {
|
XpPricing pricing = this.getById(dto.getId());
|
if (pricing == null) {
|
throw new ServiceException("定价不存在", I18nKeyConstant.PRICING_NOT_EXIST);
|
}
|
|
XpPricing update = new XpPricing();
|
update.setId(dto.getId());
|
update.setPriceRule(dto.getPriceRule());
|
this.updateById(update);
|
}
|
|
}
|