needDelivery($billId, $orderInfo)) { return ['status' => 0, 'msg' => '订单不需要配送']; } // 检查是否已经分配 if ($this->isAlreadyDispatched($billId)) { return ['status' => 0, 'msg' => '订单已分配']; } // 获取配送配置 $config = $this->getDeliveryConfig($orderInfo); // 根据配置决定分配方式 if ($config['auto_dispatch']) { return $this->autoDispatchOrder($billId, $orderInfo, $config); } else { return $this->markPendingDispatch($billId, $orderInfo); } } catch (Exception $e) { $this->logError('订单自动分配异常', [ 'bill_id' => $billId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return ['status' => 0, 'msg' => '分配失败:' . $e->getMessage()]; } } /** * 检查订单是否需要配送 * @param int $billId 订单ID * @param array $orderInfo 订单信息 * @return bool */ private function needDelivery($billId, $orderInfo = []) { if (empty($orderInfo)) { $billOrderModel = D('RetailBillOrder'); $orderInfo = $billOrderModel->getFind(['id' => $billId]); } if (empty($orderInfo)) { return false; } // 获取订单配送信息 $billOnlineModel = D('RetailBillOnline'); $onlineInfo = $billOnlineModel->getFind(['bill_id' => $billId]); if (empty($onlineInfo)) { return false; } // 检查是否为配送订单 return $onlineInfo['pickup_type'] == 2 && $orderInfo['type'] == 1 && $orderInfo['pay_state'] == 1; } /** * 检查订单是否已分配 * @param int $billId 订单ID * @return bool */ private function isAlreadyDispatched($billId) { $riderOrderModel = D('RiderOrder'); $riderOrder = $riderOrderModel->getFind([ 'bill_id' => $billId, 'order_status' => ['neq', -1] // 排除已拒绝的 ]); return !empty($riderOrder); } /** * 获取配送配置 * @param array $orderInfo 订单信息 * @return array */ private function getDeliveryConfig($orderInfo) { // 默认配置 $config = [ 'auto_dispatch' => true, 'dispatch_delay' => 0, // 延迟分配时间(分钟) 'max_distance' => 10, // 最大配送距离(公里) 'priority_riders' => [], // 优先骑手 'dispatch_strategy' => 'nearest' // 分配策略:nearest(最近), rating(评分), balanced(均衡) ]; // 根据门店获取配置 if (!empty($orderInfo['store_id'])) { $storeConfigModel = D('StoreDeliveryConfig'); $storeConfig = $storeConfigModel->getFind(['store_id' => $orderInfo['store_id']]); if (!empty($storeConfig)) { $config = array_merge($config, json_decode($storeConfig['config'], true) ?: []); } } return $config; } /** * 自动分配订单 * @param int $billId 订单ID * @param array $orderInfo 订单信息 * @param array $config 配送配置 * @return array */ private function autoDispatchOrder($billId, $orderInfo, $config) { // 如果有延迟配置,则延迟分配 if ($config['dispatch_delay'] > 0) { return $this->scheduleDelayedDispatch($billId, $orderInfo, $config); } // 立即分配 return $this->executeDispatch($billId, $orderInfo, $config); } /** * 执行订单分配 * @param int $billId 订单ID * @param array $orderInfo 订单信息 * @param array $config 配送配置 * @return array */ private function executeDispatch($billId, $orderInfo, $config) { try { $riderOrderDispatch = new RiderOrderDispatch(); $dispatchParam = [ 'bill_id' => $billId, 'merchant_id' => $orderInfo['merchant_id'], 'store_id' => $orderInfo['store_id'], 'auto_assign' => true, 'config' => $config ]; $result = $riderOrderDispatch->dispatchOrder($dispatchParam); // 记录分配结果 $this->logDispatchResult($billId, $result, 'auto'); return $result; } catch (Exception $e) { return $this->handleDispatchException($billId, $e); } } /** * 安排延迟分配 * @param int $billId 订单ID * @param array $orderInfo 订单信息 * @param array $config 配送配置 * @return array */ private function scheduleDelayedDispatch($billId, $orderInfo, $config) { // 创建延迟任务记录 $delayedDispatchModel = D('DelayedDispatch'); $delayedData = [ 'bill_id' => $billId, 'merchant_id' => $orderInfo['merchant_id'], 'store_id' => $orderInfo['store_id'], 'config' => json_encode($config), 'scheduled_time' => date('Y-m-d H:i:s', strtotime('+' . $config['dispatch_delay'] . ' minutes')), 'status' => 0, // 待执行 'created_at' => date('Y-m-d H:i:s') ]; $result = $delayedDispatchModel->add($delayedData); if ($result) { return ['status' => 1, 'msg' => '订单已安排延迟分配', 'delay_minutes' => $config['dispatch_delay']]; } else { return ['status' => 0, 'msg' => '延迟分配安排失败']; } } /** * 标记为待分配 * @param int $billId 订单ID * @param array $orderInfo 订单信息 * @return array */ private function markPendingDispatch($billId, $orderInfo) { // 创建待分配记录 $pendingDispatchModel = D('PendingDispatch'); $pendingData = [ 'bill_id' => $billId, 'merchant_id' => $orderInfo['merchant_id'], 'store_id' => $orderInfo['store_id'], 'status' => 0, // 待分配 'created_at' => date('Y-m-d H:i:s') ]; $result = $pendingDispatchModel->add($pendingData); if ($result) { return ['status' => 1, 'msg' => '订单已标记为待分配']; } else { return ['status' => 0, 'msg' => '标记待分配失败']; } } /** * 处理延迟分配任务 * @return void */ public function processScheduledDispatch() { $delayedDispatchModel = D('DelayedDispatch'); // 获取到期的延迟分配任务 $tasks = $delayedDispatchModel->getList([ 'status' => 0, 'scheduled_time' => ['elt', date('Y-m-d H:i:s')] ]); foreach ($tasks as $task) { try { // 标记任务为处理中 $delayedDispatchModel->updateData(['id' => $task['id']], ['status' => 1]); // 获取订单信息 $billOrderModel = D('RetailBillOrder'); $orderInfo = $billOrderModel->getFind(['id' => $task['bill_id']]); if (empty($orderInfo)) { $delayedDispatchModel->updateData(['id' => $task['id']], [ 'status' => -1, 'error_msg' => '订单不存在', 'processed_at' => date('Y-m-d H:i:s') ]); continue; } // 检查是否已分配 if ($this->isAlreadyDispatched($task['bill_id'])) { $delayedDispatchModel->updateData(['id' => $task['id']], [ 'status' => 2, 'error_msg' => '订单已分配', 'processed_at' => date('Y-m-d H:i:s') ]); continue; } // 执行分配 $config = json_decode($task['config'], true) ?: []; $result = $this->executeDispatch($task['bill_id'], $orderInfo, $config); // 更新任务状态 $delayedDispatchModel->updateData(['id' => $task['id']], [ 'status' => $result['status'] == 1 ? 2 : -1, 'result' => json_encode($result), 'processed_at' => date('Y-m-d H:i:s') ]); } catch (Exception $e) { // 标记任务失败 $delayedDispatchModel->updateData(['id' => $task['id']], [ 'status' => -1, 'error_msg' => $e->getMessage(), 'processed_at' => date('Y-m-d H:i:s') ]); $this->logError('延迟分配任务处理异常', [ 'task_id' => $task['id'], 'bill_id' => $task['bill_id'], 'error' => $e->getMessage() ]); } } } /** * 处理分配失败 * @param int $billId 订单ID * @param string $reason 失败原因 * @return array */ private function handleDispatchFailure($billId, $reason) { // 记录失败日志 $this->logError('订单分配失败', [ 'bill_id' => $billId, 'reason' => $reason ]); // 标记为待人工处理 return $this->markPendingDispatch($billId, []); } /** * 处理分配异常 * @param int $billId 订单ID * @param Exception $e 异常对象 * @return array */ private function handleDispatchException($billId, $e) { $this->logError('订单分配异常', [ 'bill_id' => $billId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return ['status' => 0, 'msg' => '分配异常:' . $e->getMessage()]; } /** * 记录分配结果 * @param int $billId 订单ID * @param array $result 分配结果 * @param string $type 分配类型 * @return void */ private function logDispatchResult($billId, $result, $type = 'auto') { $logModel = D('DispatchLog'); $logData = [ 'bill_id' => $billId, 'dispatch_type' => $type, 'result' => json_encode($result), 'status' => $result['status'], 'message' => $result['msg'], 'created_at' => date('Y-m-d H:i:s') ]; $logModel->add($logData); } /** * 记录错误日志 * @param string $message 错误信息 * @param array $context 上下文数据 * @return void */ private function logError($message, $context = []) { $logData = [ 'level' => 'error', 'message' => $message, 'context' => json_encode($context), 'created_at' => date('Y-m-d H:i:s') ]; // 写入系统日志 error_log(sprintf('[%s] %s %s', date('Y-m-d H:i:s'), $message, json_encode($context))); // 写入数据库日志 $logModel = D('SystemLog'); $logModel->add($logData); } } /** * 订单自动分配触发器 * 用于在订单支付完成后自动触发分配到骑手端 */ class OrderAutoDispatch { /** * 订单支付完成后的自动分配处理 * @param array $orderInfo 订单信息 * @return array */ public static function handlePaymentSuccess($orderInfo) { try { // 检查订单是否需要配送 if (!self::needDelivery($orderInfo)) { return array( 'status' => 1, 'msg' => '订单无需配送', 'data' => array('skip_reason' => 'no_delivery_needed') ); } // 检查是否已经分配过 if (self::isAlreadyDispatched($orderInfo['id'])) { return array( 'status' => 1, 'msg' => '订单已分配', 'data' => array('skip_reason' => 'already_dispatched') ); } // 获取订单配送配置 $deliveryConfig = self::getDeliveryConfig($orderInfo['merchant_id'], $orderInfo['store_id']); // 如果启用了自动分配 if ($deliveryConfig['auto_dispatch_enabled']) { return self::autoDispatchOrder($orderInfo, $deliveryConfig); } else { // 记录待分配订单 return self::markPendingDispatch($orderInfo); } } catch (Exception $e) { // 记录错误日志 self::logError('OrderAutoDispatch::handlePaymentSuccess', $e->getMessage(), $orderInfo); return array( 'status' => 0, 'msg' => '自动分配处理失败:' . $e->getMessage(), 'data' => array('error' => $e->getMessage()) ); } } /** * 检查订单是否需要配送 * @param array $orderInfo * @return bool */ private static function needDelivery($orderInfo) { // 检查订单类型和配送方式 if (isset($orderInfo['pickup_type']) && $orderInfo['pickup_type'] == 1) { // 自提订单不需要配送 return false; } if (isset($orderInfo['delivery_type']) && in_array($orderInfo['delivery_type'], [1, 5])) { // 商家配送(1)或码科配送(5)需要分配骑手 return true; } // 检查是否有配送地址 if (empty($orderInfo['delivery_address']) && empty($orderInfo['address_address'])) { return false; } return true; } /** * 检查订单是否已经分配 * @param int $orderId * @return bool */ private static function isAlreadyDispatched($orderId) { $riderOrderModel = M('rider_order'); $existingDispatch = $riderOrderModel->where(array( 'order_id' => $orderId, 'is_deleted' => 0 ))->find(); return !empty($existingDispatch); } /** * 获取配送配置 * @param int $merchantId * @param int $storeId * @return array */ private static function getDeliveryConfig($merchantId, $storeId) { $configModel = M('delivery_config'); $config = $configModel->where(array( 'merchant_id' => $merchantId, 'store_id' => $storeId, 'status' => 1 ))->find(); if (empty($config)) { // 返回默认配置 return array( 'auto_dispatch_enabled' => true, 'dispatch_delay' => 0, // 延迟分配时间(分钟) 'max_retry_times' => 3, 'retry_interval' => 5 // 重试间隔(分钟) ); } return array( 'auto_dispatch_enabled' => $config['auto_dispatch_enabled'] == 1, 'dispatch_delay' => intval($config['dispatch_delay']), 'max_retry_times' => intval($config['max_retry_times']), 'retry_interval' => intval($config['retry_interval']) ); } /** * 自动分配订单 * @param array $orderInfo * @param array $config * @return array */ private static function autoDispatchOrder($orderInfo, $config) { // 如果设置了延迟分配 if ($config['dispatch_delay'] > 0) { return self::scheduleDelayedDispatch($orderInfo, $config); } // 立即分配 return self::executeDispatch($orderInfo); } /** * 执行订单分配 * @param array $orderInfo * @return array */ private static function executeDispatch($orderInfo) { // 使用现有的订单分配类 $riderOrderDispatch = new RiderOrderDispatch(); $dispatchParams = array( 'order_id' => $orderInfo['id'], 'store_id' => $orderInfo['store_id'], 'auto_assign' => true, 'trigger_source' => 'payment_success' ); $result = $riderOrderDispatch->dispatchOrder($dispatchParams); // 记录分配日志 self::logDispatchResult($orderInfo['id'], $result, 'auto_dispatch'); return $result; } /** * 安排延迟分配 * @param array $orderInfo * @param array $config * @return array */ private static function scheduleDelayedDispatch($orderInfo, $config) { $scheduleModel = M('dispatch_schedule'); $scheduleData = array( 'order_id' => $orderInfo['id'], 'merchant_id' => $orderInfo['merchant_id'], 'store_id' => $orderInfo['store_id'], 'schedule_time' => date('Y-m-d H:i:s', time() + $config['dispatch_delay'] * 60), 'retry_times' => 0, 'max_retry_times' => $config['max_retry_times'], 'retry_interval' => $config['retry_interval'], 'status' => 0, // 0-待执行 1-已执行 2-执行失败 'create_time' => date('Y-m-d H:i:s'), 'order_data' => json_encode($orderInfo) ); $result = $scheduleModel->add($scheduleData); if ($result) { return array( 'status' => 1, 'msg' => '订单已安排延迟分配', 'data' => array( 'schedule_id' => $result, 'schedule_time' => $scheduleData['schedule_time'] ) ); } else { return array( 'status' => 0, 'msg' => '安排延迟分配失败' ); } } /** * 标记为待分配订单 * @param array $orderInfo * @return array */ private static function markPendingDispatch($orderInfo) { $pendingModel = M('pending_dispatch_order'); $pendingData = array( 'order_id' => $orderInfo['id'], 'merchant_id' => $orderInfo['merchant_id'], 'store_id' => $orderInfo['store_id'], 'order_no' => $orderInfo['order_no'], 'customer_name' => $orderInfo['customer_name'] ?? '', 'customer_phone' => $orderInfo['customer_phone'] ?? '', 'delivery_address' => $orderInfo['delivery_address'] ?? $orderInfo['address_address'] ?? '', 'order_amount' => $orderInfo['order_amount'] ?? $orderInfo['all_money'] ?? 0, 'create_time' => date('Y-m-d H:i:s'), 'status' => 0 // 0-待分配 ); $result = $pendingModel->add($pendingData); if ($result) { return array( 'status' => 1, 'msg' => '订单已加入待分配队列', 'data' => array('pending_id' => $result) ); } else { return array( 'status' => 0, 'msg' => '加入待分配队列失败' ); } } /** * 处理延迟分配任务(定时任务调用) * @return array */ public static function processScheduledDispatch() { $scheduleModel = M('dispatch_schedule'); // 获取到期的分配任务 $schedules = $scheduleModel->where(array( 'status' => 0, 'schedule_time' => array('ELT', date('Y-m-d H:i:s')) ))->select(); $processedCount = 0; $successCount = 0; $failedCount = 0; foreach ($schedules as $schedule) { $processedCount++; try { $orderInfo = json_decode($schedule['order_data'], true); $result = self::executeDispatch($orderInfo); if ($result['status'] == 1) { // 分配成功 $scheduleModel->where(array('id' => $schedule['id']))->save(array( 'status' => 1, 'execute_time' => date('Y-m-d H:i:s'), 'result' => json_encode($result) )); $successCount++; } else { // 分配失败,检查是否需要重试 self::handleDispatchFailure($schedule, $result); $failedCount++; } } catch (Exception $e) { // 处理异常 self::handleDispatchException($schedule, $e); $failedCount++; } } return array( 'status' => 1, 'msg' => '延迟分配任务处理完成', 'data' => array( 'processed' => $processedCount, 'success' => $successCount, 'failed' => $failedCount ) ); } /** * 处理分配失败 * @param array $schedule * @param array $result */ private static function handleDispatchFailure($schedule, $result) { $scheduleModel = M('dispatch_schedule'); if ($schedule['retry_times'] < $schedule['max_retry_times']) { // 安排重试 $nextRetryTime = date('Y-m-d H:i:s', time() + $schedule['retry_interval'] * 60); $scheduleModel->where(array('id' => $schedule['id']))->save(array( 'retry_times' => $schedule['retry_times'] + 1, 'schedule_time' => $nextRetryTime, 'last_error' => json_encode($result) )); } else { // 超过最大重试次数,标记为失败 $scheduleModel->where(array('id' => $schedule['id']))->save(array( 'status' => 2, 'execute_time' => date('Y-m-d H:i:s'), 'result' => json_encode($result) )); } } /** * 处理分配异常 * @param array $schedule * @param Exception $e */ private static function handleDispatchException($schedule, $e) { $scheduleModel = M('dispatch_schedule'); $errorResult = array( 'status' => 0, 'msg' => '分配异常:' . $e->getMessage(), 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ); if ($schedule['retry_times'] < $schedule['max_retry_times']) { // 安排重试 $nextRetryTime = date('Y-m-d H:i:s', time() + $schedule['retry_interval'] * 60); $scheduleModel->where(array('id' => $schedule['id']))->save(array( 'retry_times' => $schedule['retry_times'] + 1, 'schedule_time' => $nextRetryTime, 'last_error' => json_encode($errorResult) )); } else { // 超过最大重试次数,标记为失败 $scheduleModel->where(array('id' => $schedule['id']))->save(array( 'status' => 2, 'execute_time' => date('Y-m-d H:i:s'), 'result' => json_encode($errorResult) )); } // 记录错误日志 self::logError('OrderAutoDispatch::handleDispatchException', $e->getMessage(), $schedule); } /** * 记录分配结果日志 * @param int $orderId * @param array $result * @param string $type */ private static function logDispatchResult($orderId, $result, $type = 'manual') { $logModel = M('dispatch_log'); $logData = array( 'order_id' => $orderId, 'dispatch_type' => $type, 'result_status' => $result['status'], 'result_msg' => $result['msg'], 'result_data' => json_encode($result), 'create_time' => date('Y-m-d H:i:s') ); $logModel->add($logData); } /** * 记录错误日志 * @param string $method * @param string $error * @param array $context */ private static function logError($method, $error, $context = array()) { $logData = array( 'method' => $method, 'error_msg' => $error, 'context' => json_encode($context), 'create_time' => date('Y-m-d H:i:s') ); // 写入错误日志文件 $logFile = RUNTIME_PATH . 'Logs/order_auto_dispatch_' . date('Y-m-d') . '.log'; $logContent = date('Y-m-d H:i:s') . " [{$method}] {$error} Context: " . json_encode($context) . PHP_EOL; file_put_contents($logFile, $logContent, FILE_APPEND | LOCK_EX); // 同时写入数据库 try { $errorLogModel = M('dispatch_error_log'); $errorLogModel->add($logData); } catch (Exception $e) { // 如果数据库写入失败,只记录到文件 error_log("Failed to write dispatch error to database: " . $e->getMessage()); } } } ?>