SendCallbackJob.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <?php
  2. namespace App\Module\Transfer\Jobs;
  3. use App\Module\Transfer\Logics\CallbackLogic;
  4. use App\Module\Transfer\Models\TransferOrder;
  5. use App\Module\Transfer\Enums\TransferStatus;
  6. use Illuminate\Bus\Queueable;
  7. use Illuminate\Contracts\Queue\ShouldQueue;
  8. use Illuminate\Foundation\Bus\Dispatchable;
  9. use Illuminate\Queue\InteractsWithQueue;
  10. use Illuminate\Queue\SerializesModels;
  11. use Illuminate\Support\Facades\Log;
  12. /**
  13. * 发送回调通知任务
  14. */
  15. class SendCallbackJob implements ShouldQueue
  16. {
  17. use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
  18. private int $orderId;
  19. private int $retryCount;
  20. private int $maxRetries;
  21. /**
  22. * 任务最大尝试次数
  23. */
  24. public int $tries = 1; // 重试逻辑由业务控制
  25. /**
  26. * 任务超时时间(秒)
  27. */
  28. public int $timeout = 60;
  29. /**
  30. * 创建任务实例
  31. */
  32. public function __construct(int $orderId, int $retryCount = 0, int $maxRetries = 3)
  33. {
  34. $this->orderId = $orderId;
  35. $this->retryCount = $retryCount;
  36. $this->maxRetries = $maxRetries;
  37. // 设置队列名称
  38. $this->onQueue('transfer_callback');
  39. }
  40. /**
  41. * 执行任务
  42. */
  43. public function handle(): void
  44. {
  45. try {
  46. $order = TransferOrder::find($this->orderId);
  47. if (!$order) {
  48. Log::warning('Transfer order not found for callback', [
  49. 'order_id' => $this->orderId
  50. ]);
  51. return;
  52. }
  53. // 检查订单状态
  54. if ($order->isFinalStatus()) {
  55. Log::info('Transfer order already in final status, skipping callback', [
  56. 'order_id' => $order->id,
  57. 'status' => $order->status->value
  58. ]);
  59. return;
  60. }
  61. // 检查是否支持回调
  62. if (!$order->transferApp->supportsCallback()) {
  63. Log::info('Transfer app does not support callback, completing order', [
  64. 'order_id' => $order->id,
  65. 'app_id' => $order->transfer_app_id
  66. ]);
  67. $order->updateStatus(TransferStatus::COMPLETED);
  68. return;
  69. }
  70. Log::info('Sending transfer callback', [
  71. 'order_id' => $order->id,
  72. 'retry_count' => $this->retryCount,
  73. 'callback_url' => $order->transferApp->order_callback_url
  74. ]);
  75. // 发送回调
  76. $success = CallbackLogic::sendCallback($order);
  77. if ($success) {
  78. Log::info('Transfer callback sent successfully', [
  79. 'order_id' => $order->id,
  80. 'retry_count' => $this->retryCount
  81. ]);
  82. } else {
  83. $this->handleCallbackFailure($order);
  84. }
  85. } catch (\Exception $e) {
  86. Log::error('Transfer callback job failed', [
  87. 'order_id' => $this->orderId,
  88. 'retry_count' => $this->retryCount,
  89. 'error' => $e->getMessage(),
  90. 'trace' => $e->getTraceAsString()
  91. ]);
  92. // 处理回调失败
  93. $order = TransferOrder::find($this->orderId);
  94. if ($order) {
  95. $this->handleCallbackFailure($order);
  96. }
  97. }
  98. }
  99. /**
  100. * 处理回调失败
  101. */
  102. private function handleCallbackFailure(TransferOrder $order): void
  103. {
  104. if ($this->retryCount < $this->maxRetries) {
  105. // 安排重试
  106. $nextRetryCount = $this->retryCount + 1;
  107. $delayMinutes = $this->calculateRetryDelay($nextRetryCount);
  108. Log::info('Scheduling callback retry', [
  109. 'order_id' => $order->id,
  110. 'retry_count' => $nextRetryCount,
  111. 'delay_minutes' => $delayMinutes
  112. ]);
  113. // 更新订单的重试计数
  114. $callbackData = $order->callback_data;
  115. $callbackData['retry_count'] = $nextRetryCount;
  116. $callbackData['last_retry_at'] = now()->toDateTimeString();
  117. $order->update(['callback_data' => $callbackData]);
  118. // 调度重试任务
  119. self::dispatch($order->id, $nextRetryCount, $this->maxRetries)
  120. ->delay(now()->addMinutes($delayMinutes));
  121. } else {
  122. // 达到最大重试次数,记录失败
  123. Log::error('Transfer callback max retries reached', [
  124. 'order_id' => $order->id,
  125. 'retry_count' => $this->retryCount,
  126. 'max_retries' => $this->maxRetries
  127. ]);
  128. // 更新订单状态为失败
  129. $order->updateStatus(TransferStatus::FAILED, '回调发送失败,已达最大重试次数');
  130. }
  131. }
  132. /**
  133. * 计算重试延迟时间(指数退避)
  134. */
  135. private function calculateRetryDelay(int $retryCount): int
  136. {
  137. // 指数退避:1分钟、2分钟、4分钟
  138. return min(pow(2, $retryCount - 1), 30); // 最大30分钟
  139. }
  140. /**
  141. * 任务失败时的处理
  142. */
  143. public function failed(\Throwable $exception): void
  144. {
  145. Log::error('Transfer callback job failed permanently', [
  146. 'order_id' => $this->orderId,
  147. 'retry_count' => $this->retryCount,
  148. 'error' => $exception->getMessage()
  149. ]);
  150. // 更新订单状态为失败
  151. $order = TransferOrder::find($this->orderId);
  152. if ($order && !$order->isFinalStatus()) {
  153. $order->updateStatus(TransferStatus::FAILED, '回调任务失败: ' . $exception->getMessage());
  154. }
  155. }
  156. /**
  157. * 获取任务的唯一ID
  158. */
  159. public function uniqueId(): string
  160. {
  161. return "transfer_callback_{$this->orderId}_{$this->retryCount}";
  162. }
  163. /**
  164. * 静态方法:调度回调任务
  165. */
  166. public static function schedule(int $orderId, int $delayMinutes = 0): void
  167. {
  168. $job = new self($orderId);
  169. if ($delayMinutes > 0) {
  170. $job->delay(now()->addMinutes($delayMinutes));
  171. }
  172. dispatch($job);
  173. }
  174. /**
  175. * 静态方法:立即发送回调
  176. */
  177. public static function sendNow(int $orderId): void
  178. {
  179. dispatch_sync(new self($orderId));
  180. }
  181. /**
  182. * 静态方法:批量调度回调任务
  183. */
  184. public static function scheduleBatch(array $orderIds, int $delayMinutes = 0): void
  185. {
  186. foreach ($orderIds as $orderId) {
  187. self::schedule($orderId, $delayMinutes);
  188. }
  189. }
  190. }