From 1417e16294b7939cf57f25f16d8f6a0d252b149e Mon Sep 17 00:00:00 2001
From: 无关风月 <443237572@qq.com>
Date: 星期五, 13 二月 2026 11:38:28 +0800
Subject: [PATCH] 费用计算

---
 xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/holiday/HolidayUtil.java        |    3 
 xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/GpsPoint.java                   |   13 
 xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownDetailVo.java            |    4 
 xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/dto/MileageDto.java                   |   19 
 xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java  |  654 ++++++++++++++++++++++++++++----
 xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/utils/DateUtils.java |    6 
 xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingService.java         |  185 +++++++++
 xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/XpPricingUtil.java              |  296 --------------
 8 files changed, 801 insertions(+), 379 deletions(-)

diff --git a/xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownDetailVo.java b/xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownDetailVo.java
index fc809b6..30185fb 100644
--- a/xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownDetailVo.java
+++ b/xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownDetailVo.java
@@ -9,14 +9,14 @@
 @Data
 @ApiModel("订单费用明细详情VO")
 public class FareBreakdownDetailVo {
-    @ApiModelProperty(value = "type=1 普通时段 type=2 夜间行驶时段 type=3节假日时段")
+    @ApiModelProperty(value = "type=1 普通时段 type=2 夜间行驶时段 3高峰时段 4节假日时段")
     private Integer type;
     @ApiModelProperty(value = "时段 例:18:00-19:00")
     private String time;
     @ApiModelProperty(value = "里程 单位公里")
     private BigDecimal mileage;
     @ApiModelProperty(value = "时间")
-    private BigDecimal duration;
+    private long duration;
     @ApiModelProperty(value = "金额")
     private BigDecimal money;
 }
diff --git a/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/utils/DateUtils.java b/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/utils/DateUtils.java
index 7c70e5e..15c85f4 100644
--- a/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/utils/DateUtils.java
+++ b/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/utils/DateUtils.java
@@ -214,6 +214,12 @@
         ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
         return Date.from(zdt.toInstant());
     }
+    /**
+     * 增加 Date ==> LocalDate
+     */
+    public static LocalDate toLocalDate(Date date){
+        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+    }
 
     /**
      * 计算两个日期之间相差的天数
diff --git a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/dto/MileageDto.java b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/dto/MileageDto.java
new file mode 100644
index 0000000..2d26a66
--- /dev/null
+++ b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/dto/MileageDto.java
@@ -0,0 +1,19 @@
+package com.xiaopeng.order.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel(value = "计算里程费")
+public class MileageDto {
+    @ApiModelProperty(value = "时段标识")
+    private Integer mileageTypeTemp;
+    @ApiModelProperty(value = "num6")
+    private String mileage;
+
+    @ApiModelProperty(value = "num7")
+    private String time;
+}
diff --git a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
index 26bd344..30ec0e6 100644
--- a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
+++ b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -2,6 +2,7 @@
 
 import cn.hutool.core.bean.BeanUtil;
 import com.alibaba.fastjson.JSON;
+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;
@@ -47,6 +48,7 @@
 import com.xiaopeng.common.redis.service.RedisService;
 import com.xiaopeng.common.security.utils.SecurityUtils;
 import com.xiaopeng.order.constant.I18nKeyConstant;
+import com.xiaopeng.order.dto.MileageDto;
 import com.xiaopeng.order.job.jobs.EndTripJob;
 import com.xiaopeng.order.mangodb.model.OrderTrajectory;
 import com.xiaopeng.order.mangodb.service.OrderTrajectoryService;
@@ -64,6 +66,8 @@
 import com.xiaopeng.order.job.base.QuartzManager;
 import com.xiaopeng.order.job.base.TimeJobType;
 import com.xiaopeng.order.job.jobs.OrderCancelJob;
+import com.xiaopeng.order.utils.TaxiPricingService;
+import com.xiaopeng.order.utils.holiday.HolidayUtil;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -80,11 +84,10 @@
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.time.Duration;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
+import java.text.SimpleDateFormat;
+import java.time.*;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -118,6 +121,8 @@
 
     @Autowired
     private MongoTemplate mongoTemplate;
+    @Autowired
+    private HolidayUtil holidayUtil;
 //    @Autowired
 //    private XpPricingUtil xpPricingUtil;
 
@@ -847,6 +852,14 @@
 
     @Override
     public void endTrip(Long orderId, String uid) {
+        // 起步价格明细
+        List<FareBreakdownDetailVo> start = new ArrayList<>();
+        // 里程费价格明细
+        List<FareBreakdownDetailVo> mileage = new ArrayList<>();
+        // 时长费价格明细
+        List<FareBreakdownDetailVo> duration = new ArrayList<>();
+        // 远途费价格明细
+        List<FareBreakdownDetailVo> longDistance = new ArrayList<>();
         XpOrder order = this.getById(orderId);
         if (order == null) {
             throw new RuntimeException("订单不存在");
@@ -856,77 +869,8 @@
             throw new RuntimeException("订单状态错误");
         }
         order.setStatus(8);
-        // 计算实际价格
-        // 根据订单所属开通城市id 查询定价
-        R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCityId(order.getCityId());
-        XpPricing pricing = pricingByCityId.getData();
-        if (pricing == null) {
-            throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS);
-        }
-        // 查询订单轨迹
-        List<OrderTrajectory> trajectories = mongoTemplate.find(
-                new Query(Criteria.where("orderId").is(orderId))
-                        .with(Sort.by(Sort.Direction.ASC, "locatetime")),
-                OrderTrajectory.class
-        );
-        // 计算等待时间
-        LocalDateTime boardingTime = order.getBoardingTime();// 上车时间
-        LocalDateTime arrivalTime = order.getArrivalTime();// 车辆到达上车点时间
-        // 行程开始时间
-        Integer waitTime =Math.abs( (int) Duration.between(boardingTime, arrivalTime).getSeconds());
-        // 计算订单总行驶距离,单位米
-        Double totalDistance = 0.00D;
-        // 行程总时间 秒
-        LocalDateTime tripBegin = order.getTripBegin();
-        LocalDateTime tripEnd = order.getTripEnd();
-        long seconds = Duration.between(tripBegin, tripEnd).getSeconds();
-        for (OrderTrajectory trajectory : trajectories) {
-
-        }
-//        XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationPrice(
-//                order.getAppUserId().toString(),JSON.parseObject(pricing.getBasicPricingJson()),
-//                JSON.parseArray(pricing.getStartingFareJson()),
-//                pricing.getDynamicPricingJson(),
-//                pricing.getIsDynamicPrice(),
-//                totalDistance,
-//                seconds,
-//                order.getCityId()
-//        );
-
-//        Long id = pricing.getId();
-//        Integer pricingType = pricing.getPricingType();
-//        String basicPricingJson = pricing.getBasicPricingJson();
-//        String startingFareJson = pricing.getStartingFareJson();
-//        String dynamicPricingJson = pricing.getDynamicPricingJson();
-//        String priceRule = pricing.getPriceRule();
-//        // 根据计价规则计算费用
-//        BigDecimal startMoney = BigDecimal.ZERO;// 起步价
-//        BigDecimal mileageMoney = BigDecimal.ZERO;// 里程费
-//        BigDecimal durationMoney = BigDecimal.ZERO;// 时长费
-//        BigDecimal waitMoney = BigDecimal.ZERO;// 等待费
-//        BigDecimal longDistancemoney = BigDecimal.ZERO;// 远途费
-//        BigDecimal redPacketMoney = BigDecimal.ZERO;// 红包抵扣金额
-//        BigDecimal couponMoney = BigDecimal.ZERO;// 优惠券抵扣金额
-//        if (pricingType == 1) {
-//
-//        } else {
-//
-//        }
-//        if (org.springframework.util.StringUtils.hasLength(basicPricingJson)) {
-//
-//        }
-//        if (org.springframework.util.StringUtils.hasLength(startingFareJson)) {
-//
-//        }
-//        if (org.springframework.util.StringUtils.hasLength(dynamicPricingJson)) {
-//
-//        }
-//        if (org.springframework.util.StringUtils.hasLength(priceRule)) {
-//
-//        }
-
-                // 计算完价格后 挑选优惠券
-                GetUserCouponDto getUserCouponDto = new GetUserCouponDto();
+        // 计算完价格后 挑选优惠券
+        GetUserCouponDto getUserCouponDto = new GetUserCouponDto();
         getUserCouponDto.setUserId(Long.valueOf(uid));
         getUserCouponDto.setTotalAmount(order.getOrderMoney().doubleValue());
         R<UserCouponVo> userCouponVoR = xpAppUserCouponClient.getUserCoupon(getUserCouponDto);
@@ -948,7 +892,557 @@
 
             userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo);
         }
+//        // 计算实际价格
+//        // 根据订单所属开通城市id 查询定价
+//        R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCityId(order.getCityId());
+//        XpPricing pricing = pricingByCityId.getData();
+//        if (pricing == null) {
+//            throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS);
+//        }
+//        // 查询订单轨迹
+//        List<OrderTrajectory> trajectories = mongoTemplate.find(
+//                new Query(Criteria.where("orderId").is(orderId))
+//                        .with(Sort.by(Sort.Direction.ASC, "locatetime")),
+//                OrderTrajectory.class
+//        );
+//        // 计算等待时间
+//        LocalDateTime boardingTime = order.getBoardingTime();// 上车时间
+//        LocalDateTime arrivalTime = order.getArrivalTime();// 车辆到达上车点时间
+//        // 行程开始时间
+//        Integer waitTime = Math.abs((int) Duration.between(boardingTime, arrivalTime).getSeconds());
+//
+//        // 行程总时间 秒
+//        LocalDateTime tripBegin = order.getTripBegin();
+//        LocalDateTime tripEnd = order.getTripEnd();
+//        long seconds = Duration.between(tripBegin, tripEnd).getSeconds();
+//        // 默认费率1
+//        double priceRate = 1;
+//        if (pricing.getIsDynamicPrice() == 1) {
+//            // 先判断是否已经调价过了
+//            Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid);
+//            if (userDynamicPriceObj != null) {
+//                // 是否调价过了
+//                priceRate = Double.parseDouble(userDynamicPriceObj.toString());
+//            } else {
+//                // 没有调过
+//                //是否触发动态调价 redis获取
+//                Object dynamicPriceObj = redisService.getCacheObject("DYNAMIC_PRICE_" + order.getCityId());
+//                if (dynamicPriceObj != null) {
+//                    JSONObject jsonObject = JSONObject.parseObject(pricing.getDynamicPricingJson());
+//                    // 触发动态调价
+//                    // value=时间
+//                    String timeStr = dynamicPriceObj.toString();
+//                    // 判断当前距离这个时间范围的新增订单数(排除取消) / 上浮数量 算出应该增加的价格率
+//                    Integer num = this.getOrderNum(new GetOrderNumDto(timeStr, order.getCityId()));
+//                    // X单上升一次调价
+//                    Integer orderNum = getOrderNum(jsonObject, 4);
+//                    // 需要调价的次数
+//                    int count = num / orderNum;
+//                    if (count > 0) {
+//                        // 计算每次上升的费率
+//                        Integer upRate = getOrderNum(jsonObject, 5);
+//                        BigDecimal divide = new BigDecimal(upRate).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
+//                        for (int i = count; i > 0; i--) {
+//                            // 当前费率+(费率*上升百分比)
+//                            priceRate = new BigDecimal(priceRate).multiply(divide).add(new BigDecimal(priceRate)).setScale(2, RoundingMode.HALF_UP).doubleValue();
+//                        }
+//                        // 冷却时间
+//                        Integer time = getOrderNum(jsonObject, 6);
+//                        // 触发调价了
+//                        redisService.setCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid, priceRate, time.longValue(), TimeUnit.MINUTES);
+//                    }
+//
+//                }
+//            }
+//        }
+//        if (trajectories.isEmpty()) {
+//            return;
+//        }
+//        // 起步价规则
+//        String startingFareJson = pricing.getStartingFareJson();
+//        JSONArray startingFareJsonArray = JSONArray.parseArray(startingFareJson);
+//        // 基础信息规则
+//        String basicPricing = pricing.getBasicPricingJson();
+//        JSONObject basicPricingJson = JSONObject.parseObject(basicPricing);
+//
+//        // 计算过后得到起步价
+//        String num3 = "";
+//        String num4 = "";
+//        String num5 = "";
+//        String num6 = "";
+//        String num7 = "";
+//        String startTime = "";
+//        String endTime = "";
+//        // 等待费设置
+//        String num4Wait = basicPricingJson.getString("num4");
+//        String num5Wait = basicPricingJson.getString("num5");
+//        // 远途费设置
+//        String numLong6 = basicPricingJson.getString("num6");
+//        String numLong7 = basicPricingJson.getString("num7");
+//        String numLong8 = basicPricingJson.getString("num8");
+//        String numLong9 = basicPricingJson.getString("num9");
+//        String numLong10 = basicPricingJson.getString("num10");
+//        String numLong11 = basicPricingJson.getString("num11");
+//        String numLong12 = basicPricingJson.getString("num12");
+//        String numLong13 = basicPricingJson.getString("num13");
+//        String numLong14 = basicPricingJson.getString("num14");
+//        String numLong15 = basicPricingJson.getString("num15");
+//        // 节假日基础价格规则
+//        String num29 = basicPricingJson.getString("num29");
+//        String num30 = basicPricingJson.getString("num30");
+//        String num31 = basicPricingJson.getString("num31");
+//        String num32 = basicPricingJson.getString("num32");
+//        String num33 = basicPricingJson.getString("num33");
+//        String num34 = basicPricingJson.getString("num34");
+//
+//        // 高峰期基础价格规则
+//        String num21 = basicPricingJson.getString("num21");
+//        String num22 = basicPricingJson.getString("num22");
+//        String num23 = basicPricingJson.getString("num23");
+//        String num24 = basicPricingJson.getString("num24");
+//        String num25 = basicPricingJson.getString("num25");
+//        String num26 = basicPricingJson.getString("num26");
+//        String num27 = basicPricingJson.getString("num27");
+//        String num28 = basicPricingJson.getString("num28");
+//        // 夜间行驶基础价格规则
+//        String num14 = basicPricingJson.getString("num14");
+//        String num15 = basicPricingJson.getString("num15");
+//        String num16 = basicPricingJson.getString("num16");
+//        String num17 = basicPricingJson.getString("num17");
+//        String num18 = basicPricingJson.getString("num18");
+//        String num19 = basicPricingJson.getString("num19");
+//        String num20 = basicPricingJson.getString("num20");
+//        // 起步里程
+//        Double startDistance = 0.00D;
+//        // 临时超出起步里程的额外里程
+//        Double mileageDistance = 0.00D;
+//        // 起步价所属时段 type 1 普通时段 2 夜间行驶时段 3 高峰时段 4 节假日时段
+//        Integer startType = 1;
+//        // 里程费所属时段 type 0默认临时 1 普通时段 2 夜间行驶时段 3 高峰时段 4 节假日时段
+//        Integer mileageType = 0;
+//        // 里程费所属时段临时字段 用于区分时段是否变化 type 0默认临时 1 普通时段 2 夜间行驶时段 3 高峰时段 4 节假日时段
+//        Integer mileageTypeTemp = 0;
+//
+//        // 起步价时段开始时间
+//        Date startDate = DateUtils.toDate(order.getTripBegin());
+//        // 根据订单创建时间确定使用哪个起步价规则
+//        Date createTime = DateUtils.toDate(order.getCreateTime());
+//        // 起步价时段结束时间
+//        Date endDate = new Date();
+//        // 计算订单总行驶距离,单位米
+//        Double totalDistance = 0.00D;
+//        // 起步价计算完成表示
+//        boolean startFlag = true;
+//        SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
+//        if (pricing.getPricingType() == 1) {
+//
+//            // 计算起步价
+//            for (Object o : startingFareJsonArray) {
+//                JSONObject startingFare = (JSONObject) o;
+//                Calendar calendarStart = Calendar.getInstance();
+//                calendarStart.setTime(createTime);
+//                calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num1").split(":")[0]));
+//                calendarStart.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num1").split(":")[1]));
+//
+//                Calendar calendarEnd = Calendar.getInstance();
+//                calendarEnd.setTime(createTime);
+//                calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num2").split(":")[0]));
+//                calendarEnd.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num2").split(":")[1]));
+//                // 找到符合条件的直接跳出循环
+//                if (createTime.getTime() > calendarStart.getTimeInMillis() && createTime.getTime() < calendarEnd.getTimeInMillis()) {
+//                    num3 = startingFare.getString("num3");
+//                    num4 = startingFare.getString("num4");
+//                    num5 = startingFare.getString("num5");
+//                    num6 = startingFare.getString("num6");
+//                    num7 = startingFare.getString("num7");
+//                    startTime = startingFare.getString("num1");
+//                    endTime = startingFare.getString("num2");
+//                    break;
+//                }
+//            }
+//            // 判断是否为夜间行驶 起步价修改
+//            Calendar calendarStart = Calendar.getInstance();
+//            calendarStart.setTime(createTime);
+//            calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[0].split(":")[0]));
+//            calendarStart.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[0].split(":")[1]));
+//            Calendar calendarEnd = Calendar.getInstance();
+//            calendarEnd.setTime(createTime);
+//            calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[1].split(":")[0]));
+//            calendarEnd.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[1].split(":")[1]));
+//            if (createTime.getTime() > calendarStart.getTimeInMillis() && createTime.getTime() < calendarEnd.getTimeInMillis()
+//            ) {
+//                num3 = num15;
+//                num6 = num16;
+//                num7 = num17;
+//                startType = 2;
+//
+//            }
+//            // 判断是否为高峰期
+//            Calendar calendar1Start = Calendar.getInstance();
+//            calendar1Start.setTime(createTime);
+//            calendar1Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[0].split(":")[0]));
+//            calendar1Start.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[0].split(":")[1]));
+//
+//            Calendar calendar1End = Calendar.getInstance();
+//            calendar1End.setTime(createTime);
+//            calendar1End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[1].split(":")[0]));
+//            calendar1End.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[1].split(":")[1]));
+//
+//            Calendar calendar2Start = Calendar.getInstance();
+//            calendar2Start.setTime(createTime);
+//            calendar2Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[0]));
+//            calendar2Start.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[1]));
+//
+//            Calendar calendar2End = Calendar.getInstance();
+//            calendar2End.setTime(createTime);
+//            calendar2End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[1].split(":")[0]));
+//            calendar2End.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[1].split(":")[1]));
+//            // 判断是否为高峰期 起步价,里程费,时长费修改
+//            if ((createTime.getTime() > calendar1Start.getTimeInMillis() && createTime.getTime() < calendar1End.getTimeInMillis())
+//                    || (createTime.getTime() > calendar2Start.getTimeInMillis() && createTime.getTime() < calendar2End.getTimeInMillis())) {
+//                num3 = num23;
+//                num6 = num24;
+//                num7 = num25;
+//                startType = 3;
+//            }
+//            // 判断当前是否属于节假日
+//            boolean holiday = holidayUtil.isHoliday(DateUtils.toLocalDate(createTime));
+//            if (holiday) {
+//                // 判断是否为节假日 起步价修改
+//                num3 = num29;
+//                num6 = num30;
+//                num7 = num31;
+//                startType = 4;
+//            }
+//            // 查询起步价结束时间段
+//            for (OrderTrajectory trajectory : trajectories) {
+//                Long locatetime = trajectory.getLocatetime();
+//                endDate = new Date(locatetime);
+//                LocalDateTime end = DateUtils.dateToLocalDateTime(endDate);
+//                if (end.isAfter(tripBegin)) {
+//                    // 行程开始 开始计算里程数
+//                    if (startDistance >= Double.parseDouble(num4)) {
+//                        // 行驶里程大于基础配置里程 那么起步价时段计算完成
+//                        break;
+//                    }
+//                    startDistance += trajectory.getDistance();
+//                }
+//            }
+//            // 起步价价格明细封装
+//            FareBreakdownDetailVo startDetail = new FareBreakdownDetailVo();
+//            startDetail.setType(startType);
+//            startDetail.setTime(timeFormat.format(startDate) + "-" + timeFormat.format(endDate));
+//            startDetail.setMileage(new BigDecimal(startDistance / 1000).setScale(2, RoundingMode.HALF_UP));
+//
+//            // 计算startDate和endDate相差多少分钟
+//            // 将 Date 转换为 LocalDateTime
+//            LocalDateTime startDateTime = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+//            LocalDateTime endDateTime = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+//
+//            // 计算时间差(单位:分钟)
+//            long minutesDifference = Duration.between(startDateTime, endDateTime).toMinutes();
+//            startDetail.setDuration(minutesDifference);
+//            startDetail.setMoney(new BigDecimal(num3));
+//            start.add(startDetail);
+//
+//
+//            // 计算起步价格、里程、时段、行驶时间
+////            calculateStartingFare(trajectories, tripBegin, startFlag, startDistance, startingFareJsonArray, num3, num4, num5, endDate, order, num14, num15, num16, num17, startType, num21, num22, num23, num24, num25, num29, num30, num31, timeFormat, startDate, start);
+//
+//            for (OrderTrajectory trajectory : trajectories) {
+////                FareBreakdownDetailVo mileageDetail = new FareBreakdownDetailVo();
+////                mileageDetail.setType();
+////                mileageDetail.setTime();
+////                mileageDetail.setMileage();
+////                mileageDetail.setDuration();
+////                mileageDetail.setMoney();
+//
+//                Long locatetime = trajectory.getLocatetime();
+//                Date mileageDate = new Date(locatetime);
+//                LocalDateTime begin = DateUtils.dateToLocalDateTime(mileageDate);
+//                if (begin.isAfter(tripBegin)) {
+//                    totalDistance += trajectory.getDistance();
+//                    if (totalDistance >= startDistance) {
+//                        // 开始累加起步里程
+//                        mileageDistance += trajectory.getDistance();
+//                        // 判断当前里程费属于什么时段
+////                        mileageTypeTemp
+//                        MileageDto param = new MileageDto();
+//                        param.setMileage(num6);
+//                        param.setTime(num7);
+//                        param.setMileageTypeTemp(mileageTypeTemp);
+//                        MileageDto mileageDto = mileageType(param,startingFareJsonArray, mileageDate, begin, num14, num16, num17, num21, num22, num24, num25, num30, num31);
+//                        if (mileageType==0){
+//                            // 首次
+//                            mileageType = mileageDto.getMileageTypeTemp();
+//                            mileageTypeTemp= mileageDto.getMileageTypeTemp();
+//                        } else if (mileageType!=mileageTypeTemp){
+//                            // 新时段
+//                            FareBreakdownDetailVo fareBreakdownDetailVo = new FareBreakdownDetailVo();
+//                            fareBreakdownDetailVo.setType(mileageType);
+//                            fareBreakdownDetailVo.setTime(timeFormat.format(endDate)+"-"+timeFormat.format(mileageDate));
+//                            double md = mileageDistance / 1000;
+//                            fareBreakdownDetailVo.setMileage(new BigDecimal(md).setScale(2, RoundingMode.HALF_UP));
+//                            // 计算startDate和endDate相差多少分钟
+//                            // 将 Date 转换为 LocalDateTime
+//                            LocalDateTime startMileageTime = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+//                            LocalDateTime endMileageTime = mileageDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+//
+//                            // 计算时间差(单位:分钟)
+//                            long minutesDifferenceMileage = Duration.between(startMileageTime, endMileageTime).toMinutes();
+//                            startDetail.setDuration(minutesDifference);
+//                            fareBreakdownDetailVo.setDuration(minutesDifferenceMileage);
+//                            // 如果md小数位是0 那么舍弃小数,如果小数位不为0 那么进1
+//                            fareBreakdownDetailVo.setMileage(new BigDecimal(md).setScale(2, RoundingMode.HALF_UP));
+//                            Integer temp = (int)md;
+//                            if (md> temp){
+//                                temp=temp+1;
+//                            }
+//                            // 超出的里程
+//                            fareBreakdownDetailVo.setMoney(new BigDecimal(temp*6));
+//                            mileage.add(fareBreakdownDetailVo);
+//                            // 修改时段标识
+//                            mileageType = mileageTypeTemp;
+//                            // 修改新时段开始时间
+//                            endDate = mileageDate;
+//                        }else{
+//                            // 时段未变化
+//                        }
+//                    }
+//                }
+//            }
+//            // 开始计算里程费用
+//            for (OrderTrajectory trajectory : trajectories) {
+//                Long locatetime = trajectory.getLocatetime();
+//                Date mileageDate = new Date(locatetime);
+//                LocalDateTime begin = DateUtils.dateToLocalDateTime(mileageDate);
+//                // 行驶里程大于起步里程配置 开始计算里程和里程费
+//                if (begin.isAfter(tripBegin)) {
+//                    totalDistance += trajectory.getDistance();
+//                    double v = totalDistance / 1000.0;
+//                    if (v >= Double.parseDouble(num5)) {
+//                        // 增加超出起步里程的里程数
+//                        mileageDistance += trajectory.getDistance();
+//
+//                    }
+//                    if (mileageType == 0) {
+//                        // 第一次计算里程费
+//
+//                    }
+//                }
+//            }
+//        } else {
+//
+//        }
+//
+////        XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationPrice(
+////                order.getAppUserId().toString(),JSON.parseObject(pricing.getBasicPricingJson()),
+////                JSON.parseArray(pricing.getStartingFareJson()),
+////                pricing.getDynamicPricingJson(),
+////                pricing.getIsDynamicPrice(),
+////                totalDistance,
+////                seconds,
+////                order.getCityId()
+////        );
 
+
+
+    }
+
+
+    private MileageDto  mileageType(MileageDto param,JSONArray startingFareJsonArray, Date mileageDate, LocalDateTime begin, String num14, String num16, String num17, String num21, String num22, String num24, String num25, String num30, String num31) {
+        // 计算出是在哪个普通时间段内
+        for (Object o : startingFareJsonArray) {
+            JSONObject startingFare = (JSONObject) o;
+            Calendar calendarStart = Calendar.getInstance();
+            calendarStart.setTime(mileageDate);
+            calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num1").split(":")[0]));
+            calendarStart.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num1").split(":")[1]));
+
+            Calendar calendarEnd = Calendar.getInstance();
+            calendarEnd.setTime(mileageDate);
+            calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num2").split(":")[0]));
+            calendarEnd.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num2").split(":")[1]));
+            // 找到符合条件的直接跳出循环
+            if (mileageDate.getTime() > calendarStart.getTimeInMillis() && mileageDate.getTime() < calendarEnd.getTimeInMillis()) {
+                param.setMileage(startingFare.getString("num6")) ;
+                param.setTime(startingFare.getString("num7"));
+                param.setMileageTypeTemp(1);
+                break;
+            }
+        }
+        // 根据时间确定是否使用基础信息配置
+        LocalDateTime createTime = begin;
+        Date date = DateUtils.toDate(createTime);
+        LocalDate localDate = DateUtils.toLocalDate(date);
+        // 判断是否为夜间行驶 获取里程费用
+        Calendar calendarStart = Calendar.getInstance();
+        calendarStart.setTime(date);
+        calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[0].split(":")[0]));
+        calendarStart.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[0].split(":")[1]));
+        Calendar calendarEnd = Calendar.getInstance();
+        calendarEnd.setTime(date);
+        calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[1].split(":")[0]));
+        calendarEnd.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[1].split(":")[1]));
+        if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()
+        ) {
+
+            param.setMileage(num16) ;
+            param.setTime(num17);
+            param.setMileageTypeTemp(2);
+        }
+        // 判断是否为高峰期 获取里程费用
+        Calendar calendar1Start = Calendar.getInstance();
+        calendar1Start.setTime(date);
+        calendar1Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[0].split(":")[0]));
+        calendar1Start.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[0].split(":")[1]));
+
+        Calendar calendar1End = Calendar.getInstance();
+        calendar1End.setTime(date);
+        calendar1End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[1].split(":")[0]));
+        calendar1End.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[1].split(":")[1]));
+
+        Calendar calendar2Start = Calendar.getInstance();
+        calendar2Start.setTime(date);
+        calendar2Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[0]));
+        calendar2Start.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[1]));
+
+        Calendar calendar2End = Calendar.getInstance();
+        calendar2End.setTime(date);
+        calendar2End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[1].split(":")[0]));
+        calendar2End.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[1].split(":")[1]));
+        if ((date.getTime() > calendar1Start.getTimeInMillis() && date.getTime() < calendar1End.getTimeInMillis())
+                || (date.getTime() > calendar2Start.getTimeInMillis() && date.getTime() < calendar2End.getTimeInMillis())) {
+
+            param.setMileage(num24) ;
+            param.setTime(num25);
+            param.setMileageTypeTemp(3);
+        }
+        // 判断当前是否属于节假日 获取里程费用
+        boolean holiday = holidayUtil.isHoliday(localDate);
+        if (holiday) {
+            // 判断是否为节假日 起步价修改
+            param.setMileage(num30) ;
+            param.setTime(num31);
+            param.setMileageTypeTemp(4);
+        }
+        return param;
+    }
+
+    private void calculateStartingFare(List<OrderTrajectory> trajectories, LocalDateTime tripBegin, boolean startFlag, Double startDistance, JSONArray startingFareJsonArray, String num3, String num4, String num5, Date endDate, XpOrder order, String num14, String num15, String num16, String num17, Integer startType, String num21, String num22, String num23, String num24, String num25, String num29, String num30, String num31, SimpleDateFormat timeFormat, Date startDate, List<FareBreakdownDetailVo> start) {
+        String startTime;
+        String num6;
+        String num7;
+        String endTime;
+        // 根据订单创建时间确定是否使用基础信息配置
+        LocalDateTime createTime = order.getCreateTime();
+        Date date = DateUtils.toDate(createTime);
+        LocalDate localDate = DateUtils.toLocalDate(date);
+        for (Object o : startingFareJsonArray) {
+            JSONObject startingFare = (JSONObject) o;
+            Calendar calendarStart = Calendar.getInstance();
+            calendarStart.setTime(date);
+            calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num1").split(":")[0]));
+            calendarStart.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num1").split(":")[1]));
+
+            Calendar calendarEnd = Calendar.getInstance();
+            calendarEnd.setTime(date);
+            calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num2").split(":")[0]));
+            calendarEnd.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num2").split(":")[1]));
+            // 找到符合条件的直接跳出循环
+            if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) {
+                num3 = startingFare.getString("num3");
+                num4 = startingFare.getString("num4");
+                num5 = startingFare.getString("num5");
+                num6 = startingFare.getString("num6");
+                num7 = startingFare.getString("num7");
+                startTime = startingFare.getString("num1");
+                endTime = startingFare.getString("num2");
+                break;
+            }
+        }
+        // 判断是否为夜间行驶 起步价修改
+        Calendar calendarStart = Calendar.getInstance();
+        calendarStart.setTime(date);
+        calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[0].split(":")[0]));
+        calendarStart.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[0].split(":")[1]));
+        Calendar calendarEnd = Calendar.getInstance();
+        calendarEnd.setTime(date);
+        calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[1].split(":")[0]));
+        calendarEnd.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[1].split(":")[1]));
+        if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()
+        ) {
+            num3 = num15;
+            num6 = num16;
+            num7 = num17;
+            startType = 2;
+
+        }
+        // 判断是否为高峰期
+        Calendar calendar1Start = Calendar.getInstance();
+        calendar1Start.setTime(date);
+        calendar1Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[0].split(":")[0]));
+        calendar1Start.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[0].split(":")[1]));
+
+        Calendar calendar1End = Calendar.getInstance();
+        calendar1End.setTime(date);
+        calendar1End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[1].split(":")[0]));
+        calendar1End.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[1].split(":")[1]));
+
+        Calendar calendar2Start = Calendar.getInstance();
+        calendar2Start.setTime(date);
+        calendar2Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[0]));
+        calendar2Start.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[1]));
+
+        Calendar calendar2End = Calendar.getInstance();
+        calendar2End.setTime(date);
+        calendar2End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[1].split(":")[0]));
+        calendar2End.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[1].split(":")[1]));
+        // 判断是否为高峰期 起步价,里程费,时长费修改
+        if ((date.getTime() > calendar1Start.getTimeInMillis() && date.getTime() < calendar1End.getTimeInMillis())
+                || (date.getTime() > calendar2Start.getTimeInMillis() && date.getTime() < calendar2End.getTimeInMillis())) {
+            num3 = num23;
+            num6 = num24;
+            num7 = num25;
+            startType = 3;
+        }
+        // 判断当前是否属于节假日
+        boolean holiday = holidayUtil.isHoliday(localDate);
+        if (holiday) {
+            // 判断是否为节假日 起步价修改
+            num3 = num29;
+            num6 = num30;
+            num7 = num31;
+            startType = 4;
+        }
+        // 起步价价格明细封装
+        FareBreakdownDetailVo fareBreakdownDetailVo = new FareBreakdownDetailVo();
+        fareBreakdownDetailVo.setType(startType);
+        fareBreakdownDetailVo.setTime(timeFormat.format(startDate) + "-" + timeFormat.format(endDate));
+        fareBreakdownDetailVo.setMileage(new BigDecimal(startDistance / 1000).setScale(2, RoundingMode.HALF_UP));
+
+        // 计算startDate和endDate相差多少分钟
+        // 将 Date 转换为 LocalDateTime
+        LocalDateTime startDateTime = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+        LocalDateTime endDateTime = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+
+        // 计算时间差(单位:分钟)
+        long minutesDifference = Duration.between(startDateTime, endDateTime).toMinutes();
+        fareBreakdownDetailVo.setDuration(minutesDifference);
+        fareBreakdownDetailVo.setMoney(new BigDecimal(num3));
+        start.add(fareBreakdownDetailVo);
+    }
+
+    /**
+     * 获取配置  4每X单  5上升X%  6冷却
+     *
+     * @param rule
+     * @param num
+     * @return
+     */
+    private Integer getOrderNum(JSONObject rule, int num) {
+        Integer param = rule.getInteger("num" + num);
+        return param;
     }
 
     @Override
@@ -1046,7 +1540,7 @@
         // 计算距离
         // 获取上一次定位
         String oldLocation = redisService.getCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId());
-        if(StringUtils.isEmpty(oldLocation)){
+        if (StringUtils.isEmpty(oldLocation)) {
             // 首次定位
             xpOrder.setMileageKilometers(0D);
             this.updateById(xpOrder);
@@ -1055,11 +1549,11 @@
             orderTrajectoryService.create(orderTrajectory);
             // 存储首次定位
             redisService.setCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId(), orderTrajectory.getLocation());
-        }else {
+        } else {
             Map<String, Double> distance = GeodesyUtil.getDistance(orderTrajectory.getLocation(), oldLocation);
-            if(null != distance){
+            if (null != distance) {
                 Double distance1 = distance.get("WGS84");
-                if(distance1 > 50 && xpOrder.getStatus() == 6){//大于50米表示在移动,且在服务中的时候记录里程
+                if (distance1 > 50 && xpOrder.getStatus() == 6) {//大于50米表示在移动,且在服务中的时候记录里程
                     xpOrder.setMileageKilometers(BigDecimal.valueOf(xpOrder.getMileageKilometers()).add(new BigDecimal(distance1)).setScale(2, RoundingMode.HALF_EVEN).doubleValue());
                     this.updateById(xpOrder);
                     // 存储本次距离
@@ -1067,7 +1561,7 @@
                     // 存储mongo数据
                     orderTrajectoryService.create(orderTrajectory);
                 }
-            }else{
+            } else {
                 log.error("调用高德计算距离出错");
             }
         }
diff --git a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/GpsPoint.java b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/GpsPoint.java
new file mode 100644
index 0000000..a6a1366
--- /dev/null
+++ b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/GpsPoint.java
@@ -0,0 +1,13 @@
+package com.xiaopeng.order.utils;
+
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class GpsPoint {
+    private Double distance;   // 与上一点距离(米)
+    private Long locatetime;   // 毫秒时间戳
+    private Date createTime;   // 创建日期
+    // ... getter & setter
+}
\ No newline at end of file
diff --git a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingService.java b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingService.java
new file mode 100644
index 0000000..86ec28a
--- /dev/null
+++ b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingService.java
@@ -0,0 +1,185 @@
+package com.xiaopeng.order.utils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 出租车计费引擎服务
+ */
+public class TaxiPricingService {
+
+    public static void main(String[] args) {
+
+    }
+
+    // 1. 定义规则模型
+    public static class PricingRule {
+        String ruleName;      // 时段名称(如:高峰期、深夜档、普通时段A)
+        String ruleType;      // 类型:NORMAL, PEAK, NIGHT, HOLIDAY
+        LocalTime start;
+        LocalTime end;
+        BigDecimal perKmRate; // 每公里价格
+        BigDecimal perMinRate; // 每分钟价格
+
+        // 起步价相关(仅用于订单开始时的首个规则判定)
+        BigDecimal baseFare;
+        BigDecimal baseKm;
+        BigDecimal baseMin;
+
+        public PricingRule(String name, String type, String start, String end, double kmRate, double minRate, double bf, double bk, double bm) {
+            this.ruleName = name;
+            this.ruleType = type;
+            this.start = LocalTime.parse(start);
+            this.end = LocalTime.parse(end);
+            this.perKmRate = BigDecimal.valueOf(kmRate);
+            this.perMinRate = BigDecimal.valueOf(minRate);
+            this.baseFare = BigDecimal.valueOf(bf);
+            this.baseKm = BigDecimal.valueOf(bk);
+            this.baseMin = BigDecimal.valueOf(bm);
+        }
+
+        // 跨天匹配核心算法
+        public boolean contains(LocalTime now) {
+            if (start.isBefore(end)) {
+                return !now.isBefore(start) && !now.isAfter(end);
+            } else { // 处理跨天,如 22:00 - 05:00
+                return !now.isBefore(start) || !now.isAfter(end);
+            }
+        }
+    }
+
+    /**
+     * 计算服务
+     */
+    public Map<String, BigDecimal> calculateOrderFee(
+            List<GpsPoint> points,
+            PricingRule holidayRule,
+            PricingRule peakRule,
+            PricingRule nightRule,
+            List<PricingRule> normalRules,
+            List<LongDistStep> longSteps,
+            boolean isHoliday) {
+
+        if (points == null || points.size() < 2) return new HashMap<>();
+
+        // 结果存放:Key为规则名称,Value为该时段产生的超出金额
+        Map<String, BigDecimal> report = new HashMap<>();
+
+        // 1. 初始化起步参数 (以第一条定位的时间点决定起步规则)
+        GpsPoint startPoint = points.get(0);
+        PricingRule initialRule = findMatchedRule(startPoint, isHoliday, holidayRule, peakRule, nightRule, normalRules);
+
+        report.put("起步价(" + initialRule.ruleName + ")", initialRule.baseFare);
+
+        // 剩余可抵扣起步额度
+        BigDecimal remainBaseMeters = initialRule.baseKm.multiply(new BigDecimal("1000"));
+        BigDecimal remainBaseMillis = initialRule.baseMin.multiply(new BigDecimal("60000"));
+        BigDecimal totalDistanceMeters = BigDecimal.ZERO;
+
+        // 2. 循环处理每一段定位轨迹
+        for (int i = 1; i < points.size(); i++) {
+            GpsPoint curr = points.get(i);
+            GpsPoint prev = points.get(i - 1);
+
+            // 段增量
+            BigDecimal segM = BigDecimal.valueOf(curr.getDistance());
+            BigDecimal segMs = BigDecimal.valueOf(curr.getLocatetime() - prev.getLocatetime());
+            totalDistanceMeters = totalDistanceMeters.add(segM);
+
+            // 匹配当前定位点所属的规则
+            PricingRule activeRule = findMatchedRule(curr, isHoliday, holidayRule, peakRule, nightRule, normalRules);
+            String key = activeRule.ruleName;
+
+            // A. 扣除起步里程后的计费里程
+            BigDecimal billableM = BigDecimal.ZERO;
+            if (remainBaseMeters.compareTo(BigDecimal.ZERO) > 0) {
+                if (segM.compareTo(remainBaseMeters) > 0) {
+                    billableM = segM.subtract(remainBaseMeters);
+                    remainBaseMeters = BigDecimal.ZERO;
+                } else {
+                    remainBaseMeters = remainBaseMeters.subtract(segM);
+                }
+            } else {
+                billableM = segM;
+            }
+
+            // B. 扣除起步时长后的计费时长
+            BigDecimal billableMs = BigDecimal.ZERO;
+            if (remainBaseMillis.compareTo(BigDecimal.ZERO) > 0) {
+                if (segMs.compareTo(remainBaseMillis) > 0) {
+                    billableMs = segMs.subtract(remainBaseMillis);
+                    remainBaseMillis = BigDecimal.ZERO;
+                } else {
+                    remainBaseMillis = remainBaseMillis.subtract(segMs);
+                }
+            } else {
+                billableMs = segMs;
+            }
+
+            // C. 计算当前段金额 = (里程费) + (时长费) + (远途费)
+            BigDecimal distFee = billableM.divide(new BigDecimal("1000"), 10, RoundingMode.HALF_UP).multiply(activeRule.perKmRate);
+            BigDecimal timeFee = billableMs.divide(new BigDecimal("60000"), 10, RoundingMode.HALF_UP).multiply(activeRule.perMinRate);
+            BigDecimal extraLongFee = calculateLongDistExtra(totalDistanceMeters, billableM, longSteps);
+
+            BigDecimal segTotal = distFee.add(timeFee).add(extraLongFee);
+
+            // D. 归纳到对应规则
+            report.put(key, report.getOrDefault(key, BigDecimal.ZERO).add(segTotal));
+        }
+
+        // 3. 格式化输出:保留两位小数
+        return formatResult(report);
+    }
+
+    /**
+     * 优先级判定逻辑:节假日 > 高峰期 > 夜间 > 普通1 > 普通2...
+     */
+    private PricingRule findMatchedRule(GpsPoint p, boolean isHoliday, PricingRule h, PricingRule pk, PricingRule n, List<PricingRule> normals) {
+        if (isHoliday) return h;
+
+        // 将毫秒转为当地时间进行匹配
+        LocalTime time = new java.sql.Time(p.getLocatetime()).toLocalTime();
+
+        if (pk.contains(time)) return pk;
+        if (n.contains(time)) return n;
+
+        for (PricingRule rule : normals) {
+            if (rule.contains(time)) return rule;
+        }
+
+        return normals.get(0); // 兜底逻辑
+    }
+
+    private BigDecimal calculateLongDistExtra(BigDecimal totalM, BigDecimal segM, List<LongDistStep> steps) {
+        if (segM.compareTo(BigDecimal.ZERO) <= 0) return BigDecimal.ZERO;
+        BigDecimal totalKm = totalM.divide(new BigDecimal("1000"), 10, RoundingMode.HALF_UP);
+        BigDecimal rate = BigDecimal.ZERO;
+        for (LongDistStep step : steps) {
+            if (totalKm.compareTo(step.kmThreshold) > 0) {
+                rate = step.addPrice;
+                break;
+            }
+        }
+        return segM.divide(new BigDecimal("1000"), 10, RoundingMode.HALF_UP).multiply(rate);
+    }
+
+    private Map<String, BigDecimal> formatResult(Map<String, BigDecimal> map) {
+        Map<String, BigDecimal> formatted = new HashMap<>();
+        map.forEach((k, v) -> formatted.put(k, v.setScale(2, RoundingMode.HALF_UP)));
+        return formatted;
+    }
+
+    // 远途阶梯配置类
+    public static class LongDistStep {
+        BigDecimal kmThreshold;
+        BigDecimal addPrice;
+        public LongDistStep(double km, double price) {
+            this.kmThreshold = BigDecimal.valueOf(km);
+            this.addPrice = BigDecimal.valueOf(price);
+        }
+    }
+}
\ No newline at end of file
diff --git a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/XpPricingUtil.java b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/XpPricingUtil.java
index 8d4ff9d..fc09f3f 100644
--- a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/XpPricingUtil.java
+++ b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/XpPricingUtil.java
@@ -207,7 +207,6 @@
 //
 //            // 时长
 //            xpPricingCardVo.setDuration(realTime);
-//
 //            // 时长费
 //            xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num31")));
 //
@@ -357,300 +356,7 @@
 //
 //        return xpPricingCardVo;
 //    }
-//    /**
-//     *  行程结束后计算费用
-//     * @param rule 基础规则
-//     * @param distance 里程
-//     * @param duration  时间
-//     * @param wait  时间
-//     * @param waitMoney 等待费
-//     * @param openCityId 开通城市id
-//     * @param isDynamicPriceStatus 是否开启动态调价
-//     * @return
-//     * @throws Exception
-//     */
-////    public XpPricingCardVo calculationFinalPrice(String uid,JSONObject rule, JSONArray startingFareJson, double distance, long duration, long wait, Double waitMoney,Integer isDynamicPriceStatus,Long openCityId,String dynamicPricingJson) {
-////
-////
-////        // 默认费率1
-////        double priceRate=1;
-////        if(isDynamicPriceStatus==1){
-////            // 先判断是否已经调价过了
-////            Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + openCityId + "_" + uid);
-////            if(userDynamicPriceObj!=null){
-////                // 是否调价过了
-////                priceRate = Double.parseDouble(userDynamicPriceObj.toString());
-////            }else {
-////                // 没有调过
-////                //是否触发动态调价 redis获取
-////                Object dynamicPriceObj = redisService.getCacheObject("DYNAMIC_PRICE_" + openCityId);
-////                if(dynamicPriceObj!=null){
-////                    JSONObject jsonObject = JSONObject.parseObject(dynamicPricingJson);
-////                    // 触发动态调价
-////                    // value=时间
-////                    String timeStr = dynamicPriceObj.toString();
-////                    // 判断当前距离这个时间范围的新增订单数(排除取消) / 上浮数量 算出应该增加的价格率
-////                    R<Integer> r = xpOrderClient.getOrderNum(timeStr);
-////                    if(r.getData()==200){
-////                        int num = r.getData();
-////                        // X单上升一次调价
-////                        Integer orderNum = getOrderNum(jsonObject, 4);
-////                        // 需要调价的次数
-////                        int count = num / orderNum;
-////                        if(count>0){
-////                            // 计算每次上升的费率
-////                            Integer upRate = getOrderNum(jsonObject, 5);
-////                            BigDecimal divide = new BigDecimal(upRate).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
-////                            for (int i = count; i > 0; i--) {
-////                                // 当前费率+(费率*上升百分比)
-////                                priceRate =  new BigDecimal(priceRate).multiply(divide).add(new BigDecimal(priceRate)).setScale(2, RoundingMode.HALF_UP).doubleValue();
-////                            }
-////
-////                            // 冷却时间
-////                            Integer time = getOrderNum(jsonObject, 6);
-////                            // 触发调价了
-////                            redisService.setCacheObject("USER_DYNAMIC_PRICE_" + openCityId + "_" + uid, priceRate,time.longValue(), TimeUnit.MINUTES);
-////                        }
-////                    }
-////
-////                }
-////            }
-////
-////        }
-////
-////
-////
-////        XpPricingCardVo xpPricingCardVo = new XpPricingCardVo();
-////        double amount = 0;
-////        //等待费
-////        Date date = new Date();
-////        // 计算出是在那个时间段内
-////        for (Object o : startingFareJson) {
-////            JSONObject startingFare = (JSONObject) o;
-////            Calendar calendarStart = Calendar.getInstance();
-////            calendarStart.setTime(date);
-////            calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num1").split(":")[0]));
-////            calendarStart.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num1").split(":")[1]));
-////
-////            Calendar calendarEnd = Calendar.getInstance();
-////            calendarEnd.setTime(date);
-////            calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num2").split(":")[0]));
-////            calendarEnd.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num2").split(":")[1]));
-////            // 找到符合条件的直接跳出循环
-////            if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) {
-////                rule.put("num1", startingFare.get("num3"));
-////                rule.put("num2", startingFare.get("num4"));
-////                rule.put("num3", startingFare.get("num5"));
-////                rule.put("num4", startingFare.get("num6"));
-////                rule.put("num5", startingFare.get("num7"));
-////
-////                rule.put("startTime", startingFare.getString("num1"));
-////                rule.put("endTime", startingFare.getString("num2"));
-////                break;
-////            }
-////        }
-////
-////
-////        double realDistance = distance / 1000;//实际公里
-////        double realTime = duration / 60;//实际时间
-////        double exceedDistance = (realDistance - rule.getDouble("num2")) < 0 ? 0 : realDistance - rule.getDouble("num2");//超出起步里程的公里
-////        double exceedTime = (realTime - rule.getDouble("num3")) < 0 ? 0 : realTime - rule.getDouble("num3");//超过起步分钟数的时间
-////        double longDistanceOne = 0;//远途1段
-////        double longDistanceTwo = 0;//远途2段
-////        double longDistanceThree = 0;//远途3段
-////
-////
-////        //夜间服务处理逻辑
-////        Calendar calendarNight = Calendar.getInstance();
-////        calendarNight.setTime(date);
-////        calendarNight.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num14").split(" - ")[0].split(":")[0]));
-////        calendarNight.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num14").split(" - ")[0].split(":")[1]));
-////
-////        Calendar calendar = Calendar.getInstance();
-////        calendar.setTime(date);
-////        calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num14").split(" - ")[1].split(":")[0]));
-////        calendar.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num14").split(" - ")[1].split(":")[1]));
-////
-////        // 先判断今日是否节假日
-////        boolean holiday = holidayUtil.isHoliday();
-////        if (holiday) {
-////            if (realDistance > rule.getDouble("num6")) {
-////                longDistanceOne = rule.getDouble("num32") * (realDistance - rule.getDouble("num6"));
-////            }
-////            if (realDistance > rule.getDouble("num9")) {
-////                longDistanceTwo = rule.getDouble("num33") * (realDistance - rule.getDouble("num9"));
-////            }
-////            if (realDistance > rule.getDouble("num12")) {
-////                longDistanceThree = rule.getDouble("num34") * (realDistance - rule.getDouble("num12"));
-////            }
-////            amount = rule.getDouble("num29") + (exceedDistance * rule.getDouble("num30")) + (exceedTime * rule.getDouble("num31")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree;
-////
-////
-////            // 起步价
-////            xpPricingCardVo.setStartPrice(rule.getDouble("num29"));
-////            // 时间段
-////            xpPricingCardVo.setTime("节假日");
-////
-////
-////            // 里程
-////            xpPricingCardVo.setDistance(realDistance);
-////
-////            // 里程费
-////            double distancePrice = (exceedDistance * rule.getDouble("num30")) + longDistanceOne + longDistanceTwo + longDistanceThree;
-////            xpPricingCardVo.setDistancePrice(distancePrice);
-////
-////            // 时长
-////            xpPricingCardVo.setDuration(realTime);
-////
-////            // 时长费
-////            xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num31")));
-////
-////
-////        } else {
-////            if (date.getTime() > calendarNight.getTimeInMillis() && date.getTime() < calendar.getTimeInMillis()) {
-////                if (realDistance > rule.getDouble("num6")) {
-////                    longDistanceOne = rule.getDouble("num18") * (realDistance - rule.getDouble("num6"));
-////                }
-////                if (realDistance > rule.getDouble("num9")) {
-////                    longDistanceTwo = rule.getDouble("num19") * (realDistance - rule.getDouble("num9"));
-////                }
-////                if (realDistance > rule.getDouble("num12")) {
-////                    longDistanceThree = rule.getDouble("num20") * (realDistance - rule.getDouble("num12"));
-////                }
-////                amount = rule.getDouble("num15") + (exceedDistance * rule.getDouble("num16")) + (exceedTime * rule.getDouble("num17")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree;
-////
-////
-////                // 起步价
-////                xpPricingCardVo.setStartPrice(rule.getDouble("num15"));
-////                // 时间段
-////                xpPricingCardVo.setTime(rule.getString("num14"));
-////
-////                // 里程
-////                xpPricingCardVo.setDistance(realDistance);
-////
-////                // 里程费
-////                double distancePrice = (exceedDistance * rule.getDouble("num16")) + longDistanceOne + longDistanceTwo + longDistanceThree;
-////                xpPricingCardVo.setDistancePrice(distancePrice);
-////
-////                // 时长
-////                xpPricingCardVo.setDuration(realTime);
-////
-////                // 时长费
-////                xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num17")));
-////
-////
-////            } else {
-////
-////                // 高峰处理
-////                Calendar calendarStart = Calendar.getInstance();
-////                calendarStart.setTime(date);
-////                calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num21").split(" - ")[0].split(":")[0]));
-////                calendarStart.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num21").split(" - ")[0].split(":")[1]));
-////
-////                Calendar calendarEnd = Calendar.getInstance();
-////                calendarEnd.setTime(date);
-////                calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num21").split(" - ")[1].split(":")[0]));
-////                calendarEnd.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num21").split(" - ")[1].split(":")[1]));
-////
-////                Calendar s2 = Calendar.getInstance();
-////                s2.setTime(date);
-////                s2.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num22").split(" - ")[0].split(":")[0]));
-////                s2.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num22").split(" - ")[0].split(":")[1]));
-////
-////                Calendar e2 = Calendar.getInstance();
-////                e2.setTime(date);
-////                e2.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num22").split(" - ")[1].split(":")[0]));
-////                e2.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num22").split(" - ")[1].split(":")[1]));
-////                //高峰时段处理逻辑
-////                if ((date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) || (date.getTime() > s2.getTimeInMillis() && date.getTime() < e2.getTimeInMillis())) {
-////                    if (realDistance > rule.getDouble("num6")) {
-////                        longDistanceOne = rule.getDouble("num26") * (realDistance - rule.getDouble("num6"));
-////                    }
-////                    if (realDistance > rule.getDouble("num9")) {
-////                        longDistanceTwo = rule.getDouble("num27") * (realDistance - rule.getDouble("num9"));
-////                    }
-////                    if (realDistance > rule.getDouble("num12")) {
-////                        longDistanceThree = rule.getDouble("num28") * (realDistance - rule.getDouble("num12"));
-////                    }
-////                    amount = rule.getDouble("num23") + (exceedDistance * rule.getDouble("num24")) + (exceedTime * rule.getDouble("num25")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree;
-////
-////
-////                    // 起步价
-////                    xpPricingCardVo.setStartPrice(rule.getDouble("num23"));
-////                    // 时间段
-////                    if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) {
-////                        xpPricingCardVo.setTime(rule.getString("num21"));
-////                    } else {
-////                        xpPricingCardVo.setTime(rule.getString("num22"));
-////                    }
-////
-////
-////                    // 里程
-////                    xpPricingCardVo.setDistance(realDistance);
-////
-////                    // 里程费
-////                    double distancePrice = (exceedDistance * rule.getDouble("num24")) + longDistanceOne + longDistanceTwo + longDistanceThree;
-////                    xpPricingCardVo.setDistancePrice(distancePrice);
-////
-////                    // 时长
-////                    xpPricingCardVo.setDuration(realTime);
-////
-////                    // 时长费
-////                    xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num25")));
-////
-////                } else {
-////                    //其他时间段的计算
-////                    if (realDistance > rule.getDouble("num6")) {
-////                        longDistanceOne = rule.getDouble("num8") * (realDistance - rule.getDouble("num6"));
-////                    }
-////                    if (realDistance > rule.getDouble("num9")) {
-////                        longDistanceTwo = rule.getDouble("num11") * (realDistance - rule.getDouble("num9"));
-////                    }
-////                    if (realDistance > rule.getDouble("num12")) {
-////                        longDistanceThree = rule.getDouble("num13") * (realDistance - rule.getDouble("num12"));
-////                    }
-////                    amount = rule.getDouble("num1") + (exceedDistance * rule.getDouble("num4")) + (exceedTime * rule.getDouble("num5")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree;
-////
-////
-////                    // 起步价
-////                    xpPricingCardVo.setStartPrice(rule.getDouble("num1"));
-////                    // 时间段
-////                    xpPricingCardVo.setTime(rule.getString("startTime") + " - " + rule.getString("endTime"));
-////
-////                    xpPricingCardVo.setDistancePriceNormal(rule.getDouble("num2"));
-////
-////                    xpPricingCardVo.setDurationPriceNormal(rule.getDouble("num3"));
-////
-////
-////                    // 里程
-////                    xpPricingCardVo.setDistance(realDistance);
-////
-////                    // 里程费
-////                    double distancePrice = (exceedDistance * rule.getDouble("num4")) + longDistanceOne + longDistanceTwo + longDistanceThree;
-////                    xpPricingCardVo.setDistancePrice(distancePrice);
-////
-////                    // 时长
-////                    xpPricingCardVo.setDuration(realTime);
-////
-////                    // 时长费
-////                    xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num5")));
-////
-////                }
-////            }
-////        }
-////
-////        // 订单总额
-////        Double startPrice = xpPricingCardVo.getStartPrice();
-////        // 先减去起步价重新计算
-////        amount = amount - startPrice;
-////        // 动态计算费用
-////        startPrice= new BigDecimal(startPrice).multiply(new BigDecimal(priceRate)).setScale(2, RoundingMode.HALF_UP).doubleValue();
-////        amount = amount+startPrice;
-////        xpPricingCardVo.setTotalPrice(amount);
-////
-////
-////        return xpPricingCardVo;
-////    }
+//
 //
 //
 //    /**
diff --git a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/holiday/HolidayUtil.java b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/holiday/HolidayUtil.java
index 8b31883..0d5d13e 100644
--- a/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/holiday/HolidayUtil.java
+++ b/xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/holiday/HolidayUtil.java
@@ -21,8 +21,7 @@
     private RedisService redisService;
 
 
-    public  boolean isHoliday() {
-        LocalDate now = LocalDate.now();
+    public  boolean isHoliday(LocalDate now) {
 
         // 判断缓存里面是否有节假日算法
         if (redisService.getCacheObject(CacheConstants.IS_HOLIDAY+now) != null && ("1").equals(redisService.getCacheObject(CacheConstants.IS_HOLIDAY+now).toString())) {

--
Gitblit v1.7.1