RiderLocationServiceImpl.java 11.7 KB
package com.diligrp.rider.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.diligrp.rider.dto.DeliveryPricingConfigDTO;
import com.diligrp.rider.dto.LocationDTO;
import com.diligrp.rider.entity.Orders;
import com.diligrp.rider.entity.Rider;
import com.diligrp.rider.entity.RiderLocation;
import com.diligrp.rider.mapper.RiderLocationMapper;
import com.diligrp.rider.mapper.RiderMapper;
import com.diligrp.rider.mapper.OrdersMapper;
import com.diligrp.rider.service.CityService;
import com.diligrp.rider.service.RiderLocationService;
import com.diligrp.rider.util.GeoUtil;
import com.diligrp.rider.vo.AdminRiderDashboardVO;
import com.diligrp.rider.vo.AdminRiderLocationVO;
import com.diligrp.rider.vo.NearbyRiderVO;
import com.diligrp.rider.websocket.LocationPushService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Service
@RequiredArgsConstructor
@Slf4j
public class RiderLocationServiceImpl implements RiderLocationService {

    private static final long ACTIVE_WINDOW_SECONDS = 300;

    private final RiderLocationMapper locationMapper;
    private final RiderMapper riderMapper;
    private final OrdersMapper ordersMapper;
    private final CityService cityService;
    private final LocationPushService locationPushService;

    /**
     * 按骑手维度更新最新位置。
     */
    @Override
    public void updateLocation(Long riderId, LocationDTO dto) {
        long now = System.currentTimeMillis() / 1000;
        log.debug("更新骑手位置,参数 riderId={} lng={} lat={} updateTime={}",
                riderId, dto.getLng(), dto.getLat(), now);
        Rider rider = riderMapper.selectById(riderId);
        locationMapper.upsertLocation(
                riderId,
                dto.getLng().stripTrailingZeros().toPlainString(),
                dto.getLat().stripTrailingZeros().toPlainString(),
                now);
        if (rider != null && rider.getCityId() != null) {
            AdminRiderLocationVO location = new AdminRiderLocationVO();
            location.setRiderId(riderId);
            location.setRiderName(rider.getUserNickname());
            location.setCityId(rider.getCityId());
            location.setLng(dto.getLng());
            location.setLat(dto.getLat());
            location.setUpdateTime(now);
            locationPushService.pushRiderLocation(location);
        }
    }

    /**
     * 查询当前骑手最近一次上报的位置。
     */
    @Override
    public LocationDTO getLocation(Long riderId) {
        log.debug("查询骑手当前位置,参数 riderId={}", riderId);
        RiderLocation loc = locationMapper.selectOne(new LambdaQueryWrapper<RiderLocation>()
                .eq(RiderLocation::getUid, riderId));
        if (loc == null) {
            log.debug("未查询到骑手位置,riderId={}", riderId);
            return null;
        }
        LocationDTO dto = new LocationDTO();
        dto.setLng(new BigDecimal(loc.getLng()));
        dto.setLat(new BigDecimal(loc.getLat()));
        return dto;
    }

    /**
     * 查询指定商铺最近活跃的骑手位置列表。
     */
    @Override
    public List<AdminRiderLocationVO> listActiveByCity(Long cityId) {
        List<AdminRiderLocationVO> result = new ArrayList<>();
        if (cityId == null || cityId < 1) {
            return result;
        }

        List<Rider> riders = riderMapper.selectList(new LambdaQueryWrapper<Rider>()
                .eq(Rider::getCityId, cityId)
                .eq(Rider::getIsRest, 0)
                .eq(Rider::getUserStatus, 1));
        if (riders.isEmpty()) {
            return result;
        }

        List<Long> riderIds = riders.stream().map(Rider::getId).toList();
        long minUpdateTime = System.currentTimeMillis() / 1000 - ACTIVE_WINDOW_SECONDS;
        List<RiderLocation> locations = locationMapper.selectList(
                new LambdaQueryWrapper<RiderLocation>()
                        .in(RiderLocation::getUid, riderIds)
                        .ge(RiderLocation::getUpdateTime, minUpdateTime));
        if (locations.isEmpty()) {
            return result;
        }

        java.util.Map<Long, Rider> riderMap = riders.stream()
                .collect(java.util.stream.Collectors.toMap(Rider::getId, rider -> rider));
        for (RiderLocation location : locations) {
            Rider rider = riderMap.get(location.getUid());
            if (rider == null) {
                continue;
            }
            try {
                AdminRiderLocationVO vo = new AdminRiderLocationVO();
                vo.setRiderId(rider.getId());
                vo.setRiderName(rider.getUserNickname());
                vo.setCityId(rider.getCityId());
                vo.setLng(new BigDecimal(location.getLng()));
                vo.setLat(new BigDecimal(location.getLat()));
                vo.setUpdateTime(location.getUpdateTime());
                result.add(vo);
            } catch (Exception ignored) {
            }
        }
        log.debug("查询商铺活跃骑手位置完成,cityId={} count={}", cityId, result.size());
        return result;
    }

    /**
     * 查询指定商铺骑手看板数据。
     */
    @Override
    public List<AdminRiderDashboardVO> listDashboardByCity(Long cityId) {
        List<AdminRiderDashboardVO> result = new ArrayList<>();
        log.debug("开始查询商铺骑手看板,参数 cityId={}", cityId);
        if (cityId == null || cityId < 1) {
            return result;
        }

        List<Rider> riders = riderMapper.selectList(new LambdaQueryWrapper<Rider>()
                .eq(Rider::getCityId, cityId)
                .orderByDesc(Rider::getId));
        if (riders.isEmpty()) {
            log.debug("商铺下没有骑手,cityId={}", cityId);
            return result;
        }

        List<Long> riderIds = riders.stream().map(Rider::getId).toList();
        List<RiderLocation> locations = locationMapper.selectList(
                new LambdaQueryWrapper<RiderLocation>().in(RiderLocation::getUid, riderIds));
        List<Orders> holdingOrders = ordersMapper.selectList(new LambdaQueryWrapper<Orders>()
                .eq(Orders::getCityId, cityId)
                .in(Orders::getStatus, List.of(3, 4))
                .in(Orders::getRiderId, riderIds)
                .eq(Orders::getIsDel, 0));
        log.debug("商铺骑手看板基础数据查询完成,cityId={} riderCount={} locationCount={} holdingOrderCount={}",
                cityId, riders.size(), locations.size(), holdingOrders.size());

        java.util.Map<Long, RiderLocation> locationMap = new java.util.HashMap<>();
        for (RiderLocation location : locations) {
            RiderLocation current = locationMap.get(location.getUid());
            if (current == null || (current.getUpdateTime() != null
                    && location.getUpdateTime() != null
                    && location.getUpdateTime() > current.getUpdateTime())) {
                locationMap.put(location.getUid(), location);
            }
        }

        java.util.Map<Long, Integer> holdCountMap = new java.util.HashMap<>();
        for (Orders order : holdingOrders) {
            holdCountMap.merge(order.getRiderId(), 1, Integer::sum);
        }

        for (Rider rider : riders) {
            AdminRiderDashboardVO vo = new AdminRiderDashboardVO();
            vo.setRiderId(rider.getId());
            vo.setRiderName(rider.getUserNickname());
            vo.setMobile(rider.getMobile());
            vo.setStatus(buildRiderStatus(rider, locationMap.get(rider.getId())));

            int holdOrderCount = holdCountMap.getOrDefault(rider.getId(), 0);
            vo.setHoldOrderCount(holdOrderCount);
            vo.setLoadRate(calcLoadRate(rider, holdOrderCount));

            RiderLocation location = locationMap.get(rider.getId());
            if (location != null) {
                vo.setLastLocationTime(location.getUpdateTime());
                try {
                    vo.setLng(new BigDecimal(location.getLng()));
                    vo.setLat(new BigDecimal(location.getLat()));
                } catch (Exception ignored) {
                }
            }
            result.add(vo);
        }

        log.debug("查询骑手看板完成,cityId={} count={}", cityId, result.size());
        return result;
    }

    /**
     * 查询商铺范围内最近活跃的附近骑手。
     */
    @Override
    public List<NearbyRiderVO> getNearby(Long cityId, BigDecimal lng, BigDecimal lat) {
        List<NearbyRiderVO> result = new ArrayList<>();
        log.debug("开始查询附近骑手,参数 cityId={} lng={} lat={}", cityId, lng, lat);
        if (cityId == null || cityId < 1 || lng == null || lat == null) {
            return result;
        }

        DeliveryPricingConfigDTO config = cityService.getConfig(cityId);
        if (config == null) {
            return result;
        }
        BigDecimal limitKm = config.getRiderDistance();
        if (limitKm == null || limitKm.compareTo(BigDecimal.ZERO) <= 0) {
            limitKm = BigDecimal.valueOf(3);
        }

        List<Rider> riders = riderMapper.selectList(new LambdaQueryWrapper<Rider>()
                .eq(Rider::getCityId, cityId)
                .eq(Rider::getIsRest, 0)
                .eq(Rider::getUserStatus, 1));
        if (riders.isEmpty()) {
            log.debug("当前商铺没有在线骑手,cityId={}", cityId);
            return result;
        }

        List<Long> riderIds = riders.stream().map(Rider::getId).toList();
        long minUpdateTime = System.currentTimeMillis() / 1000 - ACTIVE_WINDOW_SECONDS;
        List<RiderLocation> locations = locationMapper.selectList(
                new LambdaQueryWrapper<RiderLocation>()
                        .in(RiderLocation::getUid, riderIds)
                        .ge(RiderLocation::getUpdateTime, minUpdateTime));

        double queryLat = lat.doubleValue();
        double queryLng = lng.doubleValue();
        double limitD = limitKm.doubleValue();

        for (RiderLocation loc : locations) {
            try {
                double dist = GeoUtil.calcDistanceKm(
                        queryLat, queryLng,
                        Double.parseDouble(loc.getLat()),
                        Double.parseDouble(loc.getLng()));
                if (dist <= limitD) {
                    NearbyRiderVO vo = new NearbyRiderVO();
                    vo.setLng(loc.getLng());
                    vo.setLat(loc.getLat());
                    vo.setDistance(dist);
                    result.add(vo);
                }
            } catch (Exception ignored) {
            }
        }
        log.debug("附近骑手查询完成,cityId={} 在线骑手数={} 位置记录数={} 命中数={}",
                cityId, riders.size(), locations.size(), result.size());
        return result;
    }

    private String buildRiderStatus(Rider rider, RiderLocation location) {
        if (rider.getUserStatus() == null || rider.getUserStatus() != 1) {
            return "审核中";
        }
        if (rider.getStatus() == null || rider.getStatus() != 1) {
            return "已禁用";
        }
        if (rider.getIsRest() != null && rider.getIsRest() == 1) {
            return "休息中";
        }
        long now = System.currentTimeMillis() / 1000;
        if (location == null || location.getUpdateTime() == null
                || now - location.getUpdateTime() > ACTIVE_WINDOW_SECONDS) {
            return "离线";
        }
        return "在线";
    }

    private int calcLoadRate(Rider rider, int holdOrderCount) {
        int maxHoldCount = rider.getType() != null && rider.getType() == 2 ? 4 : 2;
        if (maxHoldCount <= 0) {
            return 0;
        }
        int rate = holdOrderCount * 100 / maxHoldCount;
        return Math.min(rate, 100);
    }
}