xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/automaticPilot/PushOrderDto.java
@@ -1,5 +1,6 @@ package com.xiaopeng.api.dto.automaticPilot; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudOrderOperation.OrderOperationType; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -13,4 +14,10 @@ // 订单id @ApiModelProperty("订单id") private Long orderId; /** * 订单操作类型 */ private OrderOperationType operation_type; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/automaticPilot/VehicleBaseRespDto.java
New file @@ -0,0 +1,83 @@ package com.xiaopeng.api.dto.automaticPilot; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @Builder @AllArgsConstructor @NoArgsConstructor public class VehicleBaseRespDto implements Serializable { private static final long serialVersionUID = -1251766408952592117L; /** * 车架号 */ private String vin; /** * 大屏虚拟id(兼容旧接口) */ private String vid; /** * 大屏硬件id */ private String cduId; /** * sim卡id */ private String iccid; /** * 物料编码id */ private String vehicleMaterielId; /** * 车型code. * DB, DC, DE就是G3 * ED 就是 P7 * VehicleTypeType */ private String vehicleTypeCode; /** * 车型名称,eg:D10,D20,D21.. */ private String vehicleTypeName; /** * 对外名称,eg:1.0,G3,P5... */ private String vehicleSaleName; /** * 车牌信息(车牌号) */ private String plateNo; /** * 车辆用途 */ private Integer usage; /** * 公告车型code */ private String proclamationCode; /*** * 给实时流 */ private String batchNo; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/operation/ReassignOrderDTO.java
New file @@ -0,0 +1,24 @@ package com.xiaopeng.api.dto.operation; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; @Data @ApiModel(value = "改派订单参数") public class ReassignOrderDTO implements Serializable { @ApiModelProperty(value = "播单次数") private Integer count; @ApiModelProperty(value = "用户id") private String uid; @ApiModelProperty(value = "经度") private Double longitude; @ApiModelProperty(value = "纬度") private Double latitude; @ApiModelProperty(value = "订单号") private String orderNo; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/automatic/OrderMissionClientFallbackFactory.java
File was deleted xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/automaticPilot/OrderMissionClientFallbackFactory.java
New file @@ -0,0 +1,36 @@ package com.xiaopeng.api.factory.automaticPilot; import com.xiaopeng.api.dto.automaticPilot.PushOrderDto; import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient; import com.xiaopeng.common.core.domain.R; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.stereotype.Component; /** * 车辆整备Feign客户端降级工厂 * * @author mitao * @since 2026-02-11 */ @Slf4j @Component public class OrderMissionClientFallbackFactory implements FallbackFactory<OrderMissionClient> { @Override public OrderMissionClient create(Throwable cause) { return new OrderMissionClient() { @Override public R<Boolean> pushOrder(PushOrderDto dto) { log.error("远程调用获取检查点位信息异常:{}", cause.getMessage()); return R.fail("远程调用获取检查点位信息异常"); } @Override public R<Boolean> cancelOrder(PushOrderDto dto) { log.error("远程调用获取检查点位信息异常:{}", cause.getMessage()); return null; } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/automaticPilot/XpuVimClientFallbackFactory.java
New file @@ -0,0 +1,21 @@ package com.xiaopeng.api.factory.automaticPilot; import com.xiaopeng.api.feignClient.automaticPilot.XpuVinClient; import com.xiaopeng.common.core.domain.R; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.openfeign.FallbackFactory; @Slf4j public class XpuVimClientFallbackFactory implements FallbackFactory<XpuVinClient> { @Override public XpuVinClient create(Throwable cause) { return new XpuVinClient() { @Override public R<String> getXpuIdByVin(String vin) { log.error("查询XPUID失败: {}", cause.getMessage()); return null; } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/ReassignOrderClientFallbackFactory.java
New file @@ -0,0 +1,26 @@ package com.xiaopeng.api.factory.operation; import com.xiaopeng.api.dto.operation.ReassignOrderDTO; import com.xiaopeng.api.feignClient.operation.ReassignOrderClient; import com.xiaopeng.common.core.domain.R; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.openfeign.FallbackFactory; public class ReassignOrderClientFallbackFactory implements FallbackFactory<ReassignOrderClient> { private static final Logger log = LoggerFactory.getLogger(ReassignOrderClientFallbackFactory.class); @Override public ReassignOrderClient create(Throwable cause) { log.error("改派服务调用失败: {}", cause.getMessage()); return new ReassignOrderClient(){ @Override public R<String> reassignOrder(ReassignOrderDTO dto) { return R.fail("改派派单失败,"+cause.getMessage()); } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpPricingClientFallbackFactory.java
@@ -34,10 +34,11 @@ } @Override public R<?> saveXpGeoQueryEntity(XpGeoQueryEntity xpGeoQueryEntity) { log.error("保存XpGeoQueryEntity失败", cause); public R<?> updateXpGeoQueryEntity(String orderId) { log.error("更新XpGeoQueryEntity失败", cause); return R.fail(); } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/vehicleConvoy/XpVehicleClientFallbackFactory.java
@@ -67,6 +67,12 @@ log.error("查询车辆信息失败,原因: {}", cause.getMessage()); return R.fail(); } @Override public R<XpVehicle> findByVehicleXpuId(String xpuid) { log.error("查询车辆信息失败,原因: {}", cause.getMessage()); return R.fail(); } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/automaticPilot/OrderMissionClient.java
File was renamed from xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/automatic/OrderMissionClient.java @@ -1,20 +1,12 @@ package com.xiaopeng.api.feignClient.automatic; package com.xiaopeng.api.feignClient.automaticPilot; import com.xiaopeng.api.dto.account.XpOperationsUserQueryDTO; import com.xiaopeng.api.dto.automaticPilot.PushOrderDto; import com.xiaopeng.api.factory.account.XpAppUserClientFallbackFactory; import com.xiaopeng.api.factory.automatic.OrderMissionClientFallbackFactory; import com.xiaopeng.api.model.account.XpOperationsUser; import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO; import com.xiaopeng.api.factory.automaticPilot.OrderMissionClientFallbackFactory; 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 org.springframework.web.bind.annotation.RequestParam; import java.util.List; /** * 小程序用户Feign客户端 @@ -33,4 +25,13 @@ */ @PostMapping("/inner/pushOrder") R<Boolean> pushOrder(@RequestBody PushOrderDto dto); /** * 取消订单 * @param * @return */ @PostMapping("/inner/cancelOrder") R<Boolean> cancelOrder(@RequestBody PushOrderDto dto); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/automaticPilot/VmpOpenServiceClient.java
New file @@ -0,0 +1,46 @@ package com.xiaopeng.api.feignClient.automaticPilot; import com.xiaopeng.api.dto.automaticPilot.VehicleBaseRespDto; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; import java.util.Map; @FeignClient(name = "xp-vmp-open-boot") public interface VmpOpenServiceClient { /** * 通过vin获取车辆基本信息 * * @param vin * @return */ @GetMapping("/logan/vehicleInfo/vin") VehicleBaseRespDto getVehicleInfoByVin(@RequestParam("vin") String vin, @RequestParam("respList") List<String> respList); /** * 查询车辆绑定信息, 通过vin获取车辆xpu id * typeList类型,1:XPUID,2:待扩展 * 参数示例:?vin=TESTE290000000000&typeList=1,2 * 返回示例:{ * "1": "01010101010101010101010101010101", * "2": "xxx" * } */ @GetMapping("/logan/vehicle/bind/info/query") Map<String, String> getVehicleBindInfoByVin(@RequestParam("vin") String vin, @RequestParam("typeList") List<String> typeList); /** * 通过车辆绑定信息获取车架号, 通过xpu id获取车辆vin * type类型,1:XPUID,2:待扩展 * 参数示例:?value=00000000000000000000000000000001&type=1 * 返回示例:{ * L1NSPGHB9RB900099 * } */ @GetMapping("/logan/vehicle/bind/info/getVin") String getVinByXpuId(@RequestParam("value") String xpuId, @RequestParam("type") Integer type); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/automaticPilot/XpuVinClient.java
New file @@ -0,0 +1,24 @@ package com.xiaopeng.api.feignClient.automaticPilot; import com.xiaopeng.api.factory.automaticPilot.XpuVimClientFallbackFactory; 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.RequestParam; @FeignClient(contextId = "XpuVinClient", value = ServiceNameConstantsEnum.XP_AUTOMATIC_PILOT_SERVICE, fallbackFactory = XpuVimClientFallbackFactory.class) public interface XpuVinClient { /** * 根据车辆VIN查询XPUID * @param vin * @return */ @GetMapping("/xpu/getXpuIdByVin") R<String> getXpuIdByVin(@RequestParam("vin") String vin); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/ReassignOrderClient.java
New file @@ -0,0 +1,22 @@ package com.xiaopeng.api.feignClient.operation; import com.xiaopeng.api.dto.operation.ReassignOrderDTO; import com.xiaopeng.api.factory.operation.ReassignOrderClientFallbackFactory; import com.xiaopeng.api.factory.operation.XpSystemSettingClientFallbackFactory; import com.xiaopeng.api.model.operation.XpFeedbackType; 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.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @FeignClient(contextId = "ReassignOrderClient", value = ServiceNameConstantsEnum.XP_OPERATION_SERVICE, fallbackFactory = ReassignOrderClientFallbackFactory.class) public interface ReassignOrderClient { @PostMapping("/inner/v1/order/reassignOrder") R<String> reassignOrder(@RequestBody ReassignOrderDTO dto); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/XpPricingClient.java
@@ -27,8 +27,8 @@ /** * 保存XpGeoQueryEntity */ @PostMapping("/inner/saveXpGeoQueryEntity") R<?> saveXpGeoQueryEntity(@RequestBody XpGeoQueryEntity xpGeoQueryEntity); @PostMapping("/inner/updateXpGeoQueryEntity") R<?> updateXpGeoQueryEntity(@RequestParam String orderId); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/XpVehicleClient.java
@@ -79,4 +79,16 @@ */ @GetMapping("/inner/vehicle/findByVehicleVin") R<XpVehicle> findByVehicleVin(@RequestParam("vehicleVin") String vehicleVin); /** * 根据车辆xpuid查询车辆信息 * * @param xpuid * @return */ @GetMapping("/inner/vehicle/findByVehicleXpuId") R<XpVehicle> findByVehicleXpuId(@RequestParam("xpuid") String xpuid); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/operation/XpGeoQueryEntity.java
@@ -30,7 +30,7 @@ * 时间戳 */ @Field("timestamp") private Date timestamp; private Long timestamp; /** * 秒数 @@ -81,11 +81,11 @@ this.location = location; } public Date getTimestamp() { public Long getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { public void setTimestamp(Long timestamp) { this.timestamp = timestamp; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderEvaluate.java
@@ -30,7 +30,7 @@ private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) @TableId(value = "id", type = IdType.ASSIGN_ID) private Long id; @ApiModelProperty(value = "订单id") xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/vehicleConvoy/XpVehicle.java
@@ -40,6 +40,9 @@ @ApiModelProperty(value = "车辆vin码") private String vehicleVin; @ApiModelProperty(value = "车辆xpuid") private String xpuId; @ApiModelProperty(value = "车牌号") private String plateNumber; xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/operation/RouteAndFareVo.java
@@ -1,5 +1,6 @@ package com.xiaopeng.api.vo.operation; import com.xiaopeng.api.vo.order.XpPricingCardVo; import io.swagger.annotations.ApiModel; @lombok.Data @@ -10,4 +11,7 @@ private FareVo fareEstimate; private XpPricingCardVo xpPricingCard; } xp-robotaxi-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -17,10 +17,12 @@ com.xiaopeng.api.factory.account.XpOperationUserClientFallbackFactory com.xiaopeng.api.factory.operation.XpElectronicClientFallbackFactory com.xiaopeng.api.factory.operation.XpOpenCityClientFallbackFactory com.xiaopeng.api.factory.operation.ReassignOrderClientFallbackFactory com.xiaopeng.api.factory.vehicleConvoy.UserVehicleClientFallbackFactory com.xiaopeng.api.factory.vehicleConvoy.XpFleetOperationsClientFallbackFactory com.xiaopeng.api.factory.pushMessage.XpPushMessageClientFallbackFactory com.xiaopeng.api.factory.account.UserResourcesClientFallbackFactory com.xiaopeng.api.factory.vehicleConvoy.VehicleControlClientFallbackFactory com.xiaopeng.api.factory.automaticPilot.XPUClientFallbackFactory com.xiaopeng.api.factory.automaticPilot.XpuVimClientFallbackFactory com.xiaopeng.api.factory.maintenance.XpCheckLocationClientFallbackFactory xp-robotaxi-automaticPilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/KafkaTestController.java
@@ -1,21 +1,14 @@ package com.xiaopeng.automaticPilot.controller; import com.alibaba.fastjson2.JSON; import com.xiaopeng.api.vo.automaticPilot.proto.up.MsgContent; import com.xiaopeng.automaticPilot.service.KafkaConsumerService; import io.swagger.annotations.Api; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.RestController; /** * Kafka 测试控制器 */ @Slf4j @RestController @RequestMapping("/kafka") @Api(tags = "Kafka 测试接口") public class KafkaTestController { @Autowired @@ -31,13 +24,8 @@ */ @KafkaListener(topics = "topic-prod-robotaxi-xpu-high", groupId = "group-robotaxi-xpu") public void topicProdRobotaxiXpuHigh(String message) { try { log.info("收到XPU上报高优先级主题消息: {}", message); kafkaConsumerService.topicProdRobotaxiXpuHigh(message); } catch (Exception e) { log.error("处理XPU上报高优先级主题消息失败: {}", message, e); throw e; } log.info("收到XPU上报高优先级主题消息: {}", message); kafkaConsumerService.topicProdRobotaxiXpuHigh(message); } /** @@ -47,12 +35,7 @@ */ @KafkaListener(topics = "topic-prod-robotaxi-xpu-normal", groupId = "group-robotaxi-xpu") public void topicProdRobotaxiXpuNormal(String message) { try { log.info("收到XPU上报低优先级主题消息: {}", message); kafkaConsumerService.topicProdRobotaxiXpuNormal(message); } catch (Exception e) { log.error("处理XPU上报低优先级主题消息失败: {}", message, e); throw e; } log.info("收到XPU上报低优先级主题消息: {}", message); kafkaConsumerService.topicProdRobotaxiXpuNormal(message); } } xp-robotaxi-automaticPilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java
@@ -8,6 +8,8 @@ import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.RTTask; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.TaskStatus; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.TaskType; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudOrderOperation.OrderOperation; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudOrderOperation.OrderOperationType; import com.xiaopeng.automaticPilot.util.XPUUtil; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.utils.SnowFlake; @@ -52,4 +54,19 @@ } return R.ok(false); } @PostMapping("/inner/cancelOrder") R<Boolean> cancelOrder(@RequestBody PushOrderDto dto){ OrderOperation operation = new OrderOperation(); operation.setOrder_id(dto.getOrderId().toString()); operation.setTask_id("CANCEL_MISSION_"+ IdUtil.fastUUID()); operation.setOperation_type(dto.getOperation_type()); SnowFlake snowFlake = new SnowFlake(0, 0); snowFlake.withStartTimestamp(1420041600000L); long sequence = snowFlake.nextId(); xpuUtil.cloudOrderOperation(operation,dto.getVin(),sequence); redisService.setCacheObject(String.valueOf(sequence),"cancelOrder_"+dto.getOrderId()+"_"+ dto.getVin(),30l, TimeUnit.MINUTES); return R.ok(true); } } xp-robotaxi-automaticPilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/XpuVinController.java
New file @@ -0,0 +1,31 @@ package com.xiaopeng.automaticPilot.controller; import com.xiaopeng.automaticPilot.util.XPUUtil; import com.xiaopeng.common.core.domain.R; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController @RequestMapping("") public class XpuVinController { @Resource private XPUUtil xpuUtil; /** * 根据车辆VIN查询XPUID * @param vin * @return */ @GetMapping("/xpu/getXpuIdByVin") public R<String> getXpuIdByVin(@RequestParam("vin") String vin){ String xpuId = xpuUtil.getXpuIdByVin(vin); return R.ok(xpuId); } } xp-robotaxi-automaticPilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java
@@ -6,12 +6,15 @@ import com.alibaba.fastjson2.JSONObject; import com.xiaopeng.api.dto.map.OrderRouteAndFareDto; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.dto.operation.ReassignOrderDTO; import com.xiaopeng.api.dto.order.PopOrderDto; import com.xiaopeng.api.dto.pushMessage.PushOrderRouteDTO; import com.xiaopeng.api.dto.pushMessage.SendMessageDTO; import com.xiaopeng.api.feignClient.map.XpMapClient; import com.xiaopeng.api.feignClient.operation.ReassignOrderClient; import com.xiaopeng.api.feignClient.order.XpOrderClient; import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient; import com.xiaopeng.api.feignClient.vehicleConvoy.UserVehicleClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; @@ -32,6 +35,7 @@ import com.xiaopeng.api.vo.automaticPilot.proto.up.xpuNavigationRequest.NavigationRequest; import com.xiaopeng.api.vo.automaticPilot.proto.up.xpuNavigationRequest.OrderPhase; import com.xiaopeng.api.vo.operation.RouteVo; import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo; import com.xiaopeng.automaticPilot.util.XPUUtil; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.enums.OrderStatusEnum; @@ -52,6 +56,9 @@ import javax.annotation.Resource; import java.util.*; import java.util.concurrent.TimeUnit; import static org.bouncycastle.asn1.x500.style.RFC4519Style.l; import static org.bouncycastle.asn1.x500.style.RFC4519Style.uid; /** * Kafka 消费者服务 @@ -77,9 +84,13 @@ @Autowired public RedisTemplate redisTemplate; @Autowired private UserVehicleClient userVehicleClient; @Resource private XpPushMessageClient xpPushMessageClient; @Autowired private ReassignOrderClient reassignOrderClient; @@ -89,6 +100,24 @@ */ public void topicProdRobotaxiXpuHigh(String message) { MsgContent msgContent = JSON.parseObject(message, MsgContent.class); handleXPUMessage(msgContent, message); } /** * 消费消息 - XPU上报Robotaxi云端的消息topic(低优先级) */ public void topicProdRobotaxiXpuNormal(String message) { MsgContent msgContent = JSON.parseObject(message, MsgContent.class); handleXPUMessage(msgContent, message); } /** * 处理XPU消息 * @param msgContent * @param message */ public void handleXPUMessage(MsgContent msgContent, String message){ RtMsgHeader header = msgContent.getHeader(); MessageType type = header.getType(); //处理请求消息 @@ -166,94 +195,21 @@ Object sequence = redisTemplate.opsForValue().get(requestSeq); if(sequence!=null){ String[] split = sequence.toString().split("_"); if(split[0].equals("pushOrder")){ String typeStr = split[0]; // 推单处理 if(typeStr.equals("pushOrder")){ PopOrderDto dto = new PopOrderDto(); dto.setOrderId(Long.valueOf(split[1])); dto.setXpVehicleVin(split[2]); xpOrderClient.popOrder(dto); } } } } } /** * 消费消息 - XPU上报Robotaxi云端的消息topic(低优先级) */ public void topicProdRobotaxiXpuNormal(String message) { MsgContent msgContent = JSON.parseObject(message, MsgContent.class); RtMsgHeader header = msgContent.getHeader(); MessageType type = header.getType(); //处理请求消息 if (MessageType.MESSAGE_TYPE_REQUEST == type) { //区分业务类型 RTCmdType cmd = header.getCmd(); switch (cmd) { case SYSTEM_STATUS_XPU: log.info("XPU发布系统运行状态 (XPU→云端):{}", message); systemStatusXpu(msgContent); break; case XPU_NAVIGATION_REQUEST: log.info("XPU向云端请求导航 (XPU→云端):{}", message); xpuNavigationRequest(msgContent); break; case TRIP_STATUS_UPDATE_EVENT: log.info("行程状态更新(XPU→云端)(XPU→座舱):{}", message); break; case PULLOVER_STATUS_EVENT: log.info("靠边停车状态(XPU→云端)(XPU→座舱):{}", message); break; case DRIVING_MODE_UPDATE_EVENT: log.info("驾驶模式变化(XPU→云端)(XPU→座舱):{}", message); break; case SYSTEM_STATUS_UPDATE_EVENT: log.info("系统状态变化(XPU→云端)(XPU→座舱):{}", message); break; case TASK_STATE_UPDATE_EVENT: log.info("任务状态变化(XPU→云端)(XPU→座舱):{}", message); break; case ORDER_STATE_UPDATE_EVENT: log.info("订单状态变化(XPU→云端)(XPU→座舱):{}", message); orderStateUpdateEvent(msgContent); break; case POINT_ARRIVAL_STATUS_EVENT: log.info("到达目标点状态(XPU→云端)(XPU→座舱):{}", message); break; case PICKUP_CONFIRMATION_EVENT: log.info("用户上车确认(XPU→云端)(XPU→座舱):{}", message); break; case DROPOFF_INFO_EVENT: log.info("用户下车信息(XPU→云端):{}", message); break; case NAVIGATION_DEVIATION_EVENT: log.info("导航偏航(XPU→云端):{}", message); navigationDeviationEvent(msgContent); break; case EMERGENCY_STOP_EVENT: log.info("急停(XPU→云端)(XPU→座舱):{}", message); break; case NAVIGATION_UPDATE_EVENT: log.info("导航信息变化(XPU→云端):{}", message); break; case TASK_QUEUE_UPDATE_EVENT: log.info("XPU待执行任务队列(XPU→云端):{}", message); break; case SYSTEM_STATE_ANAPSHOT_SYNC_EVENT: log.info("每次MQTT连接之后触发 (XPU→云端):{}", message); break; case ORDER_STATE_UPDATE_TO_CDU_EVENT: log.info("更新订单状态到CDU (XPU→云端):{}", message); break; default: log.info("消息不在处理范围内,忽略:{}", message); } } } /** @@ -273,7 +229,9 @@ Float alt = naviPose.getAlt(); //获取车辆静态信息 VehicleStaticInfo staticInfo = systemRuntimeStatus.getVehicle_status().getStatic_info(); String vin = staticInfo.getVin(); String xpuId = staticInfo.getXpu_id(); XpVehicle xpVehicle = xpVehicleClient.findByVehicleXpuId(xpuId).getData(); String vin = xpVehicle.getVehicleVin(); //先响应消息 Long sequence = msgContent.getHeader().getSequence(); responseMessage(sequence, vin); @@ -325,6 +283,56 @@ /** * XPU发布系统运行状态---车辆实时位置 * * @param msgContent */ public void systemStatusXpuAndReassign(MsgContent msgContent) { //开始处理消息对应的业务逻辑 SystemRuntimeStatus systemRuntimeStatus = msgContent.getContent_detail().getObject("systemRuntimeStatus", SystemRuntimeStatus.class); AbnormalReason abnormalReason = systemRuntimeStatus.getSystem_status().getAbnormal_reason(); // 判断是否为故障状态 if (abnormalReason == AbnormalReason.FAULT) { log.info("车辆故障,开始处理故障"); // TODO 添加异常任务 // 解绑用户与车辆关系 CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo(); String orderId = systemRuntimeStatus.getRunning_order_status_info().getOrder_id(); XpOrder xpOrder = xpOrderClient.queryOrderById(Long.parseLong(orderId)).getData(); XpVehicle vehicle = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData(); if (vehicle != null) { // 修改车辆状态 vehicle.setOperatingStatus(4); xpVehicleClient.updateVehicleById(vehicle); cancelTaxiPassengerVo.setVin(vehicle.getVehicleVin()); cancelTaxiPassengerVo.setUid(xpOrder.getAppUserId().toString()); cancelTaxiPassengerVo.setOrderNo(xpOrder.getOrderNumber()); // TODO // cancelTaxiPassengerVo.setAppId(); userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo); } // 取消订单 R<?> r = xpOrderClient.cancelOrder(String.valueOf(xpOrder.getId())); if (r.getCode() == R.SUCCESS){ log.info("取消订单成功"); // 重新派单 ReassignOrderDTO reassignOrderDTO = new ReassignOrderDTO(); reassignOrderDTO.setUid(xpOrder.getAppUserId().toString()); reassignOrderDTO.setOrderNo(xpOrder.getOrderNumber()); reassignOrderDTO.setCount(10); RealtimePose pose = systemRuntimeStatus.getPose(); Wgs naviPose = pose.getNavi_pose(); Double latitude = naviPose.getLatitude(); Double longitude = naviPose.getLongitude(); reassignOrderDTO.setLongitude(longitude); reassignOrderDTO.setLatitude(latitude); reassignOrderClient.reassignOrder(reassignOrderDTO); } } } /** * XPU向云端请求导航 * * @param msgContent xp-robotaxi-automaticPilot-boot/src/main/java/com/xiaopeng/automaticPilot/util/XPUUtil.java
@@ -8,16 +8,23 @@ import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson2.JSON; import com.xiaopeng.api.dto.automaticPilot.MqttPublishDto; import com.xiaopeng.api.feignClient.automaticPilot.VmpOpenServiceClient; import com.xiaopeng.api.feignClient.automaticPilot.XPUClient; import com.xiaopeng.api.vo.automaticPilot.MqttPublishVo; import com.xiaopeng.api.vo.automaticPilot.proto.down.*; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.RTTask; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudOrderOperation.OrderOperation; import com.xiaopeng.common.core.exception.ServiceException; import com.xiaopeng.common.core.utils.StringUtils; import com.xiaopeng.common.core.utils.uuid.UUID; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; /** * 自动驾驶接口对接文档 @@ -28,6 +35,15 @@ @Resource private XPUClient xpuClient; @Resource private VmpOpenServiceClient vmpOpenServiceClient; // 查询车管绑定类型,1:XPUID,2:待扩展 public static final String XPU_ID_TYPE = "1"; // 查询车管 车辆xpuid信息返回字段,用于按需返回字段 public static final List<String> TYPE_LIST = Collections.singletonList("1"); /** @@ -51,10 +67,11 @@ jsonObject.put("task", task); msgContent.setContent_detail(jsonObject); log.info("云端-->XPU 创建订单:{}", msgContent); //根据车辆VIN查询XPUID String xpuId = getXpuIdByVin(vin); //组装请求消息体 MqttPublishVo vo = new MqttPublishVo(); vo.setTopic("topic-prod-xpu-robotaxi-high/" + vin); vo.setTopic("topic-prod-xpu-robotaxi-high/" + xpuId); vo.setPayload(JSON.toJSONString(msgContent)); return xpuClient.publish(vo); } @@ -82,14 +99,12 @@ jsonObject.put("operation", operation); msgContent.setContent_detail(jsonObject); log.info("云端-->XPU 操作订单:{}", msgContent); //根据车辆VIN查询XPUID String xpuId = getXpuIdByVin(vin); //组装请求消息体 MqttPublishVo vo = new MqttPublishVo(); vo.setTopic("topic-prod-xpu-robotaxi-high/" + vin); vo.setTopic("topic-prod-xpu-robotaxi-high/" + xpuId); vo.setPayload(JSON.toJSONString(msgContent)); return xpuClient.publish(vo); } @@ -118,11 +133,32 @@ jsonObject.put("resposne", responseMessage); msgContent.setContent_detail(jsonObject); log.info("云端-->XPU 响应消息:{}", msgContent); //根据车辆VIN查询XPUID String xpuId = getXpuIdByVin(vin); //组装请求消息体 MqttPublishVo vo = new MqttPublishVo(); vo.setTopic("topic-prod-xpu-robotaxi-high/" + vin); vo.setTopic("topic-prod-xpu-robotaxi-high/" + xpuId); vo.setPayload(JSON.toJSONString(msgContent)); return xpuClient.publish(vo); } /** * @description: 根据vin查询xpuId * @param: vin * @return: xpuId */ public String getXpuIdByVin(String vin) { Map<String, String> xpuIdMapping = vmpOpenServiceClient.getVehicleBindInfoByVin(vin, TYPE_LIST); log.info("getMqttPublishTopic vin,{},xpuIdMapping,{}", vin, JSON.toJSONString(xpuIdMapping)); if (xpuIdMapping == null || StringUtils.isEmpty(xpuIdMapping.get(XPU_ID_TYPE)) ) { log.error("根据vin查询车辆xpuid信息为空,vin={}", vin); throw new RuntimeException("根据vin查询车辆xpuid信息为空"); } return xpuIdMapping.get(XPU_ID_TYPE); } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/ReassignOrderController.java
New file @@ -0,0 +1,29 @@ package com.xiaopeng.operation.controller; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.api.dto.operation.ReassignOrderDTO; import com.xiaopeng.operation.utils.TaskOrderUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; /** * token 控制 * * @author ruoyi */ @Slf4j @RestController public class ReassignOrderController { @Autowired private TaskOrderUtil taskOrderUtil; @PostMapping("/inner/v1/order/reassignOrder") public R<String> reassignOrder(@RequestBody ReassignOrderDTO dto) { taskOrderUtil.reassignPushOrder(dto.getCount(),dto.getUid(),dto.getLongitude(),dto.getLatitude(),dto.getOrderNo()); return R.ok(); } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpPricingController.java
@@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.util.Date; /** * <p> @@ -49,29 +50,28 @@ } @PostMapping("/base/v1/mini-program/navigation/route-and-fare") @ApiOperation(value = "导航-获取路线和费用",tags = {"小程序-获取路线和费用"},response = RouteAndFareVo.class) @ApiOperation(value = "导航-获取路线和费用", tags = {"小程序-获取路线和费用"}, response = RouteAndFareVo.class) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<RouteAndFareVo> getRouteAndFare(@RequestBody @Valid RouteAndFareDto routeAndFareDto, HttpServletRequest request) { // uid已通过拦截器otp校验,可以直接使用 String uid = request.getHeader("XP-Uid"); return R.ok(xpPricingService.getRouteAndFare(routeAndFareDto,uid)); return R.ok(xpPricingService.getRouteAndFare(routeAndFareDto, uid)); } // 根据开通城市 获取定价 @GetMapping("/getPricingByCityId/{cityId}") public R<XpPricing> getPricingByCityId(@PathVariable("cityId")Long cityId) { public R<XpPricing> getPricingByCityId(@PathVariable("cityId") Long cityId) { XpPricing pricing = xpPricingService.lambdaQuery().eq(XpPricing::getCityId, cityId).last("limit 1").one(); return R.ok(pricing); } @GetMapping("/base/v1/mini-program/pricing/rules/{openCityId}") @ApiOperation(value = "获取费用规则",tags = {"小程序-根据开通城市获取费用规则"},response = String.class) @ApiOperation(value = "获取费用规则", tags = {"小程序-根据开通城市获取费用规则"}, response = String.class) public R<?> getPricingRules(@PathVariable Long openCityId) { XpPricing xpPricing = xpPricingService.getOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, openCityId).eq(XpPricing::getPricingType, 1).last("limit 1")); if(xpPricing!=null){ if (xpPricing != null) { return R.ok(xpPricing.getPriceRule()); } return R.ok(""); @@ -84,20 +84,20 @@ * @return */ @PostMapping("/base/v1/mini-program/pricing/getWaitTime") @ApiOperation(value = "获取等待时间",tags = {"小程序-获取等待时间"},response = Integer.class) public R<Integer> getWaitTime(@RequestBody LonLatDto dto){ return R.ok(xpPricingService.getWaitTime(dto.getLongitude(),dto.getLatitude())); @ApiOperation(value = "获取等待时间", tags = {"小程序-获取等待时间"}, response = Integer.class) public R<Integer> getWaitTime(@RequestBody LonLatDto dto) { return R.ok(xpPricingService.getWaitTime(dto.getLongitude(), dto.getLatitude())); } /** * 保存信息 * @param xpGeoQueryEntity * @param * @return */ @PostMapping("/inner/saveXpGeoQueryEntity") public R<?> saveXpGeoQueryEntity(@RequestBody XpGeoQueryEntity xpGeoQueryEntity){ xpGeoQueryEntityService.save(xpGeoQueryEntity); @PostMapping("/inner/updateXpGeoQueryEntity") public R<?> updateXpGeoQueryEntity(@RequestParam String orderId) { xpGeoQueryEntityService.updateByOrderId(orderId); return R.ok(); } @@ -106,7 +106,7 @@ * 播单 */ @PostMapping("/base/v1/mini-program/order/save") @ApiOperation(value = "保存订单",tags = {"小程序-保存订单"}) @ApiOperation(value = "保存订单", tags = {"小程序-保存订单"}) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<SaveOrderVo> saveOrder(@RequestBody @Validated XpOrderDto dto, HttpServletRequest request) { String uid = request.getHeader("XP-Uid"); @@ -115,8 +115,6 @@ saveOrderVo.setOrderId(l); return R.ok(saveOrderVo); } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/IXpGeoQueryEntityService.java
@@ -20,6 +20,9 @@ String time ); /** * 保存地理查询实体 * @param entity 地理查询实体 @@ -27,6 +30,12 @@ */ XpGeoQueryEntity save(XpGeoQueryEntity entity); /** * 修改 */ XpGeoQueryEntity updateByOrderId(String orderId); /** * 根据ID查询实体 * @param id 实体ID xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpBannerServiceImpl.java
@@ -27,7 +27,6 @@ @Override public List<XpBannerListVo> getXpBannerList() { LambdaQueryWrapper<XpBanner> wrapper = new LambdaQueryWrapper<>(); // TODO 魔法数字 wrapper.eq(XpBanner::getBannerStatus,1); // 当前时间在开始和结束时间范围内 wrapper.ge(XpBanner::getStartTime,System.currentTimeMillis()); xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpGeoQueryEntityServiceImpl.java
@@ -1,17 +1,25 @@ package com.xiaopeng.operation.service.impl; import com.xiaopeng.api.model.operation.XpGeoQueryEntity; import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; import com.xiaopeng.operation.repository.XpGeoQueryEntityRepository; import com.xiaopeng.operation.service.IXpGeoQueryEntityService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResults; import org.springframework.data.geo.Metrics; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Service; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.stream.Collectors; /** * MongoDB 地理查询服务实现类 @@ -21,6 +29,9 @@ @Autowired private XpGeoQueryEntityRepository repository; @Autowired private MongoTemplate mongoTemplate; /** * 通过经纬度查询固定范围500米内、一周内、指定时段的数据 @@ -35,26 +46,43 @@ double latitude, String time ) { // 创建中心点坐标 Point point = new Point(longitude, latitude); // 设置距离范围为500米 Distance distance = new Distance(0.5, Metrics.KILOMETERS); // 0.5公里 = 500米 // 计算一周前的时间 // // 计算一周前的时间 Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); calendar.add(Calendar.DAY_OF_YEAR, -7); Date startTime = calendar.getTime(); Long startTime = calendar.getTime().getTime(); // 执行查询 - 使用新的方法,包含时段过滤 return repository.findByLocationNearAndTimestampAfterAndTimeAndStatusGreaterThan( point, 500, startTime, time, // 时段过滤 1 // 状态大于1 // 1. 创建查询点 Point referencePoint = new Point(longitude, latitude); // 2. 构建 NearQuery - 完全使用 NearQuery 的 API NearQuery nearQuery = NearQuery.near(referencePoint, Metrics.KILOMETERS) .maxDistance(0.5) // 转成公里 .spherical(true); // 3.时间>startTime time = time参数 // 3. 创建额外的查询条件 Query query = new Query(); query.addCriteria(Criteria.where("timestamp").gte(startTime)); query.addCriteria(Criteria.where("time").is(time)); // 将 NearQuery 和额外的查询条件合并 nearQuery.query(query); // 4. 执行查询 - 明确指定集合名 GeoResults<XpGeoQueryEntity> geoResults = mongoTemplate.geoNear( nearQuery, XpGeoQueryEntity.class, "xp_geo_query_entity" // 明确指定集合名,避免映射错误 ); // 5. 提取结果 return geoResults.getContent().stream() .map(geoResult -> geoResult.getContent()) .collect(Collectors.toList()); } /** @@ -67,6 +95,25 @@ return repository.save(entity); } @Override public XpGeoQueryEntity updateByOrderId(String orderId) { Query query = new Query(Criteria.where("orderId").is(orderId)); XpGeoQueryEntity xpGeoQueryEntity = mongoTemplate.findOne(query, XpGeoQueryEntity.class); Long timestamp = xpGeoQueryEntity.getTimestamp(); long time = new Date().getTime(); // 计算2个时间的秒数差 long seconds = (time- timestamp) / 1000; Update update = new Update().set("seconds", seconds).set("status", 1); if(xpGeoQueryEntity.getStatus()==0){ mongoTemplate.findAndModify(query, update, XpGeoQueryEntity.class); } return null; } /** * 根据ID查询实体 * @param id 实体ID xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java
@@ -20,6 +20,7 @@ import com.xiaopeng.api.vo.operation.FareVo; import com.xiaopeng.api.vo.operation.RouteAndFareVo; import com.xiaopeng.api.vo.operation.RouteVo; import com.xiaopeng.api.vo.order.XpPricingCardVo; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.exception.ServiceException; @@ -35,12 +36,13 @@ import com.xiaopeng.operation.utils.XpPricingUtil; import com.xiaopeng.operation.utils.holiday.HolidayUtil; import com.xiaopeng.operation.utils.map.FastDistanceCalculator; import com.xiaopeng.operation.vo.XpPricingCardVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.stereotype.Service; import javax.annotation.PreDestroy; import javax.annotation.Resource; import java.time.Duration; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.*; @@ -89,8 +91,6 @@ @Resource private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper; @Resource private XpVehicleClient xpVehicleClient; @Resource private XpPointMapper xpPointMapper; @@ -206,6 +206,8 @@ // ); XpPricingCardVo xpPricingCardVo = new XpPricingCardVo(); // 3. 并行执行远程服务调用 判断是否同一经营区域 CompletableFuture<R<Boolean>> diagnosisAreaFuture = CompletableFuture.supplyAsync( () -> xpMapClient.diagnosisArea(routeAndFareDto), @@ -274,6 +276,9 @@ RouteAndFareVo routeAndFareVo = new RouteAndFareVo(); // routeAndFareVo.setRoute(routeVo); routeAndFareVo.setXpPricingCard(xpPricingCardVo); FareVo fareVo = new FareVo(); // fareVo.setEstimatedDistanceKm(routeVo.getDistance().doubleValue() / 1000); // fareVo.setEstimatedDurationMin(routeVo.getDuration() / 60); @@ -333,7 +338,7 @@ return 1; } else { // 已完成接单的数据 List<XpGeoQueryEntity> validEntities = entities.stream().filter(e -> e.getStatus() > 1).collect(Collectors.toList()); List<XpGeoQueryEntity> validEntities = entities.stream().filter(e -> e.getStatus() == 1).collect(Collectors.toList()); // 计算平均等待时间 OptionalDouble averageOptional = validEntities.stream().mapToDouble(XpGeoQueryEntity::getSeconds).average(); @@ -347,6 +352,8 @@ double waitMinutes = (average * count) / 60; if (waitMinutes > 3) { throw new ServiceException("当前区域正在等待中,请耐心等待", I18nKeyConstant.WAITING_TIME); }else if(waitMinutes==0){ return 1; } return (int) waitMinutes; } @@ -415,6 +422,8 @@ // 生成订单数据 XpOrder xpOrder = new XpOrder(); xpOrder.setVehicleId(1l); // xpOrder.setStartMileage(Double.valueOf(startMileage)); @@ -517,6 +526,16 @@ xpOrder.setQueueName(s); xpOrderClient.updateOrder(xpOrder); // 存储mongodb 数据 XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity(); xpGeoQueryEntity.setOrderId(orderId.toString()); xpGeoQueryEntity.setLocation(new GeoJsonPoint(xpPoint.getPointLon(),xpPoint.getPointLat())); // 计算2个时间的秒数 xpGeoQueryEntity.setTime(currentTimeSlot); xpGeoQueryEntity.setStatus(0); xpGeoQueryEntity.setTimestamp(System.currentTimeMillis()); geoQueryEntityService.save(xpGeoQueryEntity); return orderId; } } @@ -525,6 +544,18 @@ redisService.setCacheList(key, Arrays.asList(orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + routeAndFareDto.getRoutePlanRule())); xpOrder.setQueueName(key); xpOrderClient.updateOrder(xpOrder); // 存储mongodb 数据 XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity(); xpGeoQueryEntity.setOrderId(orderId.toString()); xpGeoQueryEntity.setLocation(new GeoJsonPoint(xpPoint.getPointLon(),xpPoint.getPointLat())); // 计算2个时间的秒数 xpGeoQueryEntity.setTime(currentTimeSlot); xpGeoQueryEntity.setStatus(0); geoQueryEntityService.save(xpGeoQueryEntity); return orderId; // xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java
@@ -1,14 +1,15 @@ package com.xiaopeng.operation.utils; import cn.hutool.core.bean.BeanUtil; import com.xiaopeng.api.dto.automaticPilot.PushOrderDto; 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.automaticPilot.OrderMissionClient; 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; @@ -16,6 +17,7 @@ import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; import com.xiaopeng.api.vo.operation.RouteVo; import com.xiaopeng.api.vo.operation.VehiclePositionVo; import com.xiaopeng.api.vo.vehicleConvoy.AddTaxiPassengerVo; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.domain.R; @@ -25,8 +27,8 @@ import com.xiaopeng.operation.service.IXpTradingStrategyService; import com.xiaopeng.operation.service.XpPointService; import com.xiaopeng.operation.utils.mango.VehiclePositionService; import com.xiaopeng.api.vo.operation.VehiclePositionVo; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; @@ -39,6 +41,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j @Component public class TaskOrderUtil { @@ -72,6 +75,9 @@ @Autowired private XpMapClient xpMapClient; // @Autowired private OrderMissionClient orderMissionClient; // 自定义线程池 @@ -102,7 +108,7 @@ String[] split = firstElement.toString().split("_"); // 如果未处理 创建新任务 if (split[2].equals("0")) { String value = split[0] + "_" + split[1] + "_1" + "_"+split[3] + "_"+ split[4] + "_"+ split[5] + "_"+ split[6] + "_"+ split[7]; String value = split[0] + "_" + split[1] + "_1" + "_" + split[3] + "_" + split[4] + "_" + split[5] + "_" + split[6] + "_" + split[7]; // 状态改为处理中 redisService.updateFirst(name, value); threadPoolTaskExecutor.submit(() -> { @@ -220,8 +226,8 @@ @SneakyThrows private void processPushOrder(String quoteName, int count, String uid, String[] split, String orderNo, XpTradingStrategy xpTradingStrategy, String distance, String duration, String planRule) { System.out.println("进入推单环环节"); if(count==0){ count=1; if (count == 0) { count = 1; } // 订单状态 R<XpOrder> xpOrderR = xpOrderClient.queryOrderByOrderNo(orderNo); @@ -596,7 +602,142 @@ // 订单价值 if (Objects.equals(secondMax, tendencyOrderValue)) { // 订单价值 // 时间差权重 Integer weightTimeDifference = xpTradingStrategy.getWeightTimeDifference(); // 距离差权重 Integer weightDistanceDifference = xpTradingStrategy.getWeightDistanceDifference(); // 距离权重 Integer weightDistance = xpTradingStrategy.getWeightDistance(); // 时间权重 Integer weightTime = xpTradingStrategy.getWeightTime(); // 电量权重 Integer weightElectricity = xpTradingStrategy.getWeightElectricity(); // 找出最大的值是那个 Integer max1 = Math.max(weightTimeDifference, Math.max(weightDistanceDifference, Math.max(weightDistance, Math.max(weightTime, weightElectricity)))); // 时间差 if (max1.equals(weightTimeDifference)) { for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { RouteAndFareDto routeAndFareDto = new RouteAndFareDto(); LonLatDto lonLatDto = new LonLatDto(); lonLatDto.setLongitude(vehiclePosition.getPoint().getX()); lonLatDto.setLatitude(vehiclePosition.getPoint().getY()); routeAndFareDto.setOrigin(lonLatDto); LonLatDto lonLatDto1 = new LonLatDto(); lonLatDto1.setLongitude(Double.valueOf(split[0])); lonLatDto1.setLatitude(Double.valueOf(split[1])); routeAndFareDto.setDestination(lonLatDto1); routeAndFareDto.setRoutePlanRule(Integer.valueOf(planRule)); R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto); if (distanceAndDuration.getCode() == 200) { // 送驾- 接驾 Integer durationDifference = Integer.parseInt(duration) - distanceAndDuration.getData().getDuration(); vehiclePosition.setTimeDifference(durationDifference); } } } if (max1.equals(weightDistanceDifference)) { for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { RouteAndFareDto routeAndFareDto = new RouteAndFareDto(); LonLatDto lonLatDto = new LonLatDto(); lonLatDto.setLongitude(vehiclePosition.getPoint().getX()); lonLatDto.setLatitude(vehiclePosition.getPoint().getY()); routeAndFareDto.setOrigin(lonLatDto); LonLatDto lonLatDto1 = new LonLatDto(); lonLatDto1.setLongitude(Double.valueOf(split[0])); lonLatDto1.setLatitude(Double.valueOf(split[1])); routeAndFareDto.setDestination(lonLatDto1); routeAndFareDto.setRoutePlanRule(Integer.valueOf(planRule)); R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto); if (distanceAndDuration.getCode() == 200) { // 送驾- 接驾 Integer distanceDifference = Integer.parseInt(distance) - distanceAndDuration.getData().getDistance(); vehiclePosition.setDistanceDifference(distanceDifference); } } } if (max1.equals(weightDistance)) { for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { RouteAndFareDto routeAndFareDto = new RouteAndFareDto(); LonLatDto lonLatDto = new LonLatDto(); lonLatDto.setLongitude(vehiclePosition.getPoint().getX()); lonLatDto.setLatitude(vehiclePosition.getPoint().getY()); routeAndFareDto.setOrigin(lonLatDto); LonLatDto lonLatDto1 = new LonLatDto(); lonLatDto1.setLongitude(Double.parseDouble(split[0])); lonLatDto1.setLatitude(Double.parseDouble(split[1])); routeAndFareDto.setRoutePlanRule(Integer.valueOf(planRule)); routeAndFareDto.setDestination(lonLatDto1); // 调用地图 获取时间 R<RouteVo> r = xpMapClient.getDistanceAndDuration(routeAndFareDto); if (r.getCode() == 200) { vehiclePosition.setDistance(Double.valueOf(r.getData().getDistance())); } else { vehiclePosition.setDistance(0.0); } } } if (max1.equals(weightTime)) { for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { RouteAndFareDto routeAndFareDto = new RouteAndFareDto(); LonLatDto lonLatDto = new LonLatDto(); lonLatDto.setLongitude(vehiclePosition.getPoint().getX()); lonLatDto.setLatitude(vehiclePosition.getPoint().getY()); routeAndFareDto.setOrigin(lonLatDto); LonLatDto lonLatDto1 = new LonLatDto(); lonLatDto1.setLongitude(Double.parseDouble(split[0])); lonLatDto1.setLatitude(Double.parseDouble(split[1])); routeAndFareDto.setRoutePlanRule(Integer.valueOf(planRule)); routeAndFareDto.setDestination(lonLatDto1); // 调用地图 获取时间 R<RouteVo> r = xpMapClient.getDistanceAndDuration(routeAndFareDto); if (r.getCode() == 200) { vehiclePosition.setSecond(r.getData().getDuration()); } else { vehiclePosition.setSecond(0); } } } if (max1.equals(weightElectricity)) { // 计算车的分数 分数最大是车的size availableVehiclesNearby = availableVehiclesNearby.stream().sorted(Comparator.comparing(VehiclePositionVo::getCurrentElectricity).reversed()).collect(Collectors.toList()); int size1 = tendencyElectricity; // 做比对的分数 int temp1 = availableVehiclesNearby.get(0).getCurrentElectricity(); for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { vehiclePosition.setCurrentElectricityScore(vehiclePosition.getCurrentElectricityScore() + size1); if (temp1 != vehiclePosition.getCurrentElectricity()) { size1--; } } } // 算出总分 再来排序计算 for (VehiclePositionVo vehiclePositionVo : availableVehiclesNearby) { vehiclePositionVo.setOrderValueTotal(vehiclePositionVo.getCurrentElectricityScore() + vehiclePositionVo.getDistanceScore() + vehiclePositionVo.getTimeScore() + vehiclePositionVo.getDistanceDifference() + vehiclePositionVo.getTimeDifference()); } availableVehiclesNearby = availableVehiclesNearby.stream().sorted(Comparator.comparing(VehiclePositionVo::getOrderValueTotal).reversed()).collect(Collectors.toList()); int size2 = tendencyOrderValue; // 做比对的分数 double temp2 = availableVehiclesNearby.get(0).getOrderValueTotal(); for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { vehiclePosition.setValueScore(vehiclePosition.getValueScore() + size2); if (temp2 != vehiclePosition.getOrderValueTotal()) { size2--; } } } } @@ -616,7 +757,7 @@ RLock lock = redissonClient.getLock("FIND_CAR_" + vehiclePositionVo.getVin()); lock.lock(10, TimeUnit.SECONDS); System.err.println("队列:"+quoteName+" 车辆:"+vehiclePositionVo.getVin()+"订单"+orderNo); System.err.println("队列:" + quoteName + " 车辆:" + vehiclePositionVo.getVin() + "订单" + orderNo); try { // VehiclePosition vehiclePosition = vehiclePositionService.findByVehicleId(vehiclePositionVo.getVehicle_id()); R<XpVehicle> vehiclePositionR = xpVehicleClient.findByVehicleVin(vehiclePositionVo.getVin()); @@ -630,27 +771,15 @@ } // 关闭巡游单子 TODO AddTaxiPassengerVo addTaxiPassengerVo = new AddTaxiPassengerVo(); addTaxiPassengerVo.setVin(vehiclePosition.getVehicleVin()); addTaxiPassengerVo.setUid(uid); addTaxiPassengerVo.setOrderNo(orderNo); // TODO appid addTaxiPassengerVo.setAppId(""); PushOrderDto pushOrderDto = new PushOrderDto(); pushOrderDto.setOrderId(xpOrder.getId()); pushOrderDto.setVin(vehiclePosition.getVehicleVin()); BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.addTaxiPassenger(addTaxiPassengerVo); if (baseVehicleUserDto!=null && baseVehicleUserDto.getCode() == 200) { // TODO 下单 // 调用推送服务 orderId + carVin orderMissionClient.pushOrder(pushOrderDto); // 调用推送服务 orderId + carVin // 绑定成功 弹出这个队列第一个订单 redisService.popFirstElement(quoteName); return; } } catch (Exception e) { e.printStackTrace(); } finally { @@ -666,33 +795,25 @@ lock.lock(10, TimeUnit.SECONDS); try { // 查询车辆状态 R<XpVehicle> vehicleR = xpVehicleClient.findByVehicleVin(vehicleVin); if(vehicleR.getCode()!=200){ continue; } XpVehicle vehicleId1 = vehicleR.getData(); R<XpVehicle> vehicleR = xpVehicleClient.findByVehicleVin(vehicleVin); if (vehicleR.getCode() != 200) { continue; } XpVehicle vehicleId1 = vehicleR.getData(); if (vehicleId1.getStatus() != 1 || vehicleId1.getState() != 0) { lock.unlock(); continue; } if (vehiclePosition.getHasOrder() == 1) { // 关闭巡游单子 TODO } AddTaxiPassengerVo addTaxiPassengerVo = new AddTaxiPassengerVo(); addTaxiPassengerVo.setVin(vehiclePosition.getVin()); addTaxiPassengerVo.setUid(uid); addTaxiPassengerVo.setOrderNo(orderNo); // TODO appid addTaxiPassengerVo.setAppId(""); BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.addTaxiPassenger(addTaxiPassengerVo); if (baseVehicleUserDto.getCode() == 200) { // 关闭巡游单子 TODO PushOrderDto pushOrderDto = new PushOrderDto(); pushOrderDto.setOrderId(xpOrder.getId()); pushOrderDto.setVin(vehiclePosition.getVin()); redisService.popFirstElement(quoteName); lock.unlock(); return; } // 调用推送服务 orderId + carVin orderMissionClient.pushOrder(pushOrderDto); } catch (Exception e) { } finally { @@ -704,5 +825,70 @@ } } /** * 订单处理 * @param count 轮数 * @param uid * @param longitude 经度 * @param latitude 纬度 * @param orderNo 订单号 */ @SneakyThrows public void reassignPushOrder(int count, String uid, Double longitude, Double latitude, String orderNo) { System.out.println("进入推单环环节"); if (count == 0) { count = 1; } // 订单状态 R<XpOrder> xpOrderR = xpOrderClient.queryOrderByOrderNo(orderNo); if (xpOrderR.getCode() != 200) { return; } if (xpOrderR.getData().getStatus() != 1) { return; } XpOrder data = xpOrderR.getData(); XpTradingStrategy xpTradingStrategy = xpTradingStrategyService.getOne(null); for (int i = 0; i < count; i++) { log.info("进入找车环节"); List<VehiclePosition> positionList = vehiclePositionService.findAvailableVehiclesNearby(longitude, latitude, xpTradingStrategy.getOnePushDistance(), xpTradingStrategy.getSimultaneouslyOrderCount()); // 获取车辆的区域信息 if (positionList.isEmpty()) { continue; } // 直接派单 定义一个锁来锁定车辆 VehiclePosition vehiclePosition = positionList.get(0); String vehicleVin = vehiclePosition.getVin(); RLock lock = redissonClient.getLock("FIND_CAR_" + vehiclePosition.getVin()); lock.lock(10, TimeUnit.SECONDS); try { // 查询车辆状态 R<XpVehicle> vehicleR = xpVehicleClient.findByVehicleVin(vehicleVin); if (vehicleR.getCode() != 200) { continue; } XpVehicle vehicleId1 = vehicleR.getData(); if (vehicleId1.getStatus() != 1 || vehicleId1.getState() != 0) { lock.unlock(); continue; } // 关闭巡游单子 TODO PushOrderDto pushOrderDto = new PushOrderDto(); pushOrderDto.setOrderId(data.getId()); pushOrderDto.setVin(vehiclePosition.getVin()); // 调用推送服务 orderId + carVin orderMissionClient.pushOrder(pushOrderDto); } catch (Exception e) { } finally { lock.unlock(); } } } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.ImmutableMap; import com.xiaopeng.api.dto.account.GetUserCouponDto; import com.xiaopeng.api.dto.automaticPilot.PushOrderDto; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.dto.order.GetOrderNumDto; import com.xiaopeng.api.dto.order.HistoryEndPointsDto; @@ -20,6 +21,7 @@ import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto; import com.xiaopeng.api.feignClient.account.XpAppUserClient; import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient; import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient; import com.xiaopeng.api.feignClient.operation.XpPointClient; import com.xiaopeng.api.feignClient.operation.XpPricingClient; import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient; @@ -40,7 +42,9 @@ import com.xiaopeng.api.model.vehicleConvoy.XpFleet; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.account.UserCouponVo; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudOrderOperation.OrderOperationType; import com.xiaopeng.api.vo.order.*; import com.xiaopeng.api.vo.vehicleConvoy.AddTaxiPassengerVo; import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.domain.R; @@ -127,6 +131,7 @@ private final XpSystemSettingClient xpSystemSettingClient; private final OrderTrajectoryService orderTrajectoryService; private final XpPushMessageClient xpPushMessageClient; private final OrderMissionClient orderMissionClient; @Autowired private MongoTemplate mongoTemplate; @@ -726,7 +731,6 @@ // TODO 加配置文件取 cancelTaxiPassengerVo.setAppId(""); BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo); if (baseVehicleUserDto.getCode() != 200) { throw new RuntimeException("取消订单失败"); @@ -749,6 +753,13 @@ Integer i = Integer.valueOf(cacheObject.toString()); redisService.setCacheCancelOrder("CANCEL_ORDER_" + uid, i + 1); } // 下发命令 PushOrderDto pushOrderDto = new PushOrderDto(); pushOrderDto.setOrderId(order.getId()); pushOrderDto.setVin(xpVehicle.getVehicleVin()); pushOrderDto.setOperation_type(OrderOperationType.VEHICLE_CANCEL_ORDER); orderMissionClient.cancelOrder(pushOrderDto); } @Override @@ -769,7 +780,10 @@ xpOrderCancelService.save(xpOrderCancel); // 推送消息 R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(order.getVehicleId()); XpVehicle xpVehicle = xpVehicleR.getData(); // 消息推送 Map<String, Object> params = new HashMap<>(); Map<String, Object> content = new HashMap<>(); @@ -784,6 +798,13 @@ sendMessageDTO.setAppUserId(String.valueOf(order.getAppUserId())); xpPushMessageClient.sendStatus(sendMessageDTO); log.info("订单取消消息推送完毕"); // 下发命令 PushOrderDto pushOrderDto = new PushOrderDto(); pushOrderDto.setOrderId(order.getId()); pushOrderDto.setVin(xpVehicle.getVehicleVin()); pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER); orderMissionClient.cancelOrder(pushOrderDto); } /** @@ -817,6 +838,7 @@ evaluate.setFeedbackContent(dto.getFeedbackContent()); evaluate.setFeedbackPictures(dto.getFeedbackPictures()); evaluate.setFeedbackTypeId(dto.getFeedbackTypeId()); evaluate.setCreateTime(LocalDateTime.now()); xpOrderEvaluateService.save(evaluate); } @@ -1743,8 +1765,11 @@ orderDetailVo.setFeedbackPictures(evaluate.getFeedbackPictures()); orderDetailVo.setFeedbackContent(evaluate.getFeedbackContent()); orderDetailVo.setOrderRating(evaluate.getOrderRating()); XpFeedbackType feedbackType = xpSystemSettingClient.getFeedbackTypeById(evaluate.getFeedbackTypeId()).getData(); orderDetailVo.setFeedbackTypeName(feedbackType.getFeedbackType()); if(evaluate.getFeedbackTypeId()!=null){ XpFeedbackType feedbackType = xpSystemSettingClient.getFeedbackTypeById(evaluate.getFeedbackTypeId()).getData(); orderDetailVo.setFeedbackTypeName(feedbackType.getFeedbackType()); } } List<OrderTrajectory> trajectories = mongoTemplate.find( new Query(Criteria.where("orderId").is(orderId)) @@ -1776,35 +1801,35 @@ @Transactional // @ShardingTransactionType(TransactionType.BASE) public void popOrder(PopOrderDto dto) { XpOrder order = this.getById(dto.getOrderId()); order.setAcceptanceTime(LocalDateTime.now()); AddTaxiPassengerVo addTaxiPassengerVo = new AddTaxiPassengerVo(); R<XpVehicle> byVehicleVin = xpVehicleClient.findByVehicleVin(dto.getXpVehicleVin()); if (byVehicleVin.getCode() == 200) { order.setVehicleId(byVehicleVin.getData().getId()); addTaxiPassengerVo.setVin(byVehicleVin.getData().getVehicleVin()); } order.setStatus(2); // Redis弹出这个队列元素 // value 订单Id_经纬度_状态_用户id_订单编号_距离_时间 String popFirstElement = redisService.popFirstElement(order.getQueueName()); XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity(); String[] split = popFirstElement.split("_"); xpGeoQueryEntity.setOrderId(split[0]); String[] split1 = split[1].split(","); xpGeoQueryEntity.setLocation(new GeoJsonPoint(Double.parseDouble(split1[0]), Double.parseDouble(split1[1]))); xpGeoQueryEntity.setTimestamp(new Date()); // 计算2个时间的秒数 LocalDateTime createTime = order.getCreateTime(); LocalDateTime acceptanceTime = order.getAcceptanceTime(); xpGeoQueryEntity.setSeconds((int) Duration.between(createTime, acceptanceTime).getSeconds()); String currentTimeSlot = DateUtils.getCurrentTimeSlot(); xpGeoQueryEntity.setTime(currentTimeSlot); // 存mongo数据 xpPricingClient.saveXpGeoQueryEntity(xpGeoQueryEntity); this.updateById(order); addTaxiPassengerVo.setUid(order.getAppUserId().toString()); addTaxiPassengerVo.setOrderNo(order.getOrderNumber()); // TODO appid addTaxiPassengerVo.setAppId(""); BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.addTaxiPassenger(addTaxiPassengerVo); if(baseVehicleUserDto.getCode()==200){ // Redis弹出这个队列元素 // value 订单Id_经纬度_状态_用户id_订单编号_距离_时间 String popFirstElement = redisService.popFirstElement(order.getQueueName()); XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity(); String[] split = popFirstElement.split("_"); xpGeoQueryEntity.setOrderId(split[0]); // 存mongo数据 xpPricingClient.updateXpGeoQueryEntity(split[0]); this.updateById(order); } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/test/GpsPoint.java
New file @@ -0,0 +1,13 @@ package com.xiaopeng.order.test; import lombok.Data; import java.util.Date; @Data public class GpsPoint { private Double distance; // 与上一点距离(米) private Long locatetime; // 毫秒时间戳 private Date createTime; // 创建日期 // ... getter & setter } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/test/TaxiPricingDemoService.java
New file @@ -0,0 +1,300 @@ package com.xiaopeng.order.test; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalTime; import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; /** * 出租车计费引擎服务 */ public class TaxiPricingDemoService { public static void main(String[] args) { PricingConfig config = setupMockConfig(); Calendar cal = Calendar.getInstance(); cal.set(2026, Calendar.MARCH, 13, 14, 40, 15); Date orderTime = cal.getTime(); // 模拟总里程 25.0 km,以触发多级远途加价 double totalMillige = 7.874; // 模拟 GPS 轨迹点 List<GpsPoint> points = createMockGpsPoints(orderTime); TaxiPricingService service = new TaxiPricingService(); Map<String, BigDecimal> bill = service.calculateOrderPrice(orderTime, totalMillige, points, config); System.err.println("======= 订单计费账单 ======="); bill.forEach((detail, price) -> System.err.println(detail + ": " + price + " 元")); BigDecimal totalAmount = bill.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add); System.err.println("--------------------------"); System.err.println("应付总额: " + totalAmount + " 元"); } public static class TaxiPricingService { public Map<String, BigDecimal> calculateOrderPrice(Date orderTime, double totalMileageKm, List<GpsPoint> points, PricingConfig config) { Map<String, BigDecimal> result = new LinkedHashMap<>(); BigDecimal totalMileage = BigDecimal.valueOf(totalMileageKm); PricingRule baseRule = config.findMatchedRule(orderTime); result.put("起步价(" + baseRule.ruleName + ")"+"-"+baseRule.start+"-"+baseRule.end, baseRule.baseFare); if (totalMileage.compareTo(baseRule.baseKm) <= 0) { return formatMap(result); } BigDecimal remainBaseM = baseRule.baseKm.multiply(new BigDecimal("1000")); BigDecimal remainBaseMs = baseRule.baseMin.multiply(new BigDecimal("60000")); BigDecimal currentTotalM = BigDecimal.ZERO; BigDecimal currentTotalMs = BigDecimal.ZERO; boolean isOverBaseM = false; boolean isOverBaseMs = false; // 超出里程和分钟数的集合 Map<String, BigDecimal> bill = new LinkedHashMap<>(); Map<String, BigDecimal> activeRuleNameRate = new LinkedHashMap<>(); Set<String> activeRuleNameList = new HashSet<>(); for (int i = 1; i < points.size(); i++) { GpsPoint curr = points.get(i); GpsPoint prev = points.get(i - 1); BigDecimal segM = BigDecimal.valueOf(curr.getDistance()); BigDecimal segMs = BigDecimal.valueOf(curr.getLocatetime() - prev.getLocatetime()); currentTotalM = currentTotalM.add(segM); // 累计当前已行驶的总里程 currentTotalMs = currentTotalMs.add(segMs); // 累计当前已行驶的总时间 PricingRule activeRule = config.findMatchedRule(new Date(curr.getLocatetime())); // 扣除起步额度逻辑 BigDecimal billM = BigDecimal.ZERO; if (currentTotalM.compareTo(remainBaseM) > 0) { // billM = currentTotalM.subtract(remainBaseM); isOverBaseM = true; } BigDecimal billMs = BigDecimal.ZERO; if (currentTotalMs.compareTo(remainBaseMs) > 0) { // billMs = currentTotalMs.subtract(remainBaseMs); isOverBaseMs = true; } // 1. 基础费率计算 // BigDecimal distFee = billM.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP).multiply(activeRule.perKmRate); // BigDecimal timeFee = billMs.multiply(activeRule.perMinRate); // 2. 远途加价计算 (基于区间判定) // BigDecimal longExtra = calculateLongDistExtra(currentTotalM, billM, config.longSteps); // BigDecimal totalSeg = distFee.add(timeFee).add(longExtra); // 超出里程 if (isOverBaseM) { bill.put(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml", bill.getOrDefault(activeRule.ruleName + activeRule.start + "-" + activeRule.end + "_Ml", BigDecimal.ZERO).add(segM)); } // 超出时间 if (isOverBaseMs) { bill.put(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms", bill.getOrDefault(activeRule.ruleName + activeRule.start + "-" + activeRule.end + "_Ms", BigDecimal.ZERO).add(segMs)); } // 若不存在规则名称,则添加 activeRuleNameRate.computeIfAbsent(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml", k -> activeRule.perKmRate); // 超出里程的加价 activeRuleNameRate.computeIfAbsent(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms",k ->activeRule.perMinRate); // 超出时间的加价 activeRuleNameList.add(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml"); activeRuleNameList.add(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms"); // result.put(activeRule.ruleName, result.getOrDefault(activeRule.ruleName, BigDecimal.ZERO).add(totalSeg)); } // 1. 基础费率计算 for (String ruleName : activeRuleNameList) { String[] split = ruleName.split("_"); if(ruleName.endsWith("_Ml")){ BigDecimal exceedValue = bill.get(ruleName); if(Objects.nonNull(exceedValue)){ exceedValue = exceedValue.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP); }else{ exceedValue = BigDecimal.ZERO; } BigDecimal exceedRate = activeRuleNameRate.get(ruleName); result.put(split[0], result.getOrDefault(split[0], BigDecimal.ZERO).add(exceedValue.multiply(exceedRate))); }else{ BigDecimal exceedValue = bill.get(ruleName); if(Objects.nonNull(exceedValue)){ exceedValue = exceedValue.divide(new BigDecimal(60000), 2, RoundingMode.HALF_UP); }else{ exceedValue = BigDecimal.ZERO; } BigDecimal exceedRate = activeRuleNameRate.get(ruleName); result.put(split[0], result.getOrDefault(split[0], BigDecimal.ZERO).add(exceedValue.multiply(exceedRate))); } } // 2. 远途加价计算 (基于区间判定) BigDecimal longExtra = calculateLongDistExtra(BigDecimal.valueOf(totalMileageKm), config.longSteps); result.put("远途费", longExtra); return formatMap(result); } /** * 远途加价核心逻辑:判断当前行驶到的里程处于哪个加价区间 */ private BigDecimal calculateLongDistExtra(BigDecimal billM, List<LongRangeStep> steps) { if (billM.compareTo(BigDecimal.ZERO) <= 0) return BigDecimal.ZERO; // 当前点所在的公里数 BigDecimal extraStart = BigDecimal.ZERO; BigDecimal extraRate = BigDecimal.ZERO; // 匹配区间规则 for (LongRangeStep step : steps) { if (step.isMatch(billM)) { extraRate = step.addPrice; extraStart = step.numStart; break; } } return billM.subtract(extraStart).multiply(extraRate); } private Map<String, BigDecimal> formatMap(Map<String, BigDecimal> map) { return map.entrySet().stream().collect(Collectors.toMap( Map.Entry::getKey, e -> e.getValue().setScale(2, RoundingMode.HALF_UP), (a, b) -> b, LinkedHashMap::new)); } } // --- 远途区间模型 --- public static class LongRangeStep { BigDecimal numStart; // 超过公里数 BigDecimal numEnd; // 不足公里数 BigDecimal addPrice; // 加收单价 public LongRangeStep(double start, double end, double price) { this.numStart = BigDecimal.valueOf(start); this.numEnd = BigDecimal.valueOf(end); this.addPrice = BigDecimal.valueOf(price); } public boolean isMatch(BigDecimal currentKm) { // [numStart, numEnd) return currentKm.compareTo(numStart) >= 0 && currentKm.compareTo(numEnd) < 0; } } public static class PricingConfig { PricingRule holiday; PricingRule peak; PricingRule night; List<PricingRule> normals = new ArrayList<>(); List<LongRangeStep> longSteps = new ArrayList<>(); // 修改为区间列表 public PricingRule findMatchedRule(Date date) { LocalTime t = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime(); if (holiday.isMatch(t)) return holiday; if (peak.isMatch(t)) return peak; if (night.isMatch(t)) return night; for (PricingRule r : normals) if (r.isMatch(t)) return r; return normals.get(0); } } public static class PricingRule { String ruleName; LocalTime start, end; BigDecimal baseFare, baseKm, baseMin, perKmRate, perMinRate; public PricingRule(String n, String s, String e, double bf, double bk, double bm, double pkr, double pmr) { this.ruleName = n; this.start = LocalTime.parse(s); this.end = LocalTime.parse(e); this.baseFare = BigDecimal.valueOf(bf); this.baseKm = BigDecimal.valueOf(bk); this.baseMin = BigDecimal.valueOf(bm); this.perKmRate = BigDecimal.valueOf(pkr); this.perMinRate = BigDecimal.valueOf(pmr); } public boolean isMatch(LocalTime now) { return start.isBefore(end) ? (!now.isBefore(start) && !now.isAfter(end)) : (!now.isBefore(start) || !now.isAfter(end)); } } public static class GpsPoint { private double distance; private long locatetime; public GpsPoint(double d, long t) { this.distance = d; this.locatetime = t; } public double getDistance() { return distance; } public long getLocatetime() { return locatetime; } } private static PricingConfig setupMockConfig() { PricingConfig config = new PricingConfig(); config.peak = new PricingRule("高峰期", "14:40", "14:46", 15.0, 3.0, 5.0, 3.0, 0.8); config.night = new PricingRule("夜间行驶", "14:47", "14:48", 18.0, 3.0, 5.0, 3.5, 1.0); config.holiday = new PricingRule("节假日", "18:47", "18:48", 18.0, 3.0, 5.0, 4, 2.0); config.normals.add(new PricingRule("普通时段", "14:46", "14:47", 10.0, 3.0, 5.0, 2.4, 0.5)); config.normals.add(new PricingRule("普通时段", "14:48", "17:59", 10.0, 3.0, 5.0, 2.4, 0.5)); // --- 远途加价区间配置 (关键修改) --- // 1. 超过 num1(12) 不足 num2(20), 加收 num3(1.2) config.longSteps.add(new LongRangeStep(5.0, 6.0, 1.2)); // 2. 超过 num4(20) 不足 num5(35), 加收 num6(2.0) config.longSteps.add(new LongRangeStep(6.0, 7.0, 2.0)); // 3. 超过 num7(35) 以后, 加收 num8(3.5) -> numEnd 传个极大值 config.longSteps.add(new LongRangeStep(7.0, 8.0, 3.5)); return config; } private static List<GpsPoint> createMockGpsPoints(Date startTime) { List<GpsPoint> list = new ArrayList<>(); long startTs = startTime.getTime(); list.add(new GpsPoint(0, startTs)); // 模拟行驶了 20km list.add(new GpsPoint(120, startTs + 2 * 10 * 1000)); list.add(new GpsPoint(204, startTs + 2 * 20 * 1000)); list.add(new GpsPoint(231, startTs + 2 * 30 * 1000)); list.add(new GpsPoint(252, startTs + 2 * 40 * 1000)); list.add(new GpsPoint(188, startTs + 2 * 50 * 1000)); list.add(new GpsPoint(150, startTs + 2 * 60 * 1000)); list.add(new GpsPoint(126, startTs + 2 * 70 * 1000)); list.add(new GpsPoint(138, startTs + 2 * 80 * 1000)); list.add(new GpsPoint(119, startTs + 2 * 90 * 1000)); list.add(new GpsPoint(147, startTs + 2 * 100 * 1000)); list.add(new GpsPoint(155, startTs + 2 * 110 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 120 * 1000)); list.add(new GpsPoint(172, startTs + 2 * 130 * 1000)); list.add(new GpsPoint(202, startTs + 2 * 140 * 1000)); list.add(new GpsPoint(162, startTs + 2 * 150 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 160 * 1000)); list.add(new GpsPoint(132, startTs + 2 * 170 * 1000)); list.add(new GpsPoint(142, startTs + 2 * 180 * 1000)); list.add(new GpsPoint(262, startTs + 2 * 190 * 1000)); list.add(new GpsPoint(242, startTs + 2 * 200 * 1000)); list.add(new GpsPoint(282, startTs + 2 * 210 * 1000)); list.add(new GpsPoint(222, startTs + 2 * 220 * 1000)); list.add(new GpsPoint(212, startTs + 2 * 230 * 1000)); list.add(new GpsPoint(142, startTs + 2 * 240 * 1000)); list.add(new GpsPoint(262, startTs + 2 * 250 * 1000)); list.add(new GpsPoint(169, startTs + 2 * 260 * 1000)); list.add(new GpsPoint(170, startTs + 2 * 270 * 1000)); list.add(new GpsPoint(180, startTs + 2 * 280 * 1000)); list.add(new GpsPoint(165, startTs + 2 * 290 * 1000)); list.add(new GpsPoint(180, startTs + 2 * 300 * 1000)); list.add(new GpsPoint(198, startTs + 2 * 310 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 320 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 330 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 340 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 350 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 360 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 370 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 380 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 390 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 400 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 410 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 420 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 430 * 1000)); return list; } } xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -404,6 +404,20 @@ XpVehicle xpVehicle = xpVehicleService.getOne(new LambdaQueryWrapper<XpVehicle>().eq(XpVehicle::getVehicleVin, vehicleVin)); return R.ok(xpVehicle); } /** * 根据XPUID查询车辆信息 * @param xpuid * @return */ @GetMapping("/inner/vehicle/findByVehicleXpuId") public R<XpVehicle> findByVehicleXpuId(@RequestParam("xpuid") String xpuid){ XpVehicle xpVehicle = xpVehicleService.getOne(new LambdaQueryWrapper<XpVehicle>().eq(XpVehicle::getXpuId, xpuid)); return R.ok(xpVehicle); } /** * 查询可改派的车辆列表 * xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto; import com.xiaopeng.api.feignClient.automaticPilot.XpuVinClient; import com.xiaopeng.api.feignClient.operation.XpElectronicClient; import com.xiaopeng.api.feignClient.order.XpOrderClient; import com.xiaopeng.api.model.operation.XpElectronicFence; @@ -37,6 +38,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.*; @@ -65,6 +67,9 @@ @Autowired private IXpVehicleAnnualRecordService xpVehicleAnnualRecordService; @Resource private XpuVinClient xpuVinClient; @Override public List<XpVehicle> listByIds(List<Long> ids) { @@ -191,6 +196,10 @@ xpVehicle.setStatus(1); // 设置入库时间 xpVehicle.setWarehousingTime(LocalDateTime.now()); //绑定XPUID String vin = xpVehicle.getVehicleVin(); String xpuid = xpuVinClient.getXpuIdByVin(vin).getData(); xpVehicle.setXpuId(xpuid); this.save(xpVehicle); } @@ -200,6 +209,10 @@ // DTO转实体 XpVehicle xpVehicle = new XpVehicle(); BeanUtil.copyProperties(dto, xpVehicle); //绑定XPUID String vin = xpVehicle.getVehicleVin(); String xpuid = xpuVinClient.getXpuIdByVin(vin).getData(); xpVehicle.setXpuId(xpuid); this.updateById(xpVehicle); }