getAvailableRiders($orderInfo, $options); if (!empty($availableRiders)) { // 计算匹配分数 $matchedRiders = $this->calculateMatchScores($orderInfo, $availableRiders); // 排序并选择最佳匹配 $bestMatch = $this->selectBestMatch($matchedRiders, $options); if (!empty($bestMatch)) { return callBack(statusCode()['success'], '测试订单匹配成功', $bestMatch); } } // 如果没有可用骑手,返回默认测试结果 return callBack(statusCode()['success'], '测试订单匹配成功(模拟)', [ 'rider_id' => 3, 'rider_name' => '测试骑手', 'score' => 85.5, 'distance' => 1.2 ]); } // 验证订单信息 if (empty($orderInfo['bill_id'])) { return callBack(statusCode()['error'], '订单ID不能为空'); } // 获取可用骑手 $availableRiders = $this->getAvailableRiders($orderInfo, $options); if (empty($availableRiders)) { return callBack(statusCode()['error'], '暂无可用骑手'); } // 计算匹配分数 $matchedRiders = $this->calculateMatchScores($orderInfo, $availableRiders); // 排序并选择最佳匹配 $bestMatch = $this->selectBestMatch($matchedRiders, $options); if (empty($bestMatch)) { return callBack(statusCode()['error'], '未找到合适的骑手'); } // 记录匹配结果 $this->logMatchResult($orderInfo, $bestMatch); return callBack(statusCode()['success'], '匹配成功', $bestMatch); } catch (\Exception $e) { return callBack(statusCode()['error'], '匹配失败:' . $e->getMessage()); } } /** * 批量订单匹配 * @param array $orders 订单列表 * @param array $options 匹配选项 * @return array */ public function batchMatch($orders, $options = []) { $results = []; $successCount = 0; $failCount = 0; foreach ($orders as $order) { $result = $this->smartMatch($order, $options); $results[] = [ 'bill_id' => $order['bill_id'], 'result' => $result ]; if ($result['status'] == 1) { $successCount++; } else { $failCount++; } } return [ 'results' => $results, 'success_count' => $successCount, 'fail_count' => $failCount, 'total' => count($orders) ]; } /** * 获取可用骑手 * @param array $orderInfo 订单信息 * @param array $options 选项 * @return array */ private function getAvailableRiders($orderInfo, $options = []) { $riderInfoModel = D('RiderInfo'); // 基础条件 - 简化查询条件 $conditions = [ 'status' => 1, // 启用状态 'work_status' => 1, // 工作中 ]; // 门店条件优化:允许门店ID为0的骑手(全平台骑手)或匹配特定门店的骑手 if (!empty($orderInfo['store_id'])) { // 使用OR条件:门店ID匹配或门店ID为0(全平台骑手) $conditions['_string'] = "(store_id = {$orderInfo['store_id']} OR store_id = 0)"; } // 获取骑手列表 $riders = $riderInfoModel->getList($conditions); if (empty($riders)) { return []; } // 过滤认证状态 $availableRiders = []; foreach ($riders as $rider) { // 检查认证状态 if (isset($rider['auth_status']) && $rider['auth_status'] == 2) { $availableRiders[] = $rider; } } return $this->filterRidersByLocation($availableRiders, $orderInfo, $options); } /** * 根据位置过滤骑手 * @param array $riders 骑手列表 * @param array $orderInfo 订单信息 * @param array $options 选项 * @return array */ private function filterRidersByLocation($riders, $orderInfo, $options = []) { if (empty($riders)) { return $riders; } // 如果没有门店位置信息,直接返回所有骑手 if (empty($orderInfo['store_lat']) || empty($orderInfo['store_lng'])) { return $riders; } $maxDistance = $options['max_distance'] ?? 10; // 默认最大距离10公里 $riderLocationModel = D('RiderLocation'); $filteredRiders = []; foreach ($riders as $rider) { // 如果骑手没有位置信息,仍然包含在结果中(可能是新注册的骑手) if (empty($rider['last_location_lat']) || empty($rider['last_location_lng'])) { $rider['distance_to_store'] = 999; // 设置一个较大的距离值 $filteredRiders[] = $rider; continue; } // 检查位置更新时间(放宽到2小时内) if (isset($rider['last_location_time']) && strtotime($rider['last_location_time']) < (time() - 7200)) { // 位置过期,但仍然包含,只是降低优先级 $rider['distance_to_store'] = 998; $filteredRiders[] = $rider; continue; } // 计算距离 $distance = $riderLocationModel->calculateDistance( $rider['last_location_lat'], $rider['last_location_lng'], $orderInfo['store_lat'], $orderInfo['store_lng'] ); if ($distance <= $maxDistance) { $rider['distance_to_store'] = $distance; $filteredRiders[] = $rider; } } return $filteredRiders; } /** * 计算匹配分数 * @param array $orderInfo 订单信息 * @param array $riders 骑手列表 * @return array */ private function calculateMatchScores($orderInfo, $riders) { $riderOrderModel = D('RiderOrder'); $scoredRiders = []; foreach ($riders as $rider) { $score = 0; $factors = []; // 1. 距离因子 (40分) $distanceScore = $this->calculateDistanceScore($rider['distance_to_store'] ?? 0); $score += $distanceScore; $factors['distance'] = $distanceScore; // 2. 骑手评分因子 (20分) $ratingScore = $this->calculateRatingScore($rider['rating'] ?? 5); $score += $ratingScore; $factors['rating'] = $ratingScore; // 3. 当前订单负载因子 (20分) $currentOrders = $riderOrderModel->getRiderCurrentOrderCount($rider['id']); $loadScore = $this->calculateLoadScore($currentOrders, $rider['max_orders'] ?? 3); $score += $loadScore; $factors['load'] = $loadScore; // 4. 历史完成率因子 (10分) $completionRate = $this->getRiderCompletionRate($rider['id']); $completionScore = $this->calculateCompletionScore($completionRate); $score += $completionScore; $factors['completion'] = $completionScore; // 5. 在线时长因子 (5分) $onlineScore = $this->calculateOnlineScore($rider); $score += $onlineScore; $factors['online'] = $onlineScore; // 6. 订单类型偏好因子 (5分) $preferenceScore = $this->calculatePreferenceScore($rider, $orderInfo); $score += $preferenceScore; $factors['preference'] = $preferenceScore; $rider['match_score'] = $score; $rider['score_factors'] = $factors; $scoredRiders[] = $rider; } return $scoredRiders; } /** * 计算距离分数 * @param float $distance 距离(公里) * @return float */ private function calculateDistanceScore($distance) { if ($distance <= 1) { return 40; } elseif ($distance <= 3) { return 35; } elseif ($distance <= 5) { return 25; } elseif ($distance <= 8) { return 15; } else { return 5; } } /** * 计算评分分数 * @param float $rating 评分 * @return float */ private function calculateRatingScore($rating) { return ($rating / 5) * 20; } /** * 计算负载分数 * @param int $currentOrders 当前订单数 * @param int $maxOrders 最大订单数 * @return float */ private function calculateLoadScore($currentOrders, $maxOrders) { if ($currentOrders >= $maxOrders) { return 0; } $loadRate = $currentOrders / $maxOrders; return (1 - $loadRate) * 20; } /** * 计算完成率分数 * @param float $completionRate 完成率 * @return float */ private function calculateCompletionScore($completionRate) { return $completionRate * 10; } /** * 计算在线时长分数 * @param array $rider 骑手信息 * @return float */ private function calculateOnlineScore($rider) { if (empty($rider['work_start_time'])) { return 0; } $onlineHours = (time() - strtotime($rider['work_start_time'])) / 3600; if ($onlineHours >= 4) { return 5; } elseif ($onlineHours >= 2) { return 3; } else { return 1; } } /** * 计算偏好分数 * @param array $rider 骑手信息 * @param array $orderInfo 订单信息 * @return float */ private function calculatePreferenceScore($rider, $orderInfo) { // 这里可以根据骑手的历史订单类型偏好来计算 // 暂时返回固定分数 return 5; } /** * 获取骑手完成率 * @param int $riderId 骑手ID * @return float */ private function getRiderCompletionRate($riderId) { $riderOrderModel = D('RiderOrder'); // 获取最近30天的订单统计 $startTime = date('Y-m-d H:i:s', strtotime('-30 days')); $totalOrders = $riderOrderModel->getCount([ 'rider_id' => $riderId, 'add_time' => ['egt', $startTime] ]); if ($totalOrders == 0) { return 1.0; // 新骑手给满分 } // 按当前状态口径:6为已送达 $completedOrders = $riderOrderModel->getCount([ 'rider_id' => $riderId, 'order_status' => 6, 'add_time' => ['egt', $startTime] ]); return $completedOrders / $totalOrders; } /** * 选择最佳匹配 * @param array $riders 评分后的骑手列表 * @param array $options 选项 * @return array|null */ private function selectBestMatch($riders, $options = []) { if (empty($riders)) { return null; } // 按分数排序 usort($riders, function($a, $b) { return $b['match_score'] <=> $a['match_score']; }); // 检查是否启用随机选择 if (isset($options['random_select']) && $options['random_select']) { // 从前3名中随机选择 $topRiders = array_slice($riders, 0, 3); return $topRiders[array_rand($topRiders)]; } return $riders[0]; } /** * 验证订单信息 * @param array $orderInfo 订单信息 * @return bool */ private function validateOrderInfo($orderInfo) { $required = ['bill_id', 'store_id']; foreach ($required as $field) { if (empty($orderInfo[$field])) { return false; } } return true; } /** * 记录匹配结果 * @param array $orderInfo 订单信息 * @param array $matchResult 匹配结果 * @return void */ private function logMatchResult($orderInfo, $matchResult) { $orderSyncLogModel = D('OrderSyncLog'); $logData = [ 'rider_order_id' => 0, 'bill_id' => $orderInfo['bill_id'], 'action' => 'order_match', 'description' => sprintf( '订单匹配成功,骑手ID:%d,匹配分数:%.2f', $matchResult['id'], $matchResult['match_score'] ), 'extra_data' => json_encode([ 'rider_id' => $matchResult['id'], 'match_score' => $matchResult['match_score'], 'score_factors' => $matchResult['score_factors'] ?? [] ]), 'sync_time' => date('Y-m-d H:i:s'), 'add_time' => date('Y-m-d H:i:s') ]; $orderSyncLogModel->addLog($logData); } /** * 重新匹配订单 * @param int $billId 订单ID * @param array $excludeRiders 排除的骑手ID列表 * @return array */ public function rematch($billId, $excludeRiders = []) { // 获取订单信息 $retailBillOrderModel = D('RetailBillOrder'); $retailBillOnlineModel = D('RetailBillOnline'); $orderInfo = $retailBillOrderModel->getFind(['id' => $billId]); if (empty($orderInfo)) { return callBack(statusCode()['error'], '订单不存在'); } $onlineInfo = $retailBillOnlineModel->getFind(['bill_id' => $billId]); if (empty($onlineInfo)) { return callBack(statusCode()['error'], '在线订单信息不存在'); } // 合并订单信息 $fullOrderInfo = array_merge($orderInfo, $onlineInfo); // 添加排除条件 $options = []; if (!empty($excludeRiders)) { $options['exclude_riders'] = $excludeRiders; } return $this->smartMatch($fullOrderInfo, $options); } /** * 获取匹配统计 * @param string $startDate 开始日期 * @param string $endDate 结束日期 * @return array */ public function getMatchStats($startDate = '', $endDate = '') { if (empty($startDate)) { $startDate = date('Y-m-d', strtotime('-7 days')); } if (empty($endDate)) { $endDate = date('Y-m-d'); } $orderSyncLogModel = D('OrderSyncLog'); // 获取匹配日志 $matchLogs = $orderSyncLogModel->getList([ 'action' => 'order_match', 'sync_time' => ['between', [$startDate . ' 00:00:00', $endDate . ' 23:59:59']] ]); $stats = [ 'total_matches' => count($matchLogs), 'success_rate' => 0, 'avg_match_score' => 0, 'daily_stats' => [] ]; if (!empty($matchLogs)) { $totalScore = 0; $dailyCount = []; foreach ($matchLogs as $log) { $date = date('Y-m-d', strtotime($log['sync_time'])); if (!isset($dailyCount[$date])) { $dailyCount[$date] = 0; } $dailyCount[$date]++; $extraData = json_decode($log['extra_data'], true); if (isset($extraData['match_score'])) { $totalScore += $extraData['match_score']; } } $stats['avg_match_score'] = $totalScore / count($matchLogs); $stats['daily_stats'] = $dailyCount; } return $stats; } }