|
|
@@ -0,0 +1,256 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace App\Module\Transfer\Logics;
|
|
|
+
|
|
|
+use App\Module\Transfer\Enums\TransferStatus;
|
|
|
+use App\Module\Transfer\Enums\TransferType;
|
|
|
+use App\Module\Transfer\Models\TransferApp;
|
|
|
+use App\Module\Transfer\Models\TransferOrder;
|
|
|
+use App\Module\Transfer\Validations\TransferInValidation;
|
|
|
+use App\Module\Transfer\Validations\TransferOutValidation;
|
|
|
+use App\Module\Fund\Service\FundService;
|
|
|
+use Illuminate\Support\Str;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Transfer模块核心业务逻辑
|
|
|
+ */
|
|
|
+class TransferLogic
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * 创建转出订单
|
|
|
+ *
|
|
|
+ * @param array $data 订单数据
|
|
|
+ * @return TransferOrder
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public static function createTransferOut(array $data): TransferOrder
|
|
|
+ {
|
|
|
+ // 验证请求数据
|
|
|
+ $validation = new TransferOutValidation($data);
|
|
|
+ $validation->validated();
|
|
|
+
|
|
|
+ // 获取应用配置
|
|
|
+ $app = TransferApp::findOrFail($data['transfer_app_id']);
|
|
|
+ if (!$app->is_enabled) {
|
|
|
+ throw new \Exception('应用已禁用');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算金额
|
|
|
+ $outAmount = (string) $data['amount'];
|
|
|
+ $amount = bcmul($outAmount, (string) $app->exchange_rate, 10);
|
|
|
+
|
|
|
+ // 生成外部订单ID
|
|
|
+ $outOrderId = self::generateOutOrderId('OUT');
|
|
|
+
|
|
|
+ // 创建订单
|
|
|
+ $order = TransferOrder::create([
|
|
|
+ 'transfer_app_id' => $app->id,
|
|
|
+ 'out_id' => $app->out_id,
|
|
|
+ 'out_order_id' => $outOrderId,
|
|
|
+ 'out_user_id' => $data['out_user_id'] ?? null,
|
|
|
+ 'user_id' => $data['user_id'],
|
|
|
+ 'currency_id' => $app->currency_id,
|
|
|
+ 'fund_id' => $app->fund_id,
|
|
|
+ 'type' => TransferType::OUT,
|
|
|
+ 'status' => TransferStatus::CREATED,
|
|
|
+ 'out_amount' => $outAmount,
|
|
|
+ 'amount' => $amount,
|
|
|
+ 'exchange_rate' => $app->exchange_rate,
|
|
|
+ 'callback_data' => $data['callback_data'] ?? [],
|
|
|
+ 'remark' => $data['remark'] ?? null,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 扣除用户资金
|
|
|
+ $fundResult = FundService::decrease(
|
|
|
+ $data['user_id'],
|
|
|
+ $app->fund_id,
|
|
|
+ $amount,
|
|
|
+ "转出到{$app->title}",
|
|
|
+ "transfer_out:{$order->id}"
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!$fundResult) {
|
|
|
+ $order->updateStatus(TransferStatus::FAILED, '资金扣除失败');
|
|
|
+ throw new \Exception('余额不足或资金操作失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否为农场内部模式
|
|
|
+ if ($app->isInternalMode()) {
|
|
|
+ // 内部模式直接完成
|
|
|
+ $order->updateStatus(TransferStatus::COMPLETED);
|
|
|
+ } else {
|
|
|
+ // 外部模式,调用外部API
|
|
|
+ OrderLogic::processTransferOut($order);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $order;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建转入订单
|
|
|
+ *
|
|
|
+ * @param array $data 订单数据
|
|
|
+ * @return TransferOrder
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public static function createTransferIn(array $data): TransferOrder
|
|
|
+ {
|
|
|
+ // 验证请求数据
|
|
|
+ $validation = new TransferInValidation($data);
|
|
|
+ $validation->validated();
|
|
|
+
|
|
|
+ // 获取应用配置
|
|
|
+ $app = TransferApp::findOrFail($data['transfer_app_id']);
|
|
|
+ if (!$app->is_enabled) {
|
|
|
+ throw new \Exception('应用已禁用');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查外部订单ID是否已存在
|
|
|
+ $existingOrder = TransferOrder::where('out_order_id', $data['business_id'])
|
|
|
+ ->where('out_id', $app->out_id)
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ if ($existingOrder) {
|
|
|
+ throw new \Exception('订单已存在');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算金额
|
|
|
+ $outAmount = (string) $data['amount'];
|
|
|
+ $amount = bcdiv($outAmount, (string) $app->exchange_rate, 10);
|
|
|
+
|
|
|
+ // 创建订单
|
|
|
+ $order = TransferOrder::create([
|
|
|
+ 'transfer_app_id' => $app->id,
|
|
|
+ 'out_id' => $app->out_id,
|
|
|
+ 'out_order_id' => $data['business_id'],
|
|
|
+ 'out_user_id' => $data['out_user_id'] ?? null,
|
|
|
+ 'user_id' => $data['user_id'],
|
|
|
+ 'currency_id' => $app->currency_id,
|
|
|
+ 'fund_id' => $app->fund_id,
|
|
|
+ 'type' => TransferType::IN,
|
|
|
+ 'status' => TransferStatus::CREATED,
|
|
|
+ 'out_amount' => $outAmount,
|
|
|
+ 'amount' => $amount,
|
|
|
+ 'exchange_rate' => $app->exchange_rate,
|
|
|
+ 'callback_data' => $data['callback_data'] ?? [],
|
|
|
+ 'remark' => $data['remark'] ?? null,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 向用户账户转入资金
|
|
|
+ $fundResult = FundService::increase(
|
|
|
+ $data['user_id'],
|
|
|
+ $app->fund_id,
|
|
|
+ $amount,
|
|
|
+ "从{$app->title}转入",
|
|
|
+ "transfer_in:{$order->id}"
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!$fundResult) {
|
|
|
+ $order->updateStatus(TransferStatus::FAILED, '资金转入失败');
|
|
|
+ throw new \Exception('资金操作失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否为农场内部模式
|
|
|
+ if ($app->isInternalMode()) {
|
|
|
+ // 内部模式直接完成
|
|
|
+ $order->updateStatus(TransferStatus::COMPLETED);
|
|
|
+ } else {
|
|
|
+ // 外部模式,更新状态为处理中
|
|
|
+ $order->updateStatus(TransferStatus::PROCESSING);
|
|
|
+
|
|
|
+ // 发送回调通知
|
|
|
+ if ($app->supportsCallback()) {
|
|
|
+ CallbackLogic::sendCallback($order);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $order;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理回调结果
|
|
|
+ *
|
|
|
+ * @param array $callbackData 回调数据
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public static function processCallback(array $callbackData): bool
|
|
|
+ {
|
|
|
+ // 查找订单
|
|
|
+ $order = TransferOrder::where('out_order_id', $callbackData['business_id'])
|
|
|
+ ->where('out_id', $callbackData['out_id'])
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ if (!$order) {
|
|
|
+ \Log::warning('Transfer callback order not found', $callbackData);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新订单状态
|
|
|
+ $status = $callbackData['success'] ? TransferStatus::COMPLETED : TransferStatus::FAILED;
|
|
|
+ $message = $callbackData['message'] ?? null;
|
|
|
+
|
|
|
+ return $order->updateStatus($status, $message);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成外部订单ID
|
|
|
+ *
|
|
|
+ * @param string $prefix 前缀
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ private static function generateOutOrderId(string $prefix = 'TR'): string
|
|
|
+ {
|
|
|
+ return $prefix . date('YmdHis') . Str::random(6);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 重试订单处理
|
|
|
+ *
|
|
|
+ * @param int $orderId 订单ID
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public static function retryOrder(int $orderId): bool
|
|
|
+ {
|
|
|
+ $order = TransferOrder::findOrFail($orderId);
|
|
|
+
|
|
|
+ if (!$order->canRetry()) {
|
|
|
+ throw new \Exception('订单状态不允许重试');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重置状态为已创建
|
|
|
+ $order->updateStatus(TransferStatus::CREATED);
|
|
|
+
|
|
|
+ // 根据订单类型重新处理
|
|
|
+ if ($order->isTransferOut()) {
|
|
|
+ OrderLogic::processTransferOut($order);
|
|
|
+ } else {
|
|
|
+ // 转入订单重试主要是重新发送回调
|
|
|
+ if ($order->transferApp->supportsCallback()) {
|
|
|
+ CallbackLogic::sendCallback($order);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手动完成订单
|
|
|
+ *
|
|
|
+ * @param int $orderId 订单ID
|
|
|
+ * @param string $remark 备注
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public static function manualComplete(int $orderId, string $remark = ''): bool
|
|
|
+ {
|
|
|
+ $order = TransferOrder::findOrFail($orderId);
|
|
|
+
|
|
|
+ if ($order->isFinalStatus()) {
|
|
|
+ throw new \Exception('订单已处于最终状态');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新状态为已完成
|
|
|
+ $order->updateStatus(TransferStatus::COMPLETED, $remark);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+}
|