feat: 重构代码架构 + 新增报表/跟单/输赢统计功能
- 拆分 HomeController → TransactionController, ReportWebController, FollowPlanController - 新增 Service 层: TransactionService, ReportService, FollowPlanService - pk10.php JS 抽离为 4 个独立文件 (sound/race/bet/poll) - 前台新增报表查询页面 (/report) + 跟单计划页面 (/follow-plan) - 后台新增跟单计划管理 + 用户输赢明细统计 - 封盘状态显示倒计时 (x:xx) - 音效仅在开奖弹窗打开时播放 - 路由按模块分组整理 - autoload 支持 App\Services 命名空间
This commit is contained in:
@@ -12,94 +12,132 @@ class ReportController extends AdminBaseController {
|
||||
$this->checkLogin(); $this->checkAdmin();
|
||||
$dateFrom = $_GET['from'] ?? date('Y-m-d', strtotime('-7 days'));
|
||||
$dateTo = $_GET['to'] ?? date('Y-m-d');
|
||||
$range = [$dateFrom . ' 00:00:00', $dateTo . ' 23:59:59'];
|
||||
|
||||
// 总投注(排除虚拟)
|
||||
$totalBet = $this->db->sum('bets', 'amount', [
|
||||
'is_virtual' => 0, 'created_at[<>]' => $range
|
||||
]) ?: 0;
|
||||
$reportService = new \App\Services\ReportService($this->db);
|
||||
|
||||
// 总派奖
|
||||
$totalWin = $this->db->sum('bets', 'win_amount', [
|
||||
'is_virtual' => 0, 'status' => 'win', 'created_at[<>]' => $range
|
||||
]) ?: 0;
|
||||
// 总体统计
|
||||
$overview = $reportService->getOverviewStats($dateFrom, $dateTo);
|
||||
extract($overview); // totalBet, totalWin, profit, profitRate, totalDeposit, totalWithdraw, totalCommission, totalRebate, totalUsers, newUsers, activeUsers
|
||||
|
||||
// 平台利润
|
||||
$profit = $totalBet - $totalWin;
|
||||
// 日明细统计
|
||||
$dailyStats = $reportService->getDailyStats($dateFrom, $dateTo);
|
||||
|
||||
// 总充值
|
||||
$totalDeposit = $this->db->sum('transactions', 'amount', [
|
||||
'is_virtual' => 0, 'type[~]' => '%deposit%', 'amount[>]' => 0,
|
||||
'created_at[<>]' => $range
|
||||
]) ?: 0;
|
||||
|
||||
// 总提现
|
||||
$totalWithdraw = abs($this->db->sum('transactions', 'amount', [
|
||||
'is_virtual' => 0, 'type[~]' => '%withdraw%', 'amount[<]' => 0,
|
||||
'created_at[<>]' => $range
|
||||
]) ?: 0);
|
||||
|
||||
// 代理佣金
|
||||
$totalCommission = $this->db->sum('agent_commissions', 'commission', [
|
||||
'created_at[<>]' => $range
|
||||
]) ?: 0;
|
||||
|
||||
// 反水
|
||||
$totalRebate = $this->db->sum('agent_commissions', 'commission', [
|
||||
'type' => 'rebate', 'created_at[<>]' => $range
|
||||
]) ?: 0;
|
||||
|
||||
// 用户统计
|
||||
$totalUsers = $this->db->count('users', ['is_virtual' => 0]);
|
||||
$newUsers = $this->db->count('users', [
|
||||
'is_virtual' => 0, 'created_at[<>]' => $range
|
||||
]);
|
||||
|
||||
// 每日明细
|
||||
$dailyStats = [];
|
||||
$start = new \DateTime($dateFrom);
|
||||
$end = new \DateTime($dateTo);
|
||||
$end->modify('+1 day');
|
||||
$interval = new \DateInterval('P1D');
|
||||
$period = new \DatePeriod($start, $interval, $end);
|
||||
foreach ($period as $day) {
|
||||
$d = $day->format('Y-m-d');
|
||||
$dr = [$d . ' 00:00:00', $d . ' 23:59:59'];
|
||||
$dailyStats[] = [
|
||||
'date' => $d,
|
||||
'bets' => $this->db->sum('bets', 'amount', ['is_virtual'=>0,'created_at[<>]'=>$dr]) ?: 0,
|
||||
'wins' => $this->db->sum('bets', 'win_amount', ['is_virtual'=>0,'status'=>'win','created_at[<>]'=>$dr]) ?: 0,
|
||||
'deposits' => $this->db->sum('transactions', 'amount', ['is_virtual'=>0,'type[~]'=>'%deposit%','amount[>]'=>0,'created_at[<>]'=>$dr]) ?: 0,
|
||||
'withdraws' => abs($this->db->sum('transactions', 'amount', ['is_virtual'=>0,'type[~]'=>'%withdraw%','amount[<]'=>0,'created_at[<>]'=>$dr]) ?: 0),
|
||||
];
|
||||
}
|
||||
// 用户排行(投注前20)
|
||||
$topUsers = $reportService->getTopUsers($dateFrom, $dateTo, 20);
|
||||
|
||||
// 代理报表
|
||||
$agentStats = [];
|
||||
$agents = $this->db->select('agents', '*', ['status' => 1]);
|
||||
foreach ($agents as $ag) {
|
||||
$playerIds = $this->db->select('users', 'id', ['agent_id' => $ag['id'], 'is_virtual' => 0]);
|
||||
$agBet = 0; $agWin = 0;
|
||||
if (!empty($playerIds)) {
|
||||
$agBet = $this->db->sum('bets', 'amount', ['user_id' => $playerIds, 'is_virtual'=>0, 'created_at[<>]'=>$range]) ?: 0;
|
||||
$agWin = $this->db->sum('bets', 'win_amount', ['user_id' => $playerIds, 'is_virtual'=>0, 'status'=>'win', 'created_at[<>]'=>$range]) ?: 0;
|
||||
}
|
||||
$agComm = $this->db->sum('agent_commissions', 'commission', ['agent_id'=>$ag['id'],'created_at[<>]'=>$range]) ?: 0;
|
||||
$agentStats[] = [
|
||||
'agent' => $ag,
|
||||
'user' => $this->db->get('users', ['username'], ['id' => $ag['user_id']]),
|
||||
'players' => count($playerIds),
|
||||
'bets' => $agBet, 'wins' => $agWin, 'commission' => $agComm,
|
||||
'profit' => $agBet - $agWin - $agComm,
|
||||
];
|
||||
}
|
||||
$agentStats = $reportService->getAgentStats($dateFrom, $dateTo);
|
||||
|
||||
// 用户输赢明细
|
||||
$winLossData = $reportService->getUserWinLoss($dateFrom, $dateTo);
|
||||
$userSummary = $winLossData['summary'];
|
||||
|
||||
$this->render('Admin/reports.php', compact(
|
||||
'dateFrom','dateTo','totalBet','totalWin','profit',
|
||||
'dateFrom','dateTo','totalBet','totalWin','profit','profitRate',
|
||||
'totalDeposit','totalWithdraw','totalCommission','totalRebate',
|
||||
'totalUsers','newUsers','dailyStats','agentStats'
|
||||
'totalUsers','newUsers','activeUsers','dailyStats','topUsers','agentStats',
|
||||
'userSummary'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* CSV 导出
|
||||
*/
|
||||
public function export() {
|
||||
$this->checkLogin(); $this->checkAdmin();
|
||||
$dateFrom = $_GET['from'] ?? date('Y-m-d', strtotime('-7 days'));
|
||||
$dateTo = $_GET['to'] ?? date('Y-m-d');
|
||||
|
||||
$reportService = new \App\Services\ReportService($this->db);
|
||||
|
||||
// 通过 Service 获取数据
|
||||
$dailyStats = $reportService->getDailyStats($dateFrom, $dateTo);
|
||||
$topUsers = $reportService->getTopUsers($dateFrom, $dateTo, 0); // 不限条数
|
||||
$winLossData = $reportService->getUserWinLoss($dateFrom, $dateTo);
|
||||
$userSummary = $winLossData['summary'];
|
||||
$userWinLossRaw = $winLossData['raw'];
|
||||
|
||||
// 输出 CSV
|
||||
header('Content-Type: text/csv; charset=utf-8');
|
||||
header('Content-Disposition: attachment; filename="report_' . $dateFrom . '_' . $dateTo . '.csv"');
|
||||
// BOM 头(Excel 兼容中文)
|
||||
echo "\xEF\xBB\xBF";
|
||||
$out = fopen('php://output', 'w');
|
||||
|
||||
// 日明细
|
||||
fputcsv($out, ['=== 日明细报表 ===']);
|
||||
fputcsv($out, ['日期', '投注额', '派彩额', '利润', '投注笔数', '中奖笔数', '胜率']);
|
||||
foreach ($dailyStats as $day) {
|
||||
$totalBet = floatval($day['total_bet'] ?? 0);
|
||||
$totalWin = floatval($day['total_win'] ?? 0);
|
||||
$betCount = intval($day['bet_count'] ?? 0);
|
||||
$winCount = intval($day['win_count'] ?? 0);
|
||||
$winRate = $betCount > 0 ? round($winCount / $betCount * 100, 1) . '%' : '0%';
|
||||
fputcsv($out, [
|
||||
$day['date'],
|
||||
number_format($totalBet, 2, '.', ''),
|
||||
number_format($totalWin, 2, '.', ''),
|
||||
number_format($totalBet - $totalWin, 2, '.', ''),
|
||||
$betCount,
|
||||
$winCount,
|
||||
$winRate
|
||||
]);
|
||||
}
|
||||
|
||||
fputcsv($out, []);
|
||||
fputcsv($out, ['=== 用户投注排行 ===']);
|
||||
fputcsv($out, ['排名', '用户名', '投注额', '派彩额', '平台盈亏', '投注笔数']);
|
||||
$rank = 1;
|
||||
foreach ($topUsers as $user) {
|
||||
fputcsv($out, [
|
||||
$rank++,
|
||||
$user['username'],
|
||||
number_format(floatval($user['total_bet']), 2, '.', ''),
|
||||
number_format(floatval($user['total_win']), 2, '.', ''),
|
||||
number_format(floatval($user['profit']), 2, '.', ''),
|
||||
intval($user['bet_count'])
|
||||
]);
|
||||
}
|
||||
|
||||
// 用户输赢明细
|
||||
fputcsv($out, []);
|
||||
fputcsv($out, ['=== 用户输赢明细 ===']);
|
||||
fputcsv($out, ['用户名', '当前余额', '总投注', '总派彩', '平台盈亏(投注-派彩)', '投注笔数', '中奖笔数', '胜率']);
|
||||
foreach ($userSummary as $ud) {
|
||||
$tb = $ud['total_bet'];
|
||||
$tw = $ud['total_win'];
|
||||
$bc = $ud['bet_count'];
|
||||
$wc = $ud['win_count'];
|
||||
$wr = $bc > 0 ? round($wc / $bc * 100, 1) . '%' : '0%';
|
||||
fputcsv($out, [
|
||||
$ud['username'],
|
||||
number_format($ud['balance'], 2, '.', ''),
|
||||
number_format($tb, 2, '.', ''),
|
||||
number_format($tw, 2, '.', ''),
|
||||
number_format($tb - $tw, 2, '.', ''),
|
||||
$bc, $wc, $wr
|
||||
]);
|
||||
}
|
||||
|
||||
// 用户日输赢明细
|
||||
fputcsv($out, []);
|
||||
fputcsv($out, ['=== 用户日输赢明细 ===']);
|
||||
fputcsv($out, ['用户名', '日期', '投注额', '派彩额', '平台盈亏', '笔数']);
|
||||
foreach ($userWinLossRaw as $dd) {
|
||||
$tb = floatval($dd['total_bet']);
|
||||
$tw = floatval($dd['total_win']);
|
||||
fputcsv($out, [
|
||||
$dd['username'], $dd['date'],
|
||||
number_format($tb, 2, '.', ''),
|
||||
number_format($tw, 2, '.', ''),
|
||||
number_format($tb - $tw, 2, '.', ''),
|
||||
intval($dd['bet_count'])
|
||||
]);
|
||||
}
|
||||
|
||||
fclose($out);
|
||||
exit;
|
||||
}
|
||||
|
||||
private function json($data) { header('Content-Type: application/json'); echo json_encode($data); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user