agent(); $other = $this->agent(); foreach (['m1' => $agent, 'm2' => $agent, 'x1' => $other] as $memberId => $owner) { DB::table('users')->insert(['member_id' => $memberId, 'username' => $memberId, 'agent_id' => $owner->id, 'created_at' => now()]); DB::table('wallets')->insert(['member_id' => $memberId, 'available_balance' => 100]); } $in = '2026-09-07 10:00:00'; $out = '2026-09-01 10:00:00'; foreach ([['m1', 500, $in], ['m2', 300, $in], ['m1', 999, $out], ['x1', 777, $in]] as [$member, $amount, $at]) { DB::table('recharges')->insert(['member_id' => $member, 'status' => 1, 'amount' => $amount, 'created_at' => $at]); } DB::table('recharges')->insert(['member_id' => 'm1', 'status' => 0, 'amount' => 50, 'created_at' => $in]); // 未到账 DB::table('payment_orders')->insert(['member_id' => 'm2', 'status' => 2, 'type' => '1', 'amount' => 200, 'created_at' => $in]); DB::table('withdraws')->insert(['member_id' => 'm1', 'status' => 1, 'amount' => 150, 'created_at' => $in]); foreach ([['人工充值', null, 80], ['人工扣款', null, -30], ['优惠活动', null, 20], ['返水', null, 10], ['人工充值', 0, 5]] as [$type, $related, $amount]) { DB::table('balance_logs')->insert(['member_id' => 'm1', 'change_type' => $type, 'related_id' => $related, 'amount' => $amount, 'created_at' => $in]); } $request = Request::create('/agent/dashboard', 'GET', ['start_date' => '2026-09-07', 'end_date' => '2026-09-07']); $request->attributes->set('agent', $agent); $this->app->instance('request', $request); $data = (new ProfileController())->dashboard($request, new AgentReportService())->getData(true); $this->assertSame(0, $data['code'], (string) $data['msg']); $summary = $data['data']; $this->assertSame(2, $summary['member_count']); $this->assertSame('200.0000', $summary['member_balance']); $this->assertSame('1000.0000', $summary['deposit_amount'], 'recharges + paid online orders in range'); $this->assertSame('150.0000', $summary['withdraw_amount']); $this->assertSame('80.0000', $summary['manual_credit']); $this->assertSame('30.0000', $summary['manual_debit']); $this->assertSame('25.0000', $summary['bonus_amount'], '优惠活动 + 人工充值(赠送)'); $this->assertSame('10.0000', $summary['rebate_amount']); $this->assertSame('815.0000', $summary['company_profit'], '1000 - 150 - 25 - 10'); $this->assertSame('0.0000', $summary['commission_amount']); $this->assertSame(0, $summary['agent_count']); $this->assertSame('own', $summary['scope']); foreach (['bet_count', 'bet_amount', 'valid_bet_amount', 'win_loss'] as $field) $this->assertArrayHasKey($field, $summary); $request = Request::create('/agent/dashboard', 'GET', ['start_date' => '2026-09-08', 'end_date' => '2026-09-07']); $request->attributes->set('agent', $agent); $this->assertNotSame(0, (new ProfileController())->dashboard($request, new AgentReportService())->getData(true)['code']); } public function test_equity_agent_sees_whole_platform_excluding_demo_accounts(): void { if (!Schema::hasColumn('users', 'from')) Schema::table('users', fn (Blueprint $t) => $t->integer('from')->nullable()); $joint = $this->agent(); $equity = $this->agent(Agent::TYPE_EQUITY); foreach (['a1' => [$joint->id, 1], 'a2' => [null, 1], 'a3' => [null, null], 'demo' => [null, 2]] as $memberId => [$agentId, $from]) { DB::table('users')->insert(['member_id' => $memberId, 'username' => $memberId, 'agent_id' => $agentId, 'from' => $from, 'created_at' => now()]); DB::table('recharges')->insert(['member_id' => $memberId, 'status' => 1, 'amount' => 100, 'created_at' => '2026-09-07 10:00:00']); } $summary = (new AgentReportService())->dashboard($equity, '2026-09-07', '2026-09-07'); $this->assertSame('platform', $summary['scope']); $this->assertSame(3, $summary['member_count'], 'all real members, no demo account, regardless of agent'); $this->assertSame('300.0000', $summary['deposit_amount']); $this->assertSame('0.0000', $summary['commission_amount']); $own = (new AgentReportService())->dashboard($joint, '2026-09-07', '2026-09-07'); $this->assertSame(['own', 1, '100.0000'], [$own['scope'], $own['member_count'], $own['deposit_amount']]); $rows = array_column((new AgentReportService())->reportRows(null, ['start_date' => '2026-09-07'])['list'], null, 'id'); $this->assertSame(0, $rows[$equity->id]['member_count'], 'admin agent report still counts only the equity agent\'s own members'); } public function test_yearly_dashboard_preserves_third_game_date_boundaries_and_finance_totals(): void { Schema::table('users', function (Blueprint $table) { $table->integer('from')->default(1); $table->integer('user_type')->default(0); }); Schema::create('third_game_orders', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('user_id')->nullable(); $table->integer('status'); $table->dateTime('last_update_time'); foreach (['bet_amount', 'valid_amount', 'settled_amount'] as $field) { $table->decimal($field, 18, 4)->nullable(); } }); $joint = $this->agent(); $equity = $this->agent(Agent::TYPE_EQUITY); $test = $this->agents->create(['username' => 'dashboard-test', 'password' => 'secret1', 'is_test' => 1]); $users = []; foreach ([['own', $joint->id, 1, 0], ['direct', null, 1, 0], ['guest', null, 2, 1], ['simulation', null, 1, 2], ['test', $test->id, 1, 0]] as [$member, $agentId, $from, $userType]) { $users[$member] = DB::table('users')->insertGetId([ 'member_id' => $member, 'username' => $member, 'agent_id' => $agentId, 'from' => $from, 'user_type' => $userType, ]); } $from = '2025-09-08'; $to = '2026-09-07'; foreach ([ [$users['own'], 1, "$from 00:00:00", 10, 8, -2], [$users['own'], 1, "$to 23:59:59", 20, null, 5], [$users['direct'], 1, "$to 12:00:00", 40, 35, 7], [$users['own'], 0, "$to 12:00:00", 999, 999, 999], [$users['own'], 1, '2025-09-07 23:59:59', 999, 999, 999], [$users['own'], 1, '2026-09-08 00:00:00', 999, 999, 999], [$users['guest'], 1, "$to 12:00:00", 999, 999, 999], [$users['simulation'], 1, "$to 12:00:00", 999, 999, 999], [$users['test'], 1, "$to 12:00:00", 999, 999, 999], [null, 1, "$to 12:00:00", 999, 999, 999], ] as [$userId, $status, $at, $bet, $valid, $win]) { DB::table('third_game_orders')->insert(['user_id' => $userId, 'status' => $status, 'last_update_time' => $at, 'bet_amount' => $bet, 'valid_amount' => $valid, 'settled_amount' => $win]); } foreach ([['人工充值', null, 40], ['订单补偿', null, 2], ['人工扣款', null, -3], ['注册赠送', null, 1], ['优惠活动', null, 2], ['即充即送', null, 3], ['充值返现', null, 4], ['老用户回归', null, 5], ['人工充值', 0, 6], ['人工充值', 123, 999], ['比比返', null, 1], ['笔笔返', null, 2], ['返水', null, 3], ['回水', null, 4], ['结算', null, 999], ] as [$type, $relatedId, $amount]) { DB::table('balance_logs')->insert(['member_id' => 'own', 'change_type' => $type, 'related_id' => $relatedId, 'amount' => $amount, 'created_at' => "$from 00:00:00"]); } $reports = new AgentReportService(); $betFields = array_flip(['bet_count', 'bet_amount', 'valid_bet_amount', 'win_loss']); $fundFields = array_flip(['manual_credit', 'manual_debit', 'bonus_amount', 'rebate_amount']); $platform = $reports->dashboard($equity, $from, $to); $own = $reports->dashboard($joint, $from, $to); // 股东的全平台统计包含后台模拟会员(注单 999 / 999 / 999),不含游客和测试会员 $this->assertSame([4, '1069.0000', '1042.0000', '1009.0000'], array_values(array_intersect_key($platform, $betFields))); $this->assertSame([2, '30.0000', '8.0000', '3.0000'], array_values(array_intersect_key($own, $betFields))); $this->assertSame(['42.0000', '3.0000', '21.0000', '10.0000'], array_values(array_intersect_key($platform, $fundFields))); $this->assertSame(array_intersect_key($platform, $fundFields), array_intersect_key($own, $fundFields)); } public function test_yearly_shareholder_dashboard_preserves_legacy_order_formulas_and_scope(): void { Schema::table('users', fn (Blueprint $table) => $table->integer('from')->default(1)); foreach (['bets' => 'profit', 'sport_game_order' => 'profit_and_loss', 'jisu_game_order' => 'profit_and_loss'] as $source => $winColumn) { Schema::create($source, function (Blueprint $table) use ($winColumn) { $table->string('member_id'); $table->integer('status'); $table->dateTime('created_at'); $table->decimal('amount', 18, 4); $table->decimal($winColumn, 18, 4)->nullable(); }); } Schema::create('lhc_order', function (Blueprint $table) { $table->string('member_id'); $table->integer('lottery_status'); $table->unsignedBigInteger('created_at'); $table->decimal('amount', 18, 4); $table->decimal('total_amount', 18, 4)->nullable(); $table->decimal('win_amount', 18, 4)->nullable(); }); $joint = $this->agent(); $equity = $this->agent(Agent::TYPE_EQUITY); foreach ([['own', $joint->id, 1], ['direct', null, 1], ['guest', null, 2]] as [$member, $agentId, $from]) { DB::table('users')->insert(['member_id' => $member, 'username' => $member, 'agent_id' => $agentId, 'from' => $from]); } $at = '2025-09-08 00:00:00'; foreach ([ ['bets', 'own', 2, 10, 'profit', 14], ['bets', 'own', 1, 999, 'profit', 999], ['sport_game_order', 'own', 1, 20, 'profit_and_loss', 999], ['jisu_game_order', 'direct', 2, 30, 'profit_and_loss', 9], ['jisu_game_order', 'guest', 2, 999, 'profit_and_loss', 999], ['jisu_game_order', 'orphan', 2, 999, 'profit_and_loss', 999], ] as [$source, $member, $status, $amount, $winColumn, $win]) { DB::table($source)->insert(['member_id' => $member, 'status' => $status, 'created_at' => $at, 'amount' => $amount, $winColumn => $win]); } foreach ([['own', 2, 40, 50, 55], ['direct', 1, 60, null, null], ['direct', 0, 999, 999, 999]] as [$member, $status, $amount, $total, $win]) { DB::table('lhc_order')->insert(['member_id' => $member, 'lottery_status' => $status, 'created_at' => strtotime($at), 'amount' => $amount, 'total_amount' => $total, 'win_amount' => $win]); } $reports = new AgentReportService(); $fields = array_flip(['bet_count', 'bet_amount', 'valid_bet_amount', 'win_loss']); $platform = $reports->dashboard($equity, '2025-09-08', '2026-09-07'); $own = $reports->dashboard($joint, '2025-09-08', '2026-09-07'); $this->assertSame([5, '170.0000', '170.0000', '-62.0000'], array_values(array_intersect_key($platform, $fields))); $this->assertSame([3, '80.0000', '80.0000', '-11.0000'], array_values(array_intersect_key($own, $fields))); } public function test_charts_group_registrations_and_deduplicate_successful_daily_deposits_across_channels(): void { $agent = $this->agent(); $other = $this->agent(); foreach ([['a', $agent, '2026-08-31'], ['b', $agent, '2026-09-02'], ['c', $other, '2026-09-02']] as [$id, $owner, $day]) { DB::table('users')->insert(['member_id' => $id, 'username' => $id, 'agent_id' => $owner->id, 'created_at' => $day . ' 00:00:00']); } DB::table('recharges')->insert(['member_id' => 'a', 'status' => 1, 'amount' => 100, 'exchange_rate' => '7.1250', 'created_at' => '2026-09-02 10:00:00']); DB::table('recharges')->insert(['member_id' => 'a', 'status' => 1, 'amount' => 20, 'created_at' => '2026-09-03 00:00:00']); foreach ([ ['a', PaymentOrderService::TYPE_PAY, PaymentOrderService::STATUS_SUCCESS, 30], ['b', PaymentOrderService::TYPE_SELF_PAY, PaymentOrderService::STATUS_SUCCESS, 40], ['c', PaymentOrderService::TYPE_PAY, PaymentOrderService::STATUS_SUCCESS, 1000], ['a', PaymentOrderService::TYPE_PAYOUT, PaymentOrderService::STATUS_SUCCESS, 1000], ['a', PaymentOrderService::TYPE_PAY, PaymentOrderService::STATUS_STAY, 1000], ] as [$member, $type, $status, $amount]) { DB::table('payment_orders')->insert(['member_id' => $member, 'type' => $type, 'status' => $status, 'amount' => $amount, 'created_at' => '2026-09-02 23:59:59']); } $range = ['start_date' => '2026-08-31', 'end_date' => '2026-09-07']; $days = $this->charts($agent, $range); $this->assertSame('own', $days['scope']); $this->assertNull($days['equity']); $this->assertSame([1, 0, 1, 0, 0, 0, 0, 0], array_column($days['registrations'], 'new_members')); $this->assertSame([0, 0, 2, 1, 0, 0, 0, 0], array_column($days['daily_deposits'], 'deposit_members')); $this->assertSame(['0.0000', '0.0000', '782.5000', '20.0000', '0.0000', '0.0000', '0.0000', '0.0000'], array_column($days['daily_deposits'], 'deposit_amount')); $weeks = $this->charts($agent, $range + ['period' => 'week']); $this->assertSame(['2026-W36', '2026-W37'], array_column($weeks['registrations'], 'period_key')); $this->assertSame([2, 0], array_column($weeks['registrations'], 'new_members')); $this->assertSame($days['daily_deposits'], $weeks['daily_deposits']); $months = $this->charts($agent, $range + ['period' => 'month']); $this->assertSame(['2026-08', '2026-09'], array_column($months['registrations'], 'period_key')); $this->assertSame([1, 1], array_column($months['registrations'], 'new_members')); } public function test_shareholder_chart_shows_current_formal_holdings_and_real_platform_members(): void { Schema::table('users', function (Blueprint $table) { $table->integer('from')->default(1); $table->integer('user_type')->default(0); }); $holder = $this->agent(Agent::TYPE_EQUITY); $other = $this->agent(Agent::TYPE_EQUITY); $test = $this->agents->create(['username' => 'test-holder', 'password' => 'secret1', 'type' => Agent::TYPE_EQUITY, 'is_test' => 1]); foreach ([[$holder, '12.3456'], [$other, '7.6544'], [$test, '9.5000']] as [$shareholder, $percent]) { Agent::query()->whereKey($shareholder->id)->update(['equity_percent' => $percent]); } AgentSetting::current()->update(['company_equity_percent' => '60.5000']); foreach ([['real', null, 1, 0], ['test', $test->id, 1, 0], ['guest', null, 2, 1], ['simulated', null, 1, 2]] as [$id, $agentId, $from, $type]) { DB::table('users')->insert(['member_id' => $id, 'username' => $id, 'agent_id' => $agentId, 'from' => $from, 'user_type' => $type, 'created_at' => now()]); DB::table('recharges')->insert(['member_id' => $id, 'status' => 1, 'amount' => 100, 'created_at' => now()]); } $charts = $this->charts($holder, ['start_date' => '2026-09-07']); $this->assertSame('platform', $charts['scope']); // 真实会员 + 后台模拟会员;游客和测试会员不算 $this->assertSame([2], array_column($charts['registrations'], 'new_members')); $this->assertSame([2], array_column($charts['daily_deposits'], 'deposit_members')); $this->assertSame(['200.0000'], array_column($charts['daily_deposits'], 'deposit_amount')); $this->assertSame([ 'own_percent' => '12.3456', 'other_percent' => '7.6544', 'company_percent' => '60.5000', 'remaining_percent' => '19.5000', ], $charts['equity']); } private function charts(Agent $agent, array $params): array { $request = Request::create('/agent/dashboard/charts', 'GET', $params); $request->attributes->set('agent', $agent); $this->app->instance('request', $request); $response = (new ProfileController())->dashboardCharts($request, new AgentPeriodReportService(), new AgentReportService())->getData(true); $this->assertSame(0, $response['code'], (string) $response['msg']); return $response['data']; } }