ManagementYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/controller/DistanceDisplayController.java
New file @@ -0,0 +1,39 @@ package com.stylefeng.guns.modular.system.controller; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.stylefeng.guns.modular.system.model.TSysDistanceDisplay; import com.stylefeng.guns.modular.system.service.ITSysDistanceDisplayService; import com.stylefeng.guns.modular.system.util.ResultUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * 距离展示配置内部接口 */ @RestController @RequestMapping("/base/distanceDisplay") public class DistanceDisplayController { @Autowired private ITSysDistanceDisplayService tSysDistanceDisplayService; /** * 查询企业乘客定位推送距离阈值,供用户端实时定位过滤使用 */ @ResponseBody @RequestMapping(value = "/passengerLocationDistance", method = RequestMethod.POST) public ResultUtil passengerLocationDistance(Integer companyId) { if (companyId == null || companyId == 0) { return ResultUtil.paranErr(); } TSysDistanceDisplay distanceDisplay = tSysDistanceDisplayService.selectOne( new EntityWrapper<TSysDistanceDisplay>().eq("companyId", companyId)); if (distanceDisplay == null || distanceDisplay.getPassengerLocationDistance() == null) { return ResultUtil.success(); } return ResultUtil.success(distanceDisplay.getPassengerLocationDistance()); } } MessagePushTravel/src/main/java/com/sinata/push/util/applets/NettyWebSocketController.java
@@ -2,6 +2,11 @@ import com.alibaba.fastjson.JSONObject; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.sinata.push.config.QYTConfig; import com.sinata.push.util.RedisUtil; import com.sinata.push.util.SinataUtil; import com.sinata.push.util.SpringUtil; @@ -13,8 +18,11 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import org.springframework.http.MediaType; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Timer; import java.util.TimerTask; @@ -23,6 +31,8 @@ public static Hashtable<String, Hashtable<ChannelHandlerContext, String>> map = new Hashtable<String, Hashtable<ChannelHandlerContext, String>>(); private RedisUtil redisUtil = SpringUtil.getObject(RedisUtil.class); private QYTConfig qytConfig = SpringUtil.getObject(QYTConfig.class); public static Hashtable<String, String> table; @@ -112,6 +122,10 @@ } // 乘客上传位置,转发到用户端服务做订单校验和司机推送 if (method.equals(Method.passengerLocation)) { forwardPassengerLocation(ctx, jsonCon); } } catch (Exception e) { if (isdebug) { NettyWebSocketController.sendMsgToClient(ctx, "__error__" + msg.toString()); @@ -121,6 +135,42 @@ } /** * 转发乘客实时定位到用户服务,由用户服务校验订单后推送给司机端 */ private void forwardPassengerLocation(ChannelHandlerContext ctx, JSONObject jsonCon) { Integer userId = jsonCon.getInteger("userId"); Integer orderId = jsonCon.getInteger("orderId"); Integer orderType = jsonCon.getInteger("orderType"); Double lon = jsonCon.getDouble("lon"); Double lat = jsonCon.getDouble("lat"); String timestamp = jsonCon.getString("timestamp"); // 基础参数不合法时直接返回错误,不再调用后端业务接口 if (userId == null || orderId == null || orderType == null || lon == null || lat == null || lon == 0 || lat == 0) { sendMsgToClient(ctx, "__error__" + jsonCon.toJSONString()); return; } Map<String, Object> params = new HashMap<>(); params.put("userId", String.valueOf(userId)); params.put("orderId", String.valueOf(orderId)); params.put("orderType", String.valueOf(orderType)); params.put("lon", String.valueOf(lon)); params.put("lat", String.valueOf(lat)); params.put("timestamp", timestamp); HttpRequest post = HttpUtil.createPost(qytConfig.getChuxingUrl() + "/user-server/base/passenger/location/push"); post.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE); post.form(params); HttpResponse execute = post.execute(); if (200 != execute.getStatus()) { System.err.println("调用user-server推送乘客定位出错"); return; } JSONObject jsonObject = JSON.parseObject(execute.body(), JSONObject.class); if (jsonObject.getIntValue("code") != 200) { System.err.println("调用user-server推送乘客定位出错:" + jsonObject.getString("msg")); } } /** * 向客户端发送消息 * * @param ctx MessagePushTravel/src/main/java/com/sinata/push/util/echo/Method.java
@@ -21,6 +21,9 @@ /** 司机上传位置 */ public static final String location = "LOCATION"; /** 乘客上传位置 */ public static final String passengerLocation = "PASSENGER_LOCATION"; } MessagePushTravel/src/main/java/com/sinata/push/util/echo/NettyServerController.java
@@ -171,6 +171,10 @@ sendMsgToClient(ctx, jsonMsg.toJSONString()); } } //乘客上传位置,转发到用户端服务做订单校验和司机推送 if(method.equals(Method.passengerLocation)){ forwardPassengerLocation(ctx, jsonCon); } //司机上传位置 if(method.equals(Method.location)){ Integer driverId = jsonCon.getInteger("driverId"); @@ -265,6 +269,42 @@ } /** * 转发乘客实时定位到用户服务,由用户服务校验订单后推送给司机端 */ private void forwardPassengerLocation(ChannelHandlerContext ctx, JSONObject jsonCon) { Integer userId = jsonCon.getInteger("userId"); Integer orderId = jsonCon.getInteger("orderId"); Integer orderType = jsonCon.getInteger("orderType"); Double lon = jsonCon.getDouble("lon"); Double lat = jsonCon.getDouble("lat"); String timestamp = jsonCon.getString("timestamp"); // 基础参数不合法时直接返回错误,不再调用后端业务接口 if (userId == null || orderId == null || orderType == null || lon == null || lat == null || lon == 0 || lat == 0) { this.sendMsgToClient(ctx, "__error__" + jsonCon.toJSONString()); return; } Map<String, Object> params = new HashMap<>(); params.put("userId", String.valueOf(userId)); params.put("orderId", String.valueOf(orderId)); params.put("orderType", String.valueOf(orderType)); params.put("lon", String.valueOf(lon)); params.put("lat", String.valueOf(lat)); params.put("timestamp", timestamp); HttpRequest post = HttpUtil.createPost(qytConfig.getChuxingUrl() + "/user-server/base/passenger/location/push"); post.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE); post.form(params); HttpResponse execute = post.execute(); if (200 != execute.getStatus()) { System.err.println("调用user-server推送乘客定位出错"); return; } JSONObject jsonObject = JSON.parseObject(execute.body(), JSONObject.class); if (jsonObject.getIntValue("code") != 200) { System.err.println("调用user-server推送乘客定位出错:" + jsonObject.getString("msg")); } } /** * 向客户端发送消息 * * @param ctx UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/api/PassengerLocationController.java
New file @@ -0,0 +1,221 @@ package com.stylefeng.guns.modular.api; import com.alibaba.fastjson.JSONObject; import com.stylefeng.guns.core.util.ToolUtil; import com.stylefeng.guns.modular.crossCity.model.OrderCrossCity; import com.stylefeng.guns.modular.crossCity.server.IOrderCrossCityService; import com.stylefeng.guns.modular.smallLogistics.model.OrderLogistics; import com.stylefeng.guns.modular.smallLogistics.server.IOrderLogisticsService; import com.stylefeng.guns.modular.specialTrain.model.OrderPrivateCar; import com.stylefeng.guns.modular.specialTrain.server.IOrderPrivateCarService; import com.stylefeng.guns.modular.system.util.GeodesyUtil; import com.stylefeng.guns.modular.system.util.PushUtil; import com.stylefeng.guns.modular.system.util.ResultUtil; import com.stylefeng.guns.modular.taxi.model.OrderTaxi; import com.stylefeng.guns.modular.taxi.service.IOrderTaxiService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.math.BigDecimal; import java.util.Map; import java.util.concurrent.TimeUnit; @RestController @RequestMapping("/base/passenger/location") public class PassengerLocationController { @Autowired private IOrderPrivateCarService orderPrivateCarService; @Autowired private IOrderTaxiService orderTaxiService; @Autowired private IOrderCrossCityService orderCrossCityService; @Autowired private IOrderLogisticsService orderLogisticsService; @Autowired private PushUtil pushUtil; @Autowired private GeodesyUtil geodesyUtil; @Autowired private RestTemplate restTemplate; @Value("${qyt.management_uri:http://127.0.0.1:8010}") private String managementUri; @Resource private RedisTemplate<String, Object> redisTemplate; @ResponseBody @PostMapping("/push") public ResultUtil push(Integer userId, Integer orderId, Integer orderType, String lon, String lat, String timestamp) { try { // 校验乘客定位上报的必填参数 if (ToolUtil.isEmpty(userId) || ToolUtil.isEmpty(orderId) || ToolUtil.isEmpty(orderType) || ToolUtil.isEmpty(lon) || ToolUtil.isEmpty(lat)) { return ResultUtil.paranErr(); } Double lonValue; Double latValue; try { lonValue = Double.valueOf(lon); latValue = Double.valueOf(lat); } catch (NumberFormatException e) { return ResultUtil.paranErr(); } if (lonValue == 0 || latValue == 0) { return ResultUtil.paranErr(); } if (ToolUtil.isEmpty(timestamp)) { timestamp = String.valueOf(System.currentTimeMillis()); } if (!isSupportedOrderType(orderType)) { return ResultUtil.error("订单类型暂不支持"); } // 根据订单类型查询对应订单,并校验乘客、司机和订单状态 OrderData orderData = queryOrder(orderId, orderType); if (orderData == null) { return ResultUtil.error("订单不存在"); } if (!userId.equals(orderData.userId)) { return ResultUtil.error("用户与订单不匹配"); } if (ToolUtil.isEmpty(orderData.driverId) || orderData.driverId == 0) { return ResultUtil.error("订单暂无司机"); } if (!Integer.valueOf(3).equals(orderData.state)) { return ResultUtil.error("订单状态不是待到达预约地点"); } if (!validLonLat(orderData.startLon, orderData.startLat)) { return ResultUtil.error("订单上车点经纬度异常"); } if (exceedPassengerLocationDistance(orderData, lonValue, latValue)) { return ResultUtil.success("乘客距离上车点超过配置范围,未推送司机", new JSONObject()); } // 只缓存乘客最新位置,不记录历史轨迹 JSONObject location = new JSONObject(); location.put("orderId", orderId); location.put("orderType", orderType); location.put("userId", userId); location.put("driverId", orderData.driverId); location.put("lon", lon); location.put("lat", lat); location.put("timestamp", timestamp); redisTemplate.opsForValue().set("dache:PASSENGER_LOCATION:" + orderType + ":" + orderId, location.toJSONString(), 30, TimeUnit.SECONDS); // 缓存成功后实时推送给订单司机 return pushUtil.pushPassengerLocationToDriver(orderData.driverId, userId, orderId, orderType, lon, lat, timestamp); } catch (Exception e) { e.printStackTrace(); return ResultUtil.runErr(); } } private boolean isSupportedOrderType(Integer orderType) { return orderType == 1 || orderType == 2 || orderType == 3 || orderType == 4 || orderType == 5; } /** * 判断乘客当前位置是否超过企业配置的上车点距离阈值 */ private boolean exceedPassengerLocationDistance(OrderData orderData, Double lon, Double lat) { BigDecimal passengerLocationDistance = queryPassengerLocationDistance(orderData.companyId); if (passengerLocationDistance == null) { return false; } Map<String, Double> distance = geodesyUtil.getDistance(lon + "," + lat, orderData.startLon + "," + orderData.startLat); Double distanceMeter = distance == null ? null : distance.get("WGS84"); if (distanceMeter == null) { return false; } BigDecimal distanceKm = BigDecimal.valueOf(distanceMeter).divide(BigDecimal.valueOf(1000)); return distanceKm.compareTo(passengerLocationDistance) > 0; } /** * 远程查询企业乘客定位推送距离阈值,查询失败时不影响主流程 */ private BigDecimal queryPassengerLocationDistance(Integer companyId) { if (ToolUtil.isEmpty(companyId) || companyId == 0) { return null; } try { String result = restTemplate.postForObject(managementUri + "/base/distanceDisplay/passengerLocationDistance?companyId=" + companyId, null, String.class); JSONObject jsonObject = JSONObject.parseObject(result); if (jsonObject == null || jsonObject.getIntValue("code") != 200 || jsonObject.get("data") == null) { return null; } return jsonObject.getBigDecimal("data"); } catch (Exception e) { System.err.println("查询乘客定位推送距离阈值失败:" + e.getMessage()); return null; } } /** * 校验经纬度是否有效 */ private boolean validLonLat(Double lon, Double lat) { return lon != null && lat != null && lon != 0 && lat != 0; } /** * 根据订单类型查询对应订单的乘客、司机和状态 */ private OrderData queryOrder(Integer orderId, Integer orderType) { switch (orderType) { case 1: OrderPrivateCar orderPrivateCar = orderPrivateCarService.selectById(orderId); return orderPrivateCar == null ? null : new OrderData(orderPrivateCar.getUserId(), orderPrivateCar.getDriverId(), orderPrivateCar.getState(), orderPrivateCar.getCompanyId(), orderPrivateCar.getStartLon(), orderPrivateCar.getStartLat()); case 2: OrderTaxi orderTaxi = orderTaxiService.selectById(orderId); return orderTaxi == null ? null : new OrderData(orderTaxi.getUserId(), orderTaxi.getDriverId(), orderTaxi.getState(), orderTaxi.getCompanyId(), orderTaxi.getStartLon(), orderTaxi.getStartLat()); case 3: OrderCrossCity orderCrossCity = orderCrossCityService.selectById(orderId); return orderCrossCity == null ? null : new OrderData(orderCrossCity.getUserId(), orderCrossCity.getDriverId(), orderCrossCity.getState(), orderCrossCity.getCompanyId(), orderCrossCity.getStartLon(), orderCrossCity.getStartLat()); case 4: case 5: OrderLogistics orderLogistics = orderLogisticsService.selectById(orderId); return orderLogistics == null ? null : new OrderData(orderLogistics.getUserId(), orderLogistics.getDriverId(), orderLogistics.getState(), orderLogistics.getCompanyId(), orderLogistics.getStartLon(), orderLogistics.getStartLat()); default: return null; } } private static class OrderData { private final Integer userId; private final Integer driverId; private final Integer state; private final Integer companyId; private final Double startLon; private final Double startLat; private OrderData(Integer userId, Integer driverId, Integer state, Integer companyId, Double startLon, Double startLat) { this.userId = userId; this.driverId = driverId; this.state = state; this.companyId = companyId; this.startLon = startLon; this.startLat = startLat; } } } UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/util/PushUtil.java
@@ -138,6 +138,43 @@ } /** * 推送乘客位置给司机端 */ public ResultUtil pushPassengerLocationToDriver(Integer driverId, Integer userId, Integer orderId, Integer orderType, String lon, String lat, String timestamp) { JSONObject jsonObject = new JSONObject(); jsonObject.put("code", 200); jsonObject.put("msg", "SUCCESS"); jsonObject.put("method", "PASSENGER_LOCATION"); Map<String, Object> map = new HashMap<>(); map.put("orderId", orderId); map.put("orderType", orderType); map.put("userId", userId); map.put("lon", lon); map.put("lat", lat); map.put("timestamp", timestamp); jsonObject.put("data", map); HttpRequest post = HttpUtil.createPost(socket_uri + "/netty/sendMsgToClient"); post.header("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE); Map<String, Object> params = new HashMap<>(); params.put("msg", jsonObject.toJSONString()); params.put("id", driverId); params.put("type", 2); params.put("bussinessType", "dache"); post.form(params); HttpResponse execute = post.execute(); if (200 != execute.getStatus()) { return ResultUtil.error("司机端定位推送失败"); } JSONObject jsonObject1 = JSON.parseObject(execute.body(), JSONObject.class); if (jsonObject1.getIntValue("code") != 200) { return ResultUtil.error(jsonObject1.getString("msg")); } return ResultUtil.success(); } /** * 推送司机位置给乘客端 */ public void pushDriverPosition(Integer orderId, Integer orderType) { UserYKDTravel/guns-admin/src/main/resources/application-dev.yml
@@ -99,6 +99,8 @@ qyt: socket_uri: http://127.0.0.1:6000 # 管理端地址,用于远程查询乘客定位推送距离阈值 management_uri: http://127.0.0.1:8010 --- @@ -129,4 +131,4 @@ # pdf生成位置 trip: sheet: filePath: E:\etraffic\server\qytPdf\ filePath: E:\etraffic\server\qytPdf\