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 | 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> | ... | ... |