liujie
2026-05-12 560be817c1342d4da523760ba38433c4c0f52983
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package com.xiaopeng.pushMessage.controller;
 
import com.alibaba.fastjson2.JSON;
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.Constants;
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 com.xiaopeng.pushMessage.utils.TrajectoryDensifier;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
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.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
 
@Slf4j
@Api(tags = "订单推送轨迹接口")
@RestController
@RequestMapping("")
public class PushOrderRouteController {
 
    @Autowired
    private RedisService redisService;
    @Autowired
    private XpPushMessageClient xpPushMessageClient;
    @Autowired
    private XpOrderClient xpOrderClient;
 
    /**
     * 通过车辆实时位置
     * 给乘客端推送剩余轨迹和剩余里程时长
     * @return
     */
    @PostMapping("/logan/inner/v1/order/pushOrderRoute")
    public R<String> pushOrderRoute(@Validated @RequestBody PushOrderRouteDTO dto) {
        Integer routeType = dto.getRouteType();
        //正常订单
        if(routeType == 1){
            //非导航偏离才需要限频
            if(PushMessageTypeEnum.ORDER_ROUTE.getDesc().equals(dto.getMessageType())){
                Long pushTime = redisService.getCacheObject("PUSH_TIME:" + dto.getOrderId());
                if(null != pushTime && System.currentTimeMillis() - pushTime < 1500){
                    return R.ok();
                }
                redisService.setCacheObject("PUSH_TIME:" + dto.getOrderId(), System.currentTimeMillis());
            }
            XpOrder xpOrder = xpOrderClient.queryOrderById(dto.getOrderId()).getData();
            if(xpOrder.getStatus() <= 1 || xpOrder.getStatus() >= 8){
                return R.ok();
            }
            // 获取路线规划
            String key = "";
            //接驾情况,获取车辆位置到起点的导航
            if(xpOrder.getStatus() >= 2 && xpOrder.getStatus() <= 4){
                key = "ORDER_NAVIGATION_START_DATA:" + xpOrder.getAppUserId();
            }else{
                key = "ORDER_NAVIGATION_END_DATA:" + xpOrder.getAppUserId();
            }
            Object cacheObject = redisService.getCacheObject(key);
            RouteVo routeVo = JSON.parseObject(cacheObject.toString(), RouteVo.class);
            log.info("缓存获取的轨迹 {} {}", key, JSON.toJSONString(routeVo));
            if(Objects.nonNull(routeVo)){
                //开始对轨迹进行点位补全,然后更新缓存中的轨迹数据
                if(null == routeVo.getDensify() || routeVo.getDensify() == 0){
                    log.info("开始执行点位补全 {}", key);
                    List<LonLatDto> polyline = routeVo.getPolyline();
                    List<LonLatDto> densify = TrajectoryDensifier.densify(polyline, 15);
                    routeVo.setPolyline(densify);
                    routeVo.setDensify(1);
                    log.info("点位补全结束 {} {}", key, JSON.toJSONString(routeVo));
                    redisService.setCacheObject(key, JSON.toJSONString(routeVo), 24L, TimeUnit.HOURS);
                }
 
                Integer distance = dto.getDistance().intValue();
                Integer duration = dto.getDuration().intValue();
                Integer speed = dto.getDuration().intValue();
                // 计算实时点位到线段的最优截取
                Map<String, List<LonLatDto>> remainingNavigation = MapMatchingUtil.getRemainingNavigation(routeVo.getPolyline(), dto.getLongitude(), dto.getLatitude());
                log.info("remainingNavigation {}", JSON.toJSONString(remainingNavigation));
                //当订单状态为5的时候(乘客已上车)需要推送导航的距离和剩余时间
                //因为这个状态,车端推的依然是接驾的剩余里程和时间,就会导致数据和小程序的页面跳转匹配不上
                //小程序是只要乘客上车后就跳转到行程开始页面
                if(5 != xpOrder.getStatus()){
                    routeVo.setDistance(distance);
                    routeVo.setDuration(duration);
                }else{
                    distance = routeVo.getDistance();
                    duration = routeVo.getDuration();
                }
                List<LonLatDto> lonLatDtos = remainingNavigation.get("unserved");
                List<LonLatDto> lonLatDtos1 = remainingNavigation.get("served");
 
                routeVo.setPolyline(lonLatDtos);
                routeVo.setServed(lonLatDtos1);
                log.info("线路截取后存储的轨迹 {}", JSON.toJSONString(routeVo));
                // 存储最新路线偏离缓存,后续进行轨迹截取
                redisService.setCacheObject(key, JSON.toJSONString(routeVo), 24L, TimeUnit.HOURS);
 
                // 封装路由详情
                Map<String, Object> route = new HashMap<>();
                route.put("speed",speed);
                route.put("distance",distance);
                route.put("duration",duration);
                route.put("polyline",JSONObject.toJSONString(lonLatDtos));
                route.put("served",JSONObject.toJSONString(lonLatDtos1));
 
                Map<String, Object> content = new HashMap<>();
                content.put("orderId",dto.getOrderId().toString());
                content.put("routeType",routeType);
                content.put("direction",dto.getDirection());
                content.put("route",JSONObject.toJSONString(route));
                content.put("endLongitude", dto.getEndLongitude());
                content.put("endLatitude", dto.getEndLatitude());
 
                // 封装轨迹信息
                Map<String, Object> params = new HashMap<>();
                params.put("type", dto.getMessageType());
                params.put("content", JSONObject.toJSONString(content));
                params.put("timestamp", new Date().getTime()+"");
 
                // 进行消息推送
                SendMessageDTO sendMessageDTO = new SendMessageDTO();
                sendMessageDTO.setAppUserId(String.valueOf(xpOrder.getAppUserId()));
                sendMessageDTO.setMessage(JSONObject.toJSONString(params));
                R<String> stringR = xpPushMessageClient.sendRoute(sendMessageDTO);
                //推送失败,重试3次
                if(stringR.getCode() != Constants.SUCCESS){
                    ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
                    executor.execute(() -> {
                        for (int i = 0; i < 3; i++) {
                            R<String> stringR1 = xpPushMessageClient.sendStatus(sendMessageDTO);
                            if(stringR1.getCode() == Constants.SUCCESS){
                                break;
                            }
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                            log.info("推送乘客端剩余导航数据消息失败:{} {} {}", stringR1.getMsg(), JSON.toJSONString(sendMessageDTO), xpOrder.getOrderNumber());
                        }
                    });
                }
 
                log.info("推送剩余导航数据成功 {} {}", xpOrder.getOrderNumber(), JSON.toJSONString(routeVo));
            }
        }
        return R.ok();
    }
 
}