Initial commit: 投注游戏平台初始化

This commit is contained in:
li
2026-02-25 01:26:58 +08:00
commit 77ca2cc8b3
275 changed files with 237479 additions and 0 deletions
+179
View File
@@ -0,0 +1,179 @@
<?php
namespace App\Controllers\Web;
use App\Core\WebBaseController;
use App\Core\I18n;
use Db\Database;
class AgentController extends WebBaseController {
private function getAgent() {
$db = new Database();
$agent = $db->get('agents', '*', ['user_id' => $this->getCurrentUserId(), 'status' => 1]);
if (!$agent) { header('Location: /'); exit; }
return $agent;
}
// 代理后台主页
public function dashboard() {
$this->checkWebLogin();
$agent = $this->getAgent();
I18n::init();
$db = new Database();
$user = $db->get('users', '*', ['id' => $this->getCurrentUserId()]);
$players = $db->select('users', ['id','username','balance','created_at','status'], [
'agent_id' => $agent['id'], 'is_virtual' => 0, 'ORDER' => ['id' => 'DESC']
]);
$today = date('Y-m-d');
$todayRange = [$today . ' 00:00:00', $today . ' 23:59:59'];
$todayComm = $db->sum('agent_commissions', 'commission', [
'agent_id' => $agent['id'], 'created_at[<>]' => $todayRange
]) ?: 0;
$totalComm = $db->sum('agent_commissions', 'commission', ['agent_id' => $agent['id']]) ?: 0;
// 玩家总投注额
$totalBets = $db->sum('bets', 'amount', ['agent_id' => $agent['id'], 'is_virtual' => 0]) ?: 0;
// 下级代理
$subAgents = [];
if ($agent['level'] == 1) {
$subAgents = $db->select('agents', '*', ['parent_id' => $agent['id']]);
foreach ($subAgents as &$sa) {
$sa['user'] = $db->get('users', ['username','balance'], ['id' => $sa['user_id']]);
$sa['player_count'] = $db->count('users', ['agent_id' => $sa['id']]);
}
}
$page = 'home';
extract(compact('agent','user','players','todayComm','totalComm','totalBets','subAgents','page'));
include __DIR__ . '/../../Views/Web/agent_dashboard.php';
}
// 赔率管理页
public function odds() {
$this->checkWebLogin();
$agent = $this->getAgent();
I18n::init();
$db = new Database();
$user = $db->get('users', '*', ['id' => $this->getCurrentUserId()]);
$game = $db->get('games', '*', ['code' => 'pk10']);
// 系统赔率
$sysOdds = $db->select('game_odds', '*', ['game_id' => $game['id']]);
$sysMap = [];
foreach ($sysOdds as $s) $sysMap[$s['type'].'_'.$s['target']] = (float)$s['odds'];
// 上级赔率
$parentMap = $sysMap;
if ($agent['parent_id']) {
$po = $db->select('agent_odds', '*', ['agent_id' => $agent['parent_id'], 'game_id' => $game['id']]);
foreach ($po as $p) $parentMap[$p['bet_type'].'_'.$p['bet_target']] = (float)$p['odds'];
}
// 当前代理赔率
$myOdds = $db->select('agent_odds', '*', ['agent_id' => $agent['id'], 'game_id' => $game['id']]);
$myMap = [];
foreach ($myOdds as $m) $myMap[$m['bet_type'].'_'.$m['bet_target']] = (float)$m['odds'];
$page = 'odds';
extract(compact('agent','user','game','sysMap','parentMap','myMap','page'));
include __DIR__ . '/../../Views/Web/agent_dashboard.php';
}
// 保存赔率
public function setOdds() {
$this->checkWebLogin();
$agent = $this->getAgent();
header('Content-Type: application/json');
$db = new Database();
$data = json_decode(file_get_contents('php://input'), true);
$gameId = (int)($data['game_id'] ?? 0);
// 上级赔率限制
$parentOdds = [];
if ($agent['parent_id']) {
$po = $db->select('agent_odds', '*', ['agent_id' => $agent['parent_id'], 'game_id' => $gameId]);
foreach ($po as $p) $parentOdds[$p['bet_type'].'_'.$p['bet_target']] = (float)$p['odds'];
}
if (empty($parentOdds)) {
$so = $db->select('game_odds', '*', ['game_id' => $gameId]);
foreach ($so as $s) $parentOdds[$s['type'].'_'.$s['target']] = (float)$s['odds'];
}
foreach ($data['odds'] ?? [] as $o) {
$key = $o['bet_type'].'_'.$o['bet_target'];
$newOdds = floatval($o['odds']);
if (isset($parentOdds[$key]) && $newOdds > $parentOdds[$key]) {
echo json_encode(['success'=>false,'message'=>"赔率不能超过上级: $key 上限 ".$parentOdds[$key]]);
return;
}
$existing = $db->get('agent_odds', 'id', [
'agent_id'=>$agent['id'],'game_id'=>$gameId,'bet_type'=>$o['bet_type'],'bet_target'=>$o['bet_target']
]);
if ($existing) {
$db->update('agent_odds', ['odds'=>$newOdds], ['id'=>$existing]);
} else {
$db->insert('agent_odds', [
'agent_id'=>$agent['id'],'game_id'=>$gameId,
'bet_type'=>$o['bet_type'],'bet_target'=>$o['bet_target'],'odds'=>$newOdds
]);
}
}
echo json_encode(['success' => true, 'message' => '赔率已更新']);
}
// 玩家投注记录
public function bets() {
$this->checkWebLogin();
$agent = $this->getAgent();
I18n::init();
$db = new Database();
$user = $db->get('users', '*', ['id' => $this->getCurrentUserId()]);
$betRecords = $db->select('bets', '*', [
'agent_id' => $agent['id'], 'is_virtual' => 0,
'ORDER' => ['id' => 'DESC'], 'LIMIT' => 200
]);
foreach ($betRecords as &$b) {
$b['username'] = $db->get('users', 'username', ['id' => $b['user_id']]) ?: '?';
}
$page = 'bets';
extract(compact('agent','user','betRecords','page'));
include __DIR__ . '/../../Views/Web/agent_dashboard.php';
}
// 佣金明细
public function commissions() {
$this->checkWebLogin();
$agent = $this->getAgent();
I18n::init();
$db = new Database();
$user = $db->get('users', '*', ['id' => $this->getCurrentUserId()]);
$records = $db->select('agent_commissions', '*', [
'agent_id' => $agent['id'], 'ORDER' => ['id' => 'DESC'], 'LIMIT' => 200
]);
foreach ($records as &$r) {
$r['username'] = $db->get('users', 'username', ['id' => $r['from_user_id']]) ?: '?';
}
$page = 'commissions';
extract(compact('agent','user','records','page'));
include __DIR__ . '/../../Views/Web/agent_dashboard.php';
}
// 佣金APIJSON
public function commissionsApi() {
$this->checkWebLogin();
$agent = $this->getAgent();
header('Content-Type: application/json');
$db = new Database();
$records = $db->select('agent_commissions', '*', [
'agent_id' => $agent['id'], 'ORDER' => ['id' => 'DESC'], 'LIMIT' => 100
]);
echo json_encode(['success' => true, 'data' => $records]);
}
}
+187
View File
@@ -0,0 +1,187 @@
<?php
namespace App\Controllers\Web;
use Db\Database;
use App\Core\WebBaseController;
use App\Core\I18n;
class AuthController extends WebBaseController {
public function loginPage() {
if ($this->isLoggedIn()) { header('Location: /'); exit; }
I18n::init();
include __DIR__ . '/../../Views/Web/login.php';
}
public function loginSubmit() {
header('Content-Type: application/json');
$data = json_decode(file_get_contents('php://input'), true);
$username = trim($data['username'] ?? '');
$password = $data['password'] ?? '';
$remember = $data['remember'] ?? false;
if (empty($username) || empty($password)) {
echo json_encode(['success' => false, 'message' => 'Please fill in all fields']);
return;
}
$db = new Database();
$user = $db->get('users', '*', ['username' => $username]);
if (!$user || !password_verify($password, $user['password'])) {
echo json_encode(['success' => false, 'message' => 'Invalid username or password']);
return;
}
if (isset($user['status']) && $user['status'] == 0) {
echo json_encode(['success' => false, 'message' => 'Account disabled']);
return;
}
// 邮箱验证检查(虚拟账户跳过)
if (empty($user['is_virtual']) && isset($user['email_verified']) && !$user['email_verified']) {
echo json_encode(['success' => false, 'message' => 'Please verify your email first', 'need_verify' => true]);
return;
}
if (session_status() === PHP_SESSION_NONE) session_start();
$_SESSION['web_user_id'] = $user['id'];
$_SESSION['web_username'] = $user['username'];
$_SESSION['web_role'] = $user['role'] ?? 'user';
$_SESSION['web_last_activity'] = time();
$_SESSION['web_ip'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['web_ua'] = $_SERVER['HTTP_USER_AGENT'];
if ($remember) $_SESSION['web_remember'] = true;
if (!empty($user['lang'])) $_SESSION['lang'] = $user['lang'];
echo json_encode(['success' => true, 'message' => 'Login successful', 'redirect' => '/']);
}
public function registerSubmit() {
header('Content-Type: application/json');
$data = json_decode(file_get_contents('php://input'), true);
$username = trim($data['username'] ?? '');
$password = $data['password'] ?? '';
$confirmPassword = $data['confirm_password'] ?? '';
$email = trim($data['email'] ?? '');
$inviteCode = trim($data['invite_code'] ?? '');
$verifyCode = trim($data['verify_code'] ?? '');
if (empty($username) || empty($password) || empty($email)) {
echo json_encode(['success' => false, 'message' => 'Please fill in all required fields']);
return;
}
if (!preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
echo json_encode(['success' => false, 'message' => 'Username: 3-20 chars, letters/numbers/underscore only']);
return;
}
if (strlen($password) < 6) {
echo json_encode(['success' => false, 'message' => 'Password must be at least 6 characters']);
return;
}
if ($password !== $confirmPassword) {
echo json_encode(['success' => false, 'message' => 'Passwords do not match']);
return;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo json_encode(['success' => false, 'message' => 'Invalid email address']);
return;
}
$db = new Database();
// 唯一性检查
if ($db->get('users', 'id', ['username' => $username])) {
echo json_encode(['success' => false, 'message' => 'Username already exists']);
return;
}
if ($db->get('users', 'id', ['email' => $email])) {
echo json_encode(['success' => false, 'message' => 'Email already registered']);
return;
}
// 验证邮箱验证码
$ev = $db->get('email_verifications', '*', [
'email' => $email, 'code' => $verifyCode, 'used' => 0,
'expires_at[>]' => date('Y-m-d H:i:s'),
'ORDER' => ['id' => 'DESC']
]);
if (!$ev) {
echo json_encode(['success' => false, 'message' => 'Invalid or expired verification code']);
return;
}
// 处理邀请码 → 代理关联
$agentId = null;
if (!empty($inviteCode)) {
$agent = $db->get('agents', '*', ['agent_code' => $inviteCode, 'status' => 1]);
if ($agent) $agentId = $agent['id'];
}
$db->insert('users', [
'username' => $username,
'password' => password_hash($password, PASSWORD_DEFAULT),
'email' => $email,
'role' => 'user',
'status' => 1,
'email_verified' => 1,
'agent_id' => $agentId,
'lang' => 'en',
'created_at' => date('Y-m-d H:i:s'),
]);
// 标记验证码已用
$db->update('email_verifications', ['used' => 1], ['id' => $ev['id']]);
echo json_encode(['success' => true, 'message' => 'Registration successful', 'redirect' => '/login']);
}
public function sendVerifyCode() {
header('Content-Type: application/json');
$data = json_decode(file_get_contents('php://input'), true);
$email = trim($data['email'] ?? '');
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo json_encode(['success' => false, 'message' => 'Invalid email']);
return;
}
// 频率限制: 60秒内只能发一次
$db = new Database();
$recent = $db->get('email_verifications', '*', [
'email' => $email,
'created_at[>]' => date('Y-m-d H:i:s', time() - 60),
]);
if ($recent) {
echo json_encode(['success' => false, 'message' => 'Please wait 60 seconds']);
return;
}
$code = str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_LEFT);
$db->insert('email_verifications', [
'email' => $email,
'code' => $code,
'expires_at' => date('Y-m-d H:i:s', time() + 600), // 10分钟
'created_at' => date('Y-m-d H:i:s'),
]);
// 发送邮件
$subject = 'Email Verification Code';
$body = "Your verification code is: <b>$code</b><br>Valid for 10 minutes.";
$sent = \App\Core\Mailer::send($email, $subject, $body);
echo json_encode(['success' => true, 'message' => 'Verification code sent']);
}
public function logout() {
if (session_status() === PHP_SESSION_NONE) session_start();
$keys = ['web_user_id','web_username','web_role','web_last_activity','web_ip','web_ua','web_remember','lang'];
foreach ($keys as $k) unset($_SESSION[$k]);
header('Location: /login');
exit;
}
private function isLoggedIn(): bool {
if (session_status() === PHP_SESSION_NONE) session_start();
return isset($_SESSION['web_user_id']);
}
}
+138
View File
@@ -0,0 +1,138 @@
<?php
namespace App\Controllers\Web;
use App\Core\WebBaseController;
use Db\Database;
class BetController extends WebBaseController {
public function placeBet() {
$this->checkWebLogin();
header('Content-Type: application/json');
$userId = $this->getCurrentUserId();
$data = json_decode(file_get_contents('php://input'), true);
if (empty($data['bets']) || empty($data['period_number']) || empty($data['game_id'])) {
echo json_encode(['success' => false, 'message' => 'Missing required fields']);
return;
}
$gameId = (int)$data['game_id'];
$periodNumber = $data['period_number'];
$bets = $data['bets'];
$db = new Database();
// 验证期号
$period = $db->get('periods', '*', ['period_number' => $periodNumber, 'game_id' => $gameId]);
if (!$period || $period['status'] !== 'pending') {
echo json_encode(['success' => false, 'message' => 'Period is not open for betting']);
return;
}
// 赔率映射
$oddsList = $db->select('game_odds', '*', ['game_id' => $gameId]);
$oddsMap = [];
foreach ($oddsList as $o) {
$oddsMap[$o['type'] . '_' . $o['target']] = (float)$o['odds'];
}
// 用户信息
$user = $db->get('users', '*', ['id' => $userId]);
$isVirtual = (int)($user['is_virtual'] ?? 0);
// 代理专属赔率覆盖
if (!empty($user['agent_id'])) {
$agentOdds = $db->select('agent_odds', '*', ['agent_id' => $user['agent_id'], 'game_id' => $gameId]);
foreach ($agentOdds as $ao) {
$oddsMap[$ao['bet_type'] . '_' . $ao['bet_target']] = (float)$ao['odds'];
}
}
// 限额
$limitsRaw = $db->select('bet_limits', '*', ['game_id' => $gameId]);
$limits = [];
foreach ($limitsRaw as $l) { $limits[$l['bet_type']] = $l; }
// 验证投注
$totalAmount = 0;
$validBets = [];
foreach ($bets as $bet) {
$type = $bet['type'] ?? '';
$target = $bet['value'] ?? $bet['target'] ?? '';
$amount = floatval($bet['amount'] ?? 0);
if ($amount <= 0) continue;
$key = $type . '_' . $target;
$odds = $oddsMap[$key] ?? null;
if ($odds === null) {
echo json_encode(['success' => false, 'message' => "Invalid bet: $key"]);
return;
}
// 限额检查
if (isset($limits[$type])) {
if ($amount < (float)$limits[$type]['min_amount']) {
echo json_encode(['success' => false, 'message' => "Min bet for $type: " . $limits[$type]['min_amount']]);
return;
}
if ($amount > (float)$limits[$type]['max_amount']) {
echo json_encode(['success' => false, 'message' => "Max bet for $type: " . $limits[$type]['max_amount']]);
return;
}
}
$totalAmount += $amount;
$validBets[] = ['type' => $type, 'target' => $target, 'amount' => $amount, 'odds' => $odds];
}
if (empty($validBets)) {
echo json_encode(['success' => false, 'message' => 'No valid bets']);
return;
}
// 事务
try {
$db->medoo->pdo->beginTransaction();
$stmt = $db->medoo->pdo->prepare("SELECT balance FROM users WHERE id = :id FOR UPDATE");
$stmt->execute([':id' => $userId]);
$userRow = $stmt->fetch(\PDO::FETCH_ASSOC);
$currentBalance = (float)$userRow['balance'];
if ($currentBalance < $totalAmount) {
$db->medoo->pdo->rollBack();
echo json_encode(['success' => false, 'message' => 'Insufficient balance']);
return;
}
$newBalance = $currentBalance - $totalAmount;
$db->update('users', ['balance' => $newBalance], ['id' => $userId]);
$db->insert('transactions', [
'user_id' => $userId, 'type' => 'bet', 'amount' => -$totalAmount,
'balance_before' => $currentBalance, 'balance_after' => $newBalance,
'related_id' => $period['id'], 'description' => ($db->get('games', 'name', ['id' => $gameId]) ?: 'Game') . ' Bet - ' . $periodNumber,
'is_virtual' => $isVirtual, 'created_at' => date('Y-m-d H:i:s'),
]);
foreach ($validBets as $vb) {
$db->insert('bets', [
'user_id' => $userId, 'game_id' => $gameId,
'period_number' => $periodNumber, 'period_id' => $period['id'],
'bet_type' => $vb['type'], 'bet_value' => $vb['target'],
'amount' => $vb['amount'], 'odds' => $vb['odds'],
'status' => 'pending', 'is_virtual' => $isVirtual,
'agent_id' => $user['agent_id'] ?? null,
'created_at' => date('Y-m-d H:i:s'),
]);
}
$db->medoo->pdo->commit();
echo json_encode(['success' => true, 'message' => 'Bet placed successfully', 'new_balance' => $newBalance]);
} catch (\Throwable $e) {
if ($db->medoo->pdo->inTransaction()) $db->medoo->pdo->rollBack();
echo json_encode(['success' => false, 'message' => 'System error']);
}
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace App\Controllers\Web;
use App\Core\WebBaseController;
use App\Core\GameFactory;
use App\Core\I18n;
use Db\Database;
class GameController extends WebBaseController {
public function play($code) {
$this->checkWebLogin();
I18n::init();
$db = new Database();
$userId = $this->getCurrentUserId();
$user = $db->get('users', ['id','username','balance','lang'], ['id' => $userId]);
$game = $db->get('games', '*', ['code' => $code, 'status' => 1]);
if (!$game) { header('Location: /'); return; }
$isAgent = !!$db->get('agents', 'id', ['user_id' => $userId, 'status' => 1]);
extract([
'user' => $user ?: ['id' => $userId, 'username' => $this->getCurrentUsername(), 'balance' => 0],
'game' => $game, 'isAgent' => $isAgent,
]);
$viewFile = __DIR__ . "/../../Views/Web/{$code}.php";
if (!file_exists($viewFile)) $viewFile = __DIR__ . "/../../Views/Web/game_generic.php";
include $viewFile;
}
public function stats($code) {
$this->checkWebLogin();
I18n::init();
$db = new Database();
$game = $db->get('games', '*', ['code' => $code, 'status' => 1]);
if (!$game) { header('Location: /'); return; }
$gameType = $game['type'] ?? $code;
$algoClass = GameFactory::getAlgorithm($gameType);
$resultTable = $algoClass::getResultTable();
$gameId = $game['id'];
$periods = $db->select('periods', ['id','period_number'], [
'game_id' => $gameId, 'status' => 'settled',
'ORDER' => ['id' => 'DESC'], 'LIMIT' => 100
]);
$results = [];
foreach ($periods as $p) {
if ($resultTable) {
$row = $db->get($resultTable, '*', ['period_id' => $p['id']]);
} else {
$row = $db->get('periods', '*', ['id' => $p['id']]);
}
if ($row) { $row['period_number'] = $p['period_number']; $results[] = $row; }
}
extract(compact('game', 'results'));
$viewFile = __DIR__ . "/../../Views/Web/{$code}_stats.php";
if (!file_exists($viewFile)) $viewFile = __DIR__ . "/../../Views/Web/game_stats_generic.php";
include $viewFile;
}
}
+170
View File
@@ -0,0 +1,170 @@
<?php
namespace App\Controllers\Web;
use App\Core\WebBaseController;
use App\Core\I18n;
use Db\Database;
class HomeController extends WebBaseController {
public function index() {
$this->checkWebLogin();
I18n::init();
$db = new Database();
$userId = $this->getCurrentUserId();
$user = $db->get('users', ['id','username','balance','lang'], ['id' => $userId]);
$game = $db->get('games', '*', ['code' => 'pk10', 'status' => 1]);
extract([
'user' => $user ?: ['id' => $userId, 'username' => $this->getCurrentUsername(), 'balance' => 0],
'game' => $game,
]);
include __DIR__ . '/../../Views/Web/home.php';
}
public function pk10Game() {
$this->checkWebLogin();
I18n::init();
$db = new Database();
$userId = $this->getCurrentUserId();
$user = $db->get('users', ['id','username','balance','lang'], ['id' => $userId]);
$game = $db->get('games', '*', ['code' => 'pk10', 'status' => 1]);
$isAgent = !!$db->get('agents', 'id', ['user_id' => $userId, 'status' => 1]);
extract([
'user' => $user ?: ['id' => $userId, 'username' => $this->getCurrentUsername(), 'balance' => 0],
'game' => $game,
'isAgent' => $isAgent,
]);
include __DIR__ . '/../../Views/Web/pk10.php';
}
public function profile() {
$this->checkWebLogin();
I18n::init();
$db = new Database();
$userId = $this->getCurrentUserId();
$user = $db->get('users', '*', ['id' => $userId]);
// 最近交易
$transactions = $db->select('transactions', '*', [
'user_id' => $userId, 'is_virtual' => 0,
'ORDER' => ['id' => 'DESC'], 'LIMIT' => 20
]);
// 最近投注
$bets = $db->select('bets', '*', [
'user_id' => $userId,
'ORDER' => ['id' => 'DESC'], 'LIMIT' => 20
]);
extract(compact('user', 'transactions', 'bets'));
include __DIR__ . '/../../Views/Web/profile.php';
}
public function bindUsdt() {
$this->checkWebLogin();
header('Content-Type: application/json');
$db = new Database();
$userId = $this->getCurrentUserId();
$data = json_decode(file_get_contents('php://input'), true);
$address = trim($data['usdt_address'] ?? '');
// TRC20地址验证: 以T开头, 34字符, base58
if (!preg_match('/^T[1-9A-HJ-NP-Za-km-z]{33}$/', $address)) {
echo json_encode(['success' => false, 'message' => 'Invalid TRC20 address']);
return;
}
// 唯一性
$exists = $db->get('users', 'id', ['usdt_address' => $address, 'id[!]' => $userId]);
if ($exists) {
echo json_encode(['success' => false, 'message' => 'Address already bound to another account']);
return;
}
$db->update('users', ['usdt_address' => $address, 'usdt_chain' => 'TRC20'], ['id' => $userId]);
echo json_encode(['success' => true, 'message' => 'Address bound successfully']);
}
public function setLang() {
header('Content-Type: application/json');
$data = json_decode(file_get_contents('php://input'), true);
$lang = $data['lang'] ?? 'en';
if (isset(I18n::LANGUAGES[$lang])) {
$_SESSION['lang'] = $lang;
setcookie('lang', $lang, time() + 86400 * 365, '/');
$db = new Database();
$userId = $this->getCurrentUserId();
if ($userId) $db->update('users', ['lang' => $lang], ['id' => $userId]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid language']);
}
}
public function lottery() {
$this->checkWebLogin();
I18n::init();
$db = new Database();
$gameCode = $_GET['game'] ?? 'pk10';
$game = $db->get('games', '*', ['code' => $gameCode, 'status' => 1]);
if (!$game) $game = $db->get('games', '*', ['code' => 'pk10', 'status' => 1]);
$gameId = $game['id'] ?? 0;
$gameType = $game['type'] ?? 'pk10';
$algoClass = \App\Core\GameFactory::getAlgorithm($gameType);
$resultTable = $algoClass::getResultTable();
$periods = $db->select('periods', '*', [
'game_id' => $gameId, 'status' => 'settled',
'ORDER' => ['id' => 'DESC'], 'LIMIT' => 50
]);
foreach ($periods as &$p) {
if ($resultTable) {
$pk = $db->get($resultTable, '*', ['period_id' => $p['id']]);
if ($pk) { foreach ($pk as $k => $v) $p[$k] = $v; }
}
}
unset($p);
extract(compact('game', 'periods'));
include __DIR__ . '/../../Views/Web/lottery.php';
}
public function pk10Stats() {
$this->checkWebLogin();
I18n::init();
$db = new Database();
$game = $db->get('games', '*', ['code' => 'pk10', 'status' => 1]);
$gameId = $game['id'] ?? 0;
// 最近100期已结算结果
$periods = $db->select('periods', ['id','period_number'], [
'game_id' => $gameId, 'status' => 'settled',
'ORDER' => ['id' => 'DESC'], 'LIMIT' => 100
]);
$results = [];
foreach ($periods as $p) {
$pk = $db->get('pk10_results', '*', ['period_id' => $p['id']]);
if ($pk) {
$pk['period_number'] = $p['period_number'];
$results[] = $pk;
}
}
extract(compact('game', 'results'));
include __DIR__ . '/../../Views/Web/pk10_stats.php';
}
public function details() {
$this->checkWebLogin();
I18n::init();
$db = new Database();
$userId = $this->getCurrentUserId();
$settled = $db->select('bets', '*', [
'user_id' => $userId, 'status[!]' => 'pending',
'ORDER' => ['id' => 'DESC'], 'LIMIT' => 50
]);
$pending = $db->select('bets', '*', [
'user_id' => $userId, 'status' => 'pending',
'ORDER' => ['id' => 'DESC'], 'LIMIT' => 50
]);
extract(compact('settled', 'pending'));
include __DIR__ . '/../../Views/Web/details.php';
}
}
+146
View File
@@ -0,0 +1,146 @@
<?php
namespace App\Controllers\Web;
use App\Core\WebBaseController;
use App\Core\GameFactory;
use Db\Database;
class PeriodController extends WebBaseController {
public function getCurrent() {
header('Content-Type: application/json');
$db = new Database();
$gameId = (int)($_GET['game_id'] ?? 0);
if (!$gameId) {
$gameId = (int)($db->get('games', 'id', ['code' => 'pk10']) ?: 0);
}
if (!$gameId) {
echo json_encode(['success' => false, 'message' => 'Game not found']);
return;
}
// 获取游戏类型和算法
$game = $db->get('games', ['id', 'type', 'period_duration', 'lock_before_end'], ['id' => $gameId]);
$gameType = $game['type'] ?? 'pk10';
$algoClass = GameFactory::getAlgorithm($gameType);
$resultTable = $algoClass::getResultTable();
$period = $db->get('periods', '*', [
'game_id' => $gameId,
'ORDER' => ['id' => 'DESC']
]);
$now = time();
$countdown = 0;
$lockCountdown = 0;
if ($period && in_array($period['status'], ['pending', 'locked'])) {
$start = strtotime($period['start_time'] ?? $period['created_at']);
$countdown = max(0, !empty($period['end_time']) ? strtotime($period['end_time']) - $now : 300 - ($now - $start));
$periodDuration = (int)($game['period_duration'] ?? 300);
$lockBefore = (int)($game['lock_before_end'] ?? 30);
$lockCountdown = max(0, ($periodDuration - $lockBefore) - ($now - $start));
}
// 获取结果的通用方法
$getResult = function($periodRow) use ($db, $resultTable, $algoClass) {
if (!$periodRow) return null;
if ($resultTable) {
return $db->get($resultTable, '*', ['period_id' => $periodRow['id']]);
}
// dice/xocdia: 结果在 periods 表中
return $periodRow;
};
// ====== 最近开奖结果 ======
$lastResult = null;
$lastResultStatus = null;
$lastDrawn = $db->get('periods', '*', [
'game_id' => $gameId,
'status' => ['drawn', 'settled'],
'ORDER' => ['id' => 'DESC']
]);
if ($lastDrawn) {
$pk10 = $getResult($lastDrawn);
if ($pk10) {
$lastResult = $pk10;
$lastResult['period_number'] = $lastDrawn['period_number'];
$lastResultStatus = $lastDrawn['status'];
}
}
// ====== locked 状态预开奖结果 ======
$raceResult = null;
if ($period && $period['status'] === 'locked' && !empty($period['result'])) {
$pk10Current = $getResult($period);
if ($pk10Current) {
$raceResult = $pk10Current;
$raceResult['period_number'] = $period['period_number'];
}
}
// history
$history = [];
$recentPeriods = $db->select('periods', ['id','period_number','result','status'], [
'game_id' => $gameId,
'status' => ['drawn', 'settled'],
'ORDER' => ['id' => 'DESC'],
'LIMIT' => 10
]);
foreach ($recentPeriods as $s) {
$pk = $getResult($s);
if ($pk) { $pk['period_number'] = $s['period_number']; $history[] = $pk; }
}
// user bets + balance
$myBets = [];
$balance = null;
$userId = $this->getWebUserId();
if ($userId && $period) {
$betsRaw = $db->select('bets', ['id', 'bet_type', 'bet_value', 'amount', 'odds', 'status', 'win_amount'], [
'user_id' => $userId,
'period_id' => $period['id'],
'ORDER' => ['id' => 'DESC']
]);
$myBets = $betsRaw ?: [];
$userRow = $db->get('users', ['balance'], ['id' => $userId]);
$balance = $userRow ? (float)$userRow['balance'] : null;
}
$lastMyBets = [];
if ($userId && $lastDrawn && $lastDrawn['status'] === 'settled') {
$lastBetsRaw = $db->select('bets', ['id', 'bet_type', 'bet_value', 'amount', 'odds', 'status', 'win_amount'], [
'user_id' => $userId,
'period_id' => $lastDrawn['id'],
'ORDER' => ['id' => 'DESC']
]);
$lastMyBets = $lastBetsRaw ?: [];
}
echo json_encode([
'success' => true,
'data' => [
'id' => $period['id'] ?? 0,
'period_number' => $period['period_number'] ?? '',
'status' => $period['status'] ?? 'none',
'remaining_seconds' => $countdown,
'lock_countdown' => $lockCountdown,
'auto_generated' => (int)($period['auto_generated'] ?? 0),
'server_time' => date('Y-m-d H:i:s'),
],
'last_result' => $lastResult,
'last_result_status' => $lastResultStatus,
'race_result' => $raceResult,
'history' => $history,
'my_bets' => $myBets,
'last_my_bets' => $lastMyBets,
'balance' => $balance,
]);
}
private function getWebUserId(): int {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
return (int)($_SESSION['web_user_id'] ?? 0);
}
}