RetryFailedOrderJob.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <?php
  2. namespace App\Module\Transfer\Jobs;
  3. use App\Module\Transfer\Models\TransferOrder;
  4. use App\Module\Transfer\Enums\TransferStatus;
  5. use App\Module\Transfer\Logics\OrderLogic;
  6. use UCore\Queue\QueueJob;
  7. use Illuminate\Support\Facades\Log;
  8. /**
  9. * 重试失败订单任务
  10. */
  11. class RetryFailedOrderJob extends QueueJob
  12. {
  13. /**
  14. * 任务最大尝试次数
  15. *
  16. * @var int
  17. */
  18. public $tries = 3;
  19. /**
  20. * 任务超时时间(秒)
  21. *
  22. * @var int
  23. */
  24. public $timeout = 300;
  25. /**
  26. * 订单ID
  27. *
  28. * @var int
  29. */
  30. protected int $orderId;
  31. /**
  32. * 重试原因
  33. *
  34. * @var string|null
  35. */
  36. protected ?string $retryReason;
  37. /**
  38. * 创建任务实例
  39. *
  40. * @param int $orderId 订单ID
  41. * @param string|null $retryReason 重试原因
  42. */
  43. public function __construct(int $orderId, ?string $retryReason = null)
  44. {
  45. $this->orderId = $orderId;
  46. $this->retryReason = $retryReason;
  47. parent::__construct(['order_id' => $orderId, 'retry_reason' => $retryReason]);
  48. }
  49. /**
  50. * 执行任务
  51. *
  52. * @return bool
  53. */
  54. public function run(): bool
  55. {
  56. try {
  57. // 获取订单
  58. $order = TransferOrder::find($this->orderId);
  59. if (!$order) {
  60. Log::warning('Retry failed order job: Order not found', [
  61. 'order_id' => $this->orderId
  62. ]);
  63. return false;
  64. }
  65. // 检查订单状态是否可以重试
  66. if (!$order->canRetry()) {
  67. Log::info('Retry failed order job: Order cannot be retried', [
  68. 'order_id' => $this->orderId,
  69. 'status' => $order->status->value
  70. ]);
  71. return false;
  72. }
  73. Log::info('Retrying failed transfer order', [
  74. 'order_id' => $this->orderId,
  75. 'type' => $order->type->value,
  76. 'retry_reason' => $this->retryReason,
  77. 'attempt' => $this->attempts()
  78. ]);
  79. // 重置订单状态为已创建
  80. $order->updateStatus(TransferStatus::CREATED, '重试处理');
  81. // 根据订单类型进行重试处理
  82. $success = false;
  83. if ($order->isTransferOut()) {
  84. $success = OrderLogic::processTransferOut($order);
  85. } elseif ($order->isTransferIn()) {
  86. // 转入订单通常不需要重试,因为资金已经到账
  87. // 但可以重试回调发送
  88. $success = $this->retryTransferInCallback($order);
  89. }
  90. if ($success) {
  91. Log::info('Transfer order retry successful', [
  92. 'order_id' => $this->orderId,
  93. 'type' => $order->type->value
  94. ]);
  95. } else {
  96. Log::warning('Transfer order retry failed', [
  97. 'order_id' => $this->orderId,
  98. 'type' => $order->type->value,
  99. 'attempt' => $this->attempts()
  100. ]);
  101. // 如果是最后一次尝试,标记为最终失败
  102. if ($this->attempts() >= $this->tries) {
  103. $order->updateStatus(TransferStatus::FAILED, '重试次数已达上限');
  104. }
  105. }
  106. } catch (\Exception $e) {
  107. Log::error('Retry failed order job error', [
  108. 'order_id' => $this->orderId,
  109. 'error' => $e->getMessage(),
  110. 'trace' => $e->getTraceAsString()
  111. ]);
  112. // 重新抛出异常以触发重试机制
  113. throw $e;
  114. }
  115. return true;
  116. }
  117. /**
  118. * 获取任务数据
  119. *
  120. * @return array
  121. */
  122. public function payload()
  123. {
  124. return [
  125. 'order_id' => $this->orderId,
  126. 'retry_reason' => $this->retryReason
  127. ];
  128. }
  129. /**
  130. * 重试转入订单的回调发送
  131. *
  132. * @param TransferOrder $order
  133. * @return bool
  134. */
  135. protected function retryTransferInCallback(TransferOrder $order): bool
  136. {
  137. try {
  138. // 如果应用支持回调,则发送回调
  139. if ($order->transferApp->supportsCallback()) {
  140. SendCallbackJob::dispatch($order);
  141. return true;
  142. }
  143. // 如果不支持回调,直接标记为完成
  144. $order->updateStatus(TransferStatus::COMPLETED);
  145. return true;
  146. } catch (\Exception $e) {
  147. Log::error('Retry transfer in callback failed', [
  148. 'order_id' => $order->id,
  149. 'error' => $e->getMessage()
  150. ]);
  151. return false;
  152. }
  153. }
  154. /**
  155. * 任务失败处理
  156. *
  157. * @param \Throwable $exception
  158. * @return void
  159. */
  160. public function failed(\Throwable $exception): void
  161. {
  162. Log::error('Retry failed order job finally failed', [
  163. 'order_id' => $this->orderId,
  164. 'error' => $exception->getMessage(),
  165. 'attempts' => $this->attempts()
  166. ]);
  167. // 尝试更新订单状态为最终失败
  168. try {
  169. $order = TransferOrder::find($this->orderId);
  170. if ($order && $order->canRetry()) {
  171. $order->updateStatus(TransferStatus::FAILED, '重试任务失败: ' . $exception->getMessage());
  172. }
  173. } catch (\Exception $e) {
  174. Log::error('Failed to update order status after retry job failed', [
  175. 'order_id' => $this->orderId,
  176. 'error' => $e->getMessage()
  177. ]);
  178. }
  179. }
  180. /**
  181. * 计算重试延迟时间
  182. *
  183. * @return int
  184. */
  185. public function backoff(): int
  186. {
  187. // 指数退避:第1次重试延迟60秒,第2次延迟120秒,第3次延迟240秒
  188. return 60 * pow(2, $this->attempts() - 1);
  189. }
  190. /**
  191. * 获取任务标识
  192. *
  193. * @return string
  194. */
  195. public function getJobIdentifier(): string
  196. {
  197. return "retry_failed_order_{$this->orderId}";
  198. }
  199. }