xuhy
2026-03-15 67db945bbfd7ae08335652c30537a68e443deb78
Merge remote-tracking branch 'origin/master'
20个文件已修改
343 ■■■■■ 已修改文件
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpAppUserCouponController.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/util/holiday/HolidayUtil.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/account/GetUserCouponDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/map/RouteAndFareDto.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/account/XpAppUserCouponClientFallbackFactory.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/account/XpAppUserCouponClient.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/UserCouponVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/constant/I18nKeyConstant.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/dto/XpOrderDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/XpPricingServiceImpl.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/utils/holiday/HolidayUtil.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages_en_US.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages_zh_CN.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/holiday/HolidayUtil.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/holiday/HolidayUtil.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/vo/XpOrderDetailVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingDemoService.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/holiday/HolidayUtil.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpAppUserCouponController.java
@@ -77,14 +77,26 @@
        UserCouponVo userCouponVo = new UserCouponVo();
        // 获取用户所有优惠券
        List<XpAppUserCoupon> couponList = xpAppUserCouponService.list(new LambdaQueryWrapper<XpAppUserCoupon>().eq(XpAppUserCoupon::getStatus, 1).eq(XpAppUserCoupon::getAppUserId,dto.getUserId()).apply("(FIND_IN_SET({0}, open_city_id) or open_city_id='0')", dto.getOpenCityId()) );
        List<XpAppUserCoupon> couponList = xpAppUserCouponService.list(new LambdaQueryWrapper<XpAppUserCoupon>().eq(dto.getUserCouponId()!=null,XpAppUserCoupon::getId, dto.getUserCouponId()).eq(XpAppUserCoupon::getStatus, 1).eq(XpAppUserCoupon::getAppUserId,dto.getUserId()).apply("(FIND_IN_SET({0}, open_city_id) or open_city_id='0')", dto.getOpenCityId()) );
        if(couponList.isEmpty()){
            return R.ok(userCouponVo);
        }
        // 计算使用时间规则
        couponList = xpAppUserCouponService.timeRule(couponList);
        if(couponList.isEmpty()){
            if(dto.getUserCouponId()!=null){
                userCouponVo.setStatus(2);
            }
            return R.ok(userCouponVo);
        }
        if(dto.getUserCouponId()!=null){
            XpAppUserCoupon coupon = couponList.get(0);
            // 判断是否满足金额
            if(coupon.getCouponType()==2){
                if(dto.getTotalAmount()<coupon.getFullMoney().doubleValue()){
                    userCouponVo.setStatus(2);
                }
            }
        }
        // 优先用满足条件且抵扣金额最大的
@@ -115,6 +127,7 @@
    /**
     * 获取优惠券列表
     */
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/util/holiday/HolidayUtil.java
@@ -9,7 +9,10 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.concurrent.TimeUnit;
@@ -63,18 +66,32 @@
        for (int i = 0; i < parse.size(); i++) {
            JSONObject jsonObject1 = parse.getJSONObject(i);
            String daytype = jsonObject1.get("daytype").toString();
            long secondsToExactEndOfDay = getSecondsToExactEndOfDay();
            if (daytype.equals("0")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            } else if (daytype.equals("1")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 1;
            } else if (daytype.equals("2")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "2", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            }
        }
        return 0;
    }
    public static long getSecondsToExactEndOfDay() {
        LocalDateTime now = LocalDateTime.now();
        // 精确到23:59:59(不包含毫秒/纳秒)
        LocalDateTime endOfDay = LocalDateTime.of(
                LocalDate.now(),
                LocalTime.of(23, 59, 59)
        );
        return Duration.between(now, endOfDay).getSeconds();
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/account/GetUserCouponDto.java
@@ -21,4 +21,7 @@
    @ApiModelProperty(value = "开通城市id")
    private Long openCityId;
    @ApiModelProperty(value = "优惠券id")
    private Long userCouponId;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/map/RouteAndFareDto.java
@@ -27,6 +27,10 @@
    private Integer routePlanRule;
    @ApiModelProperty(value = "优惠券id(是否指定使用某个优惠券)")
    private Long userCouponId;
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/account/XpAppUserCouponClientFallbackFactory.java
@@ -38,6 +38,7 @@
                log.error("优惠券使用失败,原因: {}", cause.getMessage());
                return R.fail("优惠券使用失败,"+cause.getMessage());
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/account/XpAppUserCouponClient.java
@@ -9,6 +9,7 @@
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.math.BigDecimal;
@@ -28,4 +29,5 @@
    @PostMapping("/inner/appUserCoupon/useUserCoupon")
    R<BigDecimal> useUserCoupon(@RequestBody UseUserCouponDto dto);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/UserCouponVo.java
@@ -12,4 +12,7 @@
    @ApiModelProperty("优惠券金额")
    private Double money=0.0;
    @ApiModelProperty("1正常 2不满足")
    private Integer status=1;
}
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/constant/I18nKeyConstant.java
@@ -61,4 +61,5 @@
    //城市不存在
    String CITY_NOT_FOUND = "city.not.found";
    String COUPON_NOT_SATISFY = "coupon.not.satisfy";
}
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/dto/XpOrderDto.java
@@ -49,4 +49,7 @@
    private String placementAddress;
    @ApiModelProperty("用户优惠券id")
    private Long userCouponId;
}
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/XpPricingServiceImpl.java
@@ -242,6 +242,31 @@
            );
        }
        // 优惠券
        XpPricingCardVo finalXpPricingCardVo = xpPricingCardVo;
        R<UserCouponVo> coupon = null;
        CompletableFuture<R<UserCouponVo>> couponFuture=null;
        // 判断是否指定优惠券
        if(routeAndFareDto.getUserCouponId()!=null){
            coupon = xpAppUserCouponClient.getUserCoupon(new GetUserCouponDto(Long.valueOf(userId), finalXpPricingCardVo.getTotalPrice(), routeAndFareDto.getOpenCityId(),routeAndFareDto.getUserCouponId()));
            if(coupon.getCode()==200 && coupon.getData().getStatus()==2){
                throw new ServiceException("优惠券不满足条件", I18nKeyConstant.COUPON_NOT_SATISFY);
            }
        }else {
            couponFuture= CompletableFuture.supplyAsync(
                    () -> {
                        // 优惠券
                        return xpAppUserCouponClient.getUserCoupon(
                                new GetUserCouponDto(Long.valueOf(userId), finalXpPricingCardVo.getTotalPrice(), routeAndFareDto.getOpenCityId(),null)
//                            new GetUserCouponDto(Long.valueOf(userId), 1d, routeAndFareDto.getOpenCityId())
                        );
                    },
                    executorService
            );
        }
        // 3. 并行执行远程服务调用 判断是否同一经营区域
        CompletableFuture<R<Boolean>> diagnosisAreaFuture = CompletableFuture.supplyAsync(
@@ -249,23 +274,14 @@
                executorService
        );
        // 优惠券
        XpPricingCardVo finalXpPricingCardVo = xpPricingCardVo;
        CompletableFuture<R<UserCouponVo>> couponFuture = CompletableFuture.supplyAsync(
                () -> {
                    // 优惠券
                    return xpAppUserCouponClient.getUserCoupon(
                            new GetUserCouponDto(Long.valueOf(userId), finalXpPricingCardVo.getTotalPrice(), routeAndFareDto.getOpenCityId())
//                            new GetUserCouponDto(Long.valueOf(userId), 1d, routeAndFareDto.getOpenCityId())
                    );
                },
                executorService
        );
        // 4. 等待所有并行任务完成并获取结果
        R<Boolean> diagnosisResult = diagnosisAreaFuture.join();
        R<UserCouponVo> couponResult = couponFuture.join();
        R<UserCouponVo> couponResult =null;
        if(routeAndFareDto.getUserCouponId()!=null){
            couponResult =coupon;
        }else {
            couponResult =couponFuture.join();
        }
        // 5. 处理诊断结果
        if (diagnosisResult.getCode() != R.SUCCESS) {
@@ -557,6 +573,7 @@
        }
        Long orderId = addOrderR.getData();
        xpOrder.setId(orderId);
        xpOrder.setCouponId(dto.getUserCouponId());
        // 静默处理 不推单
//        if (isSilent) {
//            log.info("订单取消次数达到上限,执行静默处理,不推单。");
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/utils/holiday/HolidayUtil.java
@@ -8,7 +8,10 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.concurrent.TimeUnit;
@@ -64,18 +67,30 @@
        for (int i = 0; i < parse.size(); i++) {
            JSONObject jsonObject1 = parse.getJSONObject(i);
            String daytype = jsonObject1.get("daytype").toString();
            long secondsToExactEndOfDay = getSecondsToExactEndOfDay();
            if (daytype.equals("0")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            } else if (daytype.equals("1")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 1;
            } else if (daytype.equals("2")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "2", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            }
        }
        return 0;
    }
    public static long getSecondsToExactEndOfDay() {
        LocalDateTime now = LocalDateTime.now();
        // 精确到23:59:59(不包含毫秒/纳秒)
        LocalDateTime endOfDay = LocalDateTime.of(
                LocalDate.now(),
                LocalTime.of(23, 59, 59)
        );
        return Duration.between(now, endOfDay).getSeconds();
    }
    /**
     * 2非工作日  0工作日  1节假日 3调休日
@@ -96,17 +111,19 @@
        for (int i = 0; i < parse.size(); i++) {
            JSONObject jsonObject1 = parse.getJSONObject(i);
            String daytype = jsonObject1.get("daytype").toString();
            long secondsToExactEndOfDay = getSecondsToExactEndOfDay();
            if (daytype.equals("0")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            } else if (daytype.equals("1")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 1;
            } else if (daytype.equals("2")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "2", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            }
        }
        return 0;
    }
}
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages.properties
@@ -1,5 +1,6 @@
# 中文
city.not.found=获取城市信息失败
coupon.not.satisfy=券不满足条件
no.current.point=该站点已停止运营
no.current.region=当前区域未开通服务
path.not.found=获取导航信息失败
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages_en_US.properties
@@ -1,5 +1,6 @@
# English (US)
city.not.found=city.not.found
coupon.not.satisfy=The coupon does not meet the conditions.
no.current.point=This site has ceased operations
no.current.region=no.current.region
path.not.found=Failed to obtain navigation information
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -1,5 +1,6 @@
# 中文
city.not.found=获取城市信息失败
coupon.not.satisfy=券不满足条件
no.current.point=该站点已停止运营
no.current.region=当前区域未开通服务
path.not.found=获取导航信息失败
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/holiday/HolidayUtil.java
@@ -10,7 +10,10 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.concurrent.TimeUnit;
@@ -64,18 +67,31 @@
        for (int i = 0; i < parse.size(); i++) {
            JSONObject jsonObject1 = parse.getJSONObject(i);
            String daytype = jsonObject1.get("daytype").toString();
            long secondsToExactEndOfDay = getSecondsToExactEndOfDay();
            if (daytype.equals("0")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            } else if (daytype.equals("1")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 1;
            } else if (daytype.equals("2")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "2", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            }
        }
        return 0;
    }
    public static long getSecondsToExactEndOfDay() {
        LocalDateTime now = LocalDateTime.now();
        // 精确到23:59:59(不包含毫秒/纳秒)
        LocalDateTime endOfDay = LocalDateTime.of(
                LocalDate.now(),
                LocalTime.of(23, 59, 59)
        );
        return Duration.between(now, endOfDay).getSeconds();
    }
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/holiday/HolidayUtil.java
@@ -10,7 +10,10 @@
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.concurrent.TimeUnit;
@@ -66,14 +69,15 @@
        for (int i = 0; i < parse.size(); i++) {
            JSONObject jsonObject1 = parse.getJSONObject(i);
            String daytype = jsonObject1.get("daytype").toString();
            long secondsToExactEndOfDay = getSecondsToExactEndOfDay();
            if (daytype.equals("0")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            } else if (daytype.equals("1")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 1;
            } else if (daytype.equals("2")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "2", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            }
        }
@@ -98,17 +102,30 @@
        for (int i = 0; i < parse.size(); i++) {
            JSONObject jsonObject1 = parse.getJSONObject(i);
            String daytype = jsonObject1.get("daytype").toString();
            long secondsToExactEndOfDay = getSecondsToExactEndOfDay();
            if (daytype.equals("0")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            } else if (daytype.equals("1")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 1;
            } else if (daytype.equals("2")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "2", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            }
        }
        return 0;
    }
    public static long getSecondsToExactEndOfDay() {
        LocalDateTime now = LocalDateTime.now();
        // 精确到23:59:59(不包含毫秒/纳秒)
        LocalDateTime endOfDay = LocalDateTime.of(
                LocalDate.now(),
                LocalTime.of(23, 59, 59)
        );
        return Duration.between(now, endOfDay).getSeconds();
    }
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/vo/XpOrderDetailVO.java
@@ -189,7 +189,7 @@
    private List<OrderPosition> orderPositionList;
    @Data
    static class OrderPosition{
    public static class OrderPosition{
        @ApiModelProperty(value = "经度")
        private Double longitude;
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -324,8 +324,16 @@
        if (Objects.nonNull(evaluate)) {
            vo.setOrderRating(evaluate.getOrderRating());
        }
        //TODO 7.查询mongodb 该订单上传的经纬度轨迹信息
        vo.setOrderPositionList(new ArrayList<>());
        // 7. 查询mongodb 该订单上传的经纬度轨迹信息(按订单id)
        List<OrderTrajectory> trajectories = queryOrderTrajectories(id);
        vo.setOrderPositionList(convertTrajectoriesToPositions(trajectories));
        // 8. 起步价、里程费、时长费、远途费:按订单费用明细聚合
        Map<Integer, BigDecimal> feeMap = sumOrderDetailMoneyByDetailType(id);
        vo.setStartMoney(feeMap.getOrDefault(1, BigDecimal.ZERO));
        vo.setMileageMoney(feeMap.getOrDefault(2, BigDecimal.ZERO));
        vo.setDurationMoney(feeMap.getOrDefault(3, BigDecimal.ZERO));
        vo.setLongDistancemoney(feeMap.getOrDefault(4, BigDecimal.ZERO));
        return vo;
    }
@@ -2473,12 +2481,15 @@
            }
        }
        List<OrderTrajectory> trajectories = mongoTemplate.find(
                new Query(Criteria.where("orderId").is(orderId))
                        .with(Sort.by(Sort.Direction.ASC, "locatetime")),
                OrderTrajectory.class
        );
        List<OrderTrajectory> trajectories = queryOrderTrajectories(orderId);
        orderDetailVo.setTrajectories(trajectories);
        // 起步价、里程费、时长费、远途费:按订单费用明细聚合,覆盖订单表字段
        Map<Integer, BigDecimal> feeMap = sumOrderDetailMoneyByDetailType(orderId);
        orderDetailVo.setStartMoney(feeMap.getOrDefault(1, BigDecimal.ZERO));
        orderDetailVo.setMileageMoney(feeMap.getOrDefault(2, BigDecimal.ZERO));
        orderDetailVo.setDurationMoney(feeMap.getOrDefault(3, BigDecimal.ZERO));
        orderDetailVo.setLongDistancemoney(feeMap.getOrDefault(4, BigDecimal.ZERO));
        if (order.getBoardingTime() != null && order.getTripEnd() != null) {
            // 计算两个时间相差多少分钟
            double exactMinutes = Duration.between(order.getBoardingTime(), order.getTripEnd()).toMillis() / (1000.0 * 60);
@@ -2492,6 +2503,82 @@
        return orderDetailVo;
    }
    /**
     * 查询订单轨迹(mongodb)
     *
     * @param orderId 订单id
     * @return 轨迹列表(按 locatetime 升序)
     */
    private List<OrderTrajectory> queryOrderTrajectories(Long orderId) {
        if (orderId == null) {
            return new ArrayList<>();
        }
        List<OrderTrajectory> trajectories = mongoTemplate.find(
                new Query(Criteria.where("orderId").is(orderId))
                        .with(Sort.by(Sort.Direction.ASC, "locatetime")),
                OrderTrajectory.class
        );
        return trajectories;
    }
    /**
     * 订单费用明细聚合:按 detail_type 汇总 money
     *
     * <p>detail_type:1=起步价 2=里程费 3=时长费 4=远途费</p>
     */
    private Map<Integer, BigDecimal> sumOrderDetailMoneyByDetailType(Long orderId) {
        if (orderId == null) {
            return Collections.emptyMap();
        }
        List<XpOrderDetail> detailList = xpOrderDetailMapper.selectList(
                new LambdaQueryWrapper<XpOrderDetail>()
                        .eq(XpOrderDetail::getOrderId, orderId)
                        .ne(XpOrderDetail::getIsDeleted, true)
        );
        if (CollectionUtils.isEmpty(detailList)) {
            return Collections.emptyMap();
        }
        Map<Integer, BigDecimal> sumMap = new HashMap<>();
        for (XpOrderDetail detail : detailList) {
            if (detail == null || detail.getDetailType() == null) {
                continue;
            }
            BigDecimal money = detail.getMoney() == null ? BigDecimal.ZERO : detail.getMoney();
            sumMap.put(detail.getDetailType(), sumMap.getOrDefault(detail.getDetailType(), BigDecimal.ZERO).add(money));
        }
        return sumMap;
    }
    /**
     * 将轨迹点转换为订单经纬度列表
     */
    private List<XpOrderDetailVO.OrderPosition> convertTrajectoriesToPositions(List<OrderTrajectory> trajectories) {
        if (CollectionUtils.isEmpty(trajectories)) {
            return new ArrayList<>();
        }
        List<XpOrderDetailVO.OrderPosition> positions = new ArrayList<>();
        for (OrderTrajectory trajectory : trajectories) {
            if (trajectory == null || StringUtils.isBlank(trajectory.getLocation())) {
                continue;
            }
            String[] parts = trajectory.getLocation().split(",");
            if (parts.length != 2) {
                continue;
            }
            try {
                double longitude = Double.parseDouble(parts[0].trim());
                double latitude = Double.parseDouble(parts[1].trim());
                XpOrderDetailVO.OrderPosition position = new XpOrderDetailVO.OrderPosition();
                position.setLongitude(longitude);
                position.setLatitude(latitude);
                positions.add(position);
            } catch (Exception e) {
                // ignore invalid point
            }
        }
        return positions;
    }
    @Override
    public R<String> feeDescription(Long orderId, String uid) {
        XpOrder order = this.getById(orderId);
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingDemoService.java
@@ -20,9 +20,9 @@
        Date orderTime = new Date();
//
        // 模拟总里程 25.0 km,以触发多级远途加价
        double totalMillige = 2.984;
        double totalMillige = 1.24;
        Calendar cal = Calendar.getInstance();
        cal.set(2026, Calendar.APRIL, 11, 15, 57, 50);
        cal.set(2026, Calendar.APRIL, 11, 15, 48, 50);
        orderTime = cal.getTime();
        // 模拟 GPS 轨迹点
        List<GpsPoint> points = createMockGpsPoints(orderTime);
@@ -37,9 +37,9 @@
        System.err.println("--------------------------");
        System.err.println("应付总额: " + totalAmount + " 元");
//        List<GpsPoint> mockGpsPoints = createMockGpsPoints(orderTime);
//        double sum = mockGpsPoints.stream().mapToDouble(GpsPoint::getDistance).sum();
//        System.err.println(sum);
        List<GpsPoint> mockGpsPoints = createMockGpsPoints(orderTime);
        double sum = mockGpsPoints.stream().mapToDouble(GpsPoint::getDistance).sum();
        System.err.println(sum);
    }
@@ -293,17 +293,17 @@
        list.add(new GpsPoint(188, startTs + 2 * 50 * 1000));
        list.add(new GpsPoint(150, startTs + 2 * 60 * 1000));
        list.add(new GpsPoint(126, startTs + 2 * 70 * 1000));
        list.add(new GpsPoint(138, startTs + 2 * 80 * 1000));
        list.add(new GpsPoint(119, startTs + 2 * 90 * 1000));
        list.add(new GpsPoint(147, startTs + 2 * 100 * 1000));
        list.add(new GpsPoint(155, startTs + 2 * 110 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 120 * 1000));
        list.add(new GpsPoint(172, startTs + 2 * 130 * 1000));
        list.add(new GpsPoint(202, startTs + 2 * 140 * 1000));
        list.add(new GpsPoint(162, startTs + 2 * 150 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 160 * 1000));
        list.add(new GpsPoint(132, startTs + 2 * 170 * 1000));
        list.add(new GpsPoint(122, startTs + 2 * 180 * 1000));
//        list.add(new GpsPoint(138, startTs + 2 * 80 * 1000));
//        list.add(new GpsPoint(119, startTs + 2 * 90 * 1000));
//        list.add(new GpsPoint(147, startTs + 2 * 100 * 1000));
//        list.add(new GpsPoint(155, startTs + 2 * 110 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 120 * 1000));
//        list.add(new GpsPoint(172, startTs + 2 * 130 * 1000));
//        list.add(new GpsPoint(202, startTs + 2 * 140 * 1000));
//        list.add(new GpsPoint(162, startTs + 2 * 150 * 1000));
//        list.add(new GpsPoint(182, startTs + 2 * 160 * 1000));
//        list.add(new GpsPoint(132, startTs + 2 * 170 * 1000));
//        list.add(new GpsPoint(122, startTs + 2 * 180 * 1000));
//        list.add(new GpsPoint(262, startTs + 2 * 190 * 1000));
//        list.add(new GpsPoint(242, startTs + 2 * 200 * 1000));
//        list.add(new GpsPoint(282, startTs + 2 * 210 * 1000));
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/holiday/HolidayUtil.java
@@ -9,7 +9,10 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.concurrent.TimeUnit;
@@ -63,17 +66,31 @@
        for (int i = 0; i < parse.size(); i++) {
            JSONObject jsonObject1 = parse.getJSONObject(i);
            String daytype = jsonObject1.get("daytype").toString();
            // 计算今天结束还差多少秒
            long secondsToExactEndOfDay = getSecondsToExactEndOfDay();
            if (daytype.equals("0")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            } else if (daytype.equals("1")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 1;
            } else if (daytype.equals("2")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "2", secondsToExactEndOfDay, TimeUnit.SECONDS);
                return 0;
            }
        }
        return 0;
    }
    public static long getSecondsToExactEndOfDay() {
        LocalDateTime now = LocalDateTime.now();
        // 精确到23:59:59(不包含毫秒/纳秒)
        LocalDateTime endOfDay = LocalDateTime.of(
                LocalDate.now(),
                LocalTime.of(23, 59, 59)
        );
        return Duration.between(now, endOfDay).getSeconds();
    }
}