From c64c8dba6ea27f1d2222a743927d2cd3606b1ef3 Mon Sep 17 00:00:00 2001
From: xuhy <3313886187@qq.com>
Date: 星期四, 12 二月 2026 20:38:05 +0800
Subject: [PATCH] 通过车辆实时定位推送订单轨迹
---
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/XpPushMessageClient.java | 10 ++
xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/utils/MapMatchingUtil.java | 123 ++++++++++++++++++++++++
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java | 6 +
xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/controller/PushOrderRouteController.java | 90 ++++++++++++++++++
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java | 3
xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/pushMessage/PushOrderRouteDTO.java | 42 ++++++++
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/PushMessageTypeEnum.java | 1
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/pushMessage/XpPushMessageClientFallbackFactory.java | 6 +
8 files changed, 280 insertions(+), 1 deletions(-)
diff --git a/xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/pushMessage/PushOrderRouteDTO.java b/xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/pushMessage/PushOrderRouteDTO.java
new file mode 100644
index 0000000..dfeb801
--- /dev/null
+++ b/xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/pushMessage/PushOrderRouteDTO.java
@@ -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;
+
+}
diff --git a/xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/pushMessage/XpPushMessageClientFallbackFactory.java b/xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/pushMessage/XpPushMessageClientFallbackFactory.java
index 832fa98..9f48a14 100644
--- a/xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/pushMessage/XpPushMessageClientFallbackFactory.java
+++ b/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());
+ }
};
}
}
diff --git a/xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/XpPushMessageClient.java b/xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/XpPushMessageClient.java
index a0ac7d1..1ff9e08 100644
--- a/xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/XpPushMessageClient.java
+++ b/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);
}
diff --git a/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java b/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java
index 71b0b9b..ceeb89e 100644
--- a/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java
+++ b/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java
@@ -120,7 +120,11 @@
*/
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:";
}
diff --git a/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/PushMessageTypeEnum.java b/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/PushMessageTypeEnum.java
index 6f81301..0182b16 100644
--- a/xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/PushMessageTypeEnum.java
+++ b/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
diff --git a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java
index ceae241..34eec00 100644
--- a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java
+++ b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java
@@ -111,6 +111,9 @@
// 存储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);
}
diff --git a/xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/controller/PushOrderRouteController.java b/xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/controller/PushOrderRouteController.java
new file mode 100644
index 0000000..588b865
--- /dev/null
+++ b/xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/controller/PushOrderRouteController.java
@@ -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();
+ }
+
+}
diff --git a/xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/utils/MapMatchingUtil.java b/xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/utils/MapMatchingUtil.java
new file mode 100644
index 0000000..5784d42
--- /dev/null
+++ b/xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/utils/MapMatchingUtil.java
@@ -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};
+ }
+
+
+}
--
Gitblit v1.7.1