Autosave: 20260221-090248

This commit is contained in:
Flatlogic Bot 2026-02-21 09:02:49 +00:00
parent ef3ca58d34
commit 9c8652e6a3
22 changed files with 249 additions and 109 deletions

View File

@ -12,7 +12,7 @@ if (!hasPermission('view_orders')) {
// Auto-settle expired orders
$db->beginTransaction();
try {
$stmt = $db->prepare("SELECT o.*, u.win_loss_control as user_control FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 0 AND DATE_ADD(o.created_at, INTERVAL o.duration SECOND) <= NOW()");
$stmt = $db->prepare("SELECT o.*, u.win_loss_control as user_control FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND DATE_ADD(o.created_at, INTERVAL o.duration SECOND) <= NOW()");
$stmt->execute();
$expired = $stmt->fetchAll();
foreach ($expired as $o) {
@ -139,7 +139,7 @@ $orders = $stmt->fetchAll();
<?php endif; ?>
</td>
<td>
<?php if ($o['status'] === 0): ?>
<?php if ($o['status'] === 'pending'): ?>
<span class="badge bg-warning">进行中</span>
<?php elseif ($o['status'] === 'won'): ?>
<span class="badge bg-success">已盈利</span>
@ -153,20 +153,20 @@ $orders = $stmt->fetchAll();
<?php if ($o['control_status'] == 1): ?>
<span class="badge bg-success">控赢</span>
<?php elseif ($o['control_status'] == 2): ?>
<span class="badge bg-danger"></span>
<span class="badge bg-danger"></span>
<?php else: ?>
<span class="badge bg-secondary">正常</span>
<?php endif; ?>
</td>
<td class="text-end">
<?php if ($o['status'] === 0): ?>
<?php if ($o['status'] === 'pending'): ?>
<form method="POST" class="d-inline">
<input type="hidden" name="order_id" value="<?= $o['id'] ?>">
<input type="hidden" name="action" value="set_control">
<div class="btn-group btn-group-sm">
<button name="control_status" value="1" class="btn btn-outline-success <?= $o['control_status'] == 1 ? 'active' : '' ?>"></button>
<button name="control_status" value="2" class="btn btn-outline-danger <?= $o['control_status'] == 2 ? 'active' : '' ?>"></button>
<button name="control_status" value="0" class="btn btn-outline-secondary <?= $o['control_status'] == 0 ? 'active' : '' ?>"></button>
<button name="control_status" value="1" class="btn btn-outline-success <?= $o['control_status'] == 1 ? 'active' : '' ?>"></button>
<button name="control_status" value="2" class="btn btn-outline-danger <?= $o['control_status'] == 2 ? 'active' : '' ?>">控输</button>
<button name="control_status" value="0" class="btn btn-outline-secondary <?= $o['control_status'] == 0 ? 'active' : '' ?>">正常</button>
</div>
</form>
<?php endif; ?>

View File

@ -71,16 +71,16 @@ $orders = $stmt->fetchAll();
<td><?= $o['status'] ?></td>
<td>
<?php if ($o['control_status'] == 1): ?><span class="badge bg-success">控赢</span>
<?php elseif ($o['control_status'] == 2): ?><span class="badge bg-danger">控</span>
<?php elseif ($o['control_status'] == 2): ?><span class="badge bg-danger">控</span>
<?php else: ?><span class="badge bg-secondary">正常</span><?php endif; ?>
</td>
<td>
<form method="POST" class="btn-group btn-group-sm">
<input type="hidden" name="order_id" value="<?= $o['id'] ?>">
<input type="hidden" name="action" value="set_control">
<button name="control_status" value="1" class="btn btn-outline-success"></button>
<button name="control_status" value="2" class="btn btn-outline-danger"></button>
<button name="control_status" value="0" class="btn btn-outline-secondary"></button>
<button name="control_status" value="1" class="btn btn-outline-success"></button>
<button name="control_status" value="2" class="btn btn-outline-danger">控输</button>
<button name="control_status" value="0" class="btn btn-outline-secondary">正常</button>
</form>
</td>
</tr>

View File

@ -309,6 +309,7 @@ ob_start();
<script>
let selectedUser = null;
let selectedIp = null;
let selectedSid = null;
let lastMsgId = 0;
let lastChatIds = new Set();
let currentUserContext = '';
@ -354,13 +355,15 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
const userId = u.user_id || 0;
const username = (u.username || '匿名访客').toString();
const uid = (u.uid || '---').toString();
const ip = (u.ip_address || '---').toString();
const rawIp = (u.ip_address || '').toString();
const rawSid = (u.session_id || '').toString();
const displayIp = rawIp || '---';
const rawRemark = (u.remark || '').toString();
const userTime = (u.user_time || '---').toString();
const registrationIp = (u.registration_ip || '---').toString();
// Search filter
if (search && !username.toLowerCase().includes(search) && !ip.includes(search) && !uid.includes(search)) {
if (search && !username.toLowerCase().includes(search) && !rawIp.includes(search) && !uid.includes(search)) {
return;
}
@ -373,7 +376,7 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
lastMsgText = '[收款账号信息]';
}
const isActive = (selectedIp === ip && selectedUser == userId);
const isActive = (selectedSid && rawSid ? selectedSid === rawSid : selectedIp === rawIp) && selectedUser == userId;
// Safe strings for onclick
const jsName = username.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
@ -381,8 +384,8 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
html += `
<div class="user-card ${isActive ? 'active' : ''}"
onclick="openChat('${userId}', '${ip}', '${jsName}', '${uid}', '${jsRemark}', '${userTime}', '${registrationIp}')">
<i class="bi bi-trash text-muted delete-chat-btn" title="删除会话" onclick="deleteChat('${userId}', '${ip}', event)"></i>
onclick="openChat('${userId}', '${rawIp}', '${rawSid}', '${jsName}', '${uid}', '${jsRemark}', '${userTime}', '${registrationIp}')">
<i class="bi bi-trash text-muted delete-chat-btn" title="删除会话" onclick="deleteChat('${userId}', '${rawIp}', '${rawSid}', event)"></i>
<div class="d-flex justify-content-between mb-1">
<span class="fw-bold small text-truncate" style="max-width: 150px;">${username}</span>
<span class="text-muted" style="font-size: 10px;">${isNaN(lastTime.getTime()) ? '---' : lastTime.toLocaleTimeString('zh-CN', {hour: '2-digit', minute:'2-digit'})}</span>
@ -391,7 +394,7 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
<div class="small text-truncate text-muted mb-1" style="font-size: 12px;">${lastMsgText}</div>
<div class="d-flex justify-content-between align-items-center" style="font-size: 10px; margin-top: 4px; padding: 4px; background: rgba(0,123,255,0.05); border-radius: 4px;">
<span class="text-secondary">UID: ${uid}</span>
<span class="text-primary fw-bold"><i class="bi bi-geo-alt-fill me-1"></i>${ip}</span>
<span class="text-primary fw-bold"><i class="bi bi-geo-alt-fill me-1"></i>${displayIp}</span>
</div>
<div class="mt-1 text-muted" style="font-size: 9px;"><i class="bi bi-person-plus me-1"></i>注册IP: ${registrationIp}</div>
</div>
@ -407,9 +410,10 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
}
}
function openChat(userId, ip, name, uid, remark, userTime, regIp) {
function openChat(userId, ip, sid, name, uid, remark, userTime, regIp) {
selectedUser = userId;
selectedIp = ip;
selectedSid = sid;
document.getElementById('header-name').innerText = name;
document.getElementById('header-uid').innerText = uid;
document.getElementById('info-ip-header').innerText = ip;
@ -419,11 +423,13 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
document.getElementById('remark-area').style.display = 'block';
// IP Location fetch
fetch(`https://ipapi.co/${ip}/json/`).then(r => r.json()).then(data => {
if (data.city) {
document.getElementById('info-ip-header').innerText = `${ip} (${data.city}, ${data.country_name})`;
}
}).catch(() => {});
if (ip && ip !== '---') {
fetch(`https://ipapi.co/${ip}/json/`).then(r => r.json()).then(data => {
if (data.city) {
document.getElementById('info-ip-header').innerText = `${ip} (${data.city}, ${data.country_name})`;
}
}).catch(() => {});
}
document.getElementById('remark-text').value = remark;
document.getElementById('info-uid').innerText = uid;
@ -446,18 +452,20 @@ async function recallMessage(msgId) {
}
}
async function deleteChat(userId, ip, event) {
async function deleteChat(userId, ip, sid, event) {
if (event) event.stopPropagation();
if (!confirm('确定删除该会话及其所有记录吗?')) return;
const fd = new URLSearchParams();
fd.append('user_id', userId);
fd.append('ip_address', ip);
fd.append('session_id', sid);
const r = await fetch('/api/chat.php?action=admin_delete_user', { method: 'POST', body: fd });
const res = await r.json();
if (res.success) {
if (selectedUser == userId && selectedIp == ip) {
if (selectedSid == sid && selectedIp == ip && selectedUser == userId) {
selectedUser = null;
selectedIp = null;
selectedSid = null;
document.getElementById('chat-header').style.display = 'none';
document.getElementById('input-area').style.display = 'none';
document.getElementById('remark-area').style.display = 'none';
@ -477,11 +485,13 @@ async function deleteUser() {
const fd = new URLSearchParams();
fd.append('user_id', selectedUser);
fd.append('ip_address', selectedIp);
fd.append('session_id', selectedSid);
const r = await fetch('/api/chat.php?action=admin_delete_user', { method: 'POST', body: fd });
const res = await r.json();
if (res.success) {
selectedUser = null;
selectedIp = null;
selectedSid = null;
document.getElementById('chat-header').style.display = 'none';
document.getElementById('input-area').style.display = 'none';
document.getElementById('remark-area').style.display = 'none';
@ -496,13 +506,13 @@ async function deleteUser() {
}
async function fetchMessages() {
if (!selectedIp && !selectedUser) return;
if (!selectedIp && !selectedUser && !selectedSid) return;
try {
const r = await fetch(`/api/chat.php?action=get_messages&user_id=${selectedUser}&ip=${selectedIp}`);
const r = await fetch(`/api/chat.php?action=get_messages&user_id=${selectedUser}&ip=${selectedIp}&session_id=${selectedSid}`);
const msgs = await r.json();
if (!msgs || !Array.isArray(msgs)) return;
const context = selectedUser + '_' + selectedIp;
const context = selectedUser + '_' + selectedIp + '_' + selectedSid;
if (currentUserContext !== context) {
document.getElementById('messages-area').innerHTML = '';
lastChatIds.clear();
@ -709,6 +719,7 @@ document.getElementById('image-input').addEventListener('change', async (e) => {
formData.append('file', file);
formData.append('user_id', selectedUser || 0);
formData.append('ip_address', selectedIp || '');
formData.append('session_id', selectedSid || '');
try {
const r = await fetch('/api/chat.php?action=upload_image', {
@ -758,6 +769,7 @@ document.getElementById('chat-form').addEventListener('submit', async (e) => {
fd.append('message', msg);
fd.append('user_id', selectedUser);
fd.append('ip_address', selectedIp);
fd.append('session_id', selectedSid);
try {
const r = await fetch('/api/chat.php?action=admin_send', { method: 'POST', body: fd });
@ -779,6 +791,7 @@ document.getElementById('save-remark-btn').addEventListener('click', async () =>
const fd = new URLSearchParams();
fd.append('user_id', selectedUser);
fd.append('ip_address', selectedIp);
fd.append('session_id', selectedSid);
fd.append('remark', remark);
const r = await fetch('/api/chat.php?action=save_remark', { method: 'POST', body: fd });

View File

@ -112,8 +112,8 @@ ob_start();
<td><?= number_format($o['amount'], 2) ?></td>
<td><span class="text-<?= $o['direction']=='buy'?'success':'danger' ?>"><?= $o['direction']=='buy'?'涨':'跌' ?></span></td>
<td>
<?php if ($o['control_status']==1): ?><span class="badge bg-success">赢</span>
<?php elseif ($o['control_status']==2): ?><span class="badge bg-danger"></span>
<?php if ($o['control_status']==1): ?><span class="badge bg-success">赢</span>
<?php elseif ($o['control_status']==2): ?><span class="badge bg-danger">控输</span>
<?php else: ?>-<?php endif; ?>
</td>
</tr>

View File

@ -55,7 +55,7 @@ if ($admin['is_agent']) {
$pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'recharge' AND r.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(r.created_at) > ?", [$agent_id, $cleared_recharge]);
$pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'withdrawal' AND r.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(r.created_at) > ?", [$agent_id, $cleared_recharge]);
$pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND agent_id = ? AND UNIX_TIMESTAMP(created_at) > ?", [$agent_id, $cleared_kyc]);
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_binary]);
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_binary]);
$active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_spot]);
$active_contract = getCount($db, "SELECT COUNT(*) FROM contract_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'open' AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_contract]);
$new_messages = getCount($db, "SELECT COUNT(*) FROM messages m JOIN users u ON m.user_id = u.id WHERE m.sender = 'user' AND u.agent_id = ? AND UNIX_TIMESTAMP(m.created_at) > ?", [$agent_id, $cleared_messages]);
@ -64,7 +64,7 @@ if ($admin['is_agent']) {
$pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'recharge' AND status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_recharge]);
$pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'withdrawal' AND status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_recharge]);
$pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_kyc]);
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders WHERE status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_binary]);
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders WHERE status = 'pending' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_binary]);
$active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders WHERE status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_spot]);
$active_contract = getCount($db, "SELECT COUNT(*) FROM contract_orders WHERE status = 'open' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_contract]);
$new_messages = getCount($db, "SELECT COUNT(*) FROM messages WHERE sender = 'user' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_messages]);

View File

@ -52,7 +52,7 @@ if ($action === 'place_order') {
->execute([$amount, $user_id]);
// Insert order
$stmt = $db->prepare("INSERT INTO binary_orders (user_id, symbol, direction, amount, duration, entry_price, profit_rate, status, created_at, ip_address) VALUES (?, ?, ?, ?, ?, ?, ?, 0, NOW(), ?)");
$stmt = $db->prepare("INSERT INTO binary_orders (user_id, symbol, direction, amount, duration, entry_price, profit_rate, status, created_at, ip_address) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', NOW(), ?)");
$stmt->execute([$user_id, $symbol, $direction, $amount, $duration, $entry_price, $profit_rate, getRealIP()]);
$order_id = $db->lastInsertId();
@ -73,7 +73,7 @@ if ($action === 'settle_order') {
$stmt->execute([$order_id, $user_id]);
$order = $stmt->fetch();
if (!$order || $order['status'] !== 0) {
if (!$order || $order['status'] !== 'pending') {
echo json_encode(['success' => false, 'error' => __('no_records_found')]);
exit;
}

View File

@ -33,23 +33,29 @@ if ($action === 'upload_image' || (isset($_POST['action']) && $_POST['action'] =
if (isset($_SESSION['admin_id'])) {
$user_id = (int)($_POST['user_id'] ?? 0);
$ip = $_POST['ip_address'] ?? '';
$sid = $_POST['session_id'] ?? '';
if ($ip === '---') $ip = '';
// If IP is missing, try to get it from messages or fallback to current IP
if (empty($ip)) {
if (empty($ip) && empty($sid)) {
if ($user_id != 0) {
$stmt = db()->prepare("SELECT ip_address FROM messages WHERE user_id = ? AND ip_address != '' ORDER BY id DESC LIMIT 1");
$stmt = db()->prepare("SELECT ip_address, session_id FROM messages WHERE user_id = ? AND ip_address != '' AND ip_address != '---' ORDER BY id DESC LIMIT 1");
$stmt->execute([$user_id]);
$ip = $stmt->fetchColumn() ?: getRealIP();
} else {
$row = $stmt->fetch();
$ip = $row['ip_address'] ?? '';
$sid = $row['session_id'] ?? '';
}
if (empty($ip)) {
$ip = getRealIP();
}
}
$sender = 'admin';
$admin_id = $_SESSION['admin_id'];
$stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $admin_id, $sender, $message, $ip]);
$stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $admin_id, $sender, $message, $ip, $sid]);
} else {
$user_id = (int)($_SESSION['user_id'] ?? 0);
$ip = getRealIP();
$sid = session_id();
// Fallback: If user_id is 0 but we find a user with this registration IP, associate it
if ($user_id === 0) {
@ -59,8 +65,8 @@ if ($action === 'upload_image' || (isset($_POST['action']) && $_POST['action'] =
}
$sender = 'user';
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address) VALUES (?, ?, ?, ?)");
$stmt->execute([$user_id, $sender, $message, $ip]);
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $sender, $message, $ip, $sid]);
}
$newId = db()->lastInsertId();
@ -69,8 +75,8 @@ if ($action === 'upload_image' || (isset($_POST['action']) && $_POST['action'] =
// Update visitors table to ensure immediate visibility for both users and admins
if ($sender === 'user') {
$user_time = date('H:i:s');
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, user_time) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP");
$stmt->execute([$user_id, $ip, $user_time]);
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, session_id, user_time) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, session_id = VALUES(session_id)");
$stmt->execute([$user_id, $ip, $sid, $user_time]);
}
echo json_encode([
@ -93,16 +99,17 @@ if ($action === 'get_messages') {
$user_id = $_SESSION['user_id'] ?? 0;
$target_user_id = $_GET['user_id'] ?? $user_id;
$target_ip = $_GET['ip'] ?? getRealIP();
$target_sid = $_GET['session_id'] ?? '';
// If admin is requesting, we use the provided user_id and ip
// If admin is requesting, we use the provided user_id and ip/session
if (isset($_SESSION['admin_id'])) {
$stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (ip_address = ? AND ip_address != '') ORDER BY created_at ASC");
$stmt->execute([$target_user_id, $target_ip]);
$stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (session_id = ? AND session_id != '') OR (ip_address = ? AND ip_address != '' AND session_id = '') ORDER BY created_at ASC");
$stmt->execute([$target_user_id, $target_sid, $target_ip]);
} else {
// User requesting their own messages
// If logged in, get by user_id. Also get by IP to catch visitor history.
$user_id = (int)($_SESSION['user_id'] ?? 0);
$ip = getRealIP();
$sid = session_id();
// Fallback: If user_id is 0 but we find a user with this IP, associate it
if ($user_id === 0) {
@ -111,8 +118,8 @@ if ($action === 'get_messages') {
$user_id = (int)($stmt->fetchColumn() ?: 0);
}
$stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (ip_address = ? AND ip_address != '') ORDER BY created_at ASC");
$stmt->execute([$user_id, $ip]);
$stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (session_id = ?) OR (ip_address = ? AND ip_address != '' AND session_id = '') ORDER BY created_at ASC");
$stmt->execute([$user_id, $sid, $ip]);
}
$messages = $stmt->fetchAll();
@ -126,6 +133,7 @@ if ($action === 'send_message') {
$user_id = (int)($_SESSION['user_id'] ?? 0);
$ip = getRealIP();
$sid = session_id();
// Fallback: If user_id is 0 but we find a user with this registration IP, associate it
if ($user_id === 0) {
@ -134,14 +142,14 @@ if ($action === 'send_message') {
$user_id = (int)($stmt->fetchColumn() ?: 0);
}
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address) VALUES (?, ?, ?, ?)");
$stmt->execute([$user_id, 'user', $message, $ip]);
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$user_id, 'user', $message, $ip, $sid]);
$newId = db()->lastInsertId();
// Also update visitors table to ensure immediate visibility
$user_time = date('H:i:s');
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, user_time) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP");
$stmt->execute([$user_id, $ip, $user_time]);
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, session_id, user_time) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, session_id = VALUES(session_id)");
$stmt->execute([$user_id, $ip, $sid, $user_time]);
echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => 'user', 'message' => $message, 'created_at' => date('Y-m-d H:i:s')]]);
exit;
@ -153,16 +161,21 @@ if ($action === 'admin_send') {
$message = $_POST['message'] ?? '';
$user_id = (int)($_POST['user_id'] ?? 0);
$target_ip = $_POST['ip_address'] ?? '';
$target_sid = $_POST['session_id'] ?? '';
if ($target_ip === '---') $target_ip = '';
if (!$message) exit(json_encode(['success' => false, 'error' => 'Empty message']));
// Robust IP matching: if target_ip is empty but user_id is set, try to find their last IP
if (empty($target_ip)) {
// Robust IP/Session matching: if target is empty but user_id is set, try to find their last known details
if (empty($target_ip) && empty($target_sid)) {
if ($user_id != 0) {
$stmt = db()->prepare("SELECT ip_address FROM messages WHERE user_id = ? AND ip_address != '' ORDER BY id DESC LIMIT 1");
$stmt = db()->prepare("SELECT ip_address, session_id FROM messages WHERE user_id = ? AND ip_address != '' AND ip_address != '---' ORDER BY id DESC LIMIT 1");
$stmt->execute([$user_id]);
$target_ip = $stmt->fetchColumn() ?: getRealIP();
} else {
$row = $stmt->fetch();
$target_ip = $row['ip_address'] ?? '';
$target_sid = $row['session_id'] ?? '';
}
if (empty($target_ip)) {
$target_ip = getRealIP();
}
}
@ -170,8 +183,8 @@ if ($action === 'admin_send') {
$admin_id = $_SESSION['admin_id'] ?? 1;
$sender = 'admin';
$stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $admin_id, $sender, $message, $target_ip]);
$stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $admin_id, $sender, $message, $target_ip, $target_sid]);
$newId = db()->lastInsertId();
echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => $sender, 'message' => $message, 'created_at' => date('Y-m-d H:i:s')]]);
exit;
@ -181,9 +194,10 @@ if ($action === 'admin_send') {
if ($action === 'ping') {
$user_id = $_SESSION['user_id'] ?? 0;
$ip = getRealIP();
$sid = session_id();
$user_time = $_GET['user_time'] ?? '';
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, user_time) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, user_time = ?");
$stmt->execute([$user_id, $ip, $user_time, $user_time]);
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, session_id, user_time) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, user_time = ?, session_id = VALUES(session_id)");
$stmt->execute([$user_id, $ip, $sid, $user_time, $user_time]);
echo json_encode(['success' => true]);
exit;
}
@ -196,18 +210,20 @@ if ($action === 'admin_get_all') {
exit;
}
try {
// Robust query to get all active chat sessions, merging guest sessions into user sessions if IP matches
// Robust query to get all active chat sessions, merging guest sessions into user sessions if IP or Session matches
// Grouping by (final_user_id, effective_sid, effective_ip) to ensure "One ID, One Conversation"
$stmt = db()->query("
SELECT
v.final_user_id as user_id,
v.ip_address,
v.effective_ip as ip_address,
v.effective_sid as session_id,
CASE
WHEN m.message LIKE '<img%' THEN '[图片消息]'
WHEN (m.message IS NULL OR m.message = '') AND v.has_recharge = 1 THEN '[充值申请]'
ELSE COALESCE(m.message, '新会话')
END as message,
COALESCE(m.created_at, v.last_activity) as created_at,
COALESCE(u.username, u.email, CONCAT('访客 ', COALESCE(v.ip_address, '0.0.0.0'))) as username,
COALESCE(u.username, u.email, CONCAT('访客 ', COALESCE(NULLIF(v.effective_ip, ''), '0.0.0.0'))) as username,
IFNULL(u.uid, '---') as uid,
IFNULL(u.registration_ip, '---') as registration_ip,
IFNULL(r.remark, '') as remark,
@ -215,47 +231,55 @@ if ($action === 'admin_get_all') {
FROM (
SELECT
final_user_id,
MAX(ip_address) as ip_address,
effective_ip,
effective_sid,
MAX(last_activity) as last_activity,
MAX(user_time) as user_time,
MAX(has_recharge) as has_recharge
FROM (
SELECT
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = messages.ip_address LIMIT 1), 0) as final_user_id,
IFNULL(ip_address, '') as ip_address,
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = m.ip_address AND m.ip_address != '---' AND m.ip_address != '' LIMIT 1), 0) as final_user_id,
CASE WHEN ip_address = '---' THEN '' ELSE IFNULL(ip_address, '') END as effective_ip,
IFNULL(session_id, '') as effective_sid,
created_at as last_activity,
NULL as user_time,
0 as has_recharge
FROM messages
FROM messages m
UNION ALL
SELECT
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = chat_visitors.ip_address LIMIT 1), 0) as final_user_id,
IFNULL(ip_address, '') as ip_address,
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = cv.ip_address AND cv.ip_address != '---' AND cv.ip_address != '' LIMIT 1), 0) as final_user_id,
CASE WHEN ip_address = '---' THEN '' ELSE IFNULL(ip_address, '') END as effective_ip,
IFNULL(session_id, '') as effective_sid,
last_ping as last_activity,
user_time,
0 as has_recharge
FROM chat_visitors
FROM chat_visitors cv
UNION ALL
SELECT
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = finance_requests.ip_address LIMIT 1), 0) as final_user_id,
IFNULL(ip_address, '') as ip_address,
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = fr.ip_address AND fr.ip_address != '---' AND fr.ip_address != '' LIMIT 1), 0) as final_user_id,
CASE WHEN ip_address = '---' THEN '' ELSE IFNULL(ip_address, '') END as effective_ip,
'' as effective_sid,
created_at as last_activity,
NULL as user_time,
1 as has_recharge
FROM finance_requests
FROM finance_requests fr
) t1
GROUP BY (CASE WHEN final_user_id = 0 THEN 0 ELSE final_user_id END), (CASE WHEN final_user_id = 0 THEN ip_address ELSE '0' END)
GROUP BY final_user_id, (CASE WHEN final_user_id = 0 THEN effective_sid ELSE '' END), (CASE WHEN final_user_id = 0 AND effective_sid = '' THEN effective_ip ELSE '' END)
) v
LEFT JOIN (
SELECT m1.* FROM messages m1
SELECT m1.*, m2.final_user_id as m_final_user_id, m2.effective_ip as m_effective_ip, m2.effective_sid as m_effective_sid FROM messages m1
INNER JOIN (
SELECT MAX(id) as max_id FROM (
SELECT id, COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = messages.ip_address LIMIT 1), 0) as final_user_id, ip_address FROM messages
) t2 GROUP BY (CASE WHEN final_user_id = 0 THEN 0 ELSE final_user_id END), (CASE WHEN final_user_id = 0 THEN ip_address ELSE '0' END)
SELECT MAX(id) as max_id, final_user_id, effective_ip, effective_sid FROM (
SELECT id,
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = messages.ip_address AND messages.ip_address != '---' AND messages.ip_address != '' LIMIT 1), 0) as final_user_id,
CASE WHEN ip_address = '---' THEN '' ELSE IFNULL(ip_address, '') END as effective_ip,
IFNULL(session_id, '') as effective_sid
FROM messages
) t2 GROUP BY final_user_id, (CASE WHEN final_user_id = 0 THEN effective_sid ELSE '' END), (CASE WHEN final_user_id = 0 AND effective_sid = '' THEN effective_ip ELSE '' END)
) m2 ON m1.id = m2.max_id
) m ON (v.final_user_id = COALESCE(NULLIF(m.user_id, 0), (SELECT id FROM users WHERE registration_ip = m.ip_address LIMIT 1), 0))
) m ON (v.final_user_id = m.m_final_user_id AND (v.final_user_id != 0 OR (v.effective_sid != '' AND v.effective_sid = m.m_effective_sid) OR (v.effective_sid = '' AND v.effective_ip = m.m_effective_ip)))
LEFT JOIN users u ON (v.final_user_id = u.id AND v.final_user_id != 0)
LEFT JOIN chat_remarks r ON (v.final_user_id = COALESCE(r.user_id, 0) AND (v.final_user_id != 0 OR IFNULL(v.ip_address, '') = IFNULL(m.ip_address, '')))
LEFT JOIN chat_remarks r ON (v.final_user_id = r.user_id AND (v.final_user_id != 0 OR (v.effective_sid != '' AND v.effective_sid = r.session_id) OR (v.effective_sid = '' AND v.effective_ip = r.ip_address)))
ORDER BY created_at DESC
");
echo json_encode($stmt->fetchAll());
@ -271,10 +295,12 @@ if ($action === 'save_remark') {
$user_id = $_POST['user_id'] ?? 0;
$ip = $_POST['ip_address'] ?? '';
$sid = $_POST['session_id'] ?? '';
if ($ip === '---') $ip = '';
$remark = $_POST['remark'] ?? '';
$stmt = db()->prepare("INSERT INTO chat_remarks (user_id, ip_address, remark) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE remark = ?");
$stmt->execute([$user_id, $ip, $remark, $remark]);
$stmt = db()->prepare("INSERT INTO chat_remarks (user_id, ip_address, session_id, remark) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE remark = ?, session_id = VALUES(session_id)");
$stmt->execute([$user_id, $ip, $sid, $remark, $remark]);
echo json_encode(['success' => true]);
exit;
@ -285,9 +311,11 @@ if ($action === 'get_remark') {
$user_id = $_GET['user_id'] ?? 0;
$ip = $_GET['ip'] ?? '';
$sid = $_GET['session_id'] ?? '';
if ($ip === '---') $ip = '';
$stmt = db()->prepare("SELECT remark FROM chat_remarks WHERE user_id = ? AND ip_address = ?");
$stmt->execute([$user_id, $ip]);
$stmt = db()->prepare("SELECT remark FROM chat_remarks WHERE user_id = ? AND (session_id = ? AND session_id != '') OR (user_id != 0 AND user_id = ?) OR (ip_address = ? AND ip_address != '' AND session_id = '')");
$stmt->execute([$user_id, $sid, $user_id, $ip]);
$remark = $stmt->fetchColumn() ?: '';
echo json_encode(['success' => true, 'remark' => $remark]);
@ -298,6 +326,8 @@ if ($action === 'admin_delete_user') {
if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized']));
$user_id = $_POST['user_id'] ?? 0;
$ip = $_POST['ip_address'] ?? '';
$sid = $_POST['session_id'] ?? '';
if ($ip === '---') $ip = '';
if ($user_id != 0) {
$stmt = db()->prepare("DELETE FROM messages WHERE user_id = ?");
@ -307,12 +337,12 @@ if ($action === 'admin_delete_user') {
$stmt = db()->prepare("DELETE FROM chat_remarks WHERE user_id = ?");
$stmt->execute([$user_id]);
} else {
$stmt = db()->prepare("DELETE FROM messages WHERE user_id = 0 AND ip_address = ?");
$stmt->execute([$ip]);
$stmt = db()->prepare("DELETE FROM chat_visitors WHERE user_id = 0 AND ip_address = ?");
$stmt->execute([$ip]);
$stmt = db()->prepare("DELETE FROM chat_remarks WHERE user_id = 0 AND ip_address = ?");
$stmt->execute([$ip]);
$stmt = db()->prepare("DELETE FROM messages WHERE user_id = 0 AND (session_id = ? OR ip_address = ?)");
$stmt->execute([$sid, $ip]);
$stmt = db()->prepare("DELETE FROM chat_visitors WHERE user_id = 0 AND (session_id = ? OR ip_address = ?)");
$stmt->execute([$sid, $ip]);
$stmt = db()->prepare("DELETE FROM chat_remarks WHERE user_id = 0 AND (session_id = ? OR ip_address = ?)");
$stmt->execute([$sid, $ip]);
}
echo json_encode(['success' => true]);
exit;

View File

@ -32,7 +32,7 @@ if ($action === 'get_orders') {
if ($tab === 'binary') {
// Auto-settle expired orders
$stmt = $db->prepare("SELECT o.*, u.win_loss_control as user_control FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.user_id = ? AND o.status = 0 AND DATE_ADD(o.created_at, INTERVAL o.duration SECOND) <= NOW()");
$stmt = $db->prepare("SELECT o.*, u.win_loss_control as user_control FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.user_id = ? AND o.status = 'pending' AND DATE_ADD(o.created_at, INTERVAL o.duration SECOND) <= NOW()");
$stmt->execute([$user_id]);
$expired = $stmt->fetchAll();
@ -88,10 +88,10 @@ if ($action === 'get_orders') {
'pnl' => $o['status'] === 'won' ? (float)($o['amount'] * $o['profit_rate'] / 100) : ($o['status'] === 'lost' ? -(float)$o['amount'] : 0),
'total' => $o['status'] === 'won' ? (float)($o['amount'] + ($o['amount'] * $o['profit_rate'] / 100)) : ($o['status'] === 'lost' ? 0.00 : '---'),
'status' => ($o['status'] === 'won' ? __('won') : ($o['status'] === 'lost' ? __('loss') : __('executing'))),
'status_type' => $o['status'] === 0 ? 'executing' : $o['status'],
'status_type' => $o['status'] === 'pending' ? 'executing' : $o['status'],
'profitRate' => $o['profit_rate']
];
if ($o['status'] === 0) {
if ($o['status'] === 'pending') {
$row['status'] = __('executing');
$row['totalSeconds'] = $o['duration'];
$elapsed = time() - strtotime($o['created_at']);

View File

@ -47,7 +47,19 @@ try {
$stmt = $db->prepare("INSERT INTO spot_orders (user_id, symbol, side, price, amount, filled, status, ip_address) VALUES (?, ?, ?, ?, ?, ?, 'filled', ?)");
$stmt->execute([$user_id, $symbol, $side, $price, $amount, $amount, getRealIP()]);
// ...
// Add receive balance
$receive_symbol = ($side === 'buy') ? $symbol : 'USDT';
$receive_amount = ($side === 'buy') ? $amount : ($price * $amount);
$stmt = $db->prepare("SELECT id FROM user_balances WHERE user_id = ? AND symbol = ?");
$stmt->execute([$user_id, $receive_symbol]);
if ($stmt->fetch()) {
$db->prepare("UPDATE user_balances SET available = available + ? WHERE user_id = ? AND symbol = ?")
->execute([$receive_amount, $user_id, $receive_symbol]);
} else {
$db->prepare("INSERT INTO user_balances (user_id, symbol, available) VALUES (?, ?, ?)")
->execute([$user_id, $receive_symbol, $receive_amount]);
}
// Record transaction
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status, ip_address) VALUES (?, ?, ?, ?, 'completed', ?)")

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -70,7 +70,7 @@ CREATE TABLE `binary_orders` (
`profit_rate` decimal(5,2) NOT NULL,
`entry_price` decimal(20,8) NOT NULL,
`close_price` decimal(20,8) DEFAULT NULL,
`status` enum(0,'won','lost','cancelled') DEFAULT 0,
`status` enum('pending','won','lost','cancelled') DEFAULT 'pending',
`control_status` tinyint(4) DEFAULT 0 COMMENT '0: normal, 1: force win, 2: force loss',
`created_at` timestamp NULL DEFAULT current_timestamp(),
`ip_address` varchar(45) DEFAULT NULL,
@ -117,10 +117,11 @@ CREATE TABLE `chat_remarks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT 0,
`ip_address` varchar(45) DEFAULT NULL,
`session_id` varchar(255) DEFAULT NULL,
`remark` text DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`,`ip_address`)
UNIQUE KEY `user_id` (`user_id`,`ip_address`,`session_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@ -146,10 +147,12 @@ CREATE TABLE `chat_visitors` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT 0,
`ip_address` varchar(45) DEFAULT NULL,
`session_id` varchar(255) DEFAULT NULL,
`last_ping` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`user_time` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`,`ip_address`)
UNIQUE KEY `user_id` (`user_id`,`ip_address`),
KEY `idx_session` (`session_id`)
) ENGINE=InnoDB AUTO_INCREMENT=57 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@ -310,8 +313,10 @@ CREATE TABLE `messages` (
`sender` enum('user','admin') DEFAULT NULL,
`message` text DEFAULT NULL,
`ip_address` varchar(45) DEFAULT NULL,
`session_id` varchar(255) DEFAULT NULL,
`created_at` timestamp NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `idx_session` (`session_id`)
) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;

View File

@ -403,6 +403,16 @@ $translations = [
'lost' => '亏损',
4 => '已拒绝',
'cancelled' => '已取消',
'hosting' => '托管中',
'today_profit' => '今日收益',
'total_profit' => '累计收益',
'order_count' => '订单数',
'btc_mining_pool' => 'BTC 矿池',
'eth_mining_pool' => 'ETH 矿池',
'usdt_mining_pool' => 'USDT 矿池',
'bnb_mining_pool' => 'BNB 矿池',
'sol_mining_pool' => 'SOL 矿池',
'avax_mining_pool' => 'AVAX 矿池',
'app_store' => '苹果商店',
'google_play' => '谷歌商店',
'android_apk' => '安卓应用',
@ -1001,6 +1011,16 @@ $translations = [
'lost' => 'Lost',
4 => 'Rejected',
'cancelled' => 'Cancelled',
'hosting' => 'Hosting',
'today_profit' => 'Today\'s Profit',
'total_profit' => 'Total Profit',
'order_count' => 'Order Count',
'btc_mining_pool' => 'BTC Mining Pool',
'eth_mining_pool' => 'ETH Mining Pool',
'usdt_mining_pool' => 'USDT Mining Pool',
'bnb_mining_pool' => 'BNB Mining Pool',
'sol_mining_pool' => 'SOL Mining Pool',
'avax_mining_pool' => 'AVAX Mining Pool',
'app_store' => 'App Store',
'google_play' => 'Google Play',
'android_apk' => 'Android APK',

View File

@ -1,6 +1,33 @@
<?php
require_once __DIR__ . '/includes/lang.php';
require_once __DIR__ . '/includes/header.php';
$stakingAmount = 0;
$todayProfit = 0;
$totalProfit = 0;
$orderCount = 0;
if ($user) {
// 托管中
$stmt = db()->prepare("SELECT SUM(amount) FROM staking_records WHERE user_id = ? AND status = 'running'");
$stmt->execute([$user['id']]);
$stakingAmount = $stmt->fetchColumn() ?: 0;
// 今日收益
$stmt = db()->prepare("SELECT SUM(amount) FROM transactions WHERE user_id = ? AND type = 'mining' AND DATE(created_at) = CURDATE()");
$stmt->execute([$user['id']]);
$todayProfit = $stmt->fetchColumn() ?: 0;
// 累计收益
$stmt = db()->prepare("SELECT SUM(amount) FROM transactions WHERE user_id = ? AND type = 'mining'");
$stmt->execute([$user['id']]);
$totalProfit = $stmt->fetchColumn() ?: 0;
// 订单数
$stmt = db()->prepare("SELECT COUNT(*) FROM staking_records WHERE user_id = ?");
$stmt->execute([$user['id']]);
$orderCount = $stmt->fetchColumn() ?: 0;
}
?>
<main class="container py-5">
<div class="text-center mb-5">
@ -10,15 +37,43 @@ require_once __DIR__ . '/includes/header.php';
</p>
</div>
<!-- Stats Section -->
<div class="row g-3 mb-5">
<div class="col-6 col-md-3">
<div class="bg-surface border border-secondary p-3 rounded-4 shadow-sm text-center">
<div class="text-white-50 small mb-1"><?= __('hosting') ?></div>
<div class="fs-5 fw-bold text-white"><?= number_format($stakingAmount, 2) ?></div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="bg-surface border border-secondary p-3 rounded-4 shadow-sm text-center">
<div class="text-white-50 small mb-1"><?= __('today_profit') ?></div>
<div class="fs-5 fw-bold text-success">+<?= number_format($todayProfit, 4) ?></div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="bg-surface border border-secondary p-3 rounded-4 shadow-sm text-center">
<div class="text-white-50 small mb-1"><?= __('total_profit') ?></div>
<div class="fs-5 fw-bold text-primary"><?= number_format($totalProfit, 4) ?></div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="bg-surface border border-secondary p-3 rounded-4 shadow-sm text-center">
<div class="text-white-50 small mb-1"><?= __('order_count') ?></div>
<div class="fs-5 fw-bold text-warning"><?= $orderCount ?></div>
</div>
</div>
</div>
<div class="row g-4 mb-5">
<?php
$pools = [
['symbol' => 'BTC', 'name' => __('bitcoin') . __('mining_pool'), 'apy' => '12.5%', 'min' => '0.01 ' . __('BTC'), 'term' => '30 ' . __('day'), 'hot' => true],
['symbol' => 'ETH', 'name' => __('ethereum') . __('mining_pool'), 'apy' => '8.2%', 'min' => '0.1 ' . __('ETH'), 'term' => __('flexible'), 'hot' => false],
['symbol' => 'USDT', 'name' => __('tether') . __('mining_pool'), 'apy' => '15.0%', 'min' => '100 ' . __('USDT'), 'term' => '90 ' . __('day'), 'hot' => true],
['symbol' => 'BNB', 'name' => __('binance_coin') . __('mining_pool'), 'apy' => '22.0%', 'min' => '1 ' . __('BNB'), 'term' => '180 ' . __('day'), 'hot' => false],
['symbol' => 'SOL', 'name' => __('solana') . __('mining_pool'), 'apy' => '14.2%', 'min' => '5 ' . __('SOL'), 'term' => '60 ' . __('day'), 'hot' => false],
['symbol' => 'AVAX', 'name' => __('avalanche') . __('mining_pool'), 'apy' => '18.5%', 'min' => '10 ' . __('AVAX'), 'term' => '120 ' . __('day'), 'hot' => false],
['symbol' => 'BTC', 'name' => __('btc_mining_pool'), 'apy' => '12.5%', 'min' => '0.01 ' . __('BTC'), 'term' => '30 ' . __('day'), 'hot' => true],
['symbol' => 'ETH', 'name' => __('eth_mining_pool'), 'apy' => '8.2%', 'min' => '0.1 ' . __('ETH'), 'term' => __('flexible'), 'hot' => false],
['symbol' => 'USDT', 'name' => __('usdt_mining_pool'), 'apy' => '15.0%', 'min' => '100 ' . __('USDT'), 'term' => '90 ' . __('day'), 'hot' => true],
['symbol' => 'BNB', 'name' => __('bnb_mining_pool'), 'apy' => '22.0%', 'min' => '1 ' . __('BNB'), 'term' => '180 ' . __('day'), 'hot' => false],
['symbol' => 'SOL', 'name' => __('sol_mining_pool'), 'apy' => '14.2%', 'min' => '5 ' . __('SOL'), 'term' => '60 ' . __('day'), 'hot' => false],
['symbol' => 'AVAX', 'name' => __('avax_mining_pool'), 'apy' => '18.5%', 'min' => '10 ' . __('AVAX'), 'term' => '120 ' . __('day'), 'hot' => false],
];
foreach ($pools as $pool): ?>

View File

@ -305,11 +305,14 @@ $kycStatusColor = [
</div>
</div>
<!-- Mobile Transaction List -->
<div class="d-md-none p-3">
<div class="d-md-none p-3 mobile-transaction-list" style="max-height: 400px; overflow-y: auto;">
<?php if (empty($transactions)): ?>
<div class="text-center py-4 text-white-50 small"><?= __('no_records_found') ?></div>
<?php else: ?>
<?php foreach ($transactions as $t):
<?php
$count = 0;
foreach ($transactions as $t):
$count++;
$typeName = __($t['type']);
$typeColor = (strpos($t['type'], 'win') !== false || $t['type'] === 'deposit' || $t['type'] === 'recharge') ? 'text-success' : ((strpos($t['type'], 'loss') !== false || $t['type'] === 'withdraw' || $t['type'] === 'withdrawal') ? 'text-danger' : 'text-primary');
$statusText = ($t['status'] === 'completed' || $t['status'] === 3) ? __(3) : ($t['status'] === 4 ? __(4) : __(0));
@ -335,6 +338,8 @@ $kycStatusColor = [
<style>
.x-small { font-size: 11px; }
.mobile-transaction-list::-webkit-scrollbar { display: none; }
.mobile-transaction-list { -ms-overflow-style: none; scrollbar-width: none; }
@media (max-width: 991px) {
.col-lg-3 { margin-bottom: 1rem; }
}