Autosave: 20260221-090248
@ -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; ?>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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 });
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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]);
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
148
api/chat.php
@ -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;
|
||||||
|
|||||||
@ -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']);
|
||||||
|
|||||||
14
api/spot.php
@ -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', ?)")
|
||||||
|
|||||||
BIN
assets/images/chat/1771661562_699968faa32f7.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
assets/images/chat/1771661942_69996a760adf1.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/images/chat/1771662235_69996b9bdbae5.png
Normal file
|
After Width: | Height: | Size: 858 KiB |
BIN
assets/images/chat/1771662254_69996bae21045.jpeg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
assets/pasted-20260221-080254-2cba8c09.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
assets/pasted-20260221-080847-a20da952.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
assets/pasted-20260221-081536-c92afd1b.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
assets/pasted-20260221-083858-38a6da88.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/pasted-20260221-085332-23d6424d.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
@ -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 */;
|
||||||
|
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
67
mining.php
@ -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): ?>
|
||||||
|
|||||||
@ -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; }
|
||||||
}
|
}
|
||||||
|
|||||||