Commit 3edd6bb939182cd08823b930d2374230dea8ce10

Authored by 蒋勇
1 parent 1a1b19a7

feat(admin): 添加骑手看板数据查询功能

- 新增 AdminRiderDashboardVO 数据传输对象
- 实现 listDashboardByCity 方法查询商铺内骑手看板数据
- 添加骑手状态构建和负载率计算逻辑
- 更新控制器添加 /dashboard 接口
- 修正城市相关术语为商铺并更新相关注释
- 添加调试日志记录和业务验证逻辑
src/main/java/com/diligrp/rider/controller/AdminLocationController.java
... ... @@ -3,9 +3,11 @@ package com.diligrp.rider.controller;
3 3 import com.diligrp.rider.common.exception.BizException;
4 4 import com.diligrp.rider.common.result.Result;
5 5 import com.diligrp.rider.service.RiderLocationService;
  6 +import com.diligrp.rider.vo.AdminRiderDashboardVO;
6 7 import com.diligrp.rider.vo.AdminRiderLocationVO;
7 8 import jakarta.servlet.http.HttpServletRequest;
8 9 import lombok.RequiredArgsConstructor;
  10 +import lombok.extern.slf4j.Slf4j;
9 11 import org.springframework.web.bind.annotation.GetMapping;
10 12 import org.springframework.web.bind.annotation.RequestMapping;
11 13 import org.springframework.web.bind.annotation.RequestParam;
... ... @@ -16,10 +18,14 @@ import java.util.List;
16 18 @RestController
17 19 @RequestMapping("/api/admin/location")
18 20 @RequiredArgsConstructor
  21 +@Slf4j
19 22 public class AdminLocationController {
20 23  
21 24 private final RiderLocationService riderLocationService;
22 25  
  26 + /**
  27 + * 查询商铺内最近活跃的骑手位置列表,供后台地图初始化使用。
  28 + */
23 29 @GetMapping("/active")
24 30 public Result<List<AdminRiderLocationVO>> active(@RequestParam(required = false) Long cityId,
25 31 HttpServletRequest request) {
... ... @@ -27,9 +33,29 @@ public class AdminLocationController {
27 33 Long resolvedCityId = "substation".equals(role)
28 34 ? (Long) request.getAttribute("cityId")
29 35 : cityId;
  36 + log.debug("查询商铺活跃骑手位置列表,请求参数 role={} cityId={} resolvedCityId={}",
  37 + role, cityId, resolvedCityId);
30 38 if (resolvedCityId == null || resolvedCityId < 1) {
31   - throw new BizException(400, "请选择城市");
  39 + throw new BizException(400, "请选择商铺");
32 40 }
33 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 53 }
54 54  
55 55 /**
56   - * 查询当前登录骑手所属城市内的附近骑手。
  56 + * 查询当前登录骑手所属商铺内的附近骑手。
57 57 */
58 58 @GetMapping("/nearby")
59 59 public Result<List<NearbyRiderVO>> nearby(
... ...
src/main/java/com/diligrp/rider/service/RiderLocationService.java
1 1 package com.diligrp.rider.service;
2 2  
3 3 import com.diligrp.rider.dto.LocationDTO;
  4 +import com.diligrp.rider.vo.AdminRiderDashboardVO;
4 5 import com.diligrp.rider.vo.AdminRiderLocationVO;
5 6 import com.diligrp.rider.vo.NearbyRiderVO;
6 7  
... ... @@ -14,12 +15,16 @@ public interface RiderLocationService {
14 15 /** 获取骑手位置 */
15 16 LocationDTO getLocation(Long riderId);
16 17  
  18 + /** 查询商铺内最近活跃的骑手位置列表 */
17 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 28 * @param lng 查询点经度
24 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 3 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4 4 import com.diligrp.rider.dto.DeliveryPricingConfigDTO;
5 5 import com.diligrp.rider.dto.LocationDTO;
  6 +import com.diligrp.rider.entity.Orders;
6 7 import com.diligrp.rider.entity.Rider;
7 8 import com.diligrp.rider.entity.RiderLocation;
8 9 import com.diligrp.rider.mapper.RiderLocationMapper;
9 10 import com.diligrp.rider.mapper.RiderMapper;
  11 +import com.diligrp.rider.mapper.OrdersMapper;
10 12 import com.diligrp.rider.service.CityService;
11 13 import com.diligrp.rider.service.RiderLocationService;
12 14 import com.diligrp.rider.util.GeoUtil;
  15 +import com.diligrp.rider.vo.AdminRiderDashboardVO;
13 16 import com.diligrp.rider.vo.AdminRiderLocationVO;
14 17 import com.diligrp.rider.vo.NearbyRiderVO;
15 18 import com.diligrp.rider.websocket.LocationPushService;
... ... @@ -30,6 +33,7 @@ public class RiderLocationServiceImpl implements RiderLocationService {
30 33  
31 34 private final RiderLocationMapper locationMapper;
32 35 private final RiderMapper riderMapper;
  36 + private final OrdersMapper ordersMapper;
33 37 private final CityService cityService;
34 38 private final LocationPushService locationPushService;
35 39  
... ... @@ -77,6 +81,9 @@ public class RiderLocationServiceImpl implements RiderLocationService {
77 81 return dto;
78 82 }
79 83  
  84 + /**
  85 + * 查询指定商铺最近活跃的骑手位置列表。
  86 + */
80 87 @Override
81 88 public List<AdminRiderLocationVO> listActiveByCity(Long cityId) {
82 89 List<AdminRiderLocationVO> result = new ArrayList<>();
... ... @@ -121,12 +128,84 @@ public class RiderLocationServiceImpl implements RiderLocationService {
121 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 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 210 @Override
132 211 public List<NearbyRiderVO> getNearby(Long cityId, BigDecimal lng, BigDecimal lat) {
... ... @@ -150,7 +229,7 @@ public class RiderLocationServiceImpl implements RiderLocationService {
150 229 .eq(Rider::getIsRest, 0)
151 230 .eq(Rider::getUserStatus, 1));
152 231 if (riders.isEmpty()) {
153   - log.debug("当前城市没有在线骑手,cityId={}", cityId);
  232 + log.debug("当前商铺没有在线骑手,cityId={}", cityId);
154 233 return result;
155 234 }
156 235  
... ... @@ -185,4 +264,31 @@ public class RiderLocationServiceImpl implements RiderLocationService {
185 264 cityId, riders.size(), locations.size(), result.size());
186 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 +}
... ...