TransferLogic.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <?php
  2. namespace App\Module\Transfer\Logics;
  3. use App\Module\Transfer\Enums\TransferStatus;
  4. use App\Module\Transfer\Enums\TransferType;
  5. use App\Module\Transfer\Models\TransferApp;
  6. use App\Module\Transfer\Models\TransferOrder;
  7. use App\Module\Transfer\Validations\TransferInValidation;
  8. use App\Module\Transfer\Validations\TransferOutValidation;
  9. use App\Module\Fund\Service\FundService;
  10. use Illuminate\Support\Str;
  11. /**
  12. * Transfer模块核心业务逻辑
  13. */
  14. class TransferLogic
  15. {
  16. /**
  17. * 创建转出订单
  18. *
  19. * @param array $data 订单数据
  20. * @return TransferOrder
  21. * @throws \Exception
  22. */
  23. public static function createTransferOut(array $data): TransferOrder
  24. {
  25. // 验证请求数据
  26. $validation = new TransferOutValidation($data);
  27. $validation->validated();
  28. // 获取应用配置
  29. $app = TransferApp::findOrFail($data['transfer_app_id']);
  30. if (!$app->is_enabled) {
  31. throw new \Exception('应用已禁用');
  32. }
  33. // 计算金额
  34. $outAmount = (string) $data['amount'];
  35. $amount = bcmul($outAmount, (string) $app->exchange_rate, 10);
  36. // 生成外部订单ID
  37. $outOrderId = self::generateOutOrderId('OUT');
  38. // 创建订单
  39. $order = TransferOrder::create([
  40. 'transfer_app_id' => $app->id,
  41. 'out_id' => $app->out_id2 ?? 0,
  42. 'out_order_id' => $outOrderId,
  43. 'out_user_id' => $data['out_user_id'] ?? null,
  44. 'user_id' => $data['user_id'],
  45. 'currency_id' => $app->currency_id,
  46. 'fund_id' => $app->fund_id,
  47. 'type' => TransferType::OUT,
  48. 'status' => TransferStatus::CREATED,
  49. 'out_amount' => $outAmount,
  50. 'amount' => $amount,
  51. 'exchange_rate' => $app->exchange_rate,
  52. 'callback_data' => $data['callback_data'] ?? [],
  53. 'remark' => $data['remark'] ?? null,
  54. ]);
  55. // 扣除用户资金
  56. $fundResult = FundService::decrease(
  57. $data['user_id'],
  58. $app->fund_id,
  59. $amount,
  60. "转出到{$app->title}",
  61. "transfer_out:{$order->id}"
  62. );
  63. if (!$fundResult) {
  64. $order->updateStatus(TransferStatus::FAILED, '资金扣除失败');
  65. throw new \Exception('余额不足或资金操作失败');
  66. }
  67. // 判断是否为农场内部模式
  68. if ($app->isInternalMode()) {
  69. // 内部模式直接完成
  70. $order->updateStatus(TransferStatus::COMPLETED);
  71. } else {
  72. // 外部模式,调用外部API
  73. OrderLogic::processTransferOut($order);
  74. }
  75. return $order;
  76. }
  77. /**
  78. * 创建转入订单
  79. *
  80. * @param array $data 订单数据
  81. * @return TransferOrder
  82. * @throws \Exception
  83. */
  84. public static function createTransferIn(array $data): TransferOrder
  85. {
  86. // 验证请求数据
  87. $validation = new TransferInValidation($data);
  88. $validation->validated();
  89. // 获取应用配置
  90. $app = TransferApp::findOrFail($data['transfer_app_id']);
  91. if (!$app->is_enabled) {
  92. throw new \Exception('应用已禁用');
  93. }
  94. // 检查外部订单ID是否已存在
  95. $existingOrder = TransferOrder::where('out_order_id', $data['business_id'])
  96. ->where('out_id', $app->out_id2 ?? 0)
  97. ->first();
  98. if ($existingOrder) {
  99. throw new \Exception('订单已存在');
  100. }
  101. // 计算金额
  102. $outAmount = (string) $data['amount'];
  103. $amount = bcdiv($outAmount, (string) $app->exchange_rate, 10);
  104. // 创建订单
  105. $order = TransferOrder::create([
  106. 'transfer_app_id' => $app->id,
  107. 'out_id' => $app->out_id2 ?? 0,
  108. 'out_order_id' => $data['business_id'],
  109. 'out_user_id' => $data['out_user_id'] ?? null,
  110. 'user_id' => $data['user_id'],
  111. 'currency_id' => $app->currency_id,
  112. 'fund_id' => $app->fund_id,
  113. 'type' => TransferType::IN,
  114. 'status' => TransferStatus::CREATED,
  115. 'out_amount' => $outAmount,
  116. 'amount' => $amount,
  117. 'exchange_rate' => $app->exchange_rate,
  118. 'callback_data' => $data['callback_data'] ?? [],
  119. 'remark' => $data['remark'] ?? null,
  120. ]);
  121. // 向用户账户转入资金
  122. $fundResult = FundService::increase(
  123. $data['user_id'],
  124. $app->fund_id,
  125. $amount,
  126. "从{$app->title}转入",
  127. "transfer_in:{$order->id}"
  128. );
  129. if (!$fundResult) {
  130. $order->updateStatus(TransferStatus::FAILED, '资金转入失败');
  131. throw new \Exception('资金操作失败');
  132. }
  133. // 判断是否为农场内部模式
  134. if ($app->isInternalMode()) {
  135. // 内部模式直接完成
  136. $order->updateStatus(TransferStatus::COMPLETED);
  137. } else {
  138. // 外部模式,更新状态为处理中
  139. $order->updateStatus(TransferStatus::PROCESSING);
  140. // 发送回调通知
  141. if ($app->supportsCallback()) {
  142. CallbackLogic::sendCallback($order);
  143. }
  144. }
  145. return $order;
  146. }
  147. /**
  148. * 处理回调结果
  149. *
  150. * @param array $callbackData 回调数据
  151. * @return bool
  152. */
  153. public static function processCallback(array $callbackData): bool
  154. {
  155. // 查找订单
  156. $order = TransferOrder::where('out_order_id', $callbackData['business_id'])
  157. ->where('out_id', $callbackData['out_id'])
  158. ->first();
  159. if (!$order) {
  160. \Log::warning('Transfer callback order not found', $callbackData);
  161. return false;
  162. }
  163. // 更新订单状态
  164. $status = $callbackData['success'] ? TransferStatus::COMPLETED : TransferStatus::FAILED;
  165. $message = $callbackData['message'] ?? null;
  166. return $order->updateStatus($status, $message);
  167. }
  168. /**
  169. * 生成外部订单ID
  170. *
  171. * @param string $prefix 前缀
  172. * @return string
  173. */
  174. private static function generateOutOrderId(string $prefix = 'TR'): string
  175. {
  176. return $prefix . date('YmdHis') . Str::random(6);
  177. }
  178. /**
  179. * 重试订单处理
  180. *
  181. * @param int $orderId 订单ID
  182. * @return bool
  183. */
  184. public static function retryOrder(int $orderId): bool
  185. {
  186. $order = TransferOrder::findOrFail($orderId);
  187. if (!$order->canRetry()) {
  188. throw new \Exception('订单状态不允许重试');
  189. }
  190. // 重置状态为已创建
  191. $order->updateStatus(TransferStatus::CREATED);
  192. // 根据订单类型重新处理
  193. if ($order->isTransferOut()) {
  194. OrderLogic::processTransferOut($order);
  195. } else {
  196. // 转入订单重试主要是重新发送回调
  197. if ($order->transferApp->supportsCallback()) {
  198. CallbackLogic::sendCallback($order);
  199. }
  200. }
  201. return true;
  202. }
  203. /**
  204. * 手动完成订单
  205. *
  206. * @param int $orderId 订单ID
  207. * @param string $remark 备注
  208. * @return bool
  209. */
  210. public static function manualComplete(int $orderId, string $remark = ''): bool
  211. {
  212. $order = TransferOrder::findOrFail($orderId);
  213. if ($order->isFinalStatus()) {
  214. throw new \Exception('订单已处于最终状态');
  215. }
  216. // 更新状态为已完成
  217. $order->updateStatus(TransferStatus::COMPLETED, $remark);
  218. return true;
  219. }
  220. }