liujie
2026-02-12 2c4388a3c769bc12504217cb3c478cde2a27979c
Merge branch 'master' of http://120.76.84.145:10101/gitblit/r/java/Robotaxi
3个文件已添加
5个文件已修改
281 ■■■■■ 已修改文件
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/pushMessage/XpPushMessageClientFallbackFactory.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/XpPushMessageClient.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/enums/PushMessageTypeEnum.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | 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-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/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/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-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:";
}
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-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);
    }
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};
    }
}