riderId; $rider = $this->riderInfo; // 检查Content-Type并解析参数 $contentType = ''; if (isset($_SERVER['CONTENT_TYPE'])) { $contentType = $_SERVER['CONTENT_TYPE']; } // 使用统一的参数处理方法 // 兼容 lat/lng 和 latitude/longitude 两种参数名 $lat = floatval($this->getRequestParam('lat', $this->getRequestParam('latitude', 0))); $lng = floatval($this->getRequestParam('lng', $this->getRequestParam('longitude', 0))); $address = trim($this->getRequestParam('address', '')); $accuracy = floatval($this->getRequestParam('accuracy', 0)); $speed = floatval($this->getRequestParam('speed', 0)); $direction = floatval($this->getRequestParam('direction', 0)); if (empty($lat) || empty($lng)) { $this->ajaxReturn(array('code' => 0, 'msg' => '位置信息不能为空')); } if (!$rider) { $this->ajaxReturn(array('code' => 0, 'msg' => '骑手不存在')); } if ($rider['work_status'] != 1) { $this->ajaxReturn(array('code' => 0, 'msg' => '骑手未上线')); } // 记录位置轨迹 $locationModel = M('rider_location'); $locationData = array( 'rider_id' => $riderId, 'lat' => $lat, 'lng' => $lng, 'address' => $address, 'accuracy' => $accuracy, 'speed' => $speed, 'direction' => $direction, 'add_time' => date('Y-m-d H:i:s') ); $result = $locationModel->add($locationData); // 定期清理历史数据(每100次上报清理一次) if (mt_rand(1, 100) === 1) { $this->autoCleanHistoryLocation(); } if ($result) { $riderModel = M('rider_info'); // 更新骑手表中的最后位置 $riderModel->where(array('id' => $riderId))->save(array( 'last_location_lat' => $lat, 'last_location_lng' => $lng, 'last_location_time' => date('Y-m-d H:i:s') )); $this->ajaxReturn(array( 'code' => 1, 'msg' => '位置上报成功' )); } else { $this->ajaxReturn(array('code' => 0, 'msg' => '位置上报失败')); } } /** * 逆地理编码 - 根据经纬度获取地址信息 */ public function reverseGeocode() { // 获取JSON格式的请求数据 $input = json_decode(file_get_contents('php://input'), true); $lat = isset($input['lat']) ? $input['lat'] : null; $lng = isset($input['lng']) ? $input['lng'] : null; $type = isset($input['type']) ? $input['type'] : 1; // 坐标类型:1-GCJ02,2-BD09 if ($lat === null || $lng === null || $lat === '' || $lng === '') { $this->ajaxReturn(array('code' => 0, 'msg' => '经纬度不能为空')); } $lat = floatval($lat); $lng = floatval($lng); if ($lat == 0 || $lng == 0) { $this->ajaxReturn(array('code' => 0, 'msg' => '经纬度参数无效')); } // 调用公共函数进行逆地理编码 $result = getReverseGeocode($lat, $lng, $type); if ($result['status'] == 1) { $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => $result['address'] )); } else { $this->ajaxReturn(array( 'code' => 0, 'msg' => $result['msg'] ?: '获取地址信息失败' )); } } /** * 获取骑手当前位置 */ public function getCurrentLocation() { $riderId = $this->riderId; $riderModel = M('rider_info'); $rider = $riderModel->field('last_location_lat, last_location_lng, last_location_time')->find($riderId); if (!$rider) { $this->ajaxReturn(array('code' => 0, 'msg' => '骑手不存在')); } $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => array( 'lat' => floatval($rider['last_location_lat']), 'lng' => floatval($rider['last_location_lng']), 'update_time' => $rider['last_location_time'] ) )); } /** * 获取位置轨迹 */ public function getLocationTrack() { $riderId = $this->riderId; $startTime = $this->getRequestParam('start_time', ''); $endTime = $this->getRequestParam('end_time', ''); $orderId = $this->getRequestParam('order_id', 0); $where = array('rider_id' => $riderId); if ($startTime && $endTime) { $where['add_time'] = array('between', array($startTime, $endTime)); } elseif ($orderId) { // 根据订单时间获取轨迹 $orderModel = M('rider_order'); $order = $orderModel->field('accept_time, delivery_time')->find($orderId); if ($order && $order['accept_time']) { $where['add_time'] = array('egt', $order['accept_time']); if ($order['delivery_time']) { $where['add_time'] = array('between', array($order['accept_time'], $order['delivery_time'])); } } } else { // 默认获取今天的轨迹 $today = date('Y-m-d'); $where['add_time'] = array('between', array($today . ' 00:00:00', $today . ' 23:59:59')); } $locationModel = M('rider_location'); $locations = $locationModel->where($where) ->order('add_time ASC') ->select(); // 格式化数据 foreach ($locations as &$location) { $location['lat'] = floatval($location['lat']); $location['lng'] = floatval($location['lng']); $location['accuracy'] = floatval($location['accuracy']); $location['speed'] = floatval($location['speed']); $location['direction'] = floatval($location['direction']); } $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => $locations )); } /** * 计算配送距离 */ public function calculateDeliveryDistance() { $riderId = $this->riderId; $orderId = $this->getRequestParam('order_id', 0); if (empty($orderId)) { $this->ajaxReturn(array('code' => 0, 'msg' => '订单ID不能为空')); } // 获取订单信息 $orderModel = M('rider_order'); $order = $orderModel->where(array('id' => $orderId, 'rider_id' => $riderId))->find(); if (!$order || $order['rider_id'] != $riderId) { $this->ajaxReturn(array('code' => 0, 'msg' => '订单不存在或无权限')); } if ($order['order_status'] < 2) { $this->ajaxReturn(array('code' => 0, 'msg' => '订单未完成')); } // 获取配送期间的位置轨迹 $locationModel = M('rider_location'); $locations = $locationModel->where(array( 'rider_id' => $riderId, 'add_time' => array('between', array($order['accept_time'], $order['delivery_time'])) ))->order('add_time ASC')->select(); if (count($locations) < 2) { $this->ajaxReturn(array( 'code' => 1, 'msg' => '轨迹点不足', 'data' => array( 'distance' => 0, 'duration' => 0 ) )); } // 计算总距离 $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; } // 计算配送时长 $startTime = strtotime($order['accept_time']); $endTime = strtotime($order['delivery_time']); $duration = $endTime - $startTime; $this->ajaxReturn(array( 'code' => 1, 'msg' => '计算成功', 'data' => array( 'distance' => round($totalDistance, 2), // 公里 'duration' => $duration, // 秒 'duration_format' => $this->formatDuration($duration), 'track_points' => count($locations) ) )); } /** * 清理历史位置数据 */ public function cleanHistoryLocation() { $days = $this->getRequestParam('days', 30); // 保留天数 $cutoffDate = date('Y-m-d H:i:s', strtotime("-{$days} days")); $locationModel = M('rider_location'); $result = $locationModel->where(array('add_time' => array('lt', $cutoffDate)))->delete(); $this->ajaxReturn(array( 'code' => 1, 'msg' => '清理完成', 'data' => array( 'deleted_count' => $result ) )); } /** * 自动清理历史位置数据(内部方法) */ private function autoCleanHistoryLocation() { try { $days = 7; // 自动清理时保留7天的数据 $cutoffDate = date('Y-m-d H:i:s', strtotime("-{$days} days")); $locationModel = M('rider_location'); // 分批删除,避免一次删除太多数据影响性能 $batchSize = 1000; $deletedTotal = 0; do { $result = $locationModel->where(array('add_time' => array('lt', $cutoffDate))) ->limit($batchSize) ->delete(); $deletedTotal += $result; // 如果删除的记录少于批次大小,说明已经清理完成 if ($result < $batchSize) { break; } // 避免长时间占用数据库,稍作休息 usleep(100000); // 休息0.1秒 } while ($result > 0); // 记录清理日志(可选) if ($deletedTotal > 0) { \Think\Log::write("自动清理位置数据:删除了 {$deletedTotal} 条记录", 'INFO'); } } catch (\Exception $e) { // 清理失败不影响主流程,只记录日志 \Think\Log::write("自动清理位置数据失败:" . $e->getMessage(), 'ERROR'); } } /** * 计算两点间距离 */ private function calculateDistance($lat1, $lng1, $lat2, $lng2) { $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; } /** * 格式化时长 */ private function formatDuration($seconds) { $hours = floor($seconds / 3600); $minutes = floor(($seconds % 3600) / 60); $seconds = $seconds % 60; if ($hours > 0) { return sprintf('%d小时%d分钟%d秒', $hours, $minutes, $seconds); } elseif ($minutes > 0) { return sprintf('%d分钟%d秒', $minutes, $seconds); } else { return sprintf('%d秒', $seconds); } } /** * 批量上传位置数据(专为前端位置服务设计) */ public function upload() { try { $riderId = $this->riderId; $rider = $this->riderInfo; if (!$rider) { $this->ajaxReturn(array('code' => 0, 'msg' => '骑手不存在')); } // 获取位置数据 - 只需要locations参数,rider_id从token中获取 $locations = $this->getRequestParam('locations', array()); if (empty($locations) || !is_array($locations)) { $this->ajaxReturn(array('code' => 0, 'msg' => '位置数据不能为空')); } $locationModel = M('rider_location'); $locationDataList = array(); $latestLocation = null; $latestTime = 0; foreach ($locations as $location) { // 验证必要字段 if (empty($location['latitude']) || empty($location['longitude']) || empty($location['timestamp'])) { continue; } // 验证坐标范围 if ($location['latitude'] < -90 || $location['latitude'] > 90 || $location['longitude'] < -180 || $location['longitude'] > 180) { continue; } $timestamp = $location['timestamp']; $recordTime = date('Y-m-d H:i:s', $timestamp / 1000); if ($timestamp > $latestTime) { $latestTime = $timestamp; $latestLocation = $location; } $locationDataList[] = array( 'rider_id' => $riderId, 'lat' => $location['latitude'], 'lng' => $location['longitude'], 'address' => isset($location['address']) ? $location['address'] : '', 'accuracy' => isset($location['accuracy']) ? $location['accuracy'] : 0, 'speed' => isset($location['speed']) ? $location['speed'] : 0, 'direction' => isset($location['heading']) ? $location['heading'] : 0, 'add_time' => $recordTime ); } if (empty($locationDataList)) { $this->ajaxReturn(array('code' => 0, 'msg' => '没有有效的位置数据')); } try { // 批量插入位置记录 $result = $locationModel->addAll($locationDataList); if ($result) { // 更新最新位置到骑手信息表 if ($latestLocation) { $riderModel = M('rider_info'); $riderModel->where(array('id' => $riderId))->save(array( 'last_location_lat' => $latestLocation['latitude'], 'last_location_lng' => $latestLocation['longitude'], 'last_location_time' => date('Y-m-d H:i:s', $latestTime / 1000) )); } $this->ajaxReturn(array( 'code' => 1, 'msg' => '位置数据上传成功', 'data' => array( 'uploaded_count' => count($locationDataList), 'latest_location' => $latestLocation ) )); } else { $this->ajaxReturn(array('code' => 0, 'msg' => '位置数据上传失败')); } } catch (\Exception $e) { $this->ajaxReturn(array('code' => 0, 'msg' => '位置数据上传异常: ' . $e->getMessage())); } } catch (\Exception $e) { $this->ajaxReturn(array('code' => 0, 'msg' => $e->getMessage())); } } /** * 开始位置追踪 */ public function startTracking() { $riderId = $this->riderId; $rider = $this->riderInfo; if (!$rider) { $this->ajaxReturn(array('code' => 0, 'msg' => '骑手不存在')); } $riderModel = M('rider_info'); // 更新追踪状态 $result = $riderModel->where(array('id' => $riderId))->save(array( 'location_tracking' => 1, 'tracking_start_time' => date('Y-m-d H:i:s') )); if ($result !== false) { $this->ajaxReturn(array('code' => 1, 'msg' => '开始位置追踪成功')); } else { $this->ajaxReturn(array('code' => 0, 'msg' => '开始位置追踪失败')); } } /** * 停止位置追踪 */ public function stopTracking() { $riderId = $this->riderId; $rider = $this->riderInfo; if (!$rider) { $this->ajaxReturn(array('code' => 0, 'msg' => '骑手不存在')); } $riderModel = M('rider_info'); // 更新追踪状态 $result = $riderModel->where(array('id' => $riderId))->save(array( 'location_tracking' => 0, 'tracking_end_time' => date('Y-m-d H:i:s') )); if ($result !== false) { $this->ajaxReturn(array('code' => 1, 'msg' => '停止位置追踪成功')); } else { $this->ajaxReturn(array('code' => 0, 'msg' => '停止位置追踪失败')); } } }