findOrFail($userId); abort_if(! $user->coach, 404, '技师信息不存在'); // 获取技师钱包 $wallet = $user->coach->wallet; // 获取钱包流水统计 $statistics = $this->getWalletStatistics($wallet->id); return [ 'available_balance' => number_format($wallet->available_balance, 2, '.', ''), // 可用余额 'frozen_amount' => number_format($wallet->frozen_amount, 2, '.', ''), // 冻结金额 'total_income' => number_format($statistics['total_income'], 2, '.', ''), // 累计收入 'total_withdraw' => number_format($statistics['total_withdraw'], 2, '.', ''), // 累计支出 'today_income' => number_format($statistics['today_income'], 2, '.', ''), // 今日收入 'month_income' => number_format($statistics['month_income'], 2, '.', ''), // 本月收入 'last_month_income' => number_format($statistics['last_month_income'], 2, '.', ''), // 上月收入 ]; } catch (\Exception $e) { Log::error('获取技师钱包信息失败', [ 'user_id' => $userId, 'error' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), ]); throw $e; } } /** * 获取钱包流水记录 * * @param int $userId 技师用户ID * @param array $params 查询参数 */ public function getWalletRecords(int $userId, array $params): array { try { // 加载用户和技师信息(优化关联加载) $user = MemberUser::with(['coach', 'coach.wallet'])->findOrFail($userId); abort_if(! $user->coach, 404, '技师信息不存在'); abort_if(! $user->coach->wallet, 404, '钱包信息不存在'); // 构建查询 $query = WalletTransRecord::where('wallet_id', $user->coach->wallet->id) // 修复参数名称错误 ->when(isset($params['type']), function ($query) use ($params) { return $query->where('trans_type', $params['type']); }) // 优化日期查询 ->when(isset($params['start_date']), function ($query) use ($params) { return $query->whereDate('created_at', '>=', $params['start_date']); }) ->when(isset($params['end_date']), function ($query) use ($params) { return $query->whereDate('created_at', '<=', $params['end_date']); }) // 添加金额范围筛选 ->when(isset($params['min_amount']), function ($query) use ($params) { return $query->where('amount', '>=', $params['min_amount']); }) ->when(isset($params['max_amount']), function ($query) use ($params) { return $query->where('amount', '<=', $params['max_amount']); }) // 添加交易状态筛选 ->when(isset($params['status']), function ($query) use ($params) { return $query->where('state', $params['status']); }) // 添加排序选项 ->when( isset($params['sort_field']) && isset($params['sort_order']), function ($query) use ($params) { return $query->orderBy( $params['sort_field'], $params['sort_order'] === 'desc' ? 'desc' : 'asc' ); }, function ($query) { return $query->orderBy('created_at', 'desc'); } ); // 分页获取数据(添加字段选择) $records = $query->paginate( $params['per_page'] ?? 10, [ 'id', 'trans_no', 'trans_type', 'amount', 'balance', 'owner_type', 'owner_id', 'remark', 'status', 'created_at', ], 'page', $params['page'] ?? 1 ); // TODO: 处理格式化数据存在的枚举映射 // 格式化数据 $items = collect($records->items())->map(function ($record) { return [ 'id' => $record->id, 'trans_no' => $record->trans_no, 'trans_type' => $this->formatTransType($record->trans_type), 'amount' => number_format($record->amount, 2, '.', ''), 'balance' => number_format($record->balance, 2, '.', ''), 'owner_type' => $this->formatOwnerType($record->owner_type), 'owner_id' => $record->owner_id, 'remark' => $record->remark, 'state' => $this->formatStatus($record->state), 'created_at' => $record->created_at->format('Y-m-d H:i:s'), ]; }); // 添加汇总信息 $summary = [ 'total_income' => $query->where('amount', '>', 0)->sum('amount'), 'total_expense' => abs($query->where('amount', '<', 0)->sum('amount')), 'record_count' => $records->total(), ]; return [ 'items' => $items, 'total' => $records->total(), 'summary' => $summary, ]; } catch (\Exception $e) { Log::error('获取钱包流水记录失败', [ 'user_id' => $userId, 'params' => $params, 'error' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), ]); 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::with(['coach', 'coach.wallet'])->findOrFail($userId); abort_if(! $user->coach, 404, '技师信息不存在'); abort_if(! $user->coach->wallet, 404, '钱包信息不存在'); // 锁定钱包记录 $wallet = Wallet::where('id', $user->coach->wallet->id) ->lockForUpdate() ->first(); // TODO: 提现金额限制 // 验证提现金额 $amount = $data['amount']; abort_if($amount <= 100, 422, '提现金额必须大于100元'); abort_if($amount > $wallet->available_balance, 422, '可提现余额不足'); // 生成交易流水号 $transNo = 'W' . 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, // 提现状态 ]); // TODO: 创建交易记录字段需关联枚举 // 创建交易记录 $record = 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, 'coach_id' => $user->coach->id, '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; } }); } /** * 格式化交易类型 */ private function formatTransType(int $type): string { return match ($type) { 1 => '收入', 2 => '支出', default => '未知', }; } /** * 格式化来源类型 */ private function formatOwnerType(string $type): string { return match ($type) { 'order' => '订单', 'withdraw' => '提现', 'refund' => '退款', 'system' => '系统', default => '其他', }; } /** * 格式化状态 */ private function formatStatus(int $status): string { return match ($status) { 1 => '成功', 2 => '处理中', 3 => '失败', default => '未知', }; } /** * 获取钱包流水统计 * * @param int $walletId 钱包ID */ private function getWalletStatistics(int $walletId): array { try { // 计算总收入和总支出 $totals = WalletTransRecord::where('wallet_id', $walletId) ->selectRaw(expression: ' SUM(CASE WHEN amount > 0 THEN amount ELSE 0 END) as total_income, ABS(SUM(CASE WHEN amount < 0 THEN amount ELSE 0 END)) as total_withdraw ')->first(); // 计算今日收入 $todayIncome = WalletTransRecord::where('wallet_id', $walletId) ->where('amount', '>', 0) ->whereDate('created_at', operator: today()) ->sum('amount'); // 计算本月收入 $monthIncome = WalletTransRecord::where('wallet_id', $walletId) ->where('amount', '>', 0) ->whereYear('created_at', now()->year) ->whereMonth('created_at', now()->month) ->sum('amount'); // 计算上月收入 $lastMonthIncome = WalletTransRecord::where('wallet_id', $walletId) ->where('amount', '>', 0) ->whereYear('created_at', now()->subMonth()->year) ->whereMonth('created_at', now()->subMonth()->month) ->sum('amount'); return [ 'total_income' => $totals->total_income ?? 0, 'total_withdraw' => $totals->total_withdraw ?? 0, 'today_income' => $todayIncome, 'month_income' => $monthIncome, 'last_month_income' => $lastMonthIncome, ]; } catch (\Exception $e) { Log::error('获取钱包统计信息失败', [ 'wallet_id' => $walletId, 'error' => $e->getMessage(), ]); return [ 'total_income' => 0, 'total_withdraw' => 0, 'today_income' => 0, 'month_income' => 0, 'last_month_income' => 0, ]; } } }