Commit dff9b9d09e45e0e92247f1218bd881641e3bdcf2
1 parent
a5543e29
feat(rider): 骑手位置管理功能优化
- 将经纬度数据类型从String改为BigDecimal,并添加数值范围验证 - 实现骑手位置上报的upsert操作,避免重复插入 - 添加详细的日志记录用于调试和监控 - 优化附近骑手查询逻辑,增加活跃时间窗口限制 - 统一坐标系精度处理,提升定位准确性
Showing
6 changed files
with
121 additions
and
45 deletions
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> |