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,43 +6,68 @@ import com.diligrp.rider.service.RiderLocationService;
6 import com.diligrp.rider.vo.NearbyRiderVO; 6 import com.diligrp.rider.vo.NearbyRiderVO;
7 import jakarta.servlet.http.HttpServletRequest; 7 import jakarta.servlet.http.HttpServletRequest;
8 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
  9 +import jakarta.validation.constraints.DecimalMax;
  10 +import jakarta.validation.constraints.DecimalMin;
9 import lombok.RequiredArgsConstructor; 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 import java.util.List; 22 import java.util.List;
13 23
14 @RestController 24 @RestController
15 @RequestMapping("/api/rider/location") 25 @RequestMapping("/api/rider/location")
16 @RequiredArgsConstructor 26 @RequiredArgsConstructor
  27 +@Validated
  28 +@Slf4j
17 public class RiderLocationController { 29 public class RiderLocationController {
18 30
19 private final RiderLocationService locationService; 31 private final RiderLocationService locationService;
20 32
21 - /** 上报位置 */ 33 + /**
  34 + * 上报骑手当前位置。
  35 + */
22 @PostMapping("/update") 36 @PostMapping("/update")
23 public Result<Void> update(@Valid @RequestBody LocationDTO dto, HttpServletRequest request) { 37 public Result<Void> update(@Valid @RequestBody LocationDTO dto, HttpServletRequest request) {
24 Long riderId = (Long) request.getAttribute("riderId"); 38 Long riderId = (Long) request.getAttribute("riderId");
  39 + log.debug("骑手位置上报,请求参数 riderId={} lng={} lat={}",
  40 + riderId, dto.getLng(), dto.getLat());
25 locationService.updateLocation(riderId, dto); 41 locationService.updateLocation(riderId, dto);
26 return Result.success(); 42 return Result.success();
27 } 43 }
28 44
29 - /** 查看自己位置 */ 45 + /**
  46 + * 回显当前登录骑手的位置。
  47 + */
30 @GetMapping 48 @GetMapping
31 public Result<LocationDTO> get(HttpServletRequest request) { 49 public Result<LocationDTO> get(HttpServletRequest request) {
32 Long riderId = (Long) request.getAttribute("riderId"); 50 Long riderId = (Long) request.getAttribute("riderId");
  51 + log.debug("查询骑手位置,请求参数 riderId={}", riderId);
33 return Result.success(locationService.getLocation(riderId)); 52 return Result.success(locationService.getLocation(riderId));
34 } 53 }
35 54
36 /** 55 /**
37 - * 附近在线骑手列表(分站地图查看用)  
38 - * Location.getNearby() 56 + * 查询当前登录骑手所属城市内的附近骑手。
39 */ 57 */
40 @GetMapping("/nearby") 58 @GetMapping("/nearby")
41 public Result<List<NearbyRiderVO>> nearby( 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 HttpServletRequest request) { 68 HttpServletRequest request) {
  69 + Long cityId = (Long) request.getAttribute("cityId");
  70 + log.debug("查询附近骑手,请求参数 cityId={} lng={} lat={}", cityId, lng, lat);
46 return Result.success(locationService.getNearby(cityId, lng, lat)); 71 return Result.success(locationService.getNearby(cityId, lng, lat));
47 } 72 }
48 } 73 }
src/main/java/com/diligrp/rider/dto/LocationDTO.java
1 package com.diligrp.rider.dto; 1 package com.diligrp.rider.dto;
2 2
  3 +import jakarta.validation.constraints.DecimalMax;
  4 +import jakarta.validation.constraints.DecimalMin;
3 import jakarta.validation.constraints.NotNull; 5 import jakarta.validation.constraints.NotNull;
4 import lombok.Data; 6 import lombok.Data;
5 7
  8 +import java.math.BigDecimal;
  9 +
6 @Data 10 @Data
7 public class LocationDTO { 11 public class LocationDTO {
8 @NotNull(message = "经度不能为空") 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 @NotNull(message = "纬度不能为空") 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,7 +3,15 @@ package com.diligrp.rider.mapper;
3 import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 import com.diligrp.rider.entity.RiderLocation; 4 import com.diligrp.rider.entity.RiderLocation;
5 import org.apache.ibatis.annotations.Mapper; 5 import org.apache.ibatis.annotations.Mapper;
  6 +import org.apache.ibatis.annotations.Param;
6 7
7 @Mapper 8 @Mapper
8 public interface RiderLocationMapper extends BaseMapper<RiderLocation> { 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,19 +3,22 @@ package com.diligrp.rider.service;
3 import com.diligrp.rider.dto.LocationDTO; 3 import com.diligrp.rider.dto.LocationDTO;
4 import com.diligrp.rider.vo.NearbyRiderVO; 4 import com.diligrp.rider.vo.NearbyRiderVO;
5 5
  6 +import java.math.BigDecimal;
6 import java.util.List; 7 import java.util.List;
7 8
8 public interface RiderLocationService { 9 public interface RiderLocationService {
9 /** 上报骑手位置 */ 10 /** 上报骑手位置 */
10 void updateLocation(Long riderId, LocationDTO dto); 11 void updateLocation(Long riderId, LocationDTO dto);
  12 +
11 /** 获取骑手位置 */ 13 /** 获取骑手位置 */
12 LocationDTO getLocation(Long riderId); 14 LocationDTO getLocation(Long riderId);
  15 +
13 /** 16 /**
14 * 获取附近在线骑手列表 17 * 获取附近在线骑手列表
15 - * Location.getNearby() 18 + *
16 * @param cityId 城市ID 19 * @param cityId 城市ID
17 * @param lng 查询点经度 20 * @param lng 查询点经度
18 * @param lat 查询点纬度 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 package com.diligrp.rider.service.impl; 1 package com.diligrp.rider.service.impl;
2 2
3 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 3 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4 -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;  
5 import com.diligrp.rider.dto.DeliveryPricingConfigDTO; 4 import com.diligrp.rider.dto.DeliveryPricingConfigDTO;
6 import com.diligrp.rider.dto.LocationDTO; 5 import com.diligrp.rider.dto.LocationDTO;
7 import com.diligrp.rider.entity.Rider; 6 import com.diligrp.rider.entity.Rider;
@@ -13,6 +12,7 @@ import com.diligrp.rider.service.RiderLocationService; @@ -13,6 +12,7 @@ import com.diligrp.rider.service.RiderLocationService;
13 import com.diligrp.rider.util.GeoUtil; 12 import com.diligrp.rider.util.GeoUtil;
14 import com.diligrp.rider.vo.NearbyRiderVO; 13 import com.diligrp.rider.vo.NearbyRiderVO;
15 import lombok.RequiredArgsConstructor; 14 import lombok.RequiredArgsConstructor;
  15 +import lombok.extern.slf4j.Slf4j;
16 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
17 17
18 import java.math.BigDecimal; 18 import java.math.BigDecimal;
@@ -21,72 +21,86 @@ import java.util.List; @@ -21,72 +21,86 @@ import java.util.List;
21 21
22 @Service 22 @Service
23 @RequiredArgsConstructor 23 @RequiredArgsConstructor
  24 +@Slf4j
24 public class RiderLocationServiceImpl implements RiderLocationService { 25 public class RiderLocationServiceImpl implements RiderLocationService {
25 26
  27 + private static final long ACTIVE_WINDOW_SECONDS = 300;
  28 +
26 private final RiderLocationMapper locationMapper; 29 private final RiderLocationMapper locationMapper;
27 private final RiderMapper riderMapper; 30 private final RiderMapper riderMapper;
28 private final CityService cityService; 31 private final CityService cityService;
29 32
  33 + /**
  34 + * 按骑手维度更新最新位置。
  35 + */
30 @Override 36 @Override
31 public void updateLocation(Long riderId, LocationDTO dto) { 37 public void updateLocation(Long riderId, LocationDTO dto) {
32 - RiderLocation existing = locationMapper.selectOne(new LambdaQueryWrapper<RiderLocation>()  
33 - .eq(RiderLocation::getUid, riderId));  
34 long now = System.currentTimeMillis() / 1000; 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 @Override 51 @Override
52 public LocationDTO getLocation(Long riderId) { 52 public LocationDTO getLocation(Long riderId) {
  53 + log.debug("查询骑手当前位置,参数 riderId={}", riderId);
53 RiderLocation loc = locationMapper.selectOne(new LambdaQueryWrapper<RiderLocation>() 54 RiderLocation loc = locationMapper.selectOne(new LambdaQueryWrapper<RiderLocation>()
54 .eq(RiderLocation::getUid, riderId)); 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 LocationDTO dto = new LocationDTO(); 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 return dto; 63 return dto;
60 } 64 }
61 65
  66 + /**
  67 + * 查询城市范围内最近活跃的附近骑手。
  68 + */
62 @Override 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 List<NearbyRiderVO> result = new ArrayList<>(); 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 DeliveryPricingConfigDTO config = cityService.getConfig(cityId); 77 DeliveryPricingConfigDTO config = cityService.getConfig(cityId);
69 - if (config == null) return result; 78 + if (config == null) {
  79 + return result;
  80 + }
70 BigDecimal limitKm = config.getRiderDistance(); 81 BigDecimal limitKm = config.getRiderDistance();
71 if (limitKm == null || limitKm.compareTo(BigDecimal.ZERO) <= 0) { 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 List<Rider> riders = riderMapper.selectList(new LambdaQueryWrapper<Rider>() 86 List<Rider> riders = riderMapper.selectList(new LambdaQueryWrapper<Rider>()
77 .eq(Rider::getCityId, cityId) 87 .eq(Rider::getCityId, cityId)
78 .eq(Rider::getIsRest, 0) 88 .eq(Rider::getIsRest, 0)
79 .eq(Rider::getUserStatus, 1)); 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 List<Long> riderIds = riders.stream().map(Rider::getId).toList(); 95 List<Long> riderIds = riders.stream().map(Rider::getId).toList();
83 -  
84 - // 查骑手位置 96 + long minUpdateTime = System.currentTimeMillis() / 1000 - ACTIVE_WINDOW_SECONDS;
85 List<RiderLocation> locations = locationMapper.selectList( 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 double limitD = limitKm.doubleValue(); 104 double limitD = limitKm.doubleValue();
91 105
92 for (RiderLocation loc : locations) { 106 for (RiderLocation loc : locations) {
@@ -102,8 +116,11 @@ public class RiderLocationServiceImpl implements RiderLocationService { @@ -102,8 +116,11 @@ public class RiderLocationServiceImpl implements RiderLocationService {
102 vo.setDistance(dist); 116 vo.setDistance(dist);
103 result.add(vo); 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 return result; 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>