| | |
| | | import com.stylefeng.guns.modular.system.model.*; |
| | | import com.stylefeng.guns.modular.system.model.vo.*; |
| | | import com.stylefeng.guns.modular.system.service.*; |
| | | import com.stylefeng.guns.modular.system.util.GeodesyUtil; |
| | | import com.stylefeng.guns.modular.system.util.ResultUtil; |
| | | import com.stylefeng.guns.modular.system.util.WeChatUtil; |
| | | import com.stylefeng.guns.modular.system.warpper.*; |
| | |
| | | |
| | | @Autowired |
| | | private WeChatUtil weChatUtil; |
| | | |
| | | @Autowired |
| | | private GeodesyUtil geodesyUtil; |
| | | |
| | | /** |
| | | * 获取短信验证码 |
| | |
| | | return null; |
| | | }).filter(Objects::nonNull).collect(Collectors.toList()); |
| | | } |
| | | // addMockNearbyDrivers(collect); |
| | | return ResultUtil.success(collect); |
| | | } |
| | | |
| | | /** |
| | | * 司机端首页订单热力图。 |
| | | * 热力图统计的是订单起点位置,不统计司机位置;订单坐标统一取 startLon/startLat。 |
| | | * radius 为前端传入的查询半径,单位公里;不传时默认查询 5 公里范围内的可接订单。 |
| | | * 返回结果只包含热力图必要字段,不返回订单ID、乘客信息、地址等敏感数据。 |
| | | */ |
| | | @ResponseBody |
| | | @PostMapping("/base/driver/queryNearbyOrderHeatMap") |
| | | @ApiOperation(value = "司机端首页订单热力图", tags = {"司机端-首页"}, notes = "热力图统计订单位置,不统计司机位置") |
| | | @ApiImplicitParams({ |
| | | @ApiImplicitParam(value = "司机当前位置经度", name = "lon", required = true, dataType = "String"), |
| | | @ApiImplicitParam(value = "司机当前位置纬度", name = "lat", required = true, dataType = "String"), |
| | | @ApiImplicitParam(value = "查询半径,单位公里,不传默认5公里", name = "radius", required = false, dataType = "String"), |
| | | @ApiImplicitParam(value = "订单业务类型:1=专车,2=出租车,3=跨城,4/5=小件物流,不传查询全部", name = "type", required = false, dataType = "int") |
| | | }) |
| | | public ResultUtil<Map<String, Object>> queryNearbyOrderHeatMap(String lon, String lat, String radius, Integer type) { |
| | | try { |
| | | if (ToolUtil.isEmpty(lon) || ToolUtil.isEmpty(lat)) { |
| | | return ResultUtil.paranErr(); |
| | | } |
| | | Double centerLon = Double.valueOf(lon); |
| | | Double centerLat = Double.valueOf(lat); |
| | | Double radiusKm = ToolUtil.isEmpty(radius) ? 5D : Double.valueOf(radius); |
| | | if (radiusKm <= 0) { |
| | | return ResultUtil.paranErr(); |
| | | } |
| | | |
| | | List<Map<String, Object>> points = new ArrayList<>(); |
| | | String centerLonLat = centerLon + "," + centerLat; |
| | | |
| | | if (type == null || type == 1) { |
| | | List<OrderPrivateCar> privateCars = orderPrivateCarService.selectList(new EntityWrapper<OrderPrivateCar>() |
| | | .eq("state", 1).isNotNull("startLon").isNotNull("startLat")); |
| | | addOrderHeatMapPoints(points, privateCars, centerLonLat, radiusKm, 1); |
| | | } |
| | | if (type == null || type == 2) { |
| | | List<OrderTaxi> taxis = orderTaxiService.selectList(new EntityWrapper<OrderTaxi>() |
| | | .eq("state", 1).isNotNull("startLon").isNotNull("startLat")); |
| | | addOrderHeatMapPoints(points, taxis, centerLonLat, radiusKm, 2); |
| | | } |
| | | if (type == null || type == 3) { |
| | | List<OrderCrossCity> crossCities = orderCrossCityService.selectList(new EntityWrapper<OrderCrossCity>() |
| | | .eq("state", 1).isNotNull("startLon").isNotNull("startLat")); |
| | | addOrderHeatMapPoints(points, crossCities, centerLonLat, radiusKm, 3); |
| | | } |
| | | if (type == null || type == 4 || type == 5) { |
| | | Wrapper<OrderLogistics> wrapper = new EntityWrapper<OrderLogistics>() |
| | | .eq("state", 1).isNotNull("startLon").isNotNull("startLat"); |
| | | if (type != null) { |
| | | wrapper.eq("type", type); |
| | | } |
| | | List<OrderLogistics> logistics = orderLogisticsService.selectList(wrapper); |
| | | addOrderHeatMapPoints(points, logistics, centerLonLat, radiusKm, null); |
| | | } |
| | | |
| | | Map<String, Object> data = new HashMap<>(); |
| | | // addMockOrderHeatMapPoints(points); |
| | | data.put("centerLon", centerLon); |
| | | data.put("centerLat", centerLat); |
| | | data.put("radius", radiusKm); |
| | | data.put("total", points.size()); |
| | | data.put("points", points); |
| | | return ResultUtil.success(data); |
| | | } catch (NumberFormatException e) { |
| | | return ResultUtil.paranErr(); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return ResultUtil.runErr(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将指定业务的订单转换为热力点,并按半径过滤。 |
| | | * 每个订单默认生成一个热力点,count=1;小件物流使用订单自身 type 区分 4/5。 |
| | | */ |
| | | private void addOrderHeatMapPoints(List<Map<String, Object>> points, List<?> orders, String centerLonLat, Double radiusKm, Integer fixedType) { |
| | | if (ToolUtil.isEmpty(orders)) { |
| | | return; |
| | | } |
| | | for (Object order : orders) { |
| | | Double startLon = null; |
| | | Double startLat = null; |
| | | Integer orderType = fixedType; |
| | | if (order instanceof OrderPrivateCar) { |
| | | OrderPrivateCar orderPrivateCar = (OrderPrivateCar) order; |
| | | startLon = orderPrivateCar.getStartLon(); |
| | | startLat = orderPrivateCar.getStartLat(); |
| | | } else if (order instanceof OrderTaxi) { |
| | | OrderTaxi orderTaxi = (OrderTaxi) order; |
| | | startLon = orderTaxi.getStartLon(); |
| | | startLat = orderTaxi.getStartLat(); |
| | | } else if (order instanceof OrderCrossCity) { |
| | | OrderCrossCity orderCrossCity = (OrderCrossCity) order; |
| | | startLon = orderCrossCity.getStartLon(); |
| | | startLat = orderCrossCity.getStartLat(); |
| | | } else if (order instanceof OrderLogistics) { |
| | | OrderLogistics orderLogistics = (OrderLogistics) order; |
| | | startLon = orderLogistics.getStartLon(); |
| | | startLat = orderLogistics.getStartLat(); |
| | | orderType = orderLogistics.getType(); |
| | | } |
| | | if (startLon == null || startLat == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<String, Double> distance = geodesyUtil.getDistance(centerLonLat, startLon + "," + startLat); |
| | | if (distance == null || distance.get("WGS84") == null || distance.get("WGS84") > radiusKm * 1000) { |
| | | continue; |
| | | } |
| | | |
| | | Map<String, Object> point = new HashMap<>(); |
| | | point.put("lon", startLon); |
| | | point.put("lat", startLat); |
| | | point.put("count", 1); |
| | | point.put("type", orderType); |
| | | points.add(point); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 测试用司机假数据:在 104.05,30.59 附近补充 5 个司机位置,便于前端联调地图展示。 |
| | | */ |
| | | private void addMockNearbyDrivers(List<Map<String, Object>> collect) { |
| | | double[][] mockPositions = { |
| | | {104.0500, 30.5900}, |
| | | {104.0512, 30.5908}, |
| | | {104.0489, 30.5894}, |
| | | {104.0520, 30.5886}, |
| | | {104.0495, 30.5915} |
| | | }; |
| | | for (int i = 0; i < mockPositions.length; i++) { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("driverId", -1001 - i); |
| | | map.put("name", "测试司机" + (i + 1)); |
| | | map.put("lot", mockPositions[i][0]); |
| | | map.put("lat", mockPositions[i][1]); |
| | | collect.add(map); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 测试用订单热力图假数据:在 104.05,30.59 附近补充 5 个订单热力点,便于前端联调热力图展示。 |
| | | */ |
| | | private void addMockOrderHeatMapPoints(List<Map<String, Object>> points) { |
| | | double[][] mockPositions = { |
| | | {104.0500, 30.5900}, |
| | | {104.0510, 30.5906}, |
| | | {104.0490, 30.5892}, |
| | | {104.0522, 30.5888}, |
| | | {104.0487, 30.5913} |
| | | }; |
| | | for (int i = 0; i < mockPositions.length; i++) { |
| | | Map<String, Object> point = new HashMap<>(); |
| | | point.put("lon", mockPositions[i][0]); |
| | | point.put("lat", mockPositions[i][1]); |
| | | point.put("count", 1); |
| | | point.put("type", i + 1); |
| | | points.add(point); |
| | | } |
| | | } |
| | | |
| | | |
| | | @ResponseBody |
| | | @PostMapping("/api/driver/queryOrderDataFromDriver") |