| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- <?php
- namespace App\Services;
- use App\Models\BalanceLog;
- use App\Models\ShareCommissionRecord;
- use App\Models\ShareSetting;
- use App\Models\User;
- use App\Models\UserLogin;
- use Illuminate\Support\Facades\DB;
- class ShareEarningReportService
- {
- public function memberDays(array $params): array
- {
- [$start, $end] = $this->dateRange($params);
- $page = (int)($params['page'] ?? 1);
- $limit = (int)($params['limit'] ?? 20);
- $dateQuery = User::query()->whereNotNull('agent_user_code')->where('agent_user_code', '<>', '')
- ->whereBetween('created_at', [$start . ' 00:00:00', $end . ' 23:59:59'])
- ->selectRaw('DATE(created_at) AS stat_date')
- ->groupByRaw('DATE(created_at)')
- ->orderByDesc('stat_date');
- $dates = $dateQuery->pluck('stat_date');
- $pageDates = $dates->slice(($page - 1) * $limit, $limit)->values();
- if ($pageDates->isEmpty()) return ['total' => $dates->count(), 'data' => []];
- $pageStart = (string)$pageDates->min();
- $pageEnd = (string)$pageDates->max();
- $basicRows = User::query()->whereNotNull('agent_user_code')->where('agent_user_code', '<>', '')
- ->whereBetween('created_at', [$pageStart . ' 00:00:00', $pageEnd . ' 23:59:59'])
- ->selectRaw('DATE(created_at) AS stat_date')
- ->selectRaw('COUNT(*) AS registered_members')
- ->selectRaw('COUNT(DISTINCT agent_user_code) AS promoter_count')
- ->groupByRaw('DATE(created_at)')->get()->keyBy('stat_date');
- $inviteeBase = User::query()->whereNotNull('agent_user_code')->where('agent_user_code', '<>', '')
- ->whereBetween('created_at', [$pageStart . ' 00:00:00', $pageEnd . ' 23:59:59'])
- ->select(['id', 'member_id'])->selectRaw('DATE(created_at) AS stat_date');
- $memberMetrics = DB::query()->fromSub($inviteeBase, 'invitee')
- ->leftJoin('balance_logs as balance_log', 'balance_log.member_id', '=', 'invitee.member_id')
- ->select(['invitee.id', 'invitee.stat_date'])
- ->selectRaw('SUM(CASE WHEN change_type IN (?, ?) THEN amount ELSE 0 END) AS deposit', ['充值', '三方充值'])
- ->selectRaw('ABS(SUM(CASE WHEN change_type IN (?, ?, ?) THEN amount ELSE 0 END)) AS withdraw', ['提现', '三方提现', '人工提现'])
- ->selectRaw('ABS(SUM(CASE WHEN change_type LIKE ? THEN amount ELSE 0 END)) AS consume', ['%投注%'])
- ->selectRaw('SUM(CASE WHEN change_type IN (?, ?, ?, ?, ?, ?) THEN amount ELSE 0 END) AS bonus', ['注册赠送', '优惠活动', '返水', '回水', '笔笔返', '比比返'])
- ->groupBy(['invitee.id', 'invitee.stat_date']);
- $validMin = (float)(ShareSetting::query()->value('valid_member_min_deposit') ?? 0);
- $metricRows = DB::query()->fromSub($memberMetrics, 'member_metric')
- ->select('stat_date')
- ->selectRaw('SUM(deposit) AS deposit_total')
- ->selectRaw('SUM(withdraw) AS withdraw_total')
- ->selectRaw('SUM(consume) AS consume_total')
- ->selectRaw('SUM(bonus) AS bonus_total')
- ->selectRaw('SUM(CASE WHEN deposit >= ? THEN 1 ELSE 0 END) AS valid_members', [$validMin])
- ->groupBy('stat_date')->get()->keyBy('stat_date');
- $pageMemberIds = User::query()->whereNotNull('agent_user_code')->where('agent_user_code', '<>', '')
- ->whereBetween('created_at', [$pageStart . ' 00:00:00', $pageEnd . ' 23:59:59'])
- ->select('member_id');
- $firstLoginIds = UserLogin::query()->where('status', UserLogin::STATUS_SUCCESS)
- ->whereIn('user_id', $pageMemberIds)
- ->select('user_id')->selectRaw('MIN(id) AS first_login_id')->groupBy('user_id');
- $prefix = DB::getTablePrefix();
- $userAlias = $prefix . 'invitee_user';
- $loginAlias = $prefix . 'first_login_row';
- $platformRows = User::query()->from('users as invitee_user')
- ->leftJoinSub($firstLoginIds, 'first_login', 'first_login.user_id', '=', 'invitee_user.member_id')
- ->leftJoin('user_login as first_login_row', 'first_login_row.id', '=', 'first_login.first_login_id')
- ->whereNotNull('invitee_user.agent_user_code')->where('invitee_user.agent_user_code', '<>', '')
- ->whereBetween('invitee_user.created_at', [$pageStart . ' 00:00:00', $pageEnd . ' 23:59:59'])
- ->selectRaw("DATE({$userAlias}.created_at) AS stat_date")
- ->selectRaw("SUM(CASE WHEN {$loginAlias}.platform = ? THEN 1 ELSE 0 END) AS h5_registered", ['H5'])
- ->selectRaw("SUM(CASE WHEN {$loginAlias}.platform = ? THEN 1 ELSE 0 END) AS ios_registered", ['iOS'])
- ->selectRaw("SUM(CASE WHEN {$loginAlias}.platform = ? THEN 1 ELSE 0 END) AS android_registered", ['Android'])
- ->groupByRaw("DATE({$userAlias}.created_at)")->get()->keyBy('stat_date');
- $rows = $pageDates->map(function ($day) use ($basicRows, $metricRows, $platformRows) {
- $basic = $basicRows->get($day);
- $metric = $metricRows->get($day);
- $platform = $platformRows->get($day);
- $deposit = (float)($metric->deposit_total ?? 0);
- $withdraw = (float)($metric->withdraw_total ?? 0);
- $bonus = (float)($metric->bonus_total ?? 0);
- return [
- 'date' => (string)$day,
- 'promoter_count' => (int)($basic->promoter_count ?? 0),
- 'registered_members' => (int)($basic->registered_members ?? 0),
- 'valid_members' => (int)($metric->valid_members ?? 0),
- 'h5_registered' => (int)($platform->h5_registered ?? 0),
- 'ios_registered' => (int)($platform->ios_registered ?? 0),
- 'android_registered' => (int)($platform->android_registered ?? 0),
- 'deposit_total' => number_format($deposit, 4, '.', ''),
- 'consume_total' => number_format((float)($metric->consume_total ?? 0), 4, '.', ''),
- 'company_profit' => number_format($deposit - $withdraw - $bonus, 4, '.', ''),
- ];
- });
- return ['total' => $dates->count(), 'data' => $rows->values()];
- }
- public function promoterDetails(array $params): array
- {
- $day = $params['date'];
- $promoterCodes = User::query()->whereNotNull('agent_user_code')->where('agent_user_code', '<>', '')
- ->whereBetween('created_at', [$day . ' 00:00:00', $day . ' 23:59:59'])
- ->select('agent_user_code')->distinct();
- $query = User::query()->whereIn('user_code', $promoterCodes);
- if (!empty($params['member_id'])) $query->where('member_id', $params['member_id']);
- if (!empty($params['account'])) $query->where('account', 'like', '%' . $params['account'] . '%');
- $total = $query->count();
- $promoters = $query->orderByDesc('id')->forPage((int)($params['page'] ?? 1), (int)($params['limit'] ?? 20))->get();
- $invitees = User::query()->whereBetween('created_at', [$day . ' 00:00:00', $day . ' 23:59:59'])
- ->whereIn('agent_user_code', $promoters->pluck('user_code'))
- ->get(['id', 'member_id', 'agent_user_code']);
- $records = ShareCommissionRecord::query()->where('stat_date', $day)->where('status', 1)
- ->whereIn('promoter_user_id', $promoters->pluck('id'))->get()->groupBy('promoter_user_id');
- $setting = ShareSetting::query()->first();
- $metricsByMember = $this->moneyMetricsByMember($invitees->pluck('member_id')->all());
- $rows = $promoters->map(function ($promoter) use ($invitees, $records, $setting, $day, $metricsByMember) {
- $children = $invitees->where('agent_user_code', $promoter->user_code);
- $metrics = $this->sumMetrics($metricsByMember, $children->pluck('member_id')->all());
- $commission = $records->get($promoter->id, collect())->where('type', 'commission')->sum('amount');
- return [
- 'date' => $day,
- 'member_id' => (string)$promoter->member_id,
- 'account' => (string)$promoter->account,
- 'nickname' => (string)$promoter->first_name,
- 'deposit_total' => $metrics['deposit'],
- 'registered_count' => $children->count(),
- 'commission' => number_format((float)$commission, 4, '.', ''),
- 'current_fee_rate' => $setting ? (string)$setting->tier_one_rate : '0.0000',
- ];
- });
- return ['total' => $total, 'data' => $rows->values()];
- }
- public function registrationDetails(array $params): array
- {
- $query = User::query()->whereNotNull('agent_user_code')->where('agent_user_code', '<>', '')
- ->whereBetween('created_at', [$params['date'] . ' 00:00:00', $params['date'] . ' 23:59:59']);
- if (!empty($params['member_id'])) $query->where('member_id', $params['member_id']);
- if (!empty($params['account'])) $query->where('account', 'like', '%' . $params['account'] . '%');
- $total = $query->count();
- $users = $query->orderByDesc('id')->forPage((int)($params['page'] ?? 1), (int)($params['limit'] ?? 20))->get();
- $metricsByMember = $this->moneyMetricsByMember($users->pluck('member_id')->all());
- $rows = $users->map(function ($user) use ($params, $metricsByMember) {
- $metrics = $metricsByMember[(string)$user->member_id] ?? $this->emptyMetrics();
- return [
- 'date' => $params['date'],
- 'member_id' => (string)$user->member_id,
- 'account' => (string)$user->account,
- 'nickname' => (string)$user->first_name,
- 'deposit_total' => $metrics['deposit'],
- 'bet_total' => $metrics['consume'],
- 'withdraw_total' => $metrics['withdraw'],
- ];
- });
- return ['total' => $total, 'data' => $rows->values()];
- }
- public function summary(array $params): array
- {
- [$start, $end] = $this->dateRange($params);
- $page = (int)($params['page'] ?? 1);
- $limit = (int)($params['limit'] ?? 20);
- $prefix = DB::getTablePrefix();
- $recordAlias = $prefix . 'share_record';
- $query = ShareCommissionRecord::query()->from('share_commission_records as share_record')
- ->leftJoin('users as promoter', 'promoter.id', '=', 'share_record.promoter_user_id')
- ->whereBetween('share_record.stat_date', [$start, $end])
- ->where('share_record.status', 1);
- if (!empty($params['account'])) $query->where('promoter.account', 'like', '%' . $params['account'] . '%');
- $grouped = $query->select([
- 'share_record.stat_date', 'share_record.promoter_user_id',
- 'promoter.member_id as promoter_member_id', 'promoter.account as promoter_account',
- 'promoter.first_name as promoter_nickname',
- ])->selectRaw("SUM(CASE WHEN {$recordAlias}.type = 'commission' THEN {$recordAlias}.base_amount ELSE 0 END) AS deposit")
- ->selectRaw("SUM(CASE WHEN {$recordAlias}.type = 'commission' THEN {$recordAlias}.amount ELSE 0 END) AS commission")
- ->selectRaw("SUM(CASE WHEN {$recordAlias}.type = 'fee_waiver' THEN {$recordAlias}.base_amount ELSE 0 END) AS sales_amount")
- ->selectRaw("MAX(CASE WHEN {$recordAlias}.type = 'fee_waiver' THEN {$recordAlias}.rate ELSE 0 END) AS fee_waiver_rate")
- ->groupBy([
- 'share_record.stat_date', 'share_record.promoter_user_id',
- 'promoter.member_id', 'promoter.account', 'promoter.first_name',
- ]);
- $total = DB::query()->fromSub(clone $grouped, 'share_summary')->count();
- $rows = $grouped->orderByDesc('share_record.stat_date')->orderByDesc('share_record.promoter_user_id')
- ->forPage($page, $limit)->get()->map(fn ($row) => [
- 'date' => $row->stat_date instanceof \DateTimeInterface
- ? $row->stat_date->format('Y-m-d')
- : substr((string)$row->stat_date, 0, 10),
- 'promoter_member_id' => (string)($row->promoter_member_id ?? ''),
- 'promoter_account' => (string)($row->promoter_account ?? ''),
- 'promoter_nickname' => (string)($row->promoter_nickname ?? ''),
- 'deposit' => number_format((float)$row->deposit, 4, '.', ''),
- 'commission' => number_format((float)$row->commission, 4, '.', ''),
- 'sales_amount' => number_format((float)$row->sales_amount, 4, '.', ''),
- 'fee_waiver_rate' => number_format((float)$row->fee_waiver_rate, 4, '.', ''),
- ]);
- $totalQuery = ShareCommissionRecord::query()->from('share_commission_records as share_record')
- ->leftJoin('users as promoter', 'promoter.id', '=', 'share_record.promoter_user_id')
- ->whereBetween('share_record.stat_date', [$start, $end])
- ->where('share_record.status', 1);
- if (!empty($params['account'])) $totalQuery->where('promoter.account', 'like', '%' . $params['account'] . '%');
- $totals = $totalQuery->selectRaw("SUM(CASE WHEN {$recordAlias}.type = 'commission' THEN {$recordAlias}.amount ELSE 0 END) AS total_commission")
- ->selectRaw("MAX(CASE WHEN {$recordAlias}.type = 'fee_waiver' THEN {$recordAlias}.rate ELSE 0 END) AS highest_fee_waiver_rate")->first();
- return [
- 'total' => $total,
- 'data' => $rows->values(),
- 'summary' => [
- 'total_commission' => number_format((float)($totals->total_commission ?? 0), 4, '.', ''),
- 'highest_fee_waiver_rate' => number_format((float)($totals->highest_fee_waiver_rate ?? 0), 4, '.', ''),
- ],
- ];
- }
- private function moneyMetricsByMember(array $memberIds): array
- {
- $memberIds = array_values(array_unique(array_filter(array_map('strval', $memberIds))));
- if (!$memberIds) return [];
- $metrics = array_fill_keys($memberIds, $this->emptyMetrics());
- $sets = [
- 'deposit' => fn ($query) => $query->whereIn('change_type', ['充值', '三方充值']),
- 'withdraw' => fn ($query) => $query->whereIn('change_type', ['提现', '三方提现', '人工提现']),
- 'consume' => fn ($query) => $query->where('change_type', 'like', '%投注%'),
- 'bonus' => fn ($query) => $query->whereIn('change_type', ['注册赠送', '优惠活动', '返水', '回水', '笔笔返', '比比返']),
- ];
- foreach ($sets as $field => $scope) {
- $query = BalanceLog::query()->whereIn('member_id', $memberIds);
- $rows = $scope($query)->groupBy('member_id')->get(['member_id', DB::raw('SUM(amount) as total')]);
- foreach ($rows as $row) {
- $value = (float)$row->total;
- $metrics[(string)$row->member_id][$field] = number_format(in_array($field, ['withdraw', 'consume'], true) ? abs($value) : $value, 4, '.', '');
- }
- }
- return $metrics;
- }
- private function sumMetrics(array $metricsByMember, array $memberIds): array
- {
- $totals = ['deposit' => 0.0, 'withdraw' => 0.0, 'consume' => 0.0, 'bonus' => 0.0];
- foreach (array_unique(array_map('strval', $memberIds)) as $memberId) {
- foreach ($totals as $field => $value) {
- $totals[$field] += (float)($metricsByMember[$memberId][$field] ?? 0);
- }
- }
- return array_map(fn ($value) => number_format($value, 4, '.', ''), $totals);
- }
- private function emptyMetrics(): array
- {
- return ['deposit' => '0.0000', 'withdraw' => '0.0000', 'consume' => '0.0000', 'bonus' => '0.0000'];
- }
- private function dateRange(array $params): array
- {
- $end = $params['end_date'] ?? date('Y-m-d');
- $start = $params['start_date'] ?? date('Y-m-d', strtotime($end . ' -30 days'));
- if (strtotime($start) > strtotime($end)) {
- throw new \RuntimeException('结束日期不能早于开始日期');
- }
- if (strtotime($end) - strtotime($start) > 366 * 86400) {
- throw new \RuntimeException('单次查询日期范围不能超过 366 天');
- }
- return [$start, $end];
- }
- }
|