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:
li
2026-03-27 18:41:06 +08:00
parent 48cfe4be04
commit 4a94fe36cf
87 changed files with 8681 additions and 646 deletions
+67 -2
View File
@@ -49,6 +49,7 @@ require_once $rootDir . '/App/Core/GameFactory.php';
require_once $rootDir . '/App/Core/PK10Algorithm.php';
require_once $rootDir . '/App/Core/DiceAlgorithm.php';
require_once $rootDir . '/App/Core/XocDiaAlgorithm.php';
require_once $rootDir . '/App/Core/SettingsHelper.php';
use Db\Database;
use App\Core\GameFactory;
@@ -208,9 +209,24 @@ function processGame($db, $game) {
} else {
doDraw($db, $periodId, $gameId, $algoClass);
}
// 推送开奖结果到 bot 群组
$drawResult = json_decode($db->get('periods', 'result', ['id' => $periodId]) ?: '[]', true);
if (!empty($drawResult)) {
$pk10Row = $db->get('pk10_results', '*', ['period_id' => $periodId]);
$championSum = $pk10Row ? (int)$pk10Row['champion_sum'] : ($drawResult[0] + $drawResult[1]);
queueBotPush($db, $gameId, 'draw', [
'event' => 'period_drawn',
'period_id' => $periodId,
'period_number' => $periodNum,
'result' => $drawResult,
'champion_sum' => $championSum,
'manual' => false,
], ['remind_draw_result' => 1], $periodNum);
}
logToDb($db, $gameId, $periodId, 'draw', '自动开奖');
$status = 'drawn';
return;
// fall through to settle immediately
}
// === 阶段3:结算 + 开新期 ===
@@ -301,7 +317,8 @@ function doPreDraw($db, $periodId, $gameId, $algoClass) {
]);
if (!empty($waterConfig) && !empty($bets)) {
return $algoClass::generateControlledResult($bets, $waterConfig);
$targetProfitRate = floatval(\App\Core\SettingsHelper::get('target_profit_rate'));
return $algoClass::generateControlledResult($bets, $waterConfig, 100, $targetProfitRate);
}
return $algoClass::generateResult();
}
@@ -356,6 +373,14 @@ function doSettle($db, $period, $algoClass, $gameType) {
$bets = $db->select('bets', '*', ['period_id' => $periodId, 'status' => 'pending']);
if (empty($bets)) {
$db->update('periods', ['status' => 'settled', 'updated_at' => date('Y-m-d H:i:s')], ['id' => $periodId]);
$gameId = (int)$period['game_id'];
queueBotPush($db, $gameId, 'system', [
'event' => 'period_settled',
'period_id' => $periodId,
'period_number' => $period['period_number'],
'wins' => 0, 'losses' => 0, 'total_payout' => 0,
'message' => 'No bets to settle',
], [], $period['period_number']);
logInfo(" ✓ 结算: 无投注,直接结算");
return;
}
@@ -402,6 +427,18 @@ function doSettle($db, $period, $algoClass, $gameType) {
doSettleAgentCommissions($db, $bets, $periodId);
$db->update('periods', ['status' => 'settled', 'updated_at' => date('Y-m-d H:i:s')], ['id' => $periodId]);
// 推送结算结果到 bot 群组
$gameId = (int)$period['game_id'];
queueBotPush($db, $gameId, 'system', [
'event' => 'period_settled',
'period_id' => $periodId,
'period_number' => $period['period_number'],
'wins' => $wins,
'losses' => $losses,
'total_payout' => (float)$totalPayout,
], [], $period['period_number']);
$db->medoo->pdo->commit();
logInfo("{$gameName}结算: 中{$wins}笔 负{$losses}笔 派奖{$totalPayout}");
} catch (\Throwable $e) {
@@ -451,3 +488,31 @@ function doSettleAgentCommissions($db, $bets, $periodId) {
]);
}
}
// ============================
// Bot 推送队列
// ============================
function queueBotPush($db, int $gameId, string $pushType, array $payload, array $groupFilters = [], ?string $periodNumber = null): void {
if ($gameId <= 0) return;
try {
$where = array_merge(['game_id' => $gameId, 'status' => 1], $groupFilters);
$groups = $db->select('bot_groups', ['id'], $where) ?: [];
$now = date('Y-m-d H:i:s');
foreach ($groups as $group) {
$db->insert('bot_push_logs', [
'group_id' => (int)$group['id'],
'period_number' => $periodNumber,
'push_type' => $pushType,
'payload_json' => json_encode($payload, JSON_UNESCAPED_UNICODE),
'status' => 'pending',
'created_at' => $now,
'updated_at' => $now,
]);
}
if (!empty($groups)) {
logInfo(" ✓ Bot推送: {$pushType}" . count($groups) . "个群组");
}
} catch (\Throwable $e) {
logInfo(" ⚠ Bot推送失败: " . $e->getMessage());
}
}