Commit 3edd6bb939182cd08823b930d2374230dea8ce10
1 parent
1a1b19a7
feat(admin): 添加骑手看板数据查询功能
- 新增 AdminRiderDashboardVO 数据传输对象 - 实现 listDashboardByCity 方法查询商铺内骑手看板数据 - 添加骑手状态构建和负载率计算逻辑 - 更新控制器添加 /dashboard 接口 - 修正城市相关术语为商铺并更新相关注释 - 添加调试日志记录和业务验证逻辑
Showing
5 changed files
with
178 additions
and
6 deletions
src/main/java/com/diligrp/rider/controller/AdminLocationController.java
| @@ -3,9 +3,11 @@ package com.diligrp.rider.controller; | @@ -3,9 +3,11 @@ package com.diligrp.rider.controller; | ||
| 3 | import com.diligrp.rider.common.exception.BizException; | 3 | import com.diligrp.rider.common.exception.BizException; |
| 4 | import com.diligrp.rider.common.result.Result; | 4 | import com.diligrp.rider.common.result.Result; |
| 5 | import com.diligrp.rider.service.RiderLocationService; | 5 | import com.diligrp.rider.service.RiderLocationService; |
| 6 | +import com.diligrp.rider.vo.AdminRiderDashboardVO; | ||
| 6 | import com.diligrp.rider.vo.AdminRiderLocationVO; | 7 | import com.diligrp.rider.vo.AdminRiderLocationVO; |
| 7 | import jakarta.servlet.http.HttpServletRequest; | 8 | import jakarta.servlet.http.HttpServletRequest; |
| 8 | import lombok.RequiredArgsConstructor; | 9 | import lombok.RequiredArgsConstructor; |
| 10 | +import lombok.extern.slf4j.Slf4j; | ||
| 9 | import org.springframework.web.bind.annotation.GetMapping; | 11 | import org.springframework.web.bind.annotation.GetMapping; |
| 10 | import org.springframework.web.bind.annotation.RequestMapping; | 12 | import org.springframework.web.bind.annotation.RequestMapping; |
| 11 | import org.springframework.web.bind.annotation.RequestParam; | 13 | import org.springframework.web.bind.annotation.RequestParam; |
| @@ -16,10 +18,14 @@ import java.util.List; | @@ -16,10 +18,14 @@ import java.util.List; | ||
| 16 | @RestController | 18 | @RestController |
| 17 | @RequestMapping("/api/admin/location") | 19 | @RequestMapping("/api/admin/location") |
| 18 | @RequiredArgsConstructor | 20 | @RequiredArgsConstructor |
| 21 | +@Slf4j | ||
| 19 | public class AdminLocationController { | 22 | public class AdminLocationController { |
| 20 | 23 | ||
| 21 | private final RiderLocationService riderLocationService; | 24 | private final RiderLocationService riderLocationService; |
| 22 | 25 | ||
| 26 | + /** | ||
| 27 | + * 查询商铺内最近活跃的骑手位置列表,供后台地图初始化使用。 | ||
| 28 | + */ | ||
| 23 | @GetMapping("/active") | 29 | @GetMapping("/active") |
| 24 | public Result<List<AdminRiderLocationVO>> active(@RequestParam(required = false) Long cityId, | 30 | public Result<List<AdminRiderLocationVO>> active(@RequestParam(required = false) Long cityId, |
| 25 | HttpServletRequest request) { | 31 | HttpServletRequest request) { |
| @@ -27,9 +33,29 @@ public class AdminLocationController { | @@ -27,9 +33,29 @@ public class AdminLocationController { | ||
| 27 | Long resolvedCityId = "substation".equals(role) | 33 | Long resolvedCityId = "substation".equals(role) |
| 28 | ? (Long) request.getAttribute("cityId") | 34 | ? (Long) request.getAttribute("cityId") |
| 29 | : cityId; | 35 | : cityId; |
| 36 | + log.debug("查询商铺活跃骑手位置列表,请求参数 role={} cityId={} resolvedCityId={}", | ||
| 37 | + role, cityId, resolvedCityId); | ||
| 30 | if (resolvedCityId == null || resolvedCityId < 1) { | 38 | if (resolvedCityId == null || resolvedCityId < 1) { |
| 31 | - throw new BizException(400, "请选择城市"); | 39 | + throw new BizException(400, "请选择商铺"); |
| 32 | } | 40 | } |
| 33 | return Result.success(riderLocationService.listActiveByCity(resolvedCityId)); | 41 | return Result.success(riderLocationService.listActiveByCity(resolvedCityId)); |
| 34 | } | 42 | } |
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 查询商铺内骑手看板数据。 | ||
| 46 | + */ | ||
| 47 | + @GetMapping("/dashboard") | ||
| 48 | + public Result<List<AdminRiderDashboardVO>> dashboard(@RequestParam(required = false) Long cityId, | ||
| 49 | + HttpServletRequest request) { | ||
| 50 | + String role = (String) request.getAttribute("role"); | ||
| 51 | + Long resolvedCityId = "substation".equals(role) | ||
| 52 | + ? (Long) request.getAttribute("cityId") | ||
| 53 | + : cityId; | ||
| 54 | + log.debug("查询商铺骑手看板数据,请求参数 role={} cityId={} resolvedCityId={}", | ||
| 55 | + role, cityId, resolvedCityId); | ||
| 56 | + if (resolvedCityId == null || resolvedCityId < 1) { | ||
| 57 | + throw new BizException(400, "请选择商铺"); | ||
| 58 | + } | ||
| 59 | + return Result.success(riderLocationService.listDashboardByCity(resolvedCityId)); | ||
| 60 | + } | ||
| 35 | } | 61 | } |
src/main/java/com/diligrp/rider/controller/RiderLocationController.java
| @@ -53,7 +53,7 @@ public class RiderLocationController { | @@ -53,7 +53,7 @@ public class RiderLocationController { | ||
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | /** | 55 | /** |
| 56 | - * 查询当前登录骑手所属城市内的附近骑手。 | 56 | + * 查询当前登录骑手所属商铺内的附近骑手。 |
| 57 | */ | 57 | */ |
| 58 | @GetMapping("/nearby") | 58 | @GetMapping("/nearby") |
| 59 | public Result<List<NearbyRiderVO>> nearby( | 59 | public Result<List<NearbyRiderVO>> nearby( |
src/main/java/com/diligrp/rider/service/RiderLocationService.java
| 1 | package com.diligrp.rider.service; | 1 | package com.diligrp.rider.service; |
| 2 | 2 | ||
| 3 | import com.diligrp.rider.dto.LocationDTO; | 3 | import com.diligrp.rider.dto.LocationDTO; |
| 4 | +import com.diligrp.rider.vo.AdminRiderDashboardVO; | ||
| 4 | import com.diligrp.rider.vo.AdminRiderLocationVO; | 5 | import com.diligrp.rider.vo.AdminRiderLocationVO; |
| 5 | import com.diligrp.rider.vo.NearbyRiderVO; | 6 | import com.diligrp.rider.vo.NearbyRiderVO; |
| 6 | 7 | ||
| @@ -14,12 +15,16 @@ public interface RiderLocationService { | @@ -14,12 +15,16 @@ public interface RiderLocationService { | ||
| 14 | /** 获取骑手位置 */ | 15 | /** 获取骑手位置 */ |
| 15 | LocationDTO getLocation(Long riderId); | 16 | LocationDTO getLocation(Long riderId); |
| 16 | 17 | ||
| 18 | + /** 查询商铺内最近活跃的骑手位置列表 */ | ||
| 17 | List<AdminRiderLocationVO> listActiveByCity(Long cityId); | 19 | List<AdminRiderLocationVO> listActiveByCity(Long cityId); |
| 18 | 20 | ||
| 21 | + /** 查询商铺内骑手看板数据 */ | ||
| 22 | + List<AdminRiderDashboardVO> listDashboardByCity(Long cityId); | ||
| 23 | + | ||
| 19 | /** | 24 | /** |
| 20 | * 获取附近在线骑手列表 | 25 | * 获取附近在线骑手列表 |
| 21 | * | 26 | * |
| 22 | - * @param cityId 城市ID | 27 | + * @param cityId 商铺ID |
| 23 | * @param lng 查询点经度 | 28 | * @param lng 查询点经度 |
| 24 | * @param lat 查询点纬度 | 29 | * @param lat 查询点纬度 |
| 25 | */ | 30 | */ |
src/main/java/com/diligrp/rider/service/impl/RiderLocationServiceImpl.java
| @@ -3,13 +3,16 @@ package com.diligrp.rider.service.impl; | @@ -3,13 +3,16 @@ package com.diligrp.rider.service.impl; | ||
| 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 4 | import com.diligrp.rider.dto.DeliveryPricingConfigDTO; | 4 | import com.diligrp.rider.dto.DeliveryPricingConfigDTO; |
| 5 | import com.diligrp.rider.dto.LocationDTO; | 5 | import com.diligrp.rider.dto.LocationDTO; |
| 6 | +import com.diligrp.rider.entity.Orders; | ||
| 6 | import com.diligrp.rider.entity.Rider; | 7 | import com.diligrp.rider.entity.Rider; |
| 7 | import com.diligrp.rider.entity.RiderLocation; | 8 | import com.diligrp.rider.entity.RiderLocation; |
| 8 | import com.diligrp.rider.mapper.RiderLocationMapper; | 9 | import com.diligrp.rider.mapper.RiderLocationMapper; |
| 9 | import com.diligrp.rider.mapper.RiderMapper; | 10 | import com.diligrp.rider.mapper.RiderMapper; |
| 11 | +import com.diligrp.rider.mapper.OrdersMapper; | ||
| 10 | import com.diligrp.rider.service.CityService; | 12 | import com.diligrp.rider.service.CityService; |
| 11 | import com.diligrp.rider.service.RiderLocationService; | 13 | import com.diligrp.rider.service.RiderLocationService; |
| 12 | import com.diligrp.rider.util.GeoUtil; | 14 | import com.diligrp.rider.util.GeoUtil; |
| 15 | +import com.diligrp.rider.vo.AdminRiderDashboardVO; | ||
| 13 | import com.diligrp.rider.vo.AdminRiderLocationVO; | 16 | import com.diligrp.rider.vo.AdminRiderLocationVO; |
| 14 | import com.diligrp.rider.vo.NearbyRiderVO; | 17 | import com.diligrp.rider.vo.NearbyRiderVO; |
| 15 | import com.diligrp.rider.websocket.LocationPushService; | 18 | import com.diligrp.rider.websocket.LocationPushService; |
| @@ -30,6 +33,7 @@ public class RiderLocationServiceImpl implements RiderLocationService { | @@ -30,6 +33,7 @@ public class RiderLocationServiceImpl implements RiderLocationService { | ||
| 30 | 33 | ||
| 31 | private final RiderLocationMapper locationMapper; | 34 | private final RiderLocationMapper locationMapper; |
| 32 | private final RiderMapper riderMapper; | 35 | private final RiderMapper riderMapper; |
| 36 | + private final OrdersMapper ordersMapper; | ||
| 33 | private final CityService cityService; | 37 | private final CityService cityService; |
| 34 | private final LocationPushService locationPushService; | 38 | private final LocationPushService locationPushService; |
| 35 | 39 | ||
| @@ -77,6 +81,9 @@ public class RiderLocationServiceImpl implements RiderLocationService { | @@ -77,6 +81,9 @@ public class RiderLocationServiceImpl implements RiderLocationService { | ||
| 77 | return dto; | 81 | return dto; |
| 78 | } | 82 | } |
| 79 | 83 | ||
| 84 | + /** | ||
| 85 | + * 查询指定商铺最近活跃的骑手位置列表。 | ||
| 86 | + */ | ||
| 80 | @Override | 87 | @Override |
| 81 | public List<AdminRiderLocationVO> listActiveByCity(Long cityId) { | 88 | public List<AdminRiderLocationVO> listActiveByCity(Long cityId) { |
| 82 | List<AdminRiderLocationVO> result = new ArrayList<>(); | 89 | List<AdminRiderLocationVO> result = new ArrayList<>(); |
| @@ -121,12 +128,84 @@ public class RiderLocationServiceImpl implements RiderLocationService { | @@ -121,12 +128,84 @@ public class RiderLocationServiceImpl implements RiderLocationService { | ||
| 121 | } catch (Exception ignored) { | 128 | } catch (Exception ignored) { |
| 122 | } | 129 | } |
| 123 | } | 130 | } |
| 124 | - log.debug("查询城市活跃骑手位置完成,cityId={} count={}", cityId, result.size()); | 131 | + log.debug("查询商铺活跃骑手位置完成,cityId={} count={}", cityId, result.size()); |
| 125 | return result; | 132 | return result; |
| 126 | } | 133 | } |
| 127 | 134 | ||
| 128 | /** | 135 | /** |
| 129 | - * 查询城市范围内最近活跃的附近骑手。 | 136 | + * 查询指定商铺骑手看板数据。 |
| 137 | + */ | ||
| 138 | + @Override | ||
| 139 | + public List<AdminRiderDashboardVO> listDashboardByCity(Long cityId) { | ||
| 140 | + List<AdminRiderDashboardVO> result = new ArrayList<>(); | ||
| 141 | + log.debug("开始查询商铺骑手看板,参数 cityId={}", cityId); | ||
| 142 | + if (cityId == null || cityId < 1) { | ||
| 143 | + return result; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + List<Rider> riders = riderMapper.selectList(new LambdaQueryWrapper<Rider>() | ||
| 147 | + .eq(Rider::getCityId, cityId) | ||
| 148 | + .orderByDesc(Rider::getId)); | ||
| 149 | + if (riders.isEmpty()) { | ||
| 150 | + log.debug("商铺下没有骑手,cityId={}", cityId); | ||
| 151 | + return result; | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + List<Long> riderIds = riders.stream().map(Rider::getId).toList(); | ||
| 155 | + List<RiderLocation> locations = locationMapper.selectList( | ||
| 156 | + new LambdaQueryWrapper<RiderLocation>().in(RiderLocation::getUid, riderIds)); | ||
| 157 | + List<Orders> holdingOrders = ordersMapper.selectList(new LambdaQueryWrapper<Orders>() | ||
| 158 | + .eq(Orders::getCityId, cityId) | ||
| 159 | + .in(Orders::getStatus, List.of(3, 4)) | ||
| 160 | + .in(Orders::getRiderId, riderIds) | ||
| 161 | + .eq(Orders::getIsDel, 0)); | ||
| 162 | + log.debug("商铺骑手看板基础数据查询完成,cityId={} riderCount={} locationCount={} holdingOrderCount={}", | ||
| 163 | + cityId, riders.size(), locations.size(), holdingOrders.size()); | ||
| 164 | + | ||
| 165 | + java.util.Map<Long, RiderLocation> locationMap = new java.util.HashMap<>(); | ||
| 166 | + for (RiderLocation location : locations) { | ||
| 167 | + RiderLocation current = locationMap.get(location.getUid()); | ||
| 168 | + if (current == null || (current.getUpdateTime() != null | ||
| 169 | + && location.getUpdateTime() != null | ||
| 170 | + && location.getUpdateTime() > current.getUpdateTime())) { | ||
| 171 | + locationMap.put(location.getUid(), location); | ||
| 172 | + } | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + java.util.Map<Long, Integer> holdCountMap = new java.util.HashMap<>(); | ||
| 176 | + for (Orders order : holdingOrders) { | ||
| 177 | + holdCountMap.merge(order.getRiderId(), 1, Integer::sum); | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + for (Rider rider : riders) { | ||
| 181 | + AdminRiderDashboardVO vo = new AdminRiderDashboardVO(); | ||
| 182 | + vo.setRiderId(rider.getId()); | ||
| 183 | + vo.setRiderName(rider.getUserNickname()); | ||
| 184 | + vo.setMobile(rider.getMobile()); | ||
| 185 | + vo.setStatus(buildRiderStatus(rider, locationMap.get(rider.getId()))); | ||
| 186 | + | ||
| 187 | + int holdOrderCount = holdCountMap.getOrDefault(rider.getId(), 0); | ||
| 188 | + vo.setHoldOrderCount(holdOrderCount); | ||
| 189 | + vo.setLoadRate(calcLoadRate(rider, holdOrderCount)); | ||
| 190 | + | ||
| 191 | + RiderLocation location = locationMap.get(rider.getId()); | ||
| 192 | + if (location != null) { | ||
| 193 | + vo.setLastLocationTime(location.getUpdateTime()); | ||
| 194 | + try { | ||
| 195 | + vo.setLng(new BigDecimal(location.getLng())); | ||
| 196 | + vo.setLat(new BigDecimal(location.getLat())); | ||
| 197 | + } catch (Exception ignored) { | ||
| 198 | + } | ||
| 199 | + } | ||
| 200 | + result.add(vo); | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + log.debug("查询骑手看板完成,cityId={} count={}", cityId, result.size()); | ||
| 204 | + return result; | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + /** | ||
| 208 | + * 查询商铺范围内最近活跃的附近骑手。 | ||
| 130 | */ | 209 | */ |
| 131 | @Override | 210 | @Override |
| 132 | public List<NearbyRiderVO> getNearby(Long cityId, BigDecimal lng, BigDecimal lat) { | 211 | public List<NearbyRiderVO> getNearby(Long cityId, BigDecimal lng, BigDecimal lat) { |
| @@ -150,7 +229,7 @@ public class RiderLocationServiceImpl implements RiderLocationService { | @@ -150,7 +229,7 @@ public class RiderLocationServiceImpl implements RiderLocationService { | ||
| 150 | .eq(Rider::getIsRest, 0) | 229 | .eq(Rider::getIsRest, 0) |
| 151 | .eq(Rider::getUserStatus, 1)); | 230 | .eq(Rider::getUserStatus, 1)); |
| 152 | if (riders.isEmpty()) { | 231 | if (riders.isEmpty()) { |
| 153 | - log.debug("当前城市没有在线骑手,cityId={}", cityId); | 232 | + log.debug("当前商铺没有在线骑手,cityId={}", cityId); |
| 154 | return result; | 233 | return result; |
| 155 | } | 234 | } |
| 156 | 235 | ||
| @@ -185,4 +264,31 @@ public class RiderLocationServiceImpl implements RiderLocationService { | @@ -185,4 +264,31 @@ public class RiderLocationServiceImpl implements RiderLocationService { | ||
| 185 | cityId, riders.size(), locations.size(), result.size()); | 264 | cityId, riders.size(), locations.size(), result.size()); |
| 186 | return result; | 265 | return result; |
| 187 | } | 266 | } |
| 267 | + | ||
| 268 | + private String buildRiderStatus(Rider rider, RiderLocation location) { | ||
| 269 | + if (rider.getUserStatus() == null || rider.getUserStatus() != 1) { | ||
| 270 | + return "审核中"; | ||
| 271 | + } | ||
| 272 | + if (rider.getStatus() == null || rider.getStatus() != 1) { | ||
| 273 | + return "已禁用"; | ||
| 274 | + } | ||
| 275 | + if (rider.getIsRest() != null && rider.getIsRest() == 1) { | ||
| 276 | + return "休息中"; | ||
| 277 | + } | ||
| 278 | + long now = System.currentTimeMillis() / 1000; | ||
| 279 | + if (location == null || location.getUpdateTime() == null | ||
| 280 | + || now - location.getUpdateTime() > ACTIVE_WINDOW_SECONDS) { | ||
| 281 | + return "离线"; | ||
| 282 | + } | ||
| 283 | + return "在线"; | ||
| 284 | + } | ||
| 285 | + | ||
| 286 | + private int calcLoadRate(Rider rider, int holdOrderCount) { | ||
| 287 | + int maxHoldCount = rider.getType() != null && rider.getType() == 2 ? 4 : 2; | ||
| 288 | + if (maxHoldCount <= 0) { | ||
| 289 | + return 0; | ||
| 290 | + } | ||
| 291 | + int rate = holdOrderCount * 100 / maxHoldCount; | ||
| 292 | + return Math.min(rate, 100); | ||
| 293 | + } | ||
| 188 | } | 294 | } |
src/main/java/com/diligrp/rider/vo/AdminRiderDashboardVO.java
0 → 100644
| 1 | +package com.diligrp.rider.vo; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import java.math.BigDecimal; | ||
| 6 | + | ||
| 7 | +@Data | ||
| 8 | +public class AdminRiderDashboardVO { | ||
| 9 | + /** 骑手ID */ | ||
| 10 | + private Long riderId; | ||
| 11 | + | ||
| 12 | + /** 骑手名称 */ | ||
| 13 | + private String riderName; | ||
| 14 | + | ||
| 15 | + /** 手机号 */ | ||
| 16 | + private String mobile; | ||
| 17 | + | ||
| 18 | + /** 当前状态 */ | ||
| 19 | + private String status; | ||
| 20 | + | ||
| 21 | + /** 当前持单量 */ | ||
| 22 | + private Integer holdOrderCount; | ||
| 23 | + | ||
| 24 | + /** 当前负载率,单位百分比 */ | ||
| 25 | + private Integer loadRate; | ||
| 26 | + | ||
| 27 | + /** 最后定位时间,时间戳秒 */ | ||
| 28 | + private Long lastLocationTime; | ||
| 29 | + | ||
| 30 | + /** 最后定位经度 */ | ||
| 31 | + private BigDecimal lng; | ||
| 32 | + | ||
| 33 | + /** 最后定位纬度 */ | ||
| 34 | + private BigDecimal lat; | ||
| 35 | +} |