Initial commit: 投注游戏平台初始化
This commit is contained in:
@@ -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';
|
||||
}
|
||||
|
||||
// 佣金API(JSON)
|
||||
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]);
|
||||
}
|
||||
}
|
||||
Executable
+187
@@ -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']);
|
||||
}
|
||||
}
|
||||
Executable
+138
@@ -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']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Executable
+170
@@ -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';
|
||||
}
|
||||
}
|
||||
Executable
+146
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user