Files
pk10/App/Controllers/Admin/PK10PeriodController.php
T
li 48cfe4be04 fix: 修复结算后余额不更新和流水缺失问题
1. 去掉PK10结算中is_virtual过滤,统一更新余额和写入transactions
   - PK10PeriodController.php (手动结算)
   - auto_period_task.php (自动结算)
2. live.php轮询回调增加余额同步显示
2026-02-25 19:09:20 +08:00

306 lines
12 KiB
PHP

<?php
namespace App\Controllers\Admin;
use App\Core\AdminBaseController;
use App\Core\PK10Algorithm;
use Db\Database;
class PK10PeriodController extends AdminBaseController {
private $db;
public function __construct(Database $db) {
$this->db = $db;
}
public function index() {
$this->checkLogin();
$gameId = $this->getGameId();
$periods = $this->db->select('periods', '*', [
'game_id' => $gameId,
'ORDER' => ['id' => 'DESC'],
'LIMIT' => 50
]);
// 附加pk10_results
foreach ($periods as &$p) {
$p['pk10'] = $this->db->get('pk10_results', '*', ['period_id' => $p['id']]);
}
$current = $this->db->get('periods', '*', [
'game_id' => $gameId,
'status[!]' => 'settled',
'ORDER' => ['id' => 'DESC']
]);
if ($current) {
$current['pk10'] = $this->db->get('pk10_results', '*', ['period_id' => $current['id']]);
}
$stats = [
'total' => $this->db->count('periods', ['game_id' => $gameId]),
'pending' => $this->db->count('periods', ['game_id' => $gameId, 'status' => 'pending']),
'drawn' => $this->db->count('periods', ['game_id' => $gameId, 'status' => 'drawn']),
'settled' => $this->db->count('periods', ['game_id' => $gameId, 'status' => 'settled']),
];
$this->render('Admin/pk10_periods.php', compact('periods', 'current', 'stats', 'gameId'));
}
public function start() {
$this->checkLogin();
$gameId = $this->getGameId();
// 检查是否有未结算期号
$active = $this->db->get('periods', 'id', [
'game_id' => $gameId,
'status[!]' => 'settled'
]);
if ($active) {
$this->json(['status' => 'error', 'message' => 'There is an active period. Please settle it first.']);
return;
}
$periodNumber = PK10Algorithm::generatePeriodNumber($gameId);
$this->db->insert('periods', [
'game_id' => $gameId,
'period_number' => $periodNumber,
'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'),
]);
$this->json(['status' => 'success', 'message' => 'New period started', 'period_number' => $periodNumber]);
}
public function lock() {
$this->checkLogin();
$data = json_decode(file_get_contents('php://input'), true);
$periodId = (int)($data['period_id'] ?? 0);
$period = $this->db->get('periods', '*', ['id' => $periodId]);
if (!$period || $period['status'] !== 'pending') {
$this->json(['status' => 'error', 'message' => 'Invalid period or status']);
return;
}
$this->db->update('periods', ['status' => 'locked'], ['id' => $periodId]);
$this->json(['status' => 'success', 'message' => 'Period locked']);
}
public function draw() {
$this->checkLogin();
$data = json_decode(file_get_contents('php://input'), true);
$periodId = (int)($data['period_id'] ?? 0);
$manual = $data['manual'] ?? false;
$period = $this->db->get('periods', '*', ['id' => $periodId]);
if (!$period || !in_array($period['status'], ['pending', 'locked'])) {
$this->json(['status' => 'error', 'message' => 'Invalid period or status']);
return;
}
$gameId = $period['game_id'];
if ($manual && !empty($data['result'])) {
// 手动补开奖
$result = array_map('intval', $data['result']);
if (count($result) !== 10 || count(array_unique($result)) !== 10) {
$this->json(['status' => 'error', 'message' => 'Result must be 10 unique numbers 1-10']);
return;
}
} else {
// 自动开奖(带放水机制)
$waterConfig = [];
$wc = $this->db->select('water_control', '*', ['game_id' => $gameId, 'enabled' => 1]);
foreach ($wc as $w) {
$waterConfig[$w['bet_type']] = (float)$w['win_rate_pct'];
}
$bets = $this->db->select('bets', '*', [
'period_id' => $periodId,
'status' => 'pending',
'is_virtual' => 0
]);
if (!empty($waterConfig) && !empty($bets)) {
$result = PK10Algorithm::generateControlledResult($bets, $waterConfig);
} else {
$result = PK10Algorithm::generateResult();
}
}
$sum = $result[0] + $result[1];
$resultJson = json_encode($result);
$this->db->medoo->pdo->beginTransaction();
try {
$this->db->update('periods', [
'status' => 'drawn',
'result' => $resultJson,
'draw_time' => date('Y-m-d H:i:s'),
], ['id' => $periodId]);
// 删除旧结果(如果手动修改)
$this->db->delete('pk10_results', ['period_id' => $periodId]);
$this->db->insert('pk10_results', [
'period_id' => $periodId,
'rank_1' => $result[0], 'rank_2' => $result[1], 'rank_3' => $result[2],
'rank_4' => $result[3], 'rank_5' => $result[4], 'rank_6' => $result[5],
'rank_7' => $result[6], 'rank_8' => $result[7], 'rank_9' => $result[8],
'rank_10' => $result[9], 'champion_sum' => $sum,
]);
$this->db->medoo->pdo->commit();
} catch (\Throwable $e) {
$this->db->medoo->pdo->rollBack();
$this->json(['status' => 'error', 'message' => 'Draw failed: ' . $e->getMessage()]);
return;
}
$this->json(['status' => 'success', 'message' => 'Draw completed', 'result' => $result, 'sum' => $sum]);
}
public function settle() {
$this->checkLogin();
$data = json_decode(file_get_contents('php://input'), true);
$periodId = (int)($data['period_id'] ?? 0);
$period = $this->db->get('periods', '*', ['id' => $periodId]);
if (!$period || $period['status'] !== 'drawn') {
$this->json(['status' => 'error', 'message' => 'Period must be drawn first']);
return;
}
$result = json_decode($period['result'], true);
if (!$result || count($result) !== 10) {
$this->json(['status' => 'error', 'message' => 'Invalid result data']);
return;
}
$bets = $this->db->select('bets', '*', ['period_id' => $periodId, 'status' => 'pending']);
if (empty($bets)) {
$this->db->update('periods', ['status' => 'settled'], ['id' => $periodId]);
$this->json(['status' => 'success', 'message' => 'No bets to settle', 'wins' => 0, 'losses' => 0]);
return;
}
$wins = 0; $losses = 0; $totalPayout = 0;
$this->db->medoo->pdo->beginTransaction();
try {
foreach ($bets as $bet) {
$isWin = PK10Algorithm::checkWin($result, $bet['bet_type'], $bet['bet_value']);
$betAmount = (float)$bet['amount'];
$odds = (float)$bet['odds'];
if ($isWin) {
$winAmount = $betAmount * $odds;
$payout = $betAmount + $winAmount;
$this->db->update('bets', [
'status' => 'win',
'win_amount' => $winAmount,
], ['id' => $bet['id']]);
$stmt = $this->db->medoo->pdo->prepare("SELECT balance FROM users WHERE id = :id FOR UPDATE");
$stmt->execute([':id' => $bet['user_id']]);
$user = $stmt->fetch(\PDO::FETCH_ASSOC);
$newBalance = (float)$user['balance'] + $payout;
$this->db->update('users', ['balance' => $newBalance], ['id' => $bet['user_id']]);
$this->db->insert('transactions', [
'user_id' => $bet['user_id'],
'type' => 'win',
'amount' => $payout,
'balance_before' => $user['balance'],
'balance_after' => $newBalance,
'related_id' => $periodId,
'description' => 'PK10 Win - Period ' . $period['period_number'],
'is_virtual' => $bet['is_virtual'],
'created_at' => date('Y-m-d H:i:s'),
]);
$totalPayout += $payout;
$wins++;
} else {
$this->db->update('bets', ['status' => 'lose', 'win_amount' => 0], ['id' => $bet['id']]);
$losses++;
}
}
// 计算代理佣金
$this->settleAgentCommissions($bets, $periodId);
$this->db->update('periods', ['status' => 'settled'], ['id' => $periodId]);
$this->db->medoo->pdo->commit();
} catch (\Throwable $e) {
$this->db->medoo->pdo->rollBack();
$this->json(['status' => 'error', 'message' => 'Settle failed: ' . $e->getMessage()]);
return;
}
$this->json(['status' => 'success', 'message' => 'Settlement completed',
'wins' => $wins, 'losses' => $losses, 'total_payout' => $totalPayout]);
}
private function settleAgentCommissions(array $bets, int $periodId) {
$agentBets = [];
foreach ($bets as $bet) {
if ((int)$bet['is_virtual']) continue;
$user = $this->db->get('users', ['agent_id'], ['id' => $bet['user_id']]);
if (!$user || !$user['agent_id']) continue;
$agentId = $user['agent_id'];
if (!isset($agentBets[$agentId])) $agentBets[$agentId] = 0;
$agentBets[$agentId] += (float)$bet['amount'];
}
foreach ($agentBets as $agentId => $totalBet) {
$agent = $this->db->get('agents', '*', ['id' => $agentId, 'status' => 1]);
if (!$agent) continue;
$commission = $totalBet * $agent['commission_rate'] / 100;
if ($commission <= 0) continue;
$this->db->insert('agent_commissions', [
'agent_id' => $agentId,
'from_user_id' => 0,
'period_id' => $periodId,
'bet_amount' => $totalBet,
'commission' => $commission,
'type' => 'bet',
'created_at' => date('Y-m-d H:i:s'),
]);
// 佣金加到代理用户余额
$agentUserId = $agent['user_id'];
$stmt = $this->db->medoo->pdo->prepare("SELECT balance FROM users WHERE id = :id FOR UPDATE");
$stmt->execute([':id' => $agentUserId]);
$au = $stmt->fetch(\PDO::FETCH_ASSOC);
$newBal = (float)$au['balance'] + $commission;
$this->db->update('users', ['balance' => $newBal], ['id' => $agentUserId]);
$this->db->insert('transactions', [
'user_id' => $agentUserId, 'type' => 'commission',
'amount' => $commission, 'balance_before' => $au['balance'],
'balance_after' => $newBal, 'related_id' => $periodId,
'description' => 'Agent commission', 'created_at' => date('Y-m-d H:i:s'),
]);
}
}
public function get($id) {
$this->checkLogin();
$period = $this->db->get('periods', '*', ['id' => (int)$id]);
if ($period) {
$period['pk10'] = $this->db->get('pk10_results', '*', ['period_id' => $period['id']]);
}
$this->json(['status' => 'success', 'data' => $period]);
}
private function getGameId(): int {
$game = $this->db->get('games', 'id', ['code' => 'pk10']);
return $game ? (int)$game : 0;
}
private function json(array $data) {
header('Content-Type: application/json');
echo json_encode($data);
}
}