123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- <?php
- namespace App\Services\Client;
- use App\Models\Wallet;
- use App\Enums\UserStatus;
- use App\Models\MemberUser;
- use App\Enums\WithdrawStatus;
- use App\Models\WalletTransRecord;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Log;
- use App\Models\WalletWithdrawRecord;
- class WalletService
- {
- /**
- * [钱包管理]获取钱包明细
- *
- * @param int $userId 用户ID
- * @param int $perPage 每页记录数
- * @return array 包含 items 和 total 的数组
- *
- * @throws \Exception
- */
- public function getWalletRecords($userId, $perPage = 10)
- {
- try {
- // 获取当前用户
- $user = MemberUser::find($userId);
- // 检查用户是否存在
- abort_if(! $user, 400, '用户不存在');
- // 检查用户状态
- abort_if($user->state != UserStatus::OPEN->value, 400, '用户状态异常');
- // 获取钱包交易记录
- $paginator = $user->wallet->transRecords()
- ->orderBy('created_at', 'desc')
- ->paginate($perPage);
- // 转换为统一的分页格式
- return [
- 'items' => $paginator->items(),
- 'total' => $paginator->total(),
- ];
- } catch (\Exception $e) {
- // 记录错误日志
- \Log::error('获取钱包明细失败', [
- 'userId' => $userId,
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString(),
- ]);
- throw $e;
- }
- }
- /**
- * 获取用户钱包
- */
- public function getUserWallet($userId)
- {
- try {
- $user = MemberUser::find($userId);
- abort_if(! $user, 400, '用户不存在');
- abort_if($user->state != UserStatus::OPEN->value, 400, '用户状态异常');
- $wallet = $user->wallet;
- if (! $wallet) {
- Log::warning('用户钱包不存在', ['user_id' => $user->id]);
- throw new \Exception('钱包不存在');
- }
- return $wallet;
- } catch (\Exception $e) {
- Log::error('获取用户钱包失败', ['error' => $e->getMessage()]);
- throw $e;
- }
- }
- /**
- * 用户钱包提现
- *
- * @param int $userId 用户ID
- * @param array $data 提现数据
- * @return array
- *
- * @throws \Exception
- */
- public function withdraw(int $userId, array $data)
- {
- return DB::transaction(function () use ($userId, $data) {
- try {
- // 获取用户信息
- $user = MemberUser::findOrFail($userId);
- abort_if($user->state != UserStatus::OPEN->value, 400, '用户状态异常');
- abort_if(! $user->wallet, 404, '钱包信息不存在');
- // 锁定钱包记录
- $wallet = Wallet::where('id', $user->wallet->id)
- ->lockForUpdate()
- ->first();
- // 验证提现金额
- $amount = $data['amount'];
- abort_if($amount <= 100, 422, '提现金额必须大于100元');
- abort_if($amount > $wallet->available_balance, 422, '可提现余额不足');
- // 生成交易流水号
- $transNo = 'CW' . date('YmdHis') . mt_rand(1000, 9999);
- // 创建提现记录
- $withdraw = WalletWithdrawRecord::create([
- 'wallet_id' => $wallet->id, // 钱包ID
- 'trans_no' => $transNo, // 交易流水号
- 'amount' => $amount, // 提现金额
- 'withdraw_type' => $data['withdraw_type'], // 提现方式(1:微信 2:支付宝 3:银行卡)
- 'withdraw_account' => $data['withdraw_account'], // 提现账号
- 'withdraw_account_name' => $data['withdraw_account_name'], // 提现账户名称
- 'state' => WithdrawStatus::PROCESSING, // 提现状态
- ]);
- // 创建交易记录
- WalletTransRecord::create([
- 'wallet_id' => $wallet->id,
- 'trans_no' => $transNo,
- 'trans_type' => 2, // 支出
- 'amount' => -$amount,
- 'before_balance' => $wallet->available_balance,
- 'after_balance' => $wallet->available_balance - $amount,
- 'owner_type' => WalletWithdrawRecord::class,
- 'owner_id' => $withdraw->id,
- 'remark' => '提现申请',
- 'state' => 2, // 处理中
- ]);
- // 冻结提现金额
- $wallet->decrement('available_balance', $amount);
- $wallet->increment('frozen_amount', $amount);
- // 记录日志
- Log::info('用户提现申请成功', [
- 'user_id' => $userId,
- 'trans_no' => $transNo,
- 'amount' => $amount,
- 'wallet_id' => $wallet->id,
- ]);
- return [
- 'message' => '提现申请已提交',
- 'trans_no' => $transNo,
- 'amount' => number_format($amount, 2, '.', ''),
- 'state' => '处理中',
- ];
- } catch (\Exception $e) {
- Log::error('用户提现申请失败', [
- 'user_id' => $userId,
- 'data' => $data,
- 'error' => $e->getMessage(),
- 'file' => $e->getFile(),
- 'line' => $e->getLine(),
- ]);
- throw $e;
- }
- });
- }
- }
|