Commit dff9b9d09e45e0e92247f1218bd881641e3bdcf2

Authored by 蒋勇
1 parent a5543e29

feat(rider): 骑手位置管理功能优化

- 将经纬度数据类型从String改为BigDecimal,并添加数值范围验证
- 实现骑手位置上报的upsert操作,避免重复插入
- 添加详细的日志记录用于调试和监控
- 优化附近骑手查询逻辑,增加活跃时间窗口限制
- 统一坐标系精度处理,提升定位准确性
src/main/java/com/diligrp/rider/controller/RiderLocationController.java
... ... @@ -6,43 +6,68 @@ import com.diligrp.rider.service.RiderLocationService;
6 6 import com.diligrp.rider.vo.NearbyRiderVO;
7 7 import jakarta.servlet.http.HttpServletRequest;
8 8 import jakarta.validation.Valid;
  9 +import jakarta.validation.constraints.DecimalMax;
  10 +import jakarta.validation.constraints.DecimalMin;
9 11 import lombok.RequiredArgsConstructor;
10   -import org.springframework.web.bind.annotation.*;
  12 +import lombok.extern.slf4j.Slf4j;
  13 +import org.springframework.validation.annotation.Validated;
  14 +import org.springframework.web.bind.annotation.GetMapping;
  15 +import org.springframework.web.bind.annotation.PostMapping;
  16 +import org.springframework.web.bind.annotation.RequestBody;
  17 +import org.springframework.web.bind.annotation.RequestMapping;
  18 +import org.springframework.web.bind.annotation.RequestParam;
  19 +import org.springframework.web.bind.annotation.RestController;
11 20  
  21 +import java.math.BigDecimal;
12 22 import java.util.List;
13 23  
14 24 @RestController
15 25 @RequestMapping("/api/rider/location")
16 26 @RequiredArgsConstructor
  27 +@Validated
  28 +@Slf4j
17 29 public class RiderLocationController {
18 30  
19 31 private final RiderLocationService locationService;
20 32  
21   - /** 上报位置 */
  33 + /**
  34 + * 上报骑手当前位置。
  35 + */
22 36 @PostMapping("/update")
23 37 public Result<Void> update(@Valid @RequestBody LocationDTO dto, HttpServletRequest request) {
24 38 Long riderId = (Long) request.getAttribute("riderId");
  39 + log.debug("骑手位置上报,请求参数 riderId={} lng={} lat={}",
  40 + riderId, dto.getLng(), dto.getLat());
25 41 locationService.updateLocation(riderId, dto);
26 42 return Result.success();
27 43 }
28 44  
29   - /** 查看自己位置 */
  45 + /**
  46 + * 回显当前登录骑手的位置。
  47 + */
30 48 @GetMapping
31 49 public Result<LocationDTO> get(HttpServletRequest request) {
32 50 Long riderId = (Long) request.getAttribute("riderId");
  51 + log.debug("查询骑手位置,请求参数 riderId={}", riderId);
33 52 return Result.success(locationService.getLocation(riderId));
34 53 }
35 54  
36 55 /**
37   - * 附近在线骑手列表(分站地图查看用)
38   - * Location.getNearby()
  56 + * 查询当前登录骑手所属城市内的附近骑手。
39 57 */
40 58 @GetMapping("/nearby")
41 59 public Result<List<NearbyRiderVO>> nearby(
42   - @RequestParam Long cityId,
43   - @RequestParam String lng,
44   - @RequestParam String lat,
  60 + @RequestParam
  61 + @DecimalMin(value = "-180.0", message = "经度超出范围")
  62 + @DecimalMax(value = "180.0", message = "经度超出范围")
  63 + BigDecimal lng,
  64 + @RequestParam
  65 + @DecimalMin(value = "-90.0", message = "纬度超出范围")
  66 + @DecimalMax(value = "90.0", message = "纬度超出范围")
  67 + BigDecimal lat,
45 68 HttpServletRequest request) {
  69 + Long cityId = (Long) request.getAttribute("cityId");
  70 + log.debug("查询附近骑手,请求参数 cityId={} lng={} lat={}", cityId, lng, lat);
46 71 return Result.success(locationService.getNearby(cityId, lng, lat));
47 72 }
48 73 }
... ...
src/main/java/com/diligrp/rider/dto/LocationDTO.java
1 1 package com.diligrp.rider.dto;
2 2  
  3 +import jakarta.validation.constraints.DecimalMax;
  4 +import jakarta.validation.constraints.DecimalMin;
3 5 import jakarta.validation.constraints.NotNull;
4 6 import lombok.Data;
5 7  
  8 +import java.math.BigDecimal;
  9 +
6 10 @Data
7 11 public class LocationDTO {
8 12 @NotNull(message = "经度不能为空")
9   - private String lng;
  13 + @DecimalMin(value = "-180.0", message = "经度超出范围")
  14 + @DecimalMax(value = "180.0", message = "经度超出范围")
  15 + private BigDecimal lng;
  16 +
10 17 @NotNull(message = "纬度不能为空")
11   - private String lat;
  18 + @DecimalMin(value = "-90.0", message = "纬度超出范围")
  19 + @DecimalMax(value = "90.0", message = "纬度超出范围")
  20 + private BigDecimal lat;
12 21 }
... ...
src/main/java/com/diligrp/rider/mapper/RiderLocationMapper.java
... ... @@ -3,7 +3,15 @@ package com.diligrp.rider.mapper;
3 3 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 4 import com.diligrp.rider.entity.RiderLocation;
5 5 import org.apache.ibatis.annotations.Mapper;
  6 +import org.apache.ibatis.annotations.Param;
6 7  
7 8 @Mapper
8 9 public interface RiderLocationMapper extends BaseMapper<RiderLocation> {
  10 + /**
  11 + * 按骑手ID写入位置,若记录已存在则更新经纬度和更新时间。
  12 + */
  13 + int upsertLocation(@Param("uid") Long uid,
  14 + @Param("lng") String lng,
  15 + @Param("lat") String lat,
  16 + @Param("updateTime") Long updateTime);
9 17 }
... ...
src/main/java/com/diligrp/rider/service/RiderLocationService.java
... ... @@ -3,19 +3,22 @@ package com.diligrp.rider.service;
3 3 import com.diligrp.rider.dto.LocationDTO;
4 4 import com.diligrp.rider.vo.NearbyRiderVO;
5 5  
  6 +import java.math.BigDecimal;
6 7 import java.util.List;
7 8  
8 9 public interface RiderLocationService {
9 10 /** 上报骑手位置 */
10 11 void updateLocation(Long riderId, LocationDTO dto);
  12 +
11 13 /** 获取骑手位置 */
12 14 LocationDTO getLocation(Long riderId);
  15 +
13 16 /**
14 17 * 获取附近在线骑手列表
15   - * Location.getNearby()
  18 + *
16 19 * @param cityId 城市ID
17 20 * @param lng 查询点经度
18 21 * @param lat 查询点纬度
19 22 */
20   - List<NearbyRiderVO> getNearby(Long cityId, String lng, String lat);
  23 + List<NearbyRiderVO> getNearby(Long cityId, BigDecimal lng, BigDecimal lat);
21 24 }
... ...
src/main/java/com/diligrp/rider/service/impl/RiderLocationServiceImpl.java
1 1 package com.diligrp.rider.service.impl;
2 2  
3 3 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4   -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
5 4 import com.diligrp.rider.dto.DeliveryPricingConfigDTO;
6 5 import com.diligrp.rider.dto.LocationDTO;
7 6 import com.diligrp.rider.entity.Rider;
... ... @@ -13,6 +12,7 @@ import com.diligrp.rider.service.RiderLocationService;
13 12 import com.diligrp.rider.util.GeoUtil;
14 13 import com.diligrp.rider.vo.NearbyRiderVO;
15 14 import lombok.RequiredArgsConstructor;
  15 +import lombok.extern.slf4j.Slf4j;
16 16 import org.springframework.stereotype.Service;
17 17  
18 18 import java.math.BigDecimal;
... ... @@ -21,72 +21,86 @@ import java.util.List;
21 21  
22 22 @Service
23 23 @RequiredArgsConstructor
  24 +@Slf4j
24 25 public class RiderLocationServiceImpl implements RiderLocationService {
25 26  
  27 + private static final long ACTIVE_WINDOW_SECONDS = 300;
  28 +
26 29 private final RiderLocationMapper locationMapper;
27 30 private final RiderMapper riderMapper;
28 31 private final CityService cityService;
29 32  
  33 + /**
  34 + * 按骑手维度更新最新位置。
  35 + */
30 36 @Override
31 37 public void updateLocation(Long riderId, LocationDTO dto) {
32   - RiderLocation existing = locationMapper.selectOne(new LambdaQueryWrapper<RiderLocation>()
33   - .eq(RiderLocation::getUid, riderId));
34 38 long now = System.currentTimeMillis() / 1000;
35   - if (existing == null) {
36   - RiderLocation loc = new RiderLocation();
37   - loc.setUid(riderId);
38   - loc.setLng(dto.getLng());
39   - loc.setLat(dto.getLat());
40   - loc.setUpdateTime(now);
41   - locationMapper.insert(loc);
42   - } else {
43   - locationMapper.update(null, new LambdaUpdateWrapper<RiderLocation>()
44   - .eq(RiderLocation::getUid, riderId)
45   - .set(RiderLocation::getLng, dto.getLng())
46   - .set(RiderLocation::getLat, dto.getLat())
47   - .set(RiderLocation::getUpdateTime, now));
48   - }
  39 + log.debug("更新骑手位置,参数 riderId={} lng={} lat={} updateTime={}",
  40 + riderId, dto.getLng(), dto.getLat(), now);
  41 + locationMapper.upsertLocation(
  42 + riderId,
  43 + dto.getLng().stripTrailingZeros().toPlainString(),
  44 + dto.getLat().stripTrailingZeros().toPlainString(),
  45 + now);
49 46 }
50 47  
  48 + /**
  49 + * 查询当前骑手最近一次上报的位置。
  50 + */
51 51 @Override
52 52 public LocationDTO getLocation(Long riderId) {
  53 + log.debug("查询骑手当前位置,参数 riderId={}", riderId);
53 54 RiderLocation loc = locationMapper.selectOne(new LambdaQueryWrapper<RiderLocation>()
54 55 .eq(RiderLocation::getUid, riderId));
55   - if (loc == null) return null;
  56 + if (loc == null) {
  57 + log.debug("未查询到骑手位置,riderId={}", riderId);
  58 + return null;
  59 + }
56 60 LocationDTO dto = new LocationDTO();
57   - dto.setLng(loc.getLng());
58   - dto.setLat(loc.getLat());
  61 + dto.setLng(new BigDecimal(loc.getLng()));
  62 + dto.setLat(new BigDecimal(loc.getLat()));
59 63 return dto;
60 64 }
61 65  
  66 + /**
  67 + * 查询城市范围内最近活跃的附近骑手。
  68 + */
62 69 @Override
63   - public List<NearbyRiderVO> getNearby(Long cityId, String lng, String lat) {
  70 + public List<NearbyRiderVO> getNearby(Long cityId, BigDecimal lng, BigDecimal lat) {
64 71 List<NearbyRiderVO> result = new ArrayList<>();
65   - if (cityId == null || cityId < 1 || lng == null || lat == null) return result;
  72 + log.debug("开始查询附近骑手,参数 cityId={} lng={} lat={}", cityId, lng, lat);
  73 + if (cityId == null || cityId < 1 || lng == null || lat == null) {
  74 + return result;
  75 + }
66 76  
67   - // 获取城市配置的附近距离范围(km)
68 77 DeliveryPricingConfigDTO config = cityService.getConfig(cityId);
69   - if (config == null) return result;
  78 + if (config == null) {
  79 + return result;
  80 + }
70 81 BigDecimal limitKm = config.getRiderDistance();
71 82 if (limitKm == null || limitKm.compareTo(BigDecimal.ZERO) <= 0) {
72   - limitKm = BigDecimal.valueOf(3); // 默认3km
  83 + limitKm = BigDecimal.valueOf(3);
73 84 }
74 85  
75   - // 查该城市所有在线(未休息)骑手
76 86 List<Rider> riders = riderMapper.selectList(new LambdaQueryWrapper<Rider>()
77 87 .eq(Rider::getCityId, cityId)
78 88 .eq(Rider::getIsRest, 0)
79 89 .eq(Rider::getUserStatus, 1));
80   - if (riders.isEmpty()) return result;
  90 + if (riders.isEmpty()) {
  91 + log.debug("当前城市没有在线骑手,cityId={}", cityId);
  92 + return result;
  93 + }
81 94  
82 95 List<Long> riderIds = riders.stream().map(Rider::getId).toList();
83   -
84   - // 查骑手位置
  96 + long minUpdateTime = System.currentTimeMillis() / 1000 - ACTIVE_WINDOW_SECONDS;
85 97 List<RiderLocation> locations = locationMapper.selectList(
86   - new LambdaQueryWrapper<RiderLocation>().in(RiderLocation::getUid, riderIds));
  98 + new LambdaQueryWrapper<RiderLocation>()
  99 + .in(RiderLocation::getUid, riderIds)
  100 + .ge(RiderLocation::getUpdateTime, minUpdateTime));
87 101  
88   - double queryLat = Double.parseDouble(lat);
89   - double queryLng = Double.parseDouble(lng);
  102 + double queryLat = lat.doubleValue();
  103 + double queryLng = lng.doubleValue();
90 104 double limitD = limitKm.doubleValue();
91 105  
92 106 for (RiderLocation loc : locations) {
... ... @@ -102,8 +116,11 @@ public class RiderLocationServiceImpl implements RiderLocationService {
102 116 vo.setDistance(dist);
103 117 result.add(vo);
104 118 }
105   - } catch (Exception ignored) {}
  119 + } catch (Exception ignored) {
  120 + }
106 121 }
  122 + log.debug("附近骑手查询完成,cityId={} 在线骑手数={} 位置记录数={} 命中数={}",
  123 + cityId, riders.size(), locations.size(), result.size());
107 124 return result;
108 125 }
109 126 }
... ...
src/main/resources/mapper/RiderLocationMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="com.diligrp.rider.mapper.RiderLocationMapper">
  4 +
  5 + <insert id="upsertLocation">
  6 + INSERT INTO rider_location(uid, lng, lat, update_time)
  7 + VALUES (#{uid}, #{lng}, #{lat}, #{updateTime})
  8 + ON DUPLICATE KEY UPDATE
  9 + lng = VALUES(lng),
  10 + lat = VALUES(lat),
  11 + update_time = VALUES(update_time)
  12 + </insert>
  13 +
  14 +</mapper>
... ...