|
@@ -0,0 +1,226 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace App\Services\Admin;
|
|
|
+
|
|
|
+use App\Models\WalletWithdrawRecord;
|
|
|
+use App\Models\WalletTransRecord;
|
|
|
+use App\Enums\WithdrawStatus;
|
|
|
+use App\Enums\WithdrawAuditStatus;
|
|
|
+use App\Enums\TransactionType;
|
|
|
+use App\Enums\TransactionStatus;
|
|
|
+use Illuminate\Support\Facades\DB;
|
|
|
+use Illuminate\Support\Facades\Log;
|
|
|
+
|
|
|
+class WalletWithdrawRecordService
|
|
|
+{
|
|
|
+
|
|
|
+ * 审核提现申请
|
|
|
+ *
|
|
|
+ * @param int $id 提现记录ID
|
|
|
+ * @param int $status 审核状态(2:通过 3:拒绝)
|
|
|
+ * @param string|null $remark 审核备注
|
|
|
+ */
|
|
|
+ public function audit(int $id, int $status, ?string $remark = null): void
|
|
|
+ {
|
|
|
+ DB::transaction(function () use ($id, $status, $remark) {
|
|
|
+
|
|
|
+ $record = WalletWithdrawRecord::lockForUpdate()->findOrFail($id);
|
|
|
+
|
|
|
+
|
|
|
+ abort_if($record->audit_state !== WithdrawAuditStatus::PENDING->value, 400, '提现申请状态异常');
|
|
|
+
|
|
|
+
|
|
|
+ $wallet = $record->wallet;
|
|
|
+ abort_if(!$wallet, 404, '钱包信息不存在');
|
|
|
+
|
|
|
+
|
|
|
+ abort_if($wallet->frozen_amount < $record->amount, 400, '钱包冻结金额异常');
|
|
|
+
|
|
|
+ if ($status === WithdrawAuditStatus::APPROVED->value) {
|
|
|
+
|
|
|
+ $this->processApproved($record, $remark);
|
|
|
+ } else {
|
|
|
+
|
|
|
+ $this->processRejected($record, $remark);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ $record->auditor = auth()->user()->name;
|
|
|
+ $record->audit_time = now();
|
|
|
+ $record->audit_remark = $remark;
|
|
|
+ $record->audit_state = $status;
|
|
|
+ $record->save();
|
|
|
+
|
|
|
+
|
|
|
+ Log::info('提现申请审核完成', [
|
|
|
+ 'withdraw_id' => $record->id,
|
|
|
+ 'external_no' => $record->external_no,
|
|
|
+ 'status' => $status,
|
|
|
+ 'remark' => $remark,
|
|
|
+ 'auditor' => $record->auditor
|
|
|
+ ]);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 处理审核通过
|
|
|
+ */
|
|
|
+ private function processApproved(WalletWithdrawRecord $record, ?string $remark): void
|
|
|
+ {
|
|
|
+ try {
|
|
|
+
|
|
|
+ $record->state = WithdrawStatus::PROCESSING->value;
|
|
|
+ $record->save();
|
|
|
+
|
|
|
+
|
|
|
+ $wallet = $record->wallet;
|
|
|
+ $currentBalance = $wallet->available_amount;
|
|
|
+ $currentRechargeBalance = $wallet->recharge_amount;
|
|
|
+
|
|
|
+
|
|
|
+ $transRecord = WalletTransRecord::create([
|
|
|
+ 'trans_no' => $this->generateTransNo(),
|
|
|
+ 'wallet_id' => $record->wallet_id,
|
|
|
+ 'owner_id' => $record->id,
|
|
|
+ 'owner_type' => 'withdraw',
|
|
|
+ 'trans_type' => 2,
|
|
|
+ 'amount' => $record->amount,
|
|
|
+ 'before_balance' => $currentBalance,
|
|
|
+ 'after_balance' => $currentBalance - $record->amount,
|
|
|
+ 'before_recharge_balance' => $currentRechargeBalance,
|
|
|
+ 'after_recharge_balance' => $currentRechargeBalance,
|
|
|
+ 'trans_time' => now(),
|
|
|
+ 'remark' => '提现打款',
|
|
|
+ 'state' => TransactionStatus::PROCESSING->value,
|
|
|
+ 'province' => $record->area_code ? substr($record->area_code, 0, 2) . '0000' : null,
|
|
|
+ 'city' => $record->area_code ? substr($record->area_code, 0, 4) . '00' : null,
|
|
|
+ 'district' => $record->area_code
|
|
|
+ ]);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ $paymentResult = $this->processPayment($record);
|
|
|
+
|
|
|
+ if ($paymentResult['success']) {
|
|
|
+
|
|
|
+
|
|
|
+ $record->state = WithdrawStatus::SUCCESS->value;
|
|
|
+ $record->withdraw_time = now();
|
|
|
+ $record->trans_no = $paymentResult['trade_no'];
|
|
|
+ $record->remark = '打款成功';
|
|
|
+ $record->save();
|
|
|
+
|
|
|
+
|
|
|
+ $transRecord->state = TransactionStatus::SUCCESS->value;
|
|
|
+ $transRecord->trans_no = $paymentResult['trade_no'];
|
|
|
+ $transRecord->save();
|
|
|
+
|
|
|
+
|
|
|
+ $record->wallet->decrement('frozen_amount', $record->amount);
|
|
|
+ } else {
|
|
|
+
|
|
|
+ throw new \Exception($paymentResult['message'] ?? '打款失败');
|
|
|
+ }
|
|
|
+ } catch (\Exception $e) {
|
|
|
+
|
|
|
+ Log::error('提现打款失败', [
|
|
|
+ 'withdraw_id' => $record->id,
|
|
|
+ 'error' => $e->getMessage()
|
|
|
+ ]);
|
|
|
+
|
|
|
+
|
|
|
+ $record->state = WithdrawStatus::FAILED->value;
|
|
|
+ $record->remark = $e->getMessage();
|
|
|
+ $record->save();
|
|
|
+
|
|
|
+
|
|
|
+ if (isset($transRecord)) {
|
|
|
+ $transRecord->state = TransactionStatus::FAILED->value;
|
|
|
+ $transRecord->save();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ $this->unfreezeBalance($record);
|
|
|
+
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 处理审核拒绝
|
|
|
+ */
|
|
|
+ private function processRejected(WalletWithdrawRecord $record, ?string $remark): void
|
|
|
+ {
|
|
|
+
|
|
|
+ $record->state = WithdrawStatus::FAILED->value;
|
|
|
+ $record->save();
|
|
|
+
|
|
|
+
|
|
|
+ $this->unfreezeBalance($record);
|
|
|
+
|
|
|
+
|
|
|
+ Log::info('提现申请已拒绝', [
|
|
|
+ 'withdraw_id' => $record->id,
|
|
|
+ 'external_no' => $record->external_no,
|
|
|
+ 'amount' => $record->amount,
|
|
|
+ 'remark' => $remark
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 解冻余额
|
|
|
+ */
|
|
|
+ private function unfreezeBalance(WalletWithdrawRecord $record): void
|
|
|
+ {
|
|
|
+ $wallet = $record->wallet;
|
|
|
+
|
|
|
+
|
|
|
+ $currentBalance = $wallet->available_amount;
|
|
|
+ $currentRechargeBalance = $wallet->recharge_amount;
|
|
|
+
|
|
|
+
|
|
|
+ $wallet->decrement('frozen_amount', $record->amount);
|
|
|
+
|
|
|
+
|
|
|
+ $wallet->increment('available_amount', $record->amount);
|
|
|
+
|
|
|
+
|
|
|
+ WalletTransRecord::create([
|
|
|
+ 'trans_no' => $this->generateTransNo(),
|
|
|
+ 'wallet_id' => $wallet->id,
|
|
|
+ 'owner_id' => $record->id,
|
|
|
+ 'owner_type' => 'withdraw',
|
|
|
+ 'trans_type' => 1,
|
|
|
+ 'amount' => $record->amount,
|
|
|
+ 'before_balance' => $currentBalance,
|
|
|
+ 'after_balance' => $currentBalance + $record->amount,
|
|
|
+ 'before_recharge_balance' => $currentRechargeBalance,
|
|
|
+ 'after_recharge_balance' => $currentRechargeBalance,
|
|
|
+ 'trans_time' => now(),
|
|
|
+ 'remark' => '提现拒绝解冻',
|
|
|
+ 'state' => TransactionStatus::SUCCESS->value
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 生成交易流水号
|
|
|
+ */
|
|
|
+ private function generateTransNo(): string
|
|
|
+ {
|
|
|
+ return 'T' . date('YmdHis') . str_pad(random_int(1, 9999), 4, '0', STR_PAD_LEFT);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 处理实际打款
|
|
|
+ * TODO: 对接实际支付服务
|
|
|
+ */
|
|
|
+ private function processPayment(WalletWithdrawRecord $record): array
|
|
|
+ {
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'trade_no' => 'PAY' . time() . random_int(1000, 9999),
|
|
|
+ 'message' => '打款成功'
|
|
|
+ ];
|
|
|
+ }
|
|
|
+}
|