liujie
2026-02-14 db4ff051b715e2a2dfb2659eb7a94de96555ca8e
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java
@@ -4,6 +4,7 @@
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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;
@@ -12,7 +13,6 @@
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.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.account.XpAppUser;
import com.xiaopeng.api.model.operation.*;
import com.xiaopeng.api.model.order.XpOrder;
@@ -20,28 +20,36 @@
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.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.PricingAddDTO;
import com.xiaopeng.operation.dto.PricingSetPriceDTO;
import com.xiaopeng.operation.dto.PricingSetRuleDTO;
import com.xiaopeng.operation.dto.XpOrderDto;
import com.xiaopeng.api.model.operation.XpGeoQueryEntity;
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.utils.XpPricingUtil;
import com.xiaopeng.operation.utils.holiday.HolidayUtil;
import com.xiaopeng.operation.utils.map.FastDistanceCalculator;
import com.xiaopeng.operation.vo.XpPricingCardVo;
import com.xiaopeng.operation.vo.PricingPageListVO;
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;
@@ -89,11 +97,6 @@
    @Resource
    private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper;
    @Resource
    private XpVehicleClient xpVehicleClient;
    @Resource
    private XpPointMapper xpPointMapper;
    @Resource
    private XpWhitelistMapper xpWhitelistMapper;
@@ -109,6 +112,9 @@
    @Autowired
    private XpElectronicFenceMapper xpElectronicFenceMapper;
    @Autowired
    private XpPointService xpPointService;
    // 自定义线程池用于并行执行任务
@@ -198,13 +204,15 @@
//            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
//        }
        // 2. 计算价格(需要在并行任务前完成,因为优惠券查询需要价格)
        // 2. 计算价格(需要在并行任务前完成,因为优惠券查询需要价格) TODO 判断是动态还是一口价
//        XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationPrice(
//                JSON.parseObject(xpPricing.getBasicPricingJson()),
//                JSON.parseArray(xpPricing.getStartingFareJson()),
//                1, 1, 0, 0.0
//        );
        XpPricingCardVo xpPricingCardVo = new XpPricingCardVo();
        // 3. 并行执行远程服务调用 判断是否同一经营区域
        CompletableFuture<R<Boolean>> diagnosisAreaFuture = CompletableFuture.supplyAsync(
@@ -272,7 +280,36 @@
        // 8. 构建响应对象
        RouteAndFareVo routeAndFareVo = new RouteAndFareVo();
//        routeAndFareVo.setRoute(routeVo);
        // 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);
@@ -333,7 +370,7 @@
            return 1;
        } else {
            // 已完成接单的数据
            List<XpGeoQueryEntity> validEntities = entities.stream().filter(e -> e.getStatus() > 1).collect(Collectors.toList());
            List<XpGeoQueryEntity> validEntities = entities.stream().filter(e -> e.getStatus() == 1).collect(Collectors.toList());
            // 计算平均等待时间
            OptionalDouble averageOptional = validEntities.stream().mapToDouble(XpGeoQueryEntity::getSeconds).average();
@@ -347,6 +384,8 @@
                    double waitMinutes = (average * count) / 60;
                    if (waitMinutes > 3) {
                        throw new ServiceException("当前区域正在等待中,请耐心等待", I18nKeyConstant.WAITING_TIME);
                    }else if(waitMinutes==0){
                        return 1;
                    }
                    return (int) waitMinutes;
                }
@@ -359,6 +398,9 @@
    @Override
    public Long saveOrder(XpOrderDto dto, String uid) {
        // 是否在运营区域时间内
        XpElectronicFenceArea xpElectronicFenceArea = xpElectronicFenceAreaMapper.selectById(dto.getXpElectronicFenceAreaId());
        XpElectronicFence xpElectronicFence = xpElectronicFenceMapper.selectById(xpElectronicFenceArea.getElectronicId());
@@ -377,10 +419,21 @@
        }
        // 获取起点经纬度
        XpPoint xpPoint = xpPointMapper.selectById(dto.getStartPointId());
        XpPoint xpPoint = xpPointService.getById(dto.getStartPointId());
        // 终点
        XpPoint endXpPoint = xpPointMapper.selectById(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);
        }
        // 判断当天取消的次数 后台配置 redis 查询
        Object cancelOrder = redisService.getCacheObject("CANCEL_ORDER_" + uid);
@@ -415,6 +468,8 @@
        // 生成订单数据
        XpOrder xpOrder = new XpOrder();
        xpOrder.setVehicleId(1l);
//        xpOrder.setStartMileage(Double.valueOf(startMileage));
@@ -517,6 +572,16 @@
                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;
            }
        }
@@ -525,6 +590,18 @@
        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);
        geoQueryEntityService.save(xpGeoQueryEntity);
        return orderId;
//
@@ -538,6 +615,7 @@
//        return null;
    }
    private Boolean isInOperationTime(String operationStrategy) {
@@ -583,5 +661,78 @@
        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 void 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().intValue());
        pricing.setRemark(dto.getRemark());
        pricing.setPricingType(dto.getPricingType());
        pricing.setBasicPricingJson("{}");
        pricing.setStartingFareJson("[]");
        pricing.setDynamicPricingJson("{}");
        pricing.setIsDynamicPrice(0);
        this.save(pricing);
    }
    @Override
    public void setPrice(PricingSetPriceDTO dto) {
        XpPricing pricing = this.getById(dto.getId());
        if (pricing == null) {
            throw new ServiceException("定价不存在", I18nKeyConstant.PRICING_NOT_EXIST);
        }
        try {
            JSONObject.parseObject(dto.getBasicPricingJson());
            JSONArray.parseArray(dto.getStartingFareJson());
            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(dto.getStartingFareJson());
        update.setDynamicPricingJson(dto.getDynamicPricingJson());
        this.updateById(update);
    }
    @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);
    }
}