add($data); } /** * 批量添加位置记录 * @param array $dataList * @return int|false */ public function addLocationBatch($dataList) { return $this->addAll($dataList); } /** * 获取骑手最新位置 * @param int $riderId 骑手ID * @return array */ public function getLatestLocation($riderId) { return $this->where(['rider_id' => $riderId]) ->order('add_time DESC') ->find(); } /** * 获取骑手位置轨迹 * @param int $riderId 骑手ID * @param string $startTime 开始时间 * @param string $endTime 结束时间 * @param int $limit 限制数量 * @return array */ public function getLocationTrack($riderId, $startTime = '', $endTime = '', $limit = 100) { $where = ['rider_id' => $riderId]; if (!empty($startTime)) { $where['add_time'][] = ['egt', $startTime]; } if (!empty($endTime)) { $where['add_time'][] = ['elt', $endTime]; } return $this->where($where) ->order('add_time ASC') ->limit($limit) ->select(); } /** * 获取指定范围内的骑手 * @param float $centerLat 中心纬度 * @param float $centerLng 中心经度 * @param float $radius 半径(公里) * @param array $riderIds 指定骑手ID列表(可选) * @return array */ public function getRidersInRange($centerLat, $centerLng, $radius, $riderIds = []) { // 计算经纬度范围 $latRange = $radius / 111; // 1度纬度约等于111公里 $lngRange = $radius / (111 * cos(deg2rad($centerLat))); // 经度范围需要考虑纬度 $minLat = $centerLat - $latRange; $maxLat = $centerLat + $latRange; $minLng = $centerLng - $lngRange; $maxLng = $centerLng + $lngRange; $where = [ 'lat' => ['between', [$minLat, $maxLat]], 'lng' => ['between', [$minLng, $maxLng]] ]; if (!empty($riderIds)) { $where['rider_id'] = ['in', $riderIds]; } // 获取每个骑手的最新位置 $subQuery = $this->field('rider_id, MAX(add_time) as max_time') ->where($where) ->group('rider_id') ->buildSql(); $locations = $this->alias('rl1') ->join("({$subQuery}) rl2 ON rl1.rider_id = rl2.rider_id AND rl1.add_time = rl2.max_time") ->field('rl1.*') ->select(); // 精确计算距离并过滤 $result = []; foreach ($locations as $location) { $distance = $this->calculateDistance($centerLat, $centerLng, $location['lat'], $location['lng']); if ($distance <= $radius) { $location['distance'] = $distance; $result[] = $location; } } // 按距离排序 usort($result, function($a, $b) { return $a['distance'] <=> $b['distance']; }); return $result; } /** * 计算两点间距离 * @param float $lat1 纬度1 * @param float $lng1 经度1 * @param float $lat2 纬度2 * @param float $lng2 经度2 * @return float 距离(公里) */ public function calculateDistance($lat1, $lng1, $lat2, $lng2) { if (empty($lat1) || empty($lng1) || empty($lat2) || empty($lng2)) { return 999; } $earthRadius = 6371; // 地球半径,单位:公里 $lat1Rad = deg2rad($lat1); $lng1Rad = deg2rad($lng1); $lat2Rad = deg2rad($lat2); $lng2Rad = deg2rad($lng2); $deltaLat = $lat2Rad - $lat1Rad; $deltaLng = $lng2Rad - $lng1Rad; $a = sin($deltaLat / 2) * sin($deltaLat / 2) + cos($lat1Rad) * cos($lat2Rad) * sin($deltaLng / 2) * sin($deltaLng / 2); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); return $earthRadius * $c; } /** * 清理过期位置记录 * @param int $days 保留天数 * @return int */ public function cleanExpiredLocations($days = 7) { $expireDate = date('Y-m-d H:i:s', strtotime("-{$days} days")); // 删除过期的位置记录 return $this->where(['add_time' => ['lt', $expireDate]])->delete(); } /** * 获取骑手活动统计 * @param int $riderId 骑手ID * @param string $date 日期 * @return array */ public function getRiderActivityStats($riderId, $date = '') { if (empty($date)) { $date = date('Y-m-d'); } $startTime = $date . ' 00:00:00'; $endTime = $date . ' 23:59:59'; $where = [ 'rider_id' => $riderId, 'add_time' => ['between', [$startTime, $endTime]] ]; $locations = $this->where($where) ->order('add_time ASC') ->select(); if (empty($locations)) { return [ 'total_distance' => 0, 'active_time' => 0, 'location_count' => 0, 'first_location_time' => '', 'last_location_time' => '' ]; } // 计算总距离 $totalDistance = 0; for ($i = 1; $i < count($locations); $i++) { $distance = $this->calculateDistance( $locations[$i-1]['lat'], $locations[$i-1]['lng'], $locations[$i]['lat'], $locations[$i]['lng'] ); $totalDistance += $distance; } // 计算活动时间 $firstTime = strtotime($locations[0]['add_time']); $lastTime = strtotime($locations[count($locations)-1]['add_time']); $activeTime = $lastTime - $firstTime; return [ 'total_distance' => round($totalDistance, 2), 'active_time' => $activeTime, 'location_count' => count($locations), 'first_location_time' => $locations[0]['add_time'], 'last_location_time' => $locations[count($locations)-1]['add_time'] ]; } /** * 获取区域内骑手密度 * @param float $centerLat 中心纬度 * @param float $centerLng 中心经度 * @param float $radius 半径(公里) * @param int $timeRange 时间范围(分钟) * @return array */ public function getRiderDensity($centerLat, $centerLng, $radius, $timeRange = 30) { $startTime = date('Y-m-d H:i:s', strtotime("-{$timeRange} minutes")); // 计算经纬度范围 $latRange = $radius / 111; $lngRange = $radius / (111 * cos(deg2rad($centerLat))); $minLat = $centerLat - $latRange; $maxLat = $centerLat + $latRange; $minLng = $centerLng - $lngRange; $maxLng = $centerLng + $lngRange; $where = [ 'lat' => ['between', [$minLat, $maxLat]], 'lng' => ['between', [$minLng, $maxLng]], 'add_time' => ['egt', $startTime] ]; // 获取活跃骑手数量 $activeRiders = $this->where($where) ->field('rider_id') ->group('rider_id') ->select(); $riderCount = count($activeRiders); $area = pi() * $radius * $radius; // 圆形区域面积 $density = $area > 0 ? $riderCount / $area : 0; return [ 'rider_count' => $riderCount, 'area' => round($area, 2), 'density' => round($density, 4), 'radius' => $radius, 'time_range' => $timeRange ]; } /** * 预测骑手到达时间 * @param int $riderId 骑手ID * @param float $destLat 目标纬度 * @param float $destLng 目标经度 * @param float $avgSpeed 平均速度(公里/小时) * @return array */ public function predictArrivalTime($riderId, $destLat, $destLng, $avgSpeed = 20) { // 获取骑手最新位置 $latestLocation = $this->getLatestLocation($riderId); if (empty($latestLocation)) { return [ 'estimated_time' => 0, 'distance' => 0, 'error' => '无法获取骑手位置' ]; } // 计算距离 $distance = $this->calculateDistance( $latestLocation['lat'], $latestLocation['lng'], $destLat, $destLng ); // 计算预计时间(分钟) $estimatedTime = ($distance / $avgSpeed) * 60; // 考虑交通因素,增加20%的时间 $estimatedTime *= 1.2; return [ 'estimated_time' => round($estimatedTime, 0), 'distance' => round($distance, 2), 'avg_speed' => $avgSpeed, 'current_location' => [ 'lat' => $latestLocation['lat'], 'lng' => $latestLocation['lng'], 'record_time' => $latestLocation['add_time'] ] ]; } /** * 删除位置记录 * @param array $where 删除条件 * @return int */ public function deleteLocation($where) { return $this->where($where)->delete(); } }