liujie
2026-06-09 c96fbe6b0aeb413a17c429133ee6d81d138144b9
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java
@@ -1,48 +1,63 @@
package com.xiaopeng.operation.service.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import cn.hutool.core.lang.Snowflake;
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.query.QueryWrapper;
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.maintenance.XpVehicleModelClient;
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.maintenance.XpVehicleModel;
import com.xiaopeng.api.model.operation.*;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
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.XpOrderDto;
import com.xiaopeng.operation.entity.XpGeoQueryEntity;
import com.xiaopeng.operation.dto.*;
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.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.PricingInfoVo;
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;
@@ -56,6 +71,7 @@
 * @author liu
 * @since 2026-02-03
 */
@Slf4j
@Service
public class XpPricingServiceImpl extends ServiceImpl<XpPricingMapper, XpPricing> implements XpPricingService {
@@ -90,12 +106,9 @@
    @Resource
    private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper;
    @Resource
    private XpVehicleClient xpVehicleClient;
    @Resource
    private XpPointMapper xpPointMapper;
    private XpVehicleModelClient xpVehicleModelClient;
    @Resource
    private XpWhitelistMapper xpWhitelistMapper;
@@ -110,6 +123,12 @@
    @Autowired
    private XpElectronicFenceMapper xpElectronicFenceMapper;
    @Autowired
    private XpPointService xpPointService;
    @Resource(name = "hutoolSnowflake")
    private Snowflake snowflake;
    // 自定义线程池用于并行执行任务
@@ -168,6 +187,7 @@
    @Override
    @Deprecated
    public RouteAndFareVo getRouteAndFare(RouteAndFareDto routeAndFareDto, String userId) {
@@ -195,17 +215,38 @@
        }
        XpPricing xpPricing = this.baseMapper.selectOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, routeAndFareDto.getOpenCityId()));
//        if (xpPricing == null) {
//            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
//        }
        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 = xpPricingUtil.calculationPrice(
//                JSON.parseObject(xpPricing.getBasicPricingJson()),
//                JSON.parseArray(xpPricing.getStartingFareJson()),
//                1, 1, 0, 0.0
//        );
        XpPricingCardVo xpPricingCardVo = new XpPricingCardVo();
        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(
@@ -213,19 +254,15 @@
                executorService
        );
//        // 算距离
//        CompletableFuture<R<RouteVo>> routeFuture = CompletableFuture.supplyAsync(
//                () -> xpMapClient.getDistanceAndDuration(routeAndFareDto),
//                executorService
//        );
        // 优惠券
        XpPricingCardVo finalXpPricingCardVo = xpPricingCardVo;
        CompletableFuture<R<UserCouponVo>> couponFuture = CompletableFuture.supplyAsync(
                () -> {
                    // 优惠券
                    return xpAppUserCouponClient.getUserCoupon(
//                                new GetUserCouponDto(Long.valueOf(userId), xpPricingCardVo.getTotalPrice(),routeAndFareDto.getOpenCityId())
                            new GetUserCouponDto(Long.valueOf(userId), 1d, routeAndFareDto.getOpenCityId())
                                new GetUserCouponDto(Long.valueOf(userId), finalXpPricingCardVo.getTotalPrice(),routeAndFareDto.getOpenCityId(),null)
//                            new GetUserCouponDto(Long.valueOf(userId), 1d, routeAndFareDto.getOpenCityId())
                    );
                },
                executorService
@@ -233,7 +270,6 @@
        // 4. 等待所有并行任务完成并获取结果
        R<Boolean> diagnosisResult = diagnosisAreaFuture.join();
//        R<RouteVo> routeResult = routeFuture.join();
        R<UserCouponVo> couponResult = couponFuture.join();
        // 5. 处理诊断结果
@@ -244,7 +280,7 @@
            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
        }
        // 6. 处理路线结果
//        // 6. 处理路线结果
//        if (routeResult.getCode() != 200) {
//            throw new RuntimeException(routeResult.getMsg());
//        }
@@ -258,9 +294,9 @@
        } else {
            minOrderDistance = systemSettingMapper.selectOne(null).getMinOrderDistance();
        }
//        if (routeVo.getDistance() < Integer.valueOf(minOrderDistance)) {
//            throw new ServiceException("当前上下车点距离过近,建议步行前往", I18nKeyConstant.TO_WORK);
//        }
        if (routeVo.getDistance() < Integer.valueOf(minOrderDistance)) {
            throw new ServiceException("当前上下车点距离过近,建议步行前往", I18nKeyConstant.TO_WORK);
        }
        // 7. 处理优惠券结果
        if (couponResult.getCode() != R.SUCCESS) {
@@ -273,16 +309,46 @@
        // 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);
//        fareVo.setEstimatedDurationMin(routeVo.getDuration() / 60);
        fareVo.setEstimatedDistanceKm(routeVo.getDistance().doubleValue() / 1000);
        fareVo.setEstimatedDurationMin(routeVo.getDuration() / 60);
        // 9. 设置费用信息
        fareVo.setOriginalFare(xpPricingCardVo.getTotalPrice());
        if (userCouponVo != null) {
            fareVo.setDiscountAmount(userCouponVo.getMoney());
            fareVo.setCouponType(userCouponVo.getCouponType());
            fareVo.setFinalFare(fareVo.getOriginalFare() - fareVo.getDiscountAmount());
            fareVo.setCouponId(userCouponVo.getId());
        } else {
@@ -291,11 +357,11 @@
        }
        // 10. 检查等待时间
        Integer waitTime = getWaitTime(routeAndFareDto.getOrigin().getLongitude(), routeAndFareDto.getOrigin().getLatitude());
        if (waitTime > 3) {
            throw new ServiceException("当前区域正在等待中,请耐心等待", I18nKeyConstant.WAITING_TIME);
        }
        fareVo.setWaitingTime(waitTime);
//        Integer waitTime = getWaitTime(routeAndFareDto.getOrigin().getLongitude(), routeAndFareDto.getOrigin().getLatitude());
//        if (waitTime > 3) {
//            throw new ServiceException("当前区域正在等待中,请耐心等待", I18nKeyConstant.WAITING_TIME);
//        }
//        fareVo.setWaitingTime(waitTime);
        // 11. 生成订单ID并缓存
@@ -334,7 +400,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();
@@ -348,6 +414,8 @@
                    double waitMinutes = (average * count) / 60;
                    if (waitMinutes > 3) {
                        throw new ServiceException("当前区域正在等待中,请耐心等待", I18nKeyConstant.WAITING_TIME);
                    } else if (waitMinutes == 0) {
                        return 1;
                    }
                    return (int) waitMinutes;
                }
@@ -360,6 +428,7 @@
    @Override
    public Long saveOrder(XpOrderDto dto, String uid) {
        // 是否在运营区域时间内
        XpElectronicFenceArea xpElectronicFenceArea = xpElectronicFenceAreaMapper.selectById(dto.getXpElectronicFenceAreaId());
        XpElectronicFence xpElectronicFence = xpElectronicFenceMapper.selectById(xpElectronicFenceArea.getElectronicId());
@@ -374,24 +443,31 @@
        // 判断是否有进行的订单
        R<Long> r = xpOrderClient.hasUnPaodOrder(uid);
        if (r.getData() > 0) {
            throw new RuntimeException("有正在进行的订单");
            throw new ServiceException("有正在进行的订单", I18nKeyConstant.HAS_UNPAID_ORDER);
        }
        // 获取起点经纬度
        XpPoint xpPoint = xpPointMapper.selectById(dto.getStartPointId());
        XpPoint xpPoint = xpPointService.getById(dto.getStartPointId());
        // 终点
        XpPoint endXpPoint = xpPointMapper.selectById(dto.getStartPointId());
        XpPoint endXpPoint = xpPointService.getById(dto.getStartPointId());
        // 判断当天取消的次数 后台配置 redis 查询
        Object cancelOrder = redisService.getCacheObject("CANCEL_ORDER_" + uid);
        // 判断点位是否在运营区域内
        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;
        if (cancelOrder != null) {
            Integer num = Integer.valueOf(cancelOrder.toString());
            if (num >= cancelCount) {
                isSilent =true;
            }
        Integer num = xpOrderClient.todayCancellationCount(Long.valueOf(uid)).getData();
        if (num >= cancelCount) {
            isSilent = true;
        }
@@ -405,22 +481,24 @@
        // 根据id获取手机号
        Boolean isPriorityDispatch = false;
        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;
            }
        }
        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.setOrderNumber(getOrderNumber());
        xpOrder.setPlacementLon(dto.getPlacementLon());
        xpOrder.setPlacementLat(dto.getPlacementLat());
        xpOrder.setPlacementAddress(dto.getPlacementAddress());
@@ -444,6 +522,8 @@
        xpOrder.setEndPointId(dto.getEndPointId());
        xpOrder.setStartPointName(xpPoint.getPointName());
        xpOrder.setEndPointName(endXpPoint.getPointName());
        xpOrder.setXpuStartTaskId(snowflake.nextId() + "");
        xpOrder.setXpuEndTaskId(snowflake.nextId() + "");
        // 当前时段
        String currentTimeSlot = DateUtils.getCurrentTimeSlot();
@@ -487,7 +567,7 @@
        Long orderId = addOrderR.getData();
        // 静默处理 不推单
        if(isSilent){
        if (isSilent) {
            return orderId;
        }
@@ -515,12 +595,39 @@
                    // 尾部
                    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;
//
@@ -535,6 +642,11 @@
    }
    private String getOrderNumber(){
        int random = Double.valueOf(Math.random() * 1000).intValue();
        return "XP" + random + System.currentTimeMillis();
    }
    private Boolean isInOperationTime(String operationStrategy) {
        JSONArray jsonArray = JSONArray.parseArray(operationStrategy);
@@ -579,5 +691,223 @@
        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());
        if(page.getRecords() != null){
            Set<String> vehicleModelIds = page.getRecords().stream().map(PricingPageListVO::getVehicleModel).collect(Collectors.toSet());
            R<List<XpVehicleModel>> listR = xpVehicleModelClient.listByIds(vehicleModelIds);
            if(listR != null && listR.getData() != null){
                List<XpVehicleModel> vehicleModels = listR.getData();
                Map<String, XpVehicleModel> vehicleModelMap = new HashMap<>();
                for (XpVehicleModel vehicleModel : vehicleModels) {
                    vehicleModelMap.put(vehicleModel.getId().toString(), vehicleModel);
                }
                for (PricingPageListVO record : page.getRecords()) {
                    XpVehicleModel xpVehicleModel = vehicleModelMap.get(record.getVehicleModel());
                    if(xpVehicleModel != null){
                        record.setVehicleModelName(xpVehicleModel.getName());
                    }
                }
            }
        }
        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);
    }
    @Override
    public void savePrice(PricingInfoVo dto, XpOfficialPartner officialPartner) {
        XpOpenCity openCity = xpOpenCityMapper.selectOne(new QueryWrapper<XpOpenCity>().eq("city_code", dto.getCityCode()));
        if (openCity == null) {
            throw new ServiceException("城市未开通");
        }
        XpPricing pricing = this.lambdaQuery()
                .eq(XpPricing::getVehicleModel, dto.getVehicleModel())
                .eq(XpPricing::getChannel, officialPartner.getBrandName())
                .eq(XpPricing::getCityId, openCity.getId().intValue())
                .eq(XpPricing::getOfficialPartnerId, officialPartner.getId())
                .one();
        if(null == pricing){
            pricing = new XpPricing();
            pricing.setOfficialPartnerId(officialPartner.getId());
            pricing.setVehicleModel(dto.getVehicleModel());
            pricing.setChannel(officialPartner.getBrandName());
            pricing.setCityId(openCity.getId());
            pricing.setRemark(dto.getRemark());
            pricing.setPricingType(dto.getPricingType());
            pricing.setBasicPricingJson("{}");
            pricing.setStartingFareJson("[]");
            pricing.setDynamicPricingJson("{}");
            pricing.setIsDynamicPrice(0);
            this.save(pricing);
        }
        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(pricing.getId());
        update.setBasicPricingJson(dto.getBasicPricingJson());
        update.setStartingFareJson(StringUtils.isNotBlank(dto.getStartingFareJson()) ? dto.getStartingFareJson() : null);
        update.setDynamicPricingJson(dto.getDynamicPricingJson());
        update.setIsDynamicPrice(dto.getIsDynamicPrice());
        update.setPriceRule(dto.getPriceRule());
        this.updateById(update);
        // 动态调价 Quartz 任务调度
        String jobName = DynamicPricingCheckJob.JOB_PREFIX + pricing.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", pricing.getId());
                QuartzManager.addJob(DynamicPricingCheckJob.class, jobName,
                        TimeJobType.DYNAMIC_PRICING_CHECK, triggerTime, jobData);
                log.info("动态调价任务已注册 pricingId={}, 供需计算周期={}分钟", pricing.getId(), periodMinutes);
            }
        } else {
            // 关闭动态调价时,移除已存在的任务
            QuartzManager.removeJob(jobName, TimeJobType.DYNAMIC_PRICING_CHECK);
            log.info("动态调价任务已移除 pricingId={}", pricing.getId());
        }
    }
}