checkRiderAuth(); if ($riderInfo['status'] != 1) { $this->ajaxReturn($riderInfo); } $riderId = $riderInfo['data']['id']; // 验证参数 if (empty($param['lat']) || empty($param['lng'])) { $this->ajaxReturn(callBack(statusCode()['error'], '位置信息不能为空')); } // 验证坐标范围 if ($param['lat'] < -90 || $param['lat'] > 90) { $this->ajaxReturn(callBack(statusCode()['error'], '纬度范围错误')); } if ($param['lng'] < -180 || $param['lng'] > 180) { $this->ajaxReturn(callBack(statusCode()['error'], '经度范围错误')); } $riderInfoModel = D('RiderInfo'); $riderLocationModel = D('RiderLocation'); // 更新骑手信息表中的最新位置 $updateData = [ 'last_location_lat' => $param['lat'], 'last_location_lng' => $param['lng'], 'last_location_time' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ]; $result = $riderInfoModel->updateRider(['id' => $riderId], $updateData); if ($result) { // 记录位置轨迹 $locationData = [ 'rider_id' => $riderId, 'lat' => $param['lat'], 'lng' => $param['lng'], 'accuracy' => $param['accuracy'] ?? 0, // GPS精度 'speed' => $param['speed'] ?? 0, // 速度 'bearing' => $param['bearing'] ?? 0, // 方向角 'altitude' => $param['altitude'] ?? 0, // 海拔 'record_time' => date('Y-m-d H:i:s'), 'add_time' => date('Y-m-d H:i:s') ]; $riderLocationModel->addLocation($locationData); // 检查是否有需要更新的订单 $this->checkOrderLocationUpdate($riderId, $param['lat'], $param['lng']); $this->ajaxReturn(callBack(statusCode()['success'], '位置更新成功')); } else { $this->ajaxReturn(callBack(statusCode()['error'], '位置更新失败')); } } /** * 批量更新位置 * @return void */ public function batchUpdateLocation() { $param = I('post.'); // 验证骑手身份 $riderInfo = $this->checkRiderAuth(); if ($riderInfo['status'] != 1) { $this->ajaxReturn($riderInfo); } $riderId = $riderInfo['data']['id']; if (empty($param['locations']) || !is_array($param['locations'])) { $this->ajaxReturn(callBack(statusCode()['error'], '位置数据不能为空')); } $riderLocationModel = D('RiderLocation'); $locationDataList = []; $latestLocation = null; $latestTime = 0; foreach ($param['locations'] as $location) { if (empty($location['lat']) || empty($location['lng']) || empty($location['record_time'])) { continue; } $recordTime = strtotime($location['record_time']); if ($recordTime > $latestTime) { $latestTime = $recordTime; $latestLocation = $location; } $locationDataList[] = [ 'rider_id' => $riderId, 'lat' => $location['lat'], 'lng' => $location['lng'], 'accuracy' => $location['accuracy'] ?? 0, 'speed' => $location['speed'] ?? 0, 'bearing' => $location['bearing'] ?? 0, 'altitude' => $location['altitude'] ?? 0, 'record_time' => $location['record_time'], 'add_time' => date('Y-m-d H:i:s') ]; } if (empty($locationDataList)) { $this->ajaxReturn(callBack(statusCode()['error'], '没有有效的位置数据')); } // 批量插入位置记录 $result = $riderLocationModel->addLocationBatch($locationDataList); if ($result) { // 更新最新位置到骑手信息表 if ($latestLocation) { $riderInfoModel = D('RiderInfo'); $riderInfoModel->updateRider(['id' => $riderId], [ 'last_location_lat' => $latestLocation['lat'], 'last_location_lng' => $latestLocation['lng'], 'last_location_time' => $latestLocation['record_time'], 'updated_at' => date('Y-m-d H:i:s') ]); } $this->ajaxReturn(callBack(statusCode()['success'], '批量位置更新成功', [ 'updated_count' => count($locationDataList) ])); } else { $this->ajaxReturn(callBack(statusCode()['error'], '批量位置更新失败')); } } /** * 获取骑手位置轨迹 * @return void */ public function getLocationTrack() { $param = I('post.'); // 验证骑手身份 $riderInfo = $this->checkRiderAuth(); if ($riderInfo['status'] != 1) { $this->ajaxReturn($riderInfo); } $riderId = $riderInfo['data']['id']; $startTime = $param['start_time'] ?? date('Y-m-d 00:00:00'); $endTime = $param['end_time'] ?? date('Y-m-d 23:59:59'); $limit = isset($param['limit']) ? intval($param['limit']) : 100; $riderLocationModel = D('RiderLocation'); $track = $riderLocationModel->getLocationTrack($riderId, $startTime, $endTime, $limit); // 计算轨迹统计信息 $stats = $riderLocationModel->getRiderActivityStats($riderId, date('Y-m-d', strtotime($startTime))); $this->ajaxReturn(callBack(statusCode()['success'], '获取位置轨迹成功', [ 'track' => $track, 'stats' => $stats ])); } /** * 获取附近的骑手 * @return void */ public function getNearbyRiders() { $param = I('post.'); // 验证骑手身份 $riderInfo = $this->checkRiderAuth(); if ($riderInfo['status'] != 1) { $this->ajaxReturn($riderInfo); } if (empty($param['lat']) || empty($param['lng'])) { $this->ajaxReturn(callBack(statusCode()['error'], '位置信息不能为空')); } $radius = isset($param['radius']) ? floatval($param['radius']) : 5; // 默认5公里 $riderLocationModel = D('RiderLocation'); // 获取附近骑手 $nearbyRiders = $riderLocationModel->getRidersInRange($param['lat'], $param['lng'], $radius); // 获取骑手详细信息 if (!empty($nearbyRiders)) { $riderIds = array_column($nearbyRiders, 'rider_id'); $riderInfoModel = D('RiderInfo'); $riderInfos = $riderInfoModel->getList(['id' => ['in', $riderIds]], 'id,real_name,phone,work_status,rating'); $riderInfoMap = []; foreach ($riderInfos as $info) { $riderInfoMap[$info['id']] = $info; } // 合并骑手信息 foreach ($nearbyRiders as &$rider) { if (isset($riderInfoMap[$rider['rider_id']])) { $rider = array_merge($rider, $riderInfoMap[$rider['rider_id']]); } } } $this->ajaxReturn(callBack(statusCode()['success'], '获取附近骑手成功', [ 'riders' => $nearbyRiders, 'total' => count($nearbyRiders), 'radius' => $radius ])); } /** * 获取区域骑手密度 * @return void */ public function getRiderDensity() { $param = I('post.'); if (empty($param['lat']) || empty($param['lng'])) { $this->ajaxReturn(callBack(statusCode()['error'], '位置信息不能为空')); } $radius = isset($param['radius']) ? floatval($param['radius']) : 3; // 默认3公里 $timeRange = isset($param['time_range']) ? intval($param['time_range']) : 30; // 默认30分钟 $riderLocationModel = D('RiderLocation'); $density = $riderLocationModel->getRiderDensity($param['lat'], $param['lng'], $radius, $timeRange); $this->ajaxReturn(callBack(statusCode()['success'], '获取骑手密度成功', $density)); } /** * 预测到达时间 * @return void */ public function predictArrivalTime() { $param = I('post.'); // 验证骑手身份 $riderInfo = $this->checkRiderAuth(); if ($riderInfo['status'] != 1) { $this->ajaxReturn($riderInfo); } $riderId = $riderInfo['data']['id']; if (empty($param['dest_lat']) || empty($param['dest_lng'])) { $this->ajaxReturn(callBack(statusCode()['error'], '目标位置不能为空')); } $avgSpeed = isset($param['avg_speed']) ? floatval($param['avg_speed']) : 20; // 默认20公里/小时 $riderLocationModel = D('RiderLocation'); $prediction = $riderLocationModel->predictArrivalTime($riderId, $param['dest_lat'], $param['dest_lng'], $avgSpeed); $this->ajaxReturn(callBack(statusCode()['success'], '预测到达时间成功', $prediction)); } /** * 开始位置追踪 * @return void */ public function startTracking() { $param = I('post.'); // 验证骑手身份 $riderInfo = $this->checkRiderAuth(); if ($riderInfo['status'] != 1) { $this->ajaxReturn($riderInfo); } $riderId = $riderInfo['data']['id']; $riderInfoModel = D('RiderInfo'); // 更新追踪状态 $result = $riderInfoModel->updateRider(['id' => $riderId], [ 'location_tracking' => 1, 'tracking_start_time' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ]); if ($result) { $this->ajaxReturn(callBack(statusCode()['success'], '开始位置追踪成功')); } else { $this->ajaxReturn(callBack(statusCode()['error'], '开始位置追踪失败')); } } /** * 停止位置追踪 * @return void */ public function stopTracking() { $param = I('post.'); // 验证骑手身份 $riderInfo = $this->checkRiderAuth(); if ($riderInfo['status'] != 1) { $this->ajaxReturn($riderInfo); } $riderId = $riderInfo['data']['id']; $riderInfoModel = D('RiderInfo'); // 更新追踪状态 $result = $riderInfoModel->updateRider(['id' => $riderId], [ 'location_tracking' => 0, 'tracking_end_time' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ]); if ($result) { $this->ajaxReturn(callBack(statusCode()['success'], '停止位置追踪成功')); } else { $this->ajaxReturn(callBack(statusCode()['error'], '停止位置追踪失败')); } } /** * 验证骑手身份 * @return array */ private function checkRiderAuth() { $token = I('post.token', ''); if (empty($token)) { return callBack(statusCode()['error'], '请先登录'); } $riderTokenModel = D('RiderToken'); $tokenInfo = $riderTokenModel->getFind(['token' => $token]); if (empty($tokenInfo)) { return callBack(statusCode()['error'], '登录已过期,请重新登录'); } // 检查token是否过期 if (strtotime($tokenInfo['expire_time']) < time()) { return callBack(statusCode()['error'], '登录已过期,请重新登录'); } // 获取骑手信息 $riderInfoModel = D('RiderInfo'); $riderInfo = $riderInfoModel->getFind(['id' => $tokenInfo['rider_id']]); if (empty($riderInfo)) { return callBack(statusCode()['error'], '骑手信息不存在'); } if ($riderInfo['status'] != 1) { return callBack(statusCode()['error'], '账号已被禁用'); } return callBack(statusCode()['success'], '验证成功', $riderInfo); } /** * 检查订单位置更新 * @param int $riderId 骑手ID * @param float $lat 纬度 * @param float $lng 经度 * @return void */ private function checkOrderLocationUpdate($riderId, $lat, $lng) { // 获取骑手当前进行中的订单(统一集合) import('@.Common.Business.OrderStatusUtil'); $inProgress = \Common\Business\OrderStatusUtil::getInProgressStatuses(); $riderOrderModel = D('RiderOrder'); $activeOrders = $riderOrderModel->getList([ 'rider_id' => $riderId, 'order_status' => ['in', implode(',', $inProgress)] ]); if (empty($activeOrders)) { return; } $riderLocationModel = D('RiderLocation'); foreach ($activeOrders as $order) { // 检查是否到达门店 if ($order['order_status'] == 1 && !empty($order['store_lat']) && !empty($order['store_lng'])) { $storeDistance = $riderLocationModel->calculateDistance($lat, $lng, $order['store_lat'], $order['store_lng']); // 如果距离门店小于100米,自动更新为已取货状态 if ($storeDistance <= 0.1) { $riderOrderModel->updateOrder(['id' => $order['id']], [ 'order_status' => 2, 'pickup_time' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ]); // 同步状态 $orderStatusSync = new \Common\Business\OrderStatusSync(); $orderStatusSync->syncOrderStatus([ 'rider_order_id' => $order['id'], 'bill_id' => $order['bill_id'], 'action' => 'pickup', 'extra_data' => [] ]); } } // 检查是否到达客户地址 if ($order['order_status'] == 3 && !empty($order['customer_lat']) && !empty($order['customer_lng'])) { $customerDistance = $riderLocationModel->calculateDistance($lat, $lng, $order['customer_lat'], $order['customer_lng']); // 如果距离客户小于50米,发送到达提醒 if ($customerDistance <= 0.05) { // 这里可以发送推送通知给客户 $this->sendArrivalNotification($order['bill_id'], $riderId); } } } } /** * 发送到达通知 * @param int $billId 订单ID * @param int $riderId 骑手ID * @return void */ private function sendArrivalNotification($billId, $riderId) { // 这里可以实现推送通知功能 // 暂时只记录日志 $orderSyncLogModel = D('OrderSyncLog'); $orderSyncLogModel->addLog([ 'rider_order_id' => 0, 'bill_id' => $billId, 'action' => 'arrival_notification', 'description' => "骑手{$riderId}即将到达", 'sync_time' => date('Y-m-d H:i:s'), 'add_time' => date('Y-m-d H:i:s') ]); } }