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 // Auto-settle expired orders
$db->beginTransaction(); $db->beginTransaction();
try { 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(); $stmt->execute();
$expired = $stmt->fetchAll(); $expired = $stmt->fetchAll();
foreach ($expired as $o) { foreach ($expired as $o) {
@ -139,7 +139,7 @@ $orders = $stmt->fetchAll();
<?php endif; ?> <?php endif; ?>
</td> </td>
<td> <td>
<?php if ($o['status'] === 0): ?> <?php if ($o['status'] === 'pending'): ?>
<span class="badge bg-warning">进行中</span> <span class="badge bg-warning">进行中</span>
<?php elseif ($o['status'] === 'won'): ?> <?php elseif ($o['status'] === 'won'): ?>
<span class="badge bg-success">已盈利</span> <span class="badge bg-success">已盈利</span>
@ -153,20 +153,20 @@ $orders = $stmt->fetchAll();
<?php if ($o['control_status'] == 1): ?> <?php if ($o['control_status'] == 1): ?>
<span class="badge bg-success">控赢</span> <span class="badge bg-success">控赢</span>
<?php elseif ($o['control_status'] == 2): ?> <?php elseif ($o['control_status'] == 2): ?>
<span class="badge bg-danger"></span> <span class="badge bg-danger"></span>
<?php else: ?> <?php else: ?>
<span class="badge bg-secondary">正常</span> <span class="badge bg-secondary">正常</span>
<?php endif; ?> <?php endif; ?>
</td> </td>
<td class="text-end"> <td class="text-end">
<?php if ($o['status'] === 0): ?> <?php if ($o['status'] === 'pending'): ?>
<form method="POST" class="d-inline"> <form method="POST" class="d-inline">
<input type="hidden" name="order_id" value="<?= $o['id'] ?>"> <input type="hidden" name="order_id" value="<?= $o['id'] ?>">
<input type="hidden" name="action" value="set_control"> <input type="hidden" name="action" value="set_control">
<div class="btn-group btn-group-sm"> <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="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="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="0" class="btn btn-outline-secondary <?= $o['control_status'] == 0 ? 'active' : '' ?>">正常</button>
</div> </div>
</form> </form>
<?php endif; ?> <?php endif; ?>

View File

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

View File

@ -309,6 +309,7 @@ ob_start();
<script> <script>
let selectedUser = null; let selectedUser = null;
let selectedIp = null; let selectedIp = null;
let selectedSid = null;
let lastMsgId = 0; let lastMsgId = 0;
let lastChatIds = new Set(); let lastChatIds = new Set();
let currentUserContext = ''; 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 userId = u.user_id || 0;
const username = (u.username || '匿名访客').toString(); const username = (u.username || '匿名访客').toString();
const uid = (u.uid || '---').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 rawRemark = (u.remark || '').toString();
const userTime = (u.user_time || '---').toString(); const userTime = (u.user_time || '---').toString();
const registrationIp = (u.registration_ip || '---').toString(); const registrationIp = (u.registration_ip || '---').toString();
// Search filter // 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; return;
} }
@ -373,7 +376,7 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
lastMsgText = '[收款账号信息]'; lastMsgText = '[收款账号信息]';
} }
const isActive = (selectedIp === ip && selectedUser == userId); const isActive = (selectedSid && rawSid ? selectedSid === rawSid : selectedIp === rawIp) && selectedUser == userId;
// Safe strings for onclick // Safe strings for onclick
const jsName = username.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); 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 += ` html += `
<div class="user-card ${isActive ? 'active' : ''}" <div class="user-card ${isActive ? 'active' : ''}"
onclick="openChat('${userId}', '${ip}', '${jsName}', '${uid}', '${jsRemark}', '${userTime}', '${registrationIp}')"> onclick="openChat('${userId}', '${rawIp}', '${rawSid}', '${jsName}', '${uid}', '${jsRemark}', '${userTime}', '${registrationIp}')">
<i class="bi bi-trash text-muted delete-chat-btn" title="删除会话" onclick="deleteChat('${userId}', '${ip}', event)"></i> <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"> <div class="d-flex justify-content-between mb-1">
<span class="fw-bold small text-truncate" style="max-width: 150px;">${username}</span> <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> <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="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;"> <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-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>
<div class="mt-1 text-muted" style="font-size: 9px;"><i class="bi bi-person-plus me-1"></i>注册IP: ${registrationIp}</div> <div class="mt-1 text-muted" style="font-size: 9px;"><i class="bi bi-person-plus me-1"></i>注册IP: ${registrationIp}</div>
</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; selectedUser = userId;
selectedIp = ip; selectedIp = ip;
selectedSid = sid;
document.getElementById('header-name').innerText = name; document.getElementById('header-name').innerText = name;
document.getElementById('header-uid').innerText = uid; document.getElementById('header-uid').innerText = uid;
document.getElementById('info-ip-header').innerText = ip; 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'; document.getElementById('remark-area').style.display = 'block';
// IP Location fetch // IP Location fetch
if (ip && ip !== '---') {
fetch(`https://ipapi.co/${ip}/json/`).then(r => r.json()).then(data => { fetch(`https://ipapi.co/${ip}/json/`).then(r => r.json()).then(data => {
if (data.city) { if (data.city) {
document.getElementById('info-ip-header').innerText = `${ip} (${data.city}, ${data.country_name})`; document.getElementById('info-ip-header').innerText = `${ip} (${data.city}, ${data.country_name})`;
} }
}).catch(() => {}); }).catch(() => {});
}
document.getElementById('remark-text').value = remark; document.getElementById('remark-text').value = remark;
document.getElementById('info-uid').innerText = uid; 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 (event) event.stopPropagation();
if (!confirm('确定删除该会话及其所有记录吗?')) return; if (!confirm('确定删除该会话及其所有记录吗?')) return;
const fd = new URLSearchParams(); const fd = new URLSearchParams();
fd.append('user_id', userId); fd.append('user_id', userId);
fd.append('ip_address', ip); 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 r = await fetch('/api/chat.php?action=admin_delete_user', { method: 'POST', body: fd });
const res = await r.json(); const res = await r.json();
if (res.success) { if (res.success) {
if (selectedUser == userId && selectedIp == ip) { if (selectedSid == sid && selectedIp == ip && selectedUser == userId) {
selectedUser = null; selectedUser = null;
selectedIp = null; selectedIp = null;
selectedSid = null;
document.getElementById('chat-header').style.display = 'none'; document.getElementById('chat-header').style.display = 'none';
document.getElementById('input-area').style.display = 'none'; document.getElementById('input-area').style.display = 'none';
document.getElementById('remark-area').style.display = 'none'; document.getElementById('remark-area').style.display = 'none';
@ -477,11 +485,13 @@ async function deleteUser() {
const fd = new URLSearchParams(); const fd = new URLSearchParams();
fd.append('user_id', selectedUser); fd.append('user_id', selectedUser);
fd.append('ip_address', selectedIp); 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 r = await fetch('/api/chat.php?action=admin_delete_user', { method: 'POST', body: fd });
const res = await r.json(); const res = await r.json();
if (res.success) { if (res.success) {
selectedUser = null; selectedUser = null;
selectedIp = null; selectedIp = null;
selectedSid = null;
document.getElementById('chat-header').style.display = 'none'; document.getElementById('chat-header').style.display = 'none';
document.getElementById('input-area').style.display = 'none'; document.getElementById('input-area').style.display = 'none';
document.getElementById('remark-area').style.display = 'none'; document.getElementById('remark-area').style.display = 'none';
@ -496,13 +506,13 @@ async function deleteUser() {
} }
async function fetchMessages() { async function fetchMessages() {
if (!selectedIp && !selectedUser) return; if (!selectedIp && !selectedUser && !selectedSid) return;
try { 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(); const msgs = await r.json();
if (!msgs || !Array.isArray(msgs)) return; if (!msgs || !Array.isArray(msgs)) return;
const context = selectedUser + '_' + selectedIp; const context = selectedUser + '_' + selectedIp + '_' + selectedSid;
if (currentUserContext !== context) { if (currentUserContext !== context) {
document.getElementById('messages-area').innerHTML = ''; document.getElementById('messages-area').innerHTML = '';
lastChatIds.clear(); lastChatIds.clear();
@ -709,6 +719,7 @@ document.getElementById('image-input').addEventListener('change', async (e) => {
formData.append('file', file); formData.append('file', file);
formData.append('user_id', selectedUser || 0); formData.append('user_id', selectedUser || 0);
formData.append('ip_address', selectedIp || ''); formData.append('ip_address', selectedIp || '');
formData.append('session_id', selectedSid || '');
try { try {
const r = await fetch('/api/chat.php?action=upload_image', { 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('message', msg);
fd.append('user_id', selectedUser); fd.append('user_id', selectedUser);
fd.append('ip_address', selectedIp); fd.append('ip_address', selectedIp);
fd.append('session_id', selectedSid);
try { try {
const r = await fetch('/api/chat.php?action=admin_send', { method: 'POST', body: fd }); 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(); const fd = new URLSearchParams();
fd.append('user_id', selectedUser); fd.append('user_id', selectedUser);
fd.append('ip_address', selectedIp); fd.append('ip_address', selectedIp);
fd.append('session_id', selectedSid);
fd.append('remark', remark); fd.append('remark', remark);
const r = await fetch('/api/chat.php?action=save_remark', { method: 'POST', body: fd }); 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><?= number_format($o['amount'], 2) ?></td>
<td><span class="text-<?= $o['direction']=='buy'?'success':'danger' ?>"><?= $o['direction']=='buy'?'涨':'跌' ?></span></td> <td><span class="text-<?= $o['direction']=='buy'?'success':'danger' ?>"><?= $o['direction']=='buy'?'涨':'跌' ?></span></td>
<td> <td>
<?php if ($o['control_status']==1): ?><span class="badge bg-success">赢</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 elseif ($o['control_status']==2): ?><span class="badge bg-danger">控输</span>
<?php else: ?>-<?php endif; ?> <?php else: ?>-<?php endif; ?>
</td> </td>
</tr> </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_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_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]); $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_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]); $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]); $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_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_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]); $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_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]); $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]); $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]); ->execute([$amount, $user_id]);
// Insert order // 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()]); $stmt->execute([$user_id, $symbol, $direction, $amount, $duration, $entry_price, $profit_rate, getRealIP()]);
$order_id = $db->lastInsertId(); $order_id = $db->lastInsertId();
@ -73,7 +73,7 @@ if ($action === 'settle_order') {
$stmt->execute([$order_id, $user_id]); $stmt->execute([$order_id, $user_id]);
$order = $stmt->fetch(); $order = $stmt->fetch();
if (!$order || $order['status'] !== 0) { if (!$order || $order['status'] !== 'pending') {
echo json_encode(['success' => false, 'error' => __('no_records_found')]); echo json_encode(['success' => false, 'error' => __('no_records_found')]);
exit; exit;
} }

View File

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

View File

@ -32,7 +32,7 @@ if ($action === 'get_orders') {
if ($tab === 'binary') { if ($tab === 'binary') {
// Auto-settle expired orders // 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]); $stmt->execute([$user_id]);
$expired = $stmt->fetchAll(); $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), '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 : '---'), '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' => ($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'] 'profitRate' => $o['profit_rate']
]; ];
if ($o['status'] === 0) { if ($o['status'] === 'pending') {
$row['status'] = __('executing'); $row['status'] = __('executing');
$row['totalSeconds'] = $o['duration']; $row['totalSeconds'] = $o['duration'];
$elapsed = time() - strtotime($o['created_at']); $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 = $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()]); $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 // Record transaction
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status, ip_address) VALUES (?, ?, ?, ?, 'completed', ?)") $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, `profit_rate` decimal(5,2) NOT NULL,
`entry_price` decimal(20,8) NOT NULL, `entry_price` decimal(20,8) NOT NULL,
`close_price` decimal(20,8) DEFAULT 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', `control_status` tinyint(4) DEFAULT 0 COMMENT '0: normal, 1: force win, 2: force loss',
`created_at` timestamp NULL DEFAULT current_timestamp(), `created_at` timestamp NULL DEFAULT current_timestamp(),
`ip_address` varchar(45) DEFAULT NULL, `ip_address` varchar(45) DEFAULT NULL,
@ -117,10 +117,11 @@ CREATE TABLE `chat_remarks` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT 0, `user_id` int(11) DEFAULT 0,
`ip_address` varchar(45) DEFAULT NULL, `ip_address` varchar(45) DEFAULT NULL,
`session_id` varchar(255) DEFAULT NULL,
`remark` text DEFAULT NULL, `remark` text DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), `updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`), 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; ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -146,10 +147,12 @@ CREATE TABLE `chat_visitors` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT 0, `user_id` int(11) DEFAULT 0,
`ip_address` varchar(45) DEFAULT NULL, `ip_address` varchar(45) DEFAULT NULL,
`session_id` varchar(255) DEFAULT NULL,
`last_ping` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), `last_ping` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`user_time` varchar(50) DEFAULT NULL, `user_time` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`), 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; ) ENGINE=InnoDB AUTO_INCREMENT=57 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@ -310,8 +313,10 @@ CREATE TABLE `messages` (
`sender` enum('user','admin') DEFAULT NULL, `sender` enum('user','admin') DEFAULT NULL,
`message` text DEFAULT NULL, `message` text DEFAULT NULL,
`ip_address` varchar(45) DEFAULT NULL, `ip_address` varchar(45) DEFAULT NULL,
`session_id` varchar(255) DEFAULT NULL,
`created_at` timestamp NULL DEFAULT current_timestamp(), `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; ) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;

View File

@ -403,6 +403,16 @@ $translations = [
'lost' => '亏损', 'lost' => '亏损',
4 => '已拒绝', 4 => '已拒绝',
'cancelled' => '已取消', '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' => '苹果商店', 'app_store' => '苹果商店',
'google_play' => '谷歌商店', 'google_play' => '谷歌商店',
'android_apk' => '安卓应用', 'android_apk' => '安卓应用',
@ -1001,6 +1011,16 @@ $translations = [
'lost' => 'Lost', 'lost' => 'Lost',
4 => 'Rejected', 4 => 'Rejected',
'cancelled' => 'Cancelled', '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', 'app_store' => 'App Store',
'google_play' => 'Google Play', 'google_play' => 'Google Play',
'android_apk' => 'Android APK', 'android_apk' => 'Android APK',

View File

@ -1,6 +1,33 @@
<?php <?php
require_once __DIR__ . '/includes/lang.php'; require_once __DIR__ . '/includes/lang.php';
require_once __DIR__ . '/includes/header.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"> <main class="container py-5">
<div class="text-center mb-5"> <div class="text-center mb-5">
@ -10,15 +37,43 @@ require_once __DIR__ . '/includes/header.php';
</p> </p>
</div> </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"> <div class="row g-4 mb-5">
<?php <?php
$pools = [ $pools = [
['symbol' => 'BTC', 'name' => __('bitcoin') . __('mining_pool'), 'apy' => '12.5%', 'min' => '0.01 ' . __('BTC'), 'term' => '30 ' . __('day'), 'hot' => true], ['symbol' => 'BTC', 'name' => __('btc_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' => 'ETH', 'name' => __('eth_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' => 'USDT', 'name' => __('usdt_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' => 'BNB', 'name' => __('bnb_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' => 'SOL', 'name' => __('sol_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' => 'AVAX', 'name' => __('avax_mining_pool'), 'apy' => '18.5%', 'min' => '10 ' . __('AVAX'), 'term' => '120 ' . __('day'), 'hot' => false],
]; ];
foreach ($pools as $pool): ?> foreach ($pools as $pool): ?>

View File

@ -305,11 +305,14 @@ $kycStatusColor = [
</div> </div>
</div> </div>
<!-- Mobile Transaction List --> <!-- 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)): ?> <?php if (empty($transactions)): ?>
<div class="text-center py-4 text-white-50 small"><?= __('no_records_found') ?></div> <div class="text-center py-4 text-white-50 small"><?= __('no_records_found') ?></div>
<?php else: ?> <?php else: ?>
<?php foreach ($transactions as $t): <?php
$count = 0;
foreach ($transactions as $t):
$count++;
$typeName = __($t['type']); $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'); $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)); $statusText = ($t['status'] === 'completed' || $t['status'] === 3) ? __(3) : ($t['status'] === 4 ? __(4) : __(0));
@ -335,6 +338,8 @@ $kycStatusColor = [
<style> <style>
.x-small { font-size: 11px; } .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) { @media (max-width: 991px) {
.col-lg-3 { margin-bottom: 1rem; } .col-lg-3 { margin-bottom: 1rem; }
} }