“mitao”
2026-02-12 d09e1c104aed44b4c2cc3c620ef28f4f5a2ec13a
Merge remote-tracking branch 'origin/master'

# Conflicts:
# xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
2 文件已重命名
13个文件已添加
30个文件已修改
2143 ■■■■■ 已修改文件
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/map/OrderRouteAndFareDto.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/operation/VehiclePositionByVinDto.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/pushMessage/PushOrderRouteDTO.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/map/XpMapClientFallbackFactory.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpPointClientFallbackFactory.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/pushMessage/XpPushMessageClientFallbackFactory.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/vehicleConvoy/XpVehicleClientFallbackFactory.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/map/XpMapClient.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/XpPointClient.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/XpPushMessageClient.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/UserVehicleClient.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/XpVehicleClient.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderEvaluate.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/VehiclePosition.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/operation/VehiclePositionVo.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownDetailVo.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownVo.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/XpPricingCardVo.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/PushMessageTypeEnum.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/web/domain/MongoBaseModel.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/web/service/BaseService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-redis/src/main/java/com/xiaopeng/common/redis/service/RedisService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/mangodb/service/XpGeoFencingPolygonService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/GDMapGeocodingUtil.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpPointManagementController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/XpPricingUtil.java 294 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/mango/VehiclePositionService.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/pom.xml 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mangodb/model/OrderTrajectory.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mangodb/service/OrderTrajectoryService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mangodb/service/impl/OrderTrajectoryServiceImpl.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/dto/XpOrderCommentDto.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/dto/XpOrderPayDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/IXpOrderService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/XpPricingUtil.java 667 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/holiday/HolidayUtil.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/controller/PushOrderRouteController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/utils/MapMatchingUtil.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/map/OrderRouteAndFareDto.java
New file
@@ -0,0 +1,31 @@
package com.xiaopeng.api.dto.map;
import com.xiaopeng.api.dto.operation.LonLatDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
@ApiModel(description = "订单得线路和运费Dto")
public class OrderRouteAndFareDto {
    @ApiModelProperty(value = "出发地")
    @NotNull(message = "出发地不能为空")
    private LonLatDto origin;
    @ApiModelProperty(value = "目的地")
    @NotNull(message = "目的地不能为空")
    private LonLatDto destination;
    @ApiModelProperty(value = "订单id")
    @NotNull(message = "订单id不能为空")
    private Long orderId;
    @ApiModelProperty(value = "轨迹类型 1=接单轨迹 2=服务轨迹 3=巡游轨迹")
    @NotNull(message = "轨迹类型不能为空")
    private Integer routeType;
    @ApiModelProperty(value = "前端不处理")
    private Integer routePlanRule;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/operation/VehiclePositionByVinDto.java
New file
@@ -0,0 +1,25 @@
package com.xiaopeng.api.dto.operation;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@ApiModel(value = "筛选车辆信息Dto")
@AllArgsConstructor
@NoArgsConstructor
public class VehiclePositionByVinDto {
    // 围栏id
    private Long fenceId;
    // 车辆vin
    private List<String> vinList;
    // 数量
    private Integer num;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/pushMessage/PushOrderRouteDTO.java
New file
@@ -0,0 +1,42 @@
package com.xiaopeng.api.dto.pushMessage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@Data
@ApiModel(value = "推送订单轨迹信息")
public class PushOrderRouteDTO implements Serializable {
    @NotNull(message = "订单id不能为空")
    @ApiModelProperty(value = "订单id")
    private Long orderId;
    @NotNull(message = "经度不能为空")
    @ApiModelProperty(value = "经度")
    private Double longitude;
    @NotNull(message = "纬度不能为空")
    @ApiModelProperty(value = "纬度")
    private Double latitude;
    @NotNull(message = "剩余距离不能为空")
    @ApiModelProperty(value = "剩余距离")
    private Integer distance;
    @NotNull(message = "剩余时间不能为空")
    @ApiModelProperty(value = "剩余时间")
    private Integer duration;
    @NotNull(message = "用户id不能为空")
    @ApiModelProperty(value = "用户id")
    private Long appUserId;
    @NotNull(message = "轨迹类型不能为空")
    @ApiModelProperty(value = "轨迹类型 1=接单轨迹 2=服务轨迹 3=巡游轨迹")
    private Integer routeType;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/map/XpMapClientFallbackFactory.java
@@ -1,5 +1,6 @@
package com.xiaopeng.api.factory.map;
import com.xiaopeng.api.dto.map.OrderRouteAndFareDto;
import com.xiaopeng.api.dto.map.RouteAndFareDto;
import com.xiaopeng.api.feignClient.map.XpMapClient;
import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient;
@@ -23,6 +24,12 @@
            }
            @Override
            public R<RouteVo> getOrderDistanceAndDuration(OrderRouteAndFareDto dto) {
                log.error("获取订单距离和时长失败:{}", cause.getMessage());
                return R.fail("获取订单距离和时长失败,"+cause.getMessage());
            }
            @Override
            public R<Boolean> diagnosisArea(RouteAndFareDto dto) {
                log.error("判断是否同一经营区域失败:{}", cause.getMessage());
                return R.fail("判断是否同一经营区域失败");
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpPointClientFallbackFactory.java
@@ -64,7 +64,7 @@
            }
            @Override
            public R queryById(Long id) {
            public R<XpPoint> queryById(Long id) {
                log.error("获取点位详情失败:{}", cause.getMessage());
                return R.fail("获取点位详情失败");
            }
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/pushMessage/XpPushMessageClientFallbackFactory.java
@@ -1,5 +1,6 @@
package com.xiaopeng.api.factory.pushMessage;
import com.xiaopeng.api.dto.pushMessage.PushOrderRouteDTO;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
@@ -41,6 +42,11 @@
            public R<String> sendVehicleLocation(SendMessageDTO dto) {
                return R.fail("推送车辆实时定位失败,"+cause.getMessage());
            }
            @Override
            public R<String> pushOrderRoute(PushOrderRouteDTO dto) {
                return R.fail("通过车辆实时定位推送订单轨迹失败,"+cause.getMessage());
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/vehicleConvoy/XpVehicleClientFallbackFactory.java
@@ -1,7 +1,9 @@
package com.xiaopeng.api.factory.vehicleConvoy;
import com.xiaopeng.api.dto.operation.VehiclePositionByVinDto;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.operation.VehiclePositionVo;
import com.xiaopeng.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
@@ -52,6 +54,12 @@
                log.error("根据围栏id查询车辆信息失败,原因: {}", cause.getMessage());
                return R.ok(new ArrayList<>());
            }
            @Override
            public R<List<String>> queryVehiclePositionByVin(VehiclePositionByVinDto dto) {
                log.error("查询车辆信息失败,原因: {}", cause.getMessage());
                return R.fail();
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/map/XpMapClient.java
@@ -1,5 +1,6 @@
package com.xiaopeng.api.feignClient.map;
import com.xiaopeng.api.dto.map.OrderRouteAndFareDto;
import com.xiaopeng.api.dto.map.RouteAndFareDto;
import com.xiaopeng.api.factory.map.XpMapClientFallbackFactory;
import com.xiaopeng.api.factory.operation.XpSystemSettingClientFallbackFactory;
@@ -14,7 +15,6 @@
@FeignClient(contextId = "XpMapClient", value = ServiceNameConstantsEnum.XP_MAP_SERVICE, fallbackFactory = XpMapClientFallbackFactory.class)
public interface XpMapClient {
    /**
     * 获取距离和时长
     * @param dto
@@ -23,6 +23,14 @@
    @PostMapping("/base/v1/mini-program/map/getDistanceAndDuration")
    R<RouteVo> getDistanceAndDuration(@RequestBody RouteAndFareDto dto);
    /**
     * 获取距离和时长
     * @param dto
     * @return
     */
    @PostMapping("/base/v1/mini-program/map/getOrderDistanceAndDuration")
    R<RouteVo> getOrderDistanceAndDuration(@RequestBody OrderRouteAndFareDto dto);
    /**
     * 判断是否同一经营区域
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/XpPointClient.java
@@ -52,7 +52,7 @@
     * 点位详情
     */
    @GetMapping("/api/v1/point/queryPointById")
    public R queryById(@RequestParam Long id);
    public R<XpPoint> queryById(@RequestParam Long id);
    /**
     * 启用/禁用
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/XpPushMessageClient.java
@@ -1,10 +1,12 @@
package com.xiaopeng.api.feignClient.pushMessage;
import com.xiaopeng.api.dto.pushMessage.PushOrderRouteDTO;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.factory.pushMessage.XpPushMessageClientFallbackFactory;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
@@ -45,5 +47,13 @@
     */
    @PostMapping("/inner/websocket/order/sendVehicleLocation")
    R<String> sendVehicleLocation(@RequestBody SendMessageDTO dto);
    /**
     * 通过车辆实时定位推送订单轨迹
     *
     * @param dto 消息实体
     * @return 通过车辆实时定位推送订单轨迹
     */
    @PostMapping("/inner/v1/order/pushOrderRoute")
    R<String> pushOrderRoute(@RequestBody PushOrderRouteDTO dto);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/UserVehicleClient.java
@@ -1,15 +1,20 @@
package com.xiaopeng.api.feignClient.vehicleConvoy;
import com.xiaopeng.api.dto.operation.VehiclePositionByVinDto;
import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto;
import com.xiaopeng.api.factory.vehicleConvoy.UserVehicleClientFallbackFactory;
import com.xiaopeng.api.vo.operation.VehiclePositionVo;
import com.xiaopeng.api.vo.vehicleConvoy.AddTaxiPassengerVo;
import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo;
import com.xiaopeng.api.vo.vehicleConvoy.GetTaxiPassengersVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
 * 小鹏内部用户中心服务
@@ -43,4 +48,6 @@
     */
    @PostMapping("/logan/vehicleTaxi/update/cancelTaxiPassenger")
    BaseVehicleUserDto cancelTaxiPassenger(@RequestBody CancelTaxiPassengerVo vo);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/XpVehicleClient.java
@@ -1,7 +1,9 @@
package com.xiaopeng.api.feignClient.vehicleConvoy;
import com.xiaopeng.api.dto.operation.VehiclePositionByVinDto;
import com.xiaopeng.api.factory.vehicleConvoy.XpVehicleClientFallbackFactory;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.operation.VehiclePositionVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum;
import org.springframework.cloud.openfeign.FeignClient;
@@ -62,4 +64,11 @@
     */
    @GetMapping("/inner/vehicle/listByFenceId")
    R<List<XpVehicle>> listByFenceId(@RequestParam("fenceId") Long fenceId);
    /**
     * 查询车辆信息 进行筛选
     */
    @PostMapping("/inner/vehicle/queryVehiclePositionByVin")
    R<List<String>> queryVehiclePositionByVin(@RequestBody VehiclePositionByVinDto dto);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderEvaluate.java
@@ -48,5 +48,10 @@
    @ApiModelProperty(value = "评价时间")
    private LocalDateTime evaluateTime;
    @ApiModelProperty(value = "问题类型id")
    private Integer feedbackTypeId;
    @ApiModelProperty(value = "反馈内容")
    private Integer feedbackContent;
    @ApiModelProperty(value = "图片 json格式")
    private Integer feedbackPictures;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/VehiclePosition.java
@@ -20,7 +20,7 @@
        @CompoundIndex(name = "location_index", def = "{'point': '2dsphere'}")
}
)
public class VehiclePosition implements Serializable {
public class  VehiclePosition implements Serializable {
    /**
     * 车辆id
     */
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/operation/VehiclePositionVo.java
File was renamed from xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/vo/VehiclePositionVo.java
@@ -1,12 +1,9 @@
package com.xiaopeng.operation.vo;
package com.xiaopeng.api.vo.operation;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.CompoundIndexes;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.Serializable;
@@ -25,9 +22,9 @@
     */
    private String vin;
    /**
     * 车辆所属围栏id
     * 车辆所属区域id
     */
    private Long electronic_id;
    private Long area_electronic_id;
    /**
     * 车辆实时位置信息
     */
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownDetailVo.java
New file
@@ -0,0 +1,22 @@
package com.xiaopeng.api.vo.order;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
@ApiModel("订单费用明细详情VO")
public class FareBreakdownDetailVo {
    @ApiModelProperty(value = "type=1 普通时段 type=2 夜间行驶时段 type=3节假日时段")
    private Integer type;
    @ApiModelProperty(value = "时段 例:18:00-19:00")
    private String time;
    @ApiModelProperty(value = "里程 单位公里")
    private BigDecimal mileage;
    @ApiModelProperty(value = "时间")
    private BigDecimal duration;
    @ApiModelProperty(value = "金额")
    private BigDecimal money;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownVo.java
@@ -5,38 +5,27 @@
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
@ApiModel("订单费用明细VO")
public class FareBreakdownVo {
    @ApiModelProperty("起步里程")
    private Double startMileage;
    @ApiModelProperty(value = "起步价")
    private BigDecimal startMoney;
    @ApiModelProperty(value = "里程公里")
    private Double mileageKilometers;
    @ApiModelProperty(value = "里程费")
    private BigDecimal mileageMoney;
    @ApiModelProperty(value = "时长分钟")
    private Double duration;
    @ApiModelProperty(value = "时长费")
    private BigDecimal durationMoney;
    @ApiModelProperty(value = "等待分钟")
    private Double wait;
    @ApiModelProperty(value = "等待费")
    private BigDecimal waitMoney;
    @ApiModelProperty(value = "远途公里")
    private Double longDistance;
    @ApiModelProperty(value = "远途费")
    private BigDecimal longDistancemoney;
    @ApiModelProperty("起步价格明细")
    private List<FareBreakdownDetailVo> start;
    @ApiModelProperty("里程费价格明细")
    private List<FareBreakdownDetailVo> mileage;
    @ApiModelProperty("时长费价格明细")
    private List<FareBreakdownDetailVo> duration;
    @ApiModelProperty("远途费价格明细")
    private List<FareBreakdownDetailVo> longDistance;
    @ApiModelProperty(value = "红包抵扣金额")
    private BigDecimal redPacketMoney;
    @ApiModelProperty(value = "优惠券抵扣金额")
    private BigDecimal couponMoney;
    @ApiModelProperty(value = "订单金额")
    private BigDecimal orderMoney;
    @ApiModelProperty(value = "订单全程公里数")
    private BigDecimal totalKilometers;
    @ApiModelProperty(value = "订单全程时间(单位分钟)")
    private BigDecimal totalTime;
    @ApiModelProperty(value = "订单行驶里程")
    private BigDecimal kilometer;
    @ApiModelProperty(value = "订单行驶时间")
    private BigDecimal time;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/XpPricingCardVo.java
New file
@@ -0,0 +1,47 @@
package com.xiaopeng.api.vo.order;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "XpPricingCardVo", description = "冒泡页价格卡片")
public class XpPricingCardVo {
    @ApiModelProperty(value = "价格总额")
    private Double totalPrice=0.0;
    @ApiModelProperty(value = "起步价格")
    private Double startPrice;
    @ApiModelProperty(value = "时间段")
    private String time;
    @ApiModelProperty(value = "距离")
    private Double distance;
    @ApiModelProperty(value = "时长")
    private Double duration;
    @ApiModelProperty(value = "里程费")
    private Double distancePrice;
    @ApiModelProperty(value = "普通时段公里数")
    private Double distancePriceNormal;
    @ApiModelProperty(value = "时长费")
    private Double durationPrice;
    @ApiModelProperty(value = "普通时段时长数")
    private Double durationPriceNormal;
    @ApiModelProperty(value = "优惠券")
    private String coupon;
}
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java
@@ -115,4 +115,16 @@
     * 附近点位距离
     */
    public static final String NEAR_POINT_DISTANCE = "NEAR_POINT_DISTANCE";
    /**
     * 订单车辆位置
     */
    public static final String ORDER_OLD_LOCATION = "order_old_location:";
    /**
     * 订单车辆轨迹
     */
    public static final String ORDER_ROUTE = "order_route:";
    /**
     * 订单车辆轨迹,禁止用于其他用处
     */
    public static final String ORDER_ROUTE_CAPTURE = "order_route_capture:";
}
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/PushMessageTypeEnum.java
@@ -11,6 +11,7 @@
    ORDER_STATUS("ORDER_STATUS"),
    VEHICLE_LOCATION("VEHICLE_LOCATION"),
    ORDER_ROUTE("ORDER_ROUTE");
    @Getter
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/web/domain/MongoBaseModel.java
New file
@@ -0,0 +1,16 @@
package com.xiaopeng.common.core.web.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
 * @author zhibing.pu
 * @Date 2024/8/26 19:05
 */
@Data
public class MongoBaseModel {
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime = new Date();
}
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/web/service/BaseService.java
File was renamed from xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/mangodb/base/BaseService.java
@@ -1,4 +1,4 @@
package com.xiaopeng.map.mangodb.base;
package com.xiaopeng.common.core.web.service;
import java.util.List;
xp-robotaxi-common/xp-common-redis/src/main/java/com/xiaopeng/common/redis/service/RedisService.java
@@ -173,8 +173,9 @@
    }
    // 获取第一个元素但不删除
    public Object getFirstElement(String key) {
        return redisTemplate.opsForList().index(key, 0);
    public String getFirstElement(String key) {
        String index = (String) redisTemplate.opsForList().index(key, 0);
        return index;
    }
    // 获取第一个元素并删除(弹出)
@@ -374,7 +375,7 @@
     */
    public Set<String> getQuoteMatchName(String name) {
        // 模糊查询key为name的值
        Set<String> keys = redisTemplate.keys("*" + name );
        Set<String> keys = redisTemplate.keys(name + "*");
        return keys;
    }
}
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java
@@ -1,11 +1,14 @@
package com.xiaopeng.map.controller;
import com.xiaopeng.api.dto.map.OrderRouteAndFareDto;
import com.xiaopeng.api.dto.map.RouteAndFareDto;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.vo.map.XpDistanceAndDurationVo;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.map.mangodb.model.GeoPoint;
import com.xiaopeng.map.mangodb.service.XpGeoFencingPolygonService;
import com.xiaopeng.map.utils.GDMapGeocodingUtil;
@@ -13,6 +16,7 @@
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -31,10 +35,12 @@
    private final GDMapGeocodingUtil gdMapGeocodingUtil;
    private final XpGeoFencingPolygonService xpGeoFencingPolygonService;
    private final RedisService redisService;
    public XpMapController(GDMapGeocodingUtil gdMapGeocodingUtil, XpGeoFencingPolygonService xpGeoFencingPolygonService) {
    public XpMapController(GDMapGeocodingUtil gdMapGeocodingUtil, XpGeoFencingPolygonService xpGeoFencingPolygonService, RedisService redisService) {
        this.gdMapGeocodingUtil = gdMapGeocodingUtil;
        this.xpGeoFencingPolygonService = xpGeoFencingPolygonService;
        this.redisService = redisService;
    }
@@ -72,5 +78,45 @@
    }
    /**
     * 获取订单距离和时长
     * @param dto
     * @return
     */
    @PostMapping("/map/getOrderDistanceAndDuration")
    public R<RouteVo> getOrderDistanceAndDuration(@Validated @RequestBody OrderRouteAndFareDto dto){
        // 获取电子围栏的禁行区域点位
        GeoPoint startPoint = new GeoPoint(dto.getOrigin().getLongitude(),dto.getOrigin().getLatitude());
        List<String> operatingArea = xpGeoFencingPolygonService.findFencesContainingPoint(startPoint, "operating_area", "points");
        String fenceId = operatingArea.get(0);
        List<Document> pointsInFence = xpGeoFencingPolygonService.findPointsByParentId(fenceId, "parentId", "prohibited_area");
        // 避让区域
        ArrayList<String> pointStrParam = new ArrayList<>();
        for (Document document : pointsInFence) {
            List<GeoPoint> points = document.getList("points", GeoPoint.class);
            // 一个区域的点位
            ArrayList<String> pointStr = new ArrayList<>();
            for (GeoPoint point : points) {
                pointStr.add( point.getLongitude()+","+point.getLatitude());
            }
            if(!pointStr.isEmpty()){
                pointStrParam.add(String.join(";", pointStr));
            }
        }
        RouteVo routeVo = gdMapGeocodingUtil.getDistanceAndTime(dto.getOrigin(), dto.getDestination(), String.join("|", pointStrParam), fenceId,dto.getRoutePlanRule());
        if(routeVo==null){
            return R.fail("没有找到路线");
        }
        // 存储redis
        redisService.setCacheObject(CacheConstants.ORDER_ROUTE + "_" + dto.getOrderId() + "_" +dto.getRouteType(), routeVo);
        // 存储路线偏离缓存,后续进行轨迹截取
        redisService.setCacheObject(CacheConstants.ORDER_ROUTE_CAPTURE + "_" + dto.getOrderId() + "_" +dto.getRouteType(), routeVo);
        return R.ok(routeVo);
    }
}
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/mangodb/service/XpGeoFencingPolygonService.java
@@ -1,7 +1,7 @@
package com.xiaopeng.map.mangodb.service;
import com.xiaopeng.api.model.map.XpGeoFencingPolygon;
import com.xiaopeng.map.mangodb.base.BaseService;
import com.xiaopeng.common.core.web.service.BaseService;
import com.xiaopeng.map.mangodb.model.GeoPoint;
import org.bson.Document;
import org.springframework.stereotype.Repository;
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/GDMapGeocodingUtil.java
@@ -5,6 +5,7 @@
import com.alibaba.fastjson.JSONObject;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.common.core.utils.HttpUtils;
import com.xiaopeng.map.mangodb.service.XpGeoFencingPolygonService;
import com.xiaopeng.map.utils.mangoUtils.MongoGeoFenceUtil;
import org.springframework.beans.factory.annotation.Autowired;
@@ -54,11 +55,15 @@
        params.put("start", start.getLongitude() + "," + start.getLatitude());
        params.put("end", end.getLongitude() + "," + end.getLatitude());
        params.put("routeUserType",2);
        params.put("voidPolygons", avoidRoads);
        if(avoidRoads!=null && !avoidRoads.isEmpty()){
            params.put("voidPolygons", avoidRoads);
        }
        if(routePlanRule!=null){
            params.put("strategy", routePlanRule);
        }
        String post = HttpUtils.post(url, JSON.toJSONString(params));
        System.out.println("高德地图返回结果:"+ post);
        String forObject = restTemplate.getForObject(url, String.class, params);
        JSONObject jsonObject = JSON.parseObject(forObject);
        String data = jsonObject.getString("data");
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpPointManagementController.java
@@ -86,7 +86,7 @@
     */
    @GetMapping("/queryPointById")
    @ApiOperation(value = "点位详情")
    public R queryById(@RequestParam Long id) {
    public R<XpPoint> queryById(@RequestParam Long id) {
        return R.ok(xpPointService.getById(id));
    }
    /**
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java
@@ -396,11 +396,11 @@
        // 获取起步里程
        XpPricing xpPricing = this.baseMapper.selectOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, dto.getOpenCityId()));
        Integer startMileage = XpPricingUtil.getStartMileage(xpPricing.getStartingFareJson());
        if (startMileage == null) {
            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
        }
//        XpPricing xpPricing = this.baseMapper.selectOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, dto.getOpenCityId()));
//        Integer startMileage = XpPricingUtil.getStartMileage(xpPricing.getStartingFareJson());
//        if (startMileage == null) {
//            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
//        }
        // 根据id获取手机号
@@ -417,7 +417,7 @@
        // 生成订单数据
        XpOrder xpOrder = new XpOrder();
        xpOrder.setStartMileage(Double.valueOf(startMileage));
//        xpOrder.setStartMileage(Double.valueOf(startMileage));
        xpOrder.setAppUserId(Long.valueOf(uid));
        xpOrder.setOrderNumber("XP" + System.currentTimeMillis());
@@ -473,10 +473,10 @@
        Integer distance = 0;
        Integer duration = 0;
        if (distanceAndDuration.getCode() != 200) {
            throw new ServiceException("订单生成失败", I18nKeyConstant.ORDER_CREATE_FAIL);
//            throw new ServiceException("订单生成失败", I18nKeyConstant.ORDER_CREATE_FAIL);
        }
        distance = distanceAndDuration.getData().getDistance();
        duration = distanceAndDuration.getData().getDuration();
//        distance = distanceAndDuration.getData().getDistance();
//        duration = distanceAndDuration.getData().getDuration();
        // 产生订单
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java
@@ -3,10 +3,14 @@
import cn.hutool.core.bean.BeanUtil;
import com.xiaopeng.api.dto.map.RouteAndFareDto;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.dto.operation.VehiclePositionByVinDto;
import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto;
import com.xiaopeng.api.feignClient.map.XpMapClient;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.UserVehicleClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.VehicleControlClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.operation.XpElectronicFence;
import com.xiaopeng.api.model.operation.XpTradingStrategy;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
@@ -15,18 +19,15 @@
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.operation.mapper.XpElectronicFenceMapper;
import com.xiaopeng.operation.service.IXpElectronicFenceAreaService;
import com.xiaopeng.operation.service.IXpTradingStrategyService;
import com.xiaopeng.operation.service.XpPointService;
import com.xiaopeng.operation.utils.mango.VehiclePositionService;
import com.xiaopeng.operation.utils.map.FastDistanceCalculator;
import com.xiaopeng.operation.vo.RouterVo;
import com.xiaopeng.operation.vo.VehiclePositionVo;
import org.redisson.Redisson;
import com.xiaopeng.api.vo.operation.VehiclePositionVo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
@@ -62,6 +63,9 @@
    private UserVehicleClient userVehicleClient;
    @Autowired
    private XpVehicleClient xpVehicleClient;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
@@ -80,21 +84,24 @@
        threadPoolTaskExecutor.initialize();
    }
    @Autowired
    private XpElectronicFenceMapper xpElectronicFenceMapper;
    /**
     * 5秒执行一次
     */
//    @Scheduled(cron = "0/5 * * * * ?")
    @Scheduled(cron = "0/5 * * * * ?")
    public void taskOrder() {
        System.out.println("5秒执行一次");
        Set<String> quoteMatchName = redisService.getQuoteMatchName(CacheConstants.QUEUE_POINT_ID);
        for (String name : quoteMatchName) {
            Object firstElement = redisService.getFirstElement(name);
            String firstElement = redisService.getFirstElement(name);
            if (firstElement != null) {
                String[] split = firstElement.toString().split("_");
                // 如果市未处理 创建新任务
                if (split[2].equals("0")) {
                    // 状态改为处理中
                    redisService.updateFirst(name, split[0] + "_" + split[1] + "_1");
//                    redisService.updateFirst(name, split[0] + "_" + split[1] + "_1"+split[3]+split[4]+split[5]+split[6]);
                    threadPoolTaskExecutor.submit(() -> {
                        orderProcess(name, firstElement.toString());
                    });
@@ -110,6 +117,7 @@
     * @param firstElement 队列第一个元素
     */
    private void orderProcess(String quoteName, String firstElement) {
        System.out.println("进入处理环节");
        XpTradingStrategy xpTradingStrategy = xpTradingStrategyService.getOne(null);
        String orderId = firstElement.split("_")[0];
        String lonLat = firstElement.split("_")[1];
@@ -190,7 +198,7 @@
        processPushOrder(quoteName, count, uid, split, orderNo, xpTradingStrategy, distance, duration, planRule);
        // 最终没找到车
        redisService.popFirstElement(quoteName);
//        redisService.popFirstElement(quoteName);
        // 取消订单
        xpOrderClient.cancelOrder(orderId);
@@ -207,6 +215,7 @@
     * @param xpTradingStrategy  规则
     */
    private void processPushOrder(String quoteName, int count, String uid, String[] split, String orderNo, XpTradingStrategy xpTradingStrategy, String distance, String duration, String planRule) {
        System.out.println("进入推单环环节");
        // 订单状态
        R<XpOrder> xpOrderR = xpOrderClient.queryOrderByOrderNo(orderNo);
        if (xpOrderR.getCode() != 200) {
@@ -215,12 +224,39 @@
        if (xpOrderR.getData().getStatus() != 1) {
            return;
        }
        XpOrder xpOrder = xpOrderR.getData();
        // 找出围栏id
        Long xpElectronicFenceAreaId = xpOrder.getXpElectronicFenceAreaId();
        XpElectronicFence xpElectronicFence = xpElectronicFenceMapper.selectById(xpElectronicFenceAreaId);
        for (int i = 0; i < count; i++) {
            System.out.println("进入找车环节");
            // 根据距离找车
            // 车多则根据配置找具体车数量 根据距离最近
            // 更改 暂不限制数量
            List<VehiclePosition> positionList = vehiclePositionService.findAvailableVehiclesNearby(Double.parseDouble(split[0]), Double.parseDouble(split[1]), xpTradingStrategy.getOnePushDistance(), xpTradingStrategy.getSimultaneouslyOrderCount());
            // 获取车辆的区域信息
            if(positionList.isEmpty()){
                continue;
            }
            List<String> vinList = positionList.stream().map(VehiclePosition::getVin).collect(Collectors.toList());
            // 获取车辆的信息 筛选出可用车辆
            R<List<String>> vehiclePositionVos = xpVehicleClient.queryVehiclePositionByVin(new VehiclePositionByVinDto(xpElectronicFence.getId(), vinList,  xpTradingStrategy.getSimultaneouslyOrderCount()));
            if(vehiclePositionVos.getCode()!=200){
                continue;
            }
            List<VehiclePositionVo> availableVehiclesNearby = BeanUtil.copyToList(positionList, VehiclePositionVo.class);
            List<String> data = vehiclePositionVos.getData();
            // 找出符合条件的
           availableVehiclesNearby = availableVehiclesNearby.stream().filter(e->data.contains(e.getVin())).collect(Collectors.toList());
//            List<VehiclePositionVo> availableVehiclesNearby =vehiclePositionVos.getData();
            if (!availableVehiclesNearby.isEmpty()) {
                if (availableVehiclesNearby.size() > 1) {
@@ -584,6 +620,9 @@
                        addTaxiPassengerVo.setOrderNo(orderNo);
                        // TODO appid
                        addTaxiPassengerVo.setAppId("");
                        BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.addTaxiPassenger(addTaxiPassengerVo);
                        if (baseVehicleUserDto.getCode() == 200) {
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/XpPricingUtil.java
@@ -357,6 +357,300 @@
        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;
//    }
    /**
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/mango/VehiclePositionService.java
@@ -40,13 +40,13 @@
                .near(referencePoint)
                .maxDistance(distanceInMeters));
        query.addCriteria(Criteria.where("status").is(1)); // 运营中
        query.addCriteria(Criteria.where("state").is(1));  // 接单中
        query.addCriteria(Criteria.where("state").is(0));  // 空闲中
        
        // 可以添加排序,按距离排序
         query.with(Sort.by(Sort.Direction.ASC, "point"));
        
        // 可以限制返回数量
         query.limit(num);
//        // 可以限制返回数量
//         query.limit(num);
        
        return mongoTemplate.find(query, VehiclePosition.class);
    }
@@ -63,4 +63,21 @@
        return mongoTemplate.findOne(query, VehiclePosition.class);
    }
    /**
     * 所有的车辆
     */
    public List<VehiclePosition> findAll() {
        // 构建查询条件
        Query query = new Query();
        query.addCriteria(Criteria.where("status").is(1)); // 运营中
        query.addCriteria(Criteria.where("state").is(1));  // 接单中
        // 可以添加排序,按距离排序
        query.with(Sort.by(Sort.Direction.ASC, "point"));
        return mongoTemplate.find(query, VehiclePosition.class);
    }
}
xp-robotaxi-order-boot/pom.xml
@@ -15,14 +15,14 @@
    </description>
    <dependencies>
        <!--        <dependency>-->
        <!--            <groupId>com.alibaba.cloud</groupId>-->
        <!--            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>-->
        <!--        </dependency>-->
        <!--        <dependency>-->
        <!--            <groupId>com.alibaba.cloud</groupId>-->
        <!--            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>-->
        <!--        </dependency>-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
@@ -120,21 +120,23 @@
        </dependency>
        <dependency>
            <groupId>com.xiaopeng.logan</groupId>
            <artifactId>logan-starter-core</artifactId>
            <version>2.3.5</version>
        </dependency>
        <dependency>
            <groupId>com.xiaopeng.logan</groupId>
            <artifactId>logan-commons-legacy</artifactId>
            <version>2.3.5</version>
        </dependency>
        <dependency>
            <groupId>com.xiaopeng.logan</groupId>
            <artifactId>logan2-namor-client</artifactId>
            <version>1.16.0</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>com.xiaopeng.logan</groupId>-->
<!--            <artifactId>logan-starter-core</artifactId>-->
<!--            <version>2.3.5</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>com.xiaopeng.logan</groupId>-->
<!--            <artifactId>logan-commons-legacy</artifactId>-->
<!--            <version>2.3.5</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>com.xiaopeng.logan</groupId>-->
<!--            <artifactId>logan2-namor-client</artifactId>-->
<!--            <version>1.16.0</version>-->
<!--        </dependency>-->
    </dependencies>
    <build>
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java
@@ -16,6 +16,7 @@
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.utils.WebUtils;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.order.mangodb.model.OrderTrajectory;
import com.xiaopeng.order.model.dto.XpOrderCommentDto;
import com.xiaopeng.order.model.dto.XpOrderPayDto;
import com.xiaopeng.order.model.exportExcel.XpOrderExportExcel;
@@ -366,6 +367,7 @@
        String uid = request.getHeader("XP-Uid");
        return xpOrderService.payment(dto,uid);
    }
    /**
     * 订单评价
     */
@@ -382,8 +384,8 @@
    /**
     * 订单状态修改-车控消息推送我方调用(内部接口)
     */
    @PostMapping("/inner/v1/order/updateOrderStatus")
    @ApiOperation(value = "订单状态推送修改")
    @PostMapping("/inner/v1/order/updateOrderStatus")
    public R<String> updateOrderStatus(@RequestParam Long orderId,
                                       @RequestParam Integer status) {
        return xpOrderService.updateOrderStatus(orderId,status);
@@ -393,10 +395,21 @@
     * 业务流程-通过订单id查询车辆信息
     */
    @ApiOperation(value = "通过订单id查询车辆信息", tags = {"小程序-业务流程"})
    @GetMapping("/api/v1/order/queryVehicleByOrderId")
    @XpClientCheck(checkToken = false, checkOtpToken = true)
    @GetMapping("/api/v1/mini-program/order/queryVehicleByOrderId")
    public R<XpVehicle> queryVehicleByOrderId(@RequestParam Long orderId, HttpServletRequest request) {
        String uid = request.getHeader("XP-Uid");
        return R.ok(xpOrderService.queryVehicleByOrderId(orderId,uid));
    }
    /**
     * 订单车辆定位实时存储-订单车辆定位实时存储(内部接口)
     */
    @ApiOperation(value = "订单车辆定位实时存储")
    @PostMapping("/inner/v1/order/saveVehicleLocation")
    public R<String> saveVehicleLocation(@RequestBody OrderTrajectory orderTrajectory) {
        xpOrderService.saveVehicleLocation(orderTrajectory);
        return R.ok();
    }
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mangodb/model/OrderTrajectory.java
New file
@@ -0,0 +1,47 @@
package com.xiaopeng.order.mangodb.model;
import com.xiaopeng.common.core.web.domain.MongoBaseModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "order_trajectory") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class OrderTrajectory extends MongoBaseModel {
    /**
     * 订单id
     */
    private Long orderId;
    /**
     * 经纬度 坐标  (104.45645456,34.41564654)
     */
    private String location;
    /**
     * 方向角(0-359°)
     */
    private Integer direction;
    /**
     * 速度
     */
    private Double speed;
    /**
     * 时间戳 毫秒
     */
    private Long locatetime;
    /**
     * 距离 米
     */
    private Double distance;
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mangodb/service/OrderTrajectoryService.java
New file
@@ -0,0 +1,15 @@
package com.xiaopeng.order.mangodb.service;
import com.xiaopeng.common.core.web.service.BaseService;
import com.xiaopeng.order.mangodb.model.OrderTrajectory;
import org.springframework.stereotype.Repository;
/**
 * MongoDB Repository for XpGeoFencingPolygon
 * @author zhibing.pu
 * @date 2023/4/29 23:53
 */
@Repository
public interface OrderTrajectoryService extends BaseService<OrderTrajectory> {
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mangodb/service/impl/OrderTrajectoryServiceImpl.java
New file
@@ -0,0 +1,33 @@
package com.xiaopeng.order.mangodb.service.impl;
import com.xiaopeng.order.mangodb.model.OrderTrajectory;
import com.xiaopeng.order.mangodb.service.OrderTrajectoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class OrderTrajectoryServiceImpl implements OrderTrajectoryService {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Override
    public int create(OrderTrajectory orderTrajectory) {
        mongoTemplate.insert(orderTrajectory);
        return 1;
    }
    @Override
    public OrderTrajectory findById(String id) {
        return mongoTemplate.findById(id, OrderTrajectory.class);
    }
    @Override
    public List<OrderTrajectory> findAll() {
        return mongoTemplate.findAll(OrderTrajectory.class);
    }
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/dto/XpOrderCommentDto.java
@@ -11,4 +11,10 @@
    @ApiModelProperty(value = "订单评分")
    private Integer orderRating;
    @ApiModelProperty(value = "问题类型id")
    private Integer feedbackTypeId;
    @ApiModelProperty(value = "反馈内容")
    private Integer feedbackContent;
    @ApiModelProperty(value = "图片 json格式")
    private Integer feedbackPictures;
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/dto/XpOrderPayDto.java
@@ -8,7 +8,4 @@
@ApiModel(value = "订单发起支付参数")
public class XpOrderPayDto {
    private Long orderId;
    @ApiModelProperty(value = "用户所选优惠券")
    private Long couponId;
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/IXpOrderService.java
@@ -13,6 +13,7 @@
import com.xiaopeng.api.vo.order.XpPointInfoVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.order.mangodb.model.OrderTrajectory;
import com.xiaopeng.order.model.dto.XpOrderCommentDto;
import com.xiaopeng.order.model.dto.XpOrderPayDto;
import com.xiaopeng.order.model.query.XpHistoryOrderListQuery;
@@ -180,4 +181,9 @@
     */
    Integer getOrderNum(GetOrderNumDto dto);
    /**
     * 保存车辆位置信息
     * @param orderTrajectory
     */
    void saveVehicleLocation(OrderTrajectory orderTrajectory);
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -26,6 +26,7 @@
import com.xiaopeng.api.feignClient.operation.XpPointClient;
import com.xiaopeng.api.feignClient.operation.XpPricingClient;
import com.xiaopeng.api.model.account.XpAppUser;
import com.xiaopeng.api.model.operation.XpPoint;
import com.xiaopeng.api.model.operation.XpPricing;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
@@ -34,14 +35,13 @@
import com.xiaopeng.api.model.order.XpOrderEvaluate;
import com.xiaopeng.api.model.order.XpOrderReassign;
import com.xiaopeng.api.vo.account.UserCouponVo;
import com.xiaopeng.api.vo.order.FareBreakdownVo;
import com.xiaopeng.api.vo.order.XpHistoryEndPointsVo;
import com.xiaopeng.api.vo.order.XpHistoryStartPointsVo;
import com.xiaopeng.api.vo.order.XpPointInfoVo;
import com.xiaopeng.api.vo.order.*;
import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.DateUtils;
import com.xiaopeng.common.core.utils.GeodesyUtil;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.core.web.domain.BaseModel;
import com.xiaopeng.common.core.web.page.PageInfo;
@@ -49,6 +49,9 @@
import com.xiaopeng.common.security.utils.SecurityUtils;
import com.xiaopeng.order.constant.I18nKeyConstant;
import com.xiaopeng.order.job.jobs.EndTripJob;
import com.xiaopeng.order.mangodb.model.OrderTrajectory;
import com.xiaopeng.order.mangodb.service.OrderTrajectoryService;
import com.xiaopeng.order.mangodb.model.OrderTrajectory;
import com.xiaopeng.order.mapper.XpOrderMapper;
import com.xiaopeng.order.model.dto.XpOrderCommentDto;
import com.xiaopeng.order.model.dto.XpOrderPayDto;
@@ -66,7 +69,12 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
@@ -107,6 +115,12 @@
    private final RedisService redisService;
    private final XpPricingClient xpPricingClient;
    private final XpSystemSettingClient xpSystemSettingClient;
    private final OrderTrajectoryService orderTrajectoryService;
    @Autowired
    private MongoTemplate mongoTemplate;
//    @Autowired
//    private XpPricingUtil xpPricingUtil;
    @Override
    public OrderStatisticsDTO getOrderStatistics(StatisticsQueryDTO query) {
@@ -769,6 +783,9 @@
        evaluate.setAppUserId(Long.valueOf(uid));
        evaluate.setOrderRating(dto.getOrderRating());
        evaluate.setEvaluateTime(LocalDateTime.now());
        evaluate.setFeedbackContent(dto.getFeedbackContent());
        evaluate.setFeedbackPictures(dto.getFeedbackPictures());
        evaluate.setFeedbackTypeId(dto.getFeedbackTypeId());
        xpOrderEvaluateService.save(evaluate);
    }
@@ -785,7 +802,10 @@
        xpOrder.setStatus(status);
        switch (status) {
            case 4:
                // 添加取消订单定时任务job
                // 查询起点点位
                Long startPointId = xpOrder.getStartPointId();
                XpPoint xpPoint = xpPointClient.queryById(startPointId).getData();
                // 添加取消订单定时任务job  时间为点位限停时长
                Map<String, ? extends Object> cancelJobMaps =
                        new ImmutableMap.Builder<String, Long>().
                                put("id", xpOrder.getId())
@@ -794,7 +814,7 @@
                        OrderCancelJob.class,
                        (OrderCancelJob.name + xpOrder.getId()).toUpperCase(),
                        TimeJobType.ORDER_CANCEL,
                        DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(5)),
                        DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(xpPoint.getParkingTimeLimit())),
                        cancelJobMaps
                );
                // 车辆到达上车点时间
@@ -847,39 +867,70 @@
        if (pricing == null) {
            throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS);
        }
        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 {
        // 查询订单轨迹
        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) {
        }
        if (org.springframework.util.StringUtils.hasLength(basicPricingJson)) {
//        XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationPrice(
//                order.getAppUserId().toString(),JSON.parseObject(pricing.getBasicPricingJson()),
//                JSON.parseArray(pricing.getStartingFareJson()),
//                pricing.getDynamicPricingJson(),
//                pricing.getIsDynamicPrice(),
//                totalDistance,
//                seconds,
//                order.getCityId()
//        );
        }
        if (org.springframework.util.StringUtils.hasLength(startingFareJson)) {
//        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)) {
//
//        }
        }
        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);
@@ -907,23 +958,43 @@
    @Override
    public R<FareBreakdownVo> fareBreakdown(Long orderId, String uid) {
        XpOrder order = this.getById(orderId);
        FareBreakdownVo fareBreakdownVo = new FareBreakdownVo();
        BeanUtils.copyProperties(order, fareBreakdownVo);
        fareBreakdownVo.setTotalKilometers(
                BigDecimal.valueOf(order.getStartMileage() + order.getMileageKilometers() + order.getLongDistance())
        // 查询订单轨迹
        List<OrderTrajectory> trajectories = mongoTemplate.find(
                new Query(Criteria.where("orderId").is(orderId))
                        .with(Sort.by(Sort.Direction.ASC, "locatetime")),
                OrderTrajectory.class
        );
        LocalDateTime createTime = order.getCreateTime();
        LocalDateTime tripEnd = order.getTripEnd();
        // 计算行程时间 单位分钟
        // 计算订单总行驶距离,单位米
        Double totalDistance = 0.00D;
        for (OrderTrajectory trajectory : trajectories) {
        // 计算时间差
        Duration duration = Duration.between(createTime, tripEnd);
        // 转换为分钟并保留一位小数
        BigDecimal minutes = BigDecimal.valueOf(duration.getSeconds())
                .divide(BigDecimal.valueOf(60), 1, RoundingMode.HALF_UP);
        }
        FareBreakdownVo fareBreakdownVo = new FareBreakdownVo();
//        fareBreakdownVo.setStart();
//        fareBreakdownVo.setMileage();
//        fareBreakdownVo.setDuration();
//        fareBreakdownVo.setLongDistance();
//        fareBreakdownVo.setRedPacketMoney();
//        fareBreakdownVo.setCouponMoney();
//        fareBreakdownVo.setOrderMoney();
        fareBreakdownVo.setTotalTime(minutes);
//        BeanUtils.copyProperties(order, fareBreakdownVo);
//        fareBreakdownVo.setTotalKilometers(
//                BigDecimal.valueOf(order.getStartMileage() + order.getMileageKilometers() + order.getLongDistance())
//        );
//        LocalDateTime createTime = order.getCreateTime();
//        LocalDateTime tripEnd = order.getTripEnd();
//        // 计算行程时间 单位分钟
//
//        // 计算时间差
//        Duration duration = Duration.between(createTime, tripEnd);
//
//        // 转换为分钟并保留一位小数
//        BigDecimal minutes = BigDecimal.valueOf(duration.getSeconds())
//                .divide(BigDecimal.valueOf(60), 1, RoundingMode.HALF_UP);
//
//        fareBreakdownVo.setTotalTime(minutes);
        return R.ok(fareBreakdownVo);
    }
@@ -938,6 +1009,7 @@
    /**
     * 添加订单
     *
     * @param xpOrder
     * @return
     */
@@ -960,6 +1032,7 @@
    /**
     * 获取订单数量
     *
     * @param dto
     * @return
     */
@@ -969,6 +1042,41 @@
        return num.intValue();
    }
    @Override
    public void saveVehicleLocation(OrderTrajectory orderTrajectory) {
        // 查询订单信息
        XpOrder xpOrder = this.getById(orderTrajectory.getOrderId());
        // 计算距离
        // 获取上一次定位
        String oldLocation = redisService.getCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId());
        if(StringUtils.isEmpty(oldLocation)){
            // 首次定位
            xpOrder.setMileageKilometers(0D);
            this.updateById(xpOrder);
            orderTrajectory.setDistance(0D);
            // 存储mongo数据
            orderTrajectoryService.create(orderTrajectory);
            // 存储首次定位
            redisService.setCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId(), orderTrajectory.getLocation());
        }else {
            Map<String, Double> distance = GeodesyUtil.getDistance(orderTrajectory.getLocation(), oldLocation);
            if(null != distance){
                Double distance1 = distance.get("WGS84");
                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);
                    // 存储本次距离
                    orderTrajectory.setDistance(distance1);
                    // 存储mongo数据
                    orderTrajectoryService.create(orderTrajectory);
                }
            }else{
                log.error("调用高德计算距离出错");
            }
        }
    }
    /**
     * 获取当前用户正在执行的订单
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/XpPricingUtil.java
New file
@@ -0,0 +1,667 @@
//package com.xiaopeng.order.utils;
//
//import com.alibaba.fastjson.JSONArray;
//import com.alibaba.fastjson.JSONObject;
//import com.xiaopeng.api.dto.order.GetOrderNumDto;
//import com.xiaopeng.api.feignClient.order.XpOrderClient;
//import com.xiaopeng.api.vo.order.XpPricingCardVo;
//import com.xiaopeng.common.core.domain.R;
//import com.xiaopeng.common.redis.service.RedisService;
//import com.xiaopeng.order.service.IXpOrderService;
//import com.xiaopeng.order.utils.holiday.HolidayUtil;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.stereotype.Component;
//
//import java.math.BigDecimal;
//import java.math.RoundingMode;
//import java.util.Calendar;
//import java.util.Date;
//import java.util.concurrent.TimeUnit;
//
///**
// * 计算价格的工具类
// */
//@Slf4j
//@Component
//public class XpPricingUtil {
//
//    @Autowired
//    private HolidayUtil holidayUtil;
//
//    @Autowired
//    private RedisService redisService;
//    @Autowired
//    private IXpOrderService orderService;
//
//    /**
//     * 获取起步里程
//     *
//     * @param startingFareJsonStr
//     * @return
//     */
//    public static Integer getStartMileage(String startingFareJsonStr) {
//        Date date = new Date();
//        JSONArray startingFareJson = JSONObject.parseArray(startingFareJsonStr);
//        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()) {
//                Integer mileage = startingFare.getInteger("num4");
//                return mileage;
//            }
//        }
//        return null;
//    }
//
//
//    /**
//     * 计算基础价格
//     * @param uid                 用户id
//     * @param rule                 基础规则
//     * @param distance             里程
//     * @param duration             时间
//     * @param isDynamicPriceStatus 是否开启动态调价
//     *
//     * @param openCityId           开通城市id
//     * @return
//     * @throws Exception
//     */
//    public XpPricingCardVo calculationPrice(String uid, JSONObject rule,
//                                            JSONArray startingFareJson,
//                                            String dynamicPricingJson,
//                                            Integer isDynamicPriceStatus,
//                                            double distance,
//                                            long duration,
//                                            Long openCityId) {
//        // 默认费率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();
//                    // 判断当前距离这个时间范围的新增订单数(排除取消) / 上浮数量 算出应该增加的价格率
//                    Integer num = orderService.getOrderNum(new GetOrderNumDto(timeStr, openCityId));
//                    // 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;
//    }
//    /**
//     *  行程结束后计算费用
//     * @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;
////    }
//
//
//    /**
//     * 获取配置  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;
//    }
//}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/holiday/HolidayUtil.java
New file
@@ -0,0 +1,80 @@
package com.xiaopeng.order.utils.holiday;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.utils.HttpUtils;
import com.xiaopeng.common.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.concurrent.TimeUnit;
@Component
public class HolidayUtil {
    @Autowired
    private RedisService redisService;
    public  boolean isHoliday() {
        LocalDate now = LocalDate.now();
        // 判断缓存里面是否有节假日算法
        if (redisService.getCacheObject(CacheConstants.IS_HOLIDAY+now) != null && ("1").equals(redisService.getCacheObject(CacheConstants.IS_HOLIDAY+now).toString())) {
            return true;
        }
        String result = HttpUtil.get("https://publicapi.xiaoai.me/holiday/day?date=" +now);
        JSONObject jsonObject = JSONObject.parseObject(result);
        Object data = jsonObject.get("data");
        JSONArray parse = JSONArray.parse(data.toString());
        for (int i = 0; i < parse.size(); i++) {
            JSONObject jsonObject1 = parse.getJSONObject(i);
            if (jsonObject1.get("daytype").toString().equals("1")) {
                redisService.setCacheObject(CacheConstants.IS_HOLIDAY+now, "1", 1l, TimeUnit.DAYS);
                return true;
            }
        }
        redisService.setCacheObject(CacheConstants.IS_HOLIDAY+now, "0", 1l, TimeUnit.DAYS);
        return false;
    }
    /**
     * 2非工作日  0工作日  1节假日 3调休日
     * @return
     */
    public  Integer isHolidayToday() {
        LocalDate now = LocalDate.now();
        Object cacheObject = redisService.getCacheObject(CacheConstants.IS_DAY + now);
        // 判断缓存里面是否有节假日算法
        if (cacheObject!=null) {
            return Integer.valueOf(cacheObject.toString());
        }
        String result = HttpUtils.sendGet("https://publicapi.xiaoai.me/holiday/day?date=" +now);
//        String result = HttpUtils.sendGet("https://publicapi.xiaoai.me/holiday/day?date=2026-01-04");
        JSONObject jsonObject = JSONObject.parseObject(result);
        Object data = jsonObject.get("data");
        JSONArray parse = JSONArray.parse(data.toString());
        for (int i = 0; i < parse.size(); i++) {
            JSONObject jsonObject1 = parse.getJSONObject(i);
            String daytype = jsonObject1.get("daytype").toString();
            if (daytype.equals("0")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                return 0;
            } else if (daytype.equals("1")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", 1l, TimeUnit.DAYS);
                return 1;
            } else if (daytype.equals("2")) {
                redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS);
                return 0;
            }
        }
        return 0;
    }
}
xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/controller/PushOrderRouteController.java
New file
@@ -0,0 +1,90 @@
package com.xiaopeng.pushMessage.controller;
import com.alibaba.fastjson2.JSONObject;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.dto.pushMessage.PushOrderRouteDTO;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.PushMessageTypeEnum;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.pushMessage.utils.MapMatchingUtil;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
@Api(tags = "订单推送轨迹接口")
@RestController
@RequestMapping("")
public class PushOrderRouteController {
    @Autowired
    private RedisService redisService;
    @Autowired
    private XpPushMessageClient xpPushMessageClient;
    /**
     * 通过定位推送订单轨迹
     * @return
     */
    @PostMapping("/inner/v1/order/pushOrderRoute")
    public R<String> pushOrderRoute(@Validated @RequestBody PushOrderRouteDTO dto) {
        RouteVo routeVo = redisService.getCacheObject(CacheConstants.ORDER_ROUTE_CAPTURE + "_" + dto.getOrderId() + "_" +dto.getRouteType());
        if(Objects.nonNull(routeVo)){
            // 计算实时点位到线段的最优截取
            List<LonLatDto> lonLatDtos = MapMatchingUtil.clipBySegmentDistance(routeVo.getPolyline(), new LonLatDto(dto.getLongitude(), dto.getLatitude()));
            routeVo.setDistance(dto.getDistance());
            routeVo.setDuration(dto.getDuration());
            routeVo.setPolyline(lonLatDtos);
            // 存储最新路线偏离缓存,后续进行轨迹截取
            redisService.setCacheObject(CacheConstants.ORDER_ROUTE_CAPTURE + "_" + dto.getOrderId() + "_" +dto.getRouteType(), routeVo);
            // 封装轨迹点位集合
            Map<String, Object> polyline = new HashMap<>();
            for (LonLatDto lonLatDto : lonLatDtos) {
                polyline.put("longitude",lonLatDto.getLongitude());
                polyline.put("latitude",lonLatDto.getLatitude());
            }
            // 封装路由详情
            Map<String, Object> route = new HashMap<>();
            route.put("distance",dto.getDistance());
            route.put("duration",dto.getDuration());
            route.put("polyline",JSONObject.toJSONString(polyline));
            // 封装消息内容
            Map<String, Object> content = new HashMap<>();
            content.put("orderId",dto.getOrderId());
            content.put("routeType",dto.getRouteType());
            content.put("route",JSONObject.toJSONString(route));
            // 封装轨迹信息
            Map<String, Object> params = new HashMap<>();
            params.put("type", PushMessageTypeEnum.ORDER_ROUTE.getDesc());
            params.put("content", JSONObject.toJSONString(content));
            params.put("timestamp", new Date().getTime()+"");
            // 进行消息推送
            SendMessageDTO sendMessageDTO = new SendMessageDTO();
            sendMessageDTO.setAppUserId(String.valueOf(dto.getAppUserId()));
            sendMessageDTO.setMessage(JSONObject.toJSONString(params));
            xpPushMessageClient.sendRoute(sendMessageDTO);
        }
        return R.ok();
    }
}
xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/utils/MapMatchingUtil.java
New file
@@ -0,0 +1,123 @@
package com.xiaopeng.pushMessage.utils;
import com.xiaopeng.api.dto.operation.LonLatDto;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Slf4j
public class MapMatchingUtil {
    /**
     * 轨迹裁剪(线段版本)
     * @param polyline
     * @param currentPoint
     * @return
     */
    public static List<LonLatDto> clipBySegmentDistance(List<LonLatDto> polyline,LonLatDto currentPoint) {
        if (polyline == null || polyline.size() < 2) {
            return Collections.emptyList();
        }
        double minDistance = Double.MAX_VALUE;
        int minSegmentIndex = 0;
        int increaseCount = 0;
        final int threshold = 3;
        final double tolerance = 1.0;
        for (int i = 0; i < polyline.size() - 1; i++) {
            double d = pointToSegmentDistance(
                    currentPoint,
                    polyline.get(i),
                    polyline.get(i + 1)
            );
            if (d < minDistance) {
                minDistance = d;
                minSegmentIndex = i;
                increaseCount = 0;
            } else if (d > minDistance + tolerance) {
                increaseCount++;
            }
            if (increaseCount >= threshold) {
                break;
            }
        }
        // 从最优线段的后一个点开始截取
        return new ArrayList<>(
                polyline.subList(minSegmentIndex + 1, polyline.size())
        );
    }
    /**
     * 点到线段距离
     * @param p
     * @param a
     * @param b
     * @return
     */
    public static double pointToSegmentDistance(
            LonLatDto p,
            LonLatDto a,
            LonLatDto b) {
        double refLat = p.getLatitude();
        double[] pXY = toXY(p.getLatitude(), p.getLongitude(), refLat);
        double[] aXY = toXY(a.getLatitude(), a.getLongitude(), refLat);
        double[] bXY = toXY(b.getLatitude(), b.getLongitude(), refLat);
        double x = pXY[0];
        double y = pXY[1];
        double x1 = aXY[0];
        double y1 = aXY[1];
        double x2 = bXY[0];
        double y2 = bXY[1];
        double dx = x2 - x1;
        double dy = y2 - y1;
        if (dx == 0 && dy == 0) {
            return Math.hypot(x - x1, y - y1);
        }
        double t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
        if (t < 0) {
            return Math.hypot(x - x1, y - y1);
        } else if (t > 1) {
            return Math.hypot(x - x2, y - y2);
        }
        double projX = x1 + t * dx;
        double projY = y1 + t * dy;
        return Math.hypot(x - projX, y - projY);
    }
    /**
     * 经纬度转平面坐标(局部近似)
     * @param lat
     * @param lon
     * @param refLat
     * @return
     */
    private static double[] toXY(double lat, double lon, double refLat) {
        double R = 6378137; // 地球半径
        double x = Math.toRadians(lon) * R * Math.cos(Math.toRadians(refLat));
        double y = Math.toRadians(lat) * R;
        return new double[]{x, y};
    }
}
xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -23,10 +23,12 @@
import com.xiaopeng.vehicleConvoy.model.exportExcel.XpVehicleExportExcel;
import com.xiaopeng.vehicleConvoy.model.importExcel.XpVehicleImportExcel;
import com.xiaopeng.vehicleConvoy.model.query.*;
import com.xiaopeng.vehicleConvoy.model.vo.XpVehicleVO;
import com.xiaopeng.vehicleConvoy.service.IXpVehicleAnnualRecordService;
import com.xiaopeng.vehicleConvoy.service.IXpVehicleService;
import io.swagger.annotations.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -371,6 +373,26 @@
    }
    /**
     * 查询车辆位置信息 做筛选
     * @param dto
     * @return
     */
    @PostMapping("/inner/vehicle/queryVehiclePositionByVin")
    public R<List<String>> queryVehiclePositionByVin(@RequestBody VehiclePositionByVinDto dto){
        List<XpVehicle> list = xpVehicleService.list(new LambdaQueryWrapper<XpVehicle>().eq(XpVehicle::getState, 0).eq(XpVehicle::getStatus, 1).eq(XpVehicle::getElectronicId, dto.getFenceId()).in(XpVehicle::getVehicleVin,
                dto.getVinList()));
        if(list.isEmpty()){
            return R.ok(new ArrayList<>());
        }else {
            List<String> vehiclePositionVos = new ArrayList<>(list.size());
            for (XpVehicle xpVehicle : list) {
                vehiclePositionVos.add(xpVehicle.getVehicleVin());
            }
            return R.ok(vehiclePositionVos);
        }
    }
    /**
     * 查询可改派的车辆列表
     *
     * @return 车辆列表
@@ -441,7 +463,7 @@
     * 业务流程-车门解锁/后备箱解锁
     */
    @ApiOperation(value = "打开车门操作", tags = {"小程序-业务流程"})
    @PostMapping("/api/v1/order/vehicle/unlock")
    @PostMapping("/api/v1/mini-program/order/vehicle/unlock")
    public R<Integer> unlock(@RequestBody UnlockAppletDTO dto) {
        return R.ok(xpVehicleService.unlock(dto));
    }
@@ -450,7 +472,7 @@
     * 业务流程-打开设置温度
     */
    @ApiOperation(value = "打开设置温度", tags = {"小程序-业务流程"})
    @PostMapping("/api/v1/order/ac/set-temperature")
    @PostMapping("/api/v1/mini-program/order/ac/set-temperature")
    public R<Integer> setTemperature(@RequestBody SetTemperatureDTO dto) {
        return R.ok(xpVehicleService.setTemperature(dto));
    }
@@ -459,7 +481,7 @@
     * 业务流程-通过订单id查询当前车辆温度
     */
    @ApiOperation(value = "通过订单id查询当前车辆温度", tags = {"小程序-业务流程"})
    @GetMapping("/api/v1/order/queryTemperatureByOrderId")
    @GetMapping("/api/v1/mini-program/order/queryTemperatureByOrderId")
    public R<VehicleTemperatureDto> queryTemperatureByOrderId(@RequestParam Long orderId, HttpServletRequest request) {
        String uid = request.getHeader("XP-Uid");
        VehicleTemperatureDto vehicleTemperatureDto = xpVehicleService.queryTemperatureByOrderId(orderId,uid);