新增统计,小游戏,系统设置

This commit is contained in:
2026-06-11 13:49:20 +08:00
parent 3120c56620
commit 064ba727e0
11 changed files with 827 additions and 144 deletions

View File

@@ -265,4 +265,287 @@ class MoneyLog extends Backend
'report_table' => $finalReport
]);
}
public function dailyReport()
{
// 1. 从配置中获取动态的小游戏列表
$games = config('mini_game') ?: [];
// 2. 接收筛选参数,默认展示当月数据
$start = $this->request->param('start/s', date('Y-m-01'));
$end = $this->request->param('end/s', date('Y-m-t'));
$type = $this->request->param('type/s', 'daily'); // daily, monthly, yearly
// 转换时间戳范围
$startTime = strtotime($start . ' 00:00:00');
$endTime = strtotime($end . ' 23:59:59');
// 根据 type 动态决定 MySQL 的日期分组格式
$dateFormat = '%Y-%m-%d';
if ($type === 'monthly') $dateFormat = '%Y-%m';
if ($type === 'yearly') $dateFormat = '%Y';
// 3. 🌟 动态生成小游戏的 SQL 统计子句
$promoSqlPieces = [];
foreach ($games as $gameId => $gameName) {
// 过滤掉不合法的非数字KEY防止 SQL 注入风险
$gameId = (int)$gameId;
// 顺便给生成的列取个易读的别名比如game_268_count, game_268_total
$promoSqlPieces[] = "COUNT(CASE WHEN game_type = {$gameId} THEN 1 END) AS game_{$gameId}_count";
$promoSqlPieces[] = "SUM(CASE WHEN game_type = {$gameId} THEN amount ELSE 0 END) AS game_{$gameId}_total";
}
// 将动态生成的片段用逗号拼接到一起
$dynamicPromoSelect = !empty($promoSqlPieces) ? ', ' . implode(', ', $promoSqlPieces) : '';
// 4. 核心聚合 SQL 1基于余额变动表统计
$moneyLogSql = "
SELECT
FROM_UNIXTIME(create_time, '{$dateFormat}') AS report_date,
COUNT(CASE WHEN type = 1 THEN 1 END) AS deposit_count,
SUM(CASE WHEN type = 1 THEN money ELSE 0 END) / 100 AS deposit_total,
COUNT(CASE WHEN type = 2 THEN 1 END) AS withdraw_count,
SUM(CASE WHEN type = 2 THEN money ELSE 0 END) / 100 AS withdraw_total,
(SUM(CASE WHEN type = 1 THEN money ELSE 0 END) - SUM(CASE WHEN type = 2 THEN money ELSE 0 END)) / 100 AS win_lose,
COUNT(CASE WHEN label = 2 THEN 1 END) AS unclaim_count,
SUM(CASE WHEN label = 2 THEN money ELSE 0 END) / 100 AS unclaim_total,
COUNT(DISTINCT user_id) AS active_member,
COUNT(DISTINCT CASE WHEN label = 1 THEN user_id END) AS new_deposit
FROM ba_user_money_log
WHERE create_time BETWEEN {$startTime} AND {$endTime}
GROUP BY report_date
";
$moneyData = Db::query($moneyLogSql);
// 5. 核心聚合 SQL 2👉 注入动态字段,统计小游戏奖励
$promoSql = "
SELECT
FROM_UNIXTIME(create_time, '{$dateFormat}') AS report_date
{$dynamicPromoSelect} -- 🌟 动态生成的 COUNT 和 SUM 语句放在这里
FROM ba_promo_reward
WHERE create_time BETWEEN {$startTime} AND {$endTime}
GROUP BY report_date
";
$promoData = Db::query($promoSql);
// 6. 新注册用户统计
$userRegisterSql = "
SELECT
FROM_UNIXTIME(create_time, '{$dateFormat}') AS report_date,
COUNT(id) AS new_register
FROM ba_user
WHERE create_time BETWEEN {$startTime} AND {$endTime}
GROUP BY report_date
";
$registerData = class_exists('\think\facade\Db') ? Db::query($userRegisterSql) : [];
// 7. 数据集结构重组与动态初始化
$reportMap = [];
$current = $startTime;
while ($current <= $endTime) {
$dateStr = date(str_replace('%', '', $dateFormat), $current);
// 构建一天的基础数据结构
$baseRow = [
'date' => $dateStr,
'deposit_count' => 0, 'deposit_total' => 0.00,
'withdraw_count' => 0, 'withdraw_total' => 0.00,
'win_lose' => 0.00,
'unclaim_count' => 0, 'unclaim_total' => 0.00,
'active_member' => 0,
'new_deposit' => 0,
'new_register' => 0,
];
// 🌟 动态初始化游戏字段的初始值(全部设为 0
foreach ($games as $gameId => $gameName) {
$baseRow["game_{$gameId}_count"] = 0;
$baseRow["game_{$gameId}_total"] = 0.00;
}
$reportMap[$dateStr] = $baseRow;
$current = strtotime("+1 day", $current);
}
// 合并资产变动数据
foreach ($moneyData as $row) {
if (isset($reportMap[$row['report_date']])) {
$reportMap[$row['report_date']] = array_merge($reportMap[$row['report_date']], $row);
}
}
// 合并动态小游戏数据
foreach ($promoData as $row) {
if (isset($reportMap[$row['report_date']])) {
$reportMap[$row['report_date']] = array_merge($reportMap[$row['report_date']], $row);
}
}
// 合并注册用户数据
foreach ($registerData as $row) {
if (isset($reportMap[$row['report_date']])) {
$reportMap[$row['report_date']]['new_register'] = $row['new_register'];
}
}
$list = array_values($reportMap);
// 9. 返回结果给前端组件
$this->success('Success', [
'start' => $start,
'end' => $end,
'type' => $type,
'games' => $games, // 🌟 把游戏映射送给前端,方便前端循环生成表格的 header 列
'list' => $list,
]);
}
public function customerReport()
{
// 1. 获取动态的小游戏配置
// 示例: [268 => 'plinko ball', 269 => 'smash eggs', 270 => 'spin wheel']
$games = config('mini_game') ?: [];
// 2. 接收前端筛选参数
$start = $this->request->param('start/s', '');
$end = $this->request->param('end/s', '');
$username = $this->request->param('username/s', '');
$loseRebate = $this->request->param('lose_rebate/f', 0); // 输值返利百分比,如输入 5 代表 5%
$limit = $this->request->param('limit/d', 10); // 🌟 每页条数默认10条
// 3. 构建用户主表的查询条件
$userWhere = [];
if (!empty($username)) {
$userWhere[] = ['username', 'LIKE', '%' . (string)$username . '%'];
}
if (!empty($start) || !empty($end)) {
$startTime = !empty($start) ? strtotime($start . ' 00:00:00') : 0;
$endTime = !empty($end) ? strtotime($end . ' 23:59:59') : time();
$userWhere[] = ['create_time', 'between', [$startTime, $endTime]];
}
// 4. 查询基础用户列表 (对应图片中的基本行)
// 🌟 假设你的用户主表去前缀叫 'user',如果不是请更换成实际表名
$users = Db::name('user')
->where($userWhere)
->field('id, username, create_time')
->order('create_time', 'desc') // 对应图片中最新注册的用户排在最上方
->paginate($limit);
$count = $users->total();
$currentPage = $users->currentPage();
$lastPage = $users->lastPage();
// 如果没查到用户,直接返回空列表,免去后续的多表聚合
if (empty($users->items())) {
$this->success('Success', [
'start' => $start,
'end' => $end,
'username' => $username,
'games' => $games,
'list' => [],
'count' => $count,
'current_page' => $currentPage,
'last_page' => $lastPage,
]);
}
// 提取当前页/当前筛选出的所有用户 ID 集合
$userIds = array_column($users->items(), 'id');
$userIdsStr = implode(',', $userIds);
// 5. 🌟 聚合资金流水数据 (Deposit / Withdraw / WinLose)
$moneyLogSql = "
SELECT
user_id,
COUNT(CASE WHEN type = 1 THEN 1 END) AS deposit_count,
SUM(CASE WHEN type = 1 THEN money ELSE 0 END) / 100 AS deposit_total,
COUNT(CASE WHEN type = 2 THEN 1 END) AS withdraw_count,
SUM(CASE WHEN type = 2 THEN money ELSE 0 END) / 100 AS withdraw_total,
(SUM(CASE WHEN type = 1 THEN money ELSE 0 END) - SUM(CASE WHEN type = 2 THEN money ELSE 0 END)) / 100 AS win_lose
FROM ba_user_money_log
WHERE user_id IN ({$userIdsStr})
GROUP BY user_id
";
$moneyData = Db::query($moneyLogSql);
// 重组为以 user_id 为键的关联数组,方便后续读取
$moneySummary = array_column($moneyData, null, 'user_id');
// 6. 🌟 动态拼装并聚合小游戏奖励数据 (与配置中的 gameId 挂钩)
$promoSummary = [];
if (!empty($games)) {
$promoPieces = [];
foreach ($games as $gameId => $name) {
$gameId = (int)$gameId;
$promoPieces[] = "COUNT(CASE WHEN game_type = {$gameId} THEN 1 END) AS game_{$gameId}_count";
$promoPieces[] = "SUM(CASE WHEN game_type = {$gameId} THEN amount ELSE 0 END) AS game_{$gameId}_total";
}
$promoSql = "SELECT user_id, " . implode(', ', $promoPieces) . "
FROM ba_promo_reward
WHERE user_id IN ({$userIdsStr})
GROUP BY user_id";
$promoData = Db::query($promoSql);
$promoSummary = array_column($promoData, null, 'user_id');
}
// 7. 🌟 聚合下线推荐人数 (REFERRAL Downline)
$referralSql = "SELECT parent_id, COUNT(id) AS referral_count
FROM ba_user
WHERE parent_id IN ({$userIdsStr})
GROUP BY parent_id";
$referralData = Db::query($referralSql);
$referralSummary = array_column($referralData, null, 'parent_id');
// 8. 🌟 循环用户主表,拼装全维度报表行
$list = [];
$loseRebatePercent = floatval($loseRebate) / 100; // 转换为小数进行百分比计算
foreach ($users as $user) {
$uid = $user['id'];
// 提取资金数据,若该用户没充提过,则给一套默认清零数据
$money = $moneySummary[$uid] ?? [
'deposit_count' => 0, 'deposit_total' => 0.00,
'withdraw_count' => 0, 'withdraw_total' => 0.00,
'win_lose' => 0.00
];
// 提取下线推荐人数
$referralCount = $referralSummary[$uid]['referral_count'] ?? 0;
// 计算输值返利:只有当 WIN/LOSE 为负数(代表用户输钱)且前端传了返利比时才计算
$winLose = $money['win_lose'];
$calculatedRebate = 0.00;
if ($winLose < 0 && $loseRebatePercent > 0) {
$calculatedRebate = abs($winLose) * $loseRebatePercent;
}
// 组装用户基础维度的列
$row = [
'register_date' => date('Y-m-d H:i:s', $user['create_time']),
'username' => $user['username'],
'deposit_count' => $money['deposit_count'],
'deposit_total' => $money['deposit_total'],
'withdraw_count' => $money['withdraw_count'],
'withdraw_total' => $money['withdraw_total'],
'win_lose' => $winLose,
'lose_rebate' => $calculatedRebate,
'referral_count' => $referralCount,
];
// 🌟 动态注入配置文件中每一个小游戏的数据列
foreach ($games as $gameId => $name) {
$row["game_{$gameId}_count"] = $promoSummary[$uid]["game_{$gameId}_count"] ?? 0;
$row["game_{$gameId}_total"] = $promoSummary[$uid]["game_{$gameId}_total"] ?? 0.00;
}
$list[] = $row;
}
// 9. 返回给前端
$this->success('Success', compact(
'start', 'end', 'username', 'games', 'list',
'count', 'currentPage', 'lastPage'
));
}
}