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 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 polyline = routeVo.getPolyline(); List 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> 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 lonLatDtos = remainingNavigation.get("unserved"); List 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 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 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 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 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 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(); } }