文件
mashangyoujiu/Application/Common/Business/OrderAutoDispatch.class.php

811 行
26 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
namespace Common\Business;
use Common\Business\RiderOrderDispatch;
/**
* 订单自动分配业务类
* Class OrderAutoDispatch
* @package Common\Business
*/
class OrderAutoDispatch
{
/**
* 处理支付成功后的自动分配
* @param int $billId 订单ID
* @param array $orderInfo 订单信息
* @return array
*/
public function handlePaymentSuccess($billId, $orderInfo = [])
{
try {
// 检查订单是否需要配送
if (!$this->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());
}
}
}
?>