Autosave: 20260221-090248
@ -12,7 +12,7 @@ if (!hasPermission('view_orders')) {
|
||||
// Auto-settle expired orders
|
||||
$db->beginTransaction();
|
||||
try {
|
||||
$stmt = $db->prepare("SELECT o.*, u.win_loss_control as user_control FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 0 AND DATE_ADD(o.created_at, INTERVAL o.duration SECOND) <= NOW()");
|
||||
$stmt = $db->prepare("SELECT o.*, u.win_loss_control as user_control FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND DATE_ADD(o.created_at, INTERVAL o.duration SECOND) <= NOW()");
|
||||
$stmt->execute();
|
||||
$expired = $stmt->fetchAll();
|
||||
foreach ($expired as $o) {
|
||||
@ -139,7 +139,7 @@ $orders = $stmt->fetchAll();
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($o['status'] === 0): ?>
|
||||
<?php if ($o['status'] === 'pending'): ?>
|
||||
<span class="badge bg-warning">进行中</span>
|
||||
<?php elseif ($o['status'] === 'won'): ?>
|
||||
<span class="badge bg-success">已盈利</span>
|
||||
@ -153,20 +153,20 @@ $orders = $stmt->fetchAll();
|
||||
<?php if ($o['control_status'] == 1): ?>
|
||||
<span class="badge bg-success">控赢</span>
|
||||
<?php elseif ($o['control_status'] == 2): ?>
|
||||
<span class="badge bg-danger">控亏</span>
|
||||
<span class="badge bg-danger">控输</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary">正常</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<?php if ($o['status'] === 0): ?>
|
||||
<?php if ($o['status'] === 'pending'): ?>
|
||||
<form method="POST" class="d-inline">
|
||||
<input type="hidden" name="order_id" value="<?= $o['id'] ?>">
|
||||
<input type="hidden" name="action" value="set_control">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button name="control_status" value="1" class="btn btn-outline-success <?= $o['control_status'] == 1 ? 'active' : '' ?>">赢</button>
|
||||
<button name="control_status" value="2" class="btn btn-outline-danger <?= $o['control_status'] == 2 ? 'active' : '' ?>">亏</button>
|
||||
<button name="control_status" value="0" class="btn btn-outline-secondary <?= $o['control_status'] == 0 ? 'active' : '' ?>">改</button>
|
||||
<button name="control_status" value="1" class="btn btn-outline-success <?= $o['control_status'] == 1 ? 'active' : '' ?>">控赢</button>
|
||||
<button name="control_status" value="2" class="btn btn-outline-danger <?= $o['control_status'] == 2 ? 'active' : '' ?>">控输</button>
|
||||
<button name="control_status" value="0" class="btn btn-outline-secondary <?= $o['control_status'] == 0 ? 'active' : '' ?>">正常</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
@ -71,16 +71,16 @@ $orders = $stmt->fetchAll();
|
||||
<td><?= $o['status'] ?></td>
|
||||
<td>
|
||||
<?php if ($o['control_status'] == 1): ?><span class="badge bg-success">控赢</span>
|
||||
<?php elseif ($o['control_status'] == 2): ?><span class="badge bg-danger">控亏</span>
|
||||
<?php elseif ($o['control_status'] == 2): ?><span class="badge bg-danger">控输</span>
|
||||
<?php else: ?><span class="badge bg-secondary">正常</span><?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<form method="POST" class="btn-group btn-group-sm">
|
||||
<input type="hidden" name="order_id" value="<?= $o['id'] ?>">
|
||||
<input type="hidden" name="action" value="set_control">
|
||||
<button name="control_status" value="1" class="btn btn-outline-success">赢</button>
|
||||
<button name="control_status" value="2" class="btn btn-outline-danger">亏</button>
|
||||
<button name="control_status" value="0" class="btn btn-outline-secondary">改</button>
|
||||
<button name="control_status" value="1" class="btn btn-outline-success">控赢</button>
|
||||
<button name="control_status" value="2" class="btn btn-outline-danger">控输</button>
|
||||
<button name="control_status" value="0" class="btn btn-outline-secondary">正常</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -309,6 +309,7 @@ ob_start();
|
||||
<script>
|
||||
let selectedUser = null;
|
||||
let selectedIp = null;
|
||||
let selectedSid = null;
|
||||
let lastMsgId = 0;
|
||||
let lastChatIds = new Set();
|
||||
let currentUserContext = '';
|
||||
@ -354,13 +355,15 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
|
||||
const userId = u.user_id || 0;
|
||||
const username = (u.username || '匿名访客').toString();
|
||||
const uid = (u.uid || '---').toString();
|
||||
const ip = (u.ip_address || '---').toString();
|
||||
const rawIp = (u.ip_address || '').toString();
|
||||
const rawSid = (u.session_id || '').toString();
|
||||
const displayIp = rawIp || '---';
|
||||
const rawRemark = (u.remark || '').toString();
|
||||
const userTime = (u.user_time || '---').toString();
|
||||
const registrationIp = (u.registration_ip || '---').toString();
|
||||
|
||||
// Search filter
|
||||
if (search && !username.toLowerCase().includes(search) && !ip.includes(search) && !uid.includes(search)) {
|
||||
if (search && !username.toLowerCase().includes(search) && !rawIp.includes(search) && !uid.includes(search)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -373,7 +376,7 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
|
||||
lastMsgText = '[收款账号信息]';
|
||||
}
|
||||
|
||||
const isActive = (selectedIp === ip && selectedUser == userId);
|
||||
const isActive = (selectedSid && rawSid ? selectedSid === rawSid : selectedIp === rawIp) && selectedUser == userId;
|
||||
|
||||
// Safe strings for onclick
|
||||
const jsName = username.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
||||
@ -381,8 +384,8 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
|
||||
|
||||
html += `
|
||||
<div class="user-card ${isActive ? 'active' : ''}"
|
||||
onclick="openChat('${userId}', '${ip}', '${jsName}', '${uid}', '${jsRemark}', '${userTime}', '${registrationIp}')">
|
||||
<i class="bi bi-trash text-muted delete-chat-btn" title="删除会话" onclick="deleteChat('${userId}', '${ip}', event)"></i>
|
||||
onclick="openChat('${userId}', '${rawIp}', '${rawSid}', '${jsName}', '${uid}', '${jsRemark}', '${userTime}', '${registrationIp}')">
|
||||
<i class="bi bi-trash text-muted delete-chat-btn" title="删除会话" onclick="deleteChat('${userId}', '${rawIp}', '${rawSid}', event)"></i>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span class="fw-bold small text-truncate" style="max-width: 150px;">${username}</span>
|
||||
<span class="text-muted" style="font-size: 10px;">${isNaN(lastTime.getTime()) ? '---' : lastTime.toLocaleTimeString('zh-CN', {hour: '2-digit', minute:'2-digit'})}</span>
|
||||
@ -391,7 +394,7 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
|
||||
<div class="small text-truncate text-muted mb-1" style="font-size: 12px;">${lastMsgText}</div>
|
||||
<div class="d-flex justify-content-between align-items-center" style="font-size: 10px; margin-top: 4px; padding: 4px; background: rgba(0,123,255,0.05); border-radius: 4px;">
|
||||
<span class="text-secondary">UID: ${uid}</span>
|
||||
<span class="text-primary fw-bold"><i class="bi bi-geo-alt-fill me-1"></i>${ip}</span>
|
||||
<span class="text-primary fw-bold"><i class="bi bi-geo-alt-fill me-1"></i>${displayIp}</span>
|
||||
</div>
|
||||
<div class="mt-1 text-muted" style="font-size: 9px;"><i class="bi bi-person-plus me-1"></i>注册IP: ${registrationIp}</div>
|
||||
</div>
|
||||
@ -407,9 +410,10 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
|
||||
}
|
||||
}
|
||||
|
||||
function openChat(userId, ip, name, uid, remark, userTime, regIp) {
|
||||
function openChat(userId, ip, sid, name, uid, remark, userTime, regIp) {
|
||||
selectedUser = userId;
|
||||
selectedIp = ip;
|
||||
selectedSid = sid;
|
||||
document.getElementById('header-name').innerText = name;
|
||||
document.getElementById('header-uid').innerText = uid;
|
||||
document.getElementById('info-ip-header').innerText = ip;
|
||||
@ -419,11 +423,13 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
|
||||
document.getElementById('remark-area').style.display = 'block';
|
||||
|
||||
// IP Location fetch
|
||||
fetch(`https://ipapi.co/${ip}/json/`).then(r => r.json()).then(data => {
|
||||
if (data.city) {
|
||||
document.getElementById('info-ip-header').innerText = `${ip} (${data.city}, ${data.country_name})`;
|
||||
}
|
||||
}).catch(() => {});
|
||||
if (ip && ip !== '---') {
|
||||
fetch(`https://ipapi.co/${ip}/json/`).then(r => r.json()).then(data => {
|
||||
if (data.city) {
|
||||
document.getElementById('info-ip-header').innerText = `${ip} (${data.city}, ${data.country_name})`;
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
document.getElementById('remark-text').value = remark;
|
||||
document.getElementById('info-uid').innerText = uid;
|
||||
@ -446,18 +452,20 @@ async function recallMessage(msgId) {
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteChat(userId, ip, event) {
|
||||
async function deleteChat(userId, ip, sid, event) {
|
||||
if (event) event.stopPropagation();
|
||||
if (!confirm('确定删除该会话及其所有记录吗?')) return;
|
||||
const fd = new URLSearchParams();
|
||||
fd.append('user_id', userId);
|
||||
fd.append('ip_address', ip);
|
||||
fd.append('session_id', sid);
|
||||
const r = await fetch('/api/chat.php?action=admin_delete_user', { method: 'POST', body: fd });
|
||||
const res = await r.json();
|
||||
if (res.success) {
|
||||
if (selectedUser == userId && selectedIp == ip) {
|
||||
if (selectedSid == sid && selectedIp == ip && selectedUser == userId) {
|
||||
selectedUser = null;
|
||||
selectedIp = null;
|
||||
selectedSid = null;
|
||||
document.getElementById('chat-header').style.display = 'none';
|
||||
document.getElementById('input-area').style.display = 'none';
|
||||
document.getElementById('remark-area').style.display = 'none';
|
||||
@ -477,11 +485,13 @@ async function deleteUser() {
|
||||
const fd = new URLSearchParams();
|
||||
fd.append('user_id', selectedUser);
|
||||
fd.append('ip_address', selectedIp);
|
||||
fd.append('session_id', selectedSid);
|
||||
const r = await fetch('/api/chat.php?action=admin_delete_user', { method: 'POST', body: fd });
|
||||
const res = await r.json();
|
||||
if (res.success) {
|
||||
selectedUser = null;
|
||||
selectedIp = null;
|
||||
selectedSid = null;
|
||||
document.getElementById('chat-header').style.display = 'none';
|
||||
document.getElementById('input-area').style.display = 'none';
|
||||
document.getElementById('remark-area').style.display = 'none';
|
||||
@ -496,13 +506,13 @@ async function deleteUser() {
|
||||
}
|
||||
|
||||
async function fetchMessages() {
|
||||
if (!selectedIp && !selectedUser) return;
|
||||
if (!selectedIp && !selectedUser && !selectedSid) return;
|
||||
try {
|
||||
const r = await fetch(`/api/chat.php?action=get_messages&user_id=${selectedUser}&ip=${selectedIp}`);
|
||||
const r = await fetch(`/api/chat.php?action=get_messages&user_id=${selectedUser}&ip=${selectedIp}&session_id=${selectedSid}`);
|
||||
const msgs = await r.json();
|
||||
if (!msgs || !Array.isArray(msgs)) return;
|
||||
|
||||
const context = selectedUser + '_' + selectedIp;
|
||||
const context = selectedUser + '_' + selectedIp + '_' + selectedSid;
|
||||
if (currentUserContext !== context) {
|
||||
document.getElementById('messages-area').innerHTML = '';
|
||||
lastChatIds.clear();
|
||||
@ -709,6 +719,7 @@ document.getElementById('image-input').addEventListener('change', async (e) => {
|
||||
formData.append('file', file);
|
||||
formData.append('user_id', selectedUser || 0);
|
||||
formData.append('ip_address', selectedIp || '');
|
||||
formData.append('session_id', selectedSid || '');
|
||||
|
||||
try {
|
||||
const r = await fetch('/api/chat.php?action=upload_image', {
|
||||
@ -758,6 +769,7 @@ document.getElementById('chat-form').addEventListener('submit', async (e) => {
|
||||
fd.append('message', msg);
|
||||
fd.append('user_id', selectedUser);
|
||||
fd.append('ip_address', selectedIp);
|
||||
fd.append('session_id', selectedSid);
|
||||
|
||||
try {
|
||||
const r = await fetch('/api/chat.php?action=admin_send', { method: 'POST', body: fd });
|
||||
@ -779,6 +791,7 @@ document.getElementById('save-remark-btn').addEventListener('click', async () =>
|
||||
const fd = new URLSearchParams();
|
||||
fd.append('user_id', selectedUser);
|
||||
fd.append('ip_address', selectedIp);
|
||||
fd.append('session_id', selectedSid);
|
||||
fd.append('remark', remark);
|
||||
|
||||
const r = await fetch('/api/chat.php?action=save_remark', { method: 'POST', body: fd });
|
||||
|
||||
@ -112,8 +112,8 @@ ob_start();
|
||||
<td><?= number_format($o['amount'], 2) ?></td>
|
||||
<td><span class="text-<?= $o['direction']=='buy'?'success':'danger' ?>"><?= $o['direction']=='buy'?'涨':'跌' ?></span></td>
|
||||
<td>
|
||||
<?php if ($o['control_status']==1): ?><span class="badge bg-success">赢</span>
|
||||
<?php elseif ($o['control_status']==2): ?><span class="badge bg-danger">亏</span>
|
||||
<?php if ($o['control_status']==1): ?><span class="badge bg-success">控赢</span>
|
||||
<?php elseif ($o['control_status']==2): ?><span class="badge bg-danger">控输</span>
|
||||
<?php else: ?>-<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -55,7 +55,7 @@ if ($admin['is_agent']) {
|
||||
$pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'recharge' AND r.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(r.created_at) > ?", [$agent_id, $cleared_recharge]);
|
||||
$pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'withdrawal' AND r.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(r.created_at) > ?", [$agent_id, $cleared_recharge]);
|
||||
$pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND agent_id = ? AND UNIX_TIMESTAMP(created_at) > ?", [$agent_id, $cleared_kyc]);
|
||||
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_binary]);
|
||||
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_binary]);
|
||||
$active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_spot]);
|
||||
$active_contract = getCount($db, "SELECT COUNT(*) FROM contract_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'open' AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_contract]);
|
||||
$new_messages = getCount($db, "SELECT COUNT(*) FROM messages m JOIN users u ON m.user_id = u.id WHERE m.sender = 'user' AND u.agent_id = ? AND UNIX_TIMESTAMP(m.created_at) > ?", [$agent_id, $cleared_messages]);
|
||||
@ -64,7 +64,7 @@ if ($admin['is_agent']) {
|
||||
$pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'recharge' AND status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_recharge]);
|
||||
$pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'withdrawal' AND status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_recharge]);
|
||||
$pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_kyc]);
|
||||
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders WHERE status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_binary]);
|
||||
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders WHERE status = 'pending' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_binary]);
|
||||
$active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders WHERE status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_spot]);
|
||||
$active_contract = getCount($db, "SELECT COUNT(*) FROM contract_orders WHERE status = 'open' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_contract]);
|
||||
$new_messages = getCount($db, "SELECT COUNT(*) FROM messages WHERE sender = 'user' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_messages]);
|
||||
|
||||
@ -52,7 +52,7 @@ if ($action === 'place_order') {
|
||||
->execute([$amount, $user_id]);
|
||||
|
||||
// Insert order
|
||||
$stmt = $db->prepare("INSERT INTO binary_orders (user_id, symbol, direction, amount, duration, entry_price, profit_rate, status, created_at, ip_address) VALUES (?, ?, ?, ?, ?, ?, ?, 0, NOW(), ?)");
|
||||
$stmt = $db->prepare("INSERT INTO binary_orders (user_id, symbol, direction, amount, duration, entry_price, profit_rate, status, created_at, ip_address) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', NOW(), ?)");
|
||||
$stmt->execute([$user_id, $symbol, $direction, $amount, $duration, $entry_price, $profit_rate, getRealIP()]);
|
||||
$order_id = $db->lastInsertId();
|
||||
|
||||
@ -73,7 +73,7 @@ if ($action === 'settle_order') {
|
||||
$stmt->execute([$order_id, $user_id]);
|
||||
$order = $stmt->fetch();
|
||||
|
||||
if (!$order || $order['status'] !== 0) {
|
||||
if (!$order || $order['status'] !== 'pending') {
|
||||
echo json_encode(['success' => false, 'error' => __('no_records_found')]);
|
||||
exit;
|
||||
}
|
||||
|
||||
148
api/chat.php
@ -33,23 +33,29 @@ if ($action === 'upload_image' || (isset($_POST['action']) && $_POST['action'] =
|
||||
if (isset($_SESSION['admin_id'])) {
|
||||
$user_id = (int)($_POST['user_id'] ?? 0);
|
||||
$ip = $_POST['ip_address'] ?? '';
|
||||
$sid = $_POST['session_id'] ?? '';
|
||||
if ($ip === '---') $ip = '';
|
||||
// If IP is missing, try to get it from messages or fallback to current IP
|
||||
if (empty($ip)) {
|
||||
if (empty($ip) && empty($sid)) {
|
||||
if ($user_id != 0) {
|
||||
$stmt = db()->prepare("SELECT ip_address FROM messages WHERE user_id = ? AND ip_address != '' ORDER BY id DESC LIMIT 1");
|
||||
$stmt = db()->prepare("SELECT ip_address, session_id FROM messages WHERE user_id = ? AND ip_address != '' AND ip_address != '---' ORDER BY id DESC LIMIT 1");
|
||||
$stmt->execute([$user_id]);
|
||||
$ip = $stmt->fetchColumn() ?: getRealIP();
|
||||
} else {
|
||||
$row = $stmt->fetch();
|
||||
$ip = $row['ip_address'] ?? '';
|
||||
$sid = $row['session_id'] ?? '';
|
||||
}
|
||||
if (empty($ip)) {
|
||||
$ip = getRealIP();
|
||||
}
|
||||
}
|
||||
$sender = 'admin';
|
||||
$admin_id = $_SESSION['admin_id'];
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $admin_id, $sender, $message, $ip]);
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $admin_id, $sender, $message, $ip, $sid]);
|
||||
} else {
|
||||
$user_id = (int)($_SESSION['user_id'] ?? 0);
|
||||
$ip = getRealIP();
|
||||
$sid = session_id();
|
||||
|
||||
// Fallback: If user_id is 0 but we find a user with this registration IP, associate it
|
||||
if ($user_id === 0) {
|
||||
@ -59,8 +65,8 @@ if ($action === 'upload_image' || (isset($_POST['action']) && $_POST['action'] =
|
||||
}
|
||||
|
||||
$sender = 'user';
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $sender, $message, $ip]);
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $sender, $message, $ip, $sid]);
|
||||
}
|
||||
|
||||
$newId = db()->lastInsertId();
|
||||
@ -69,8 +75,8 @@ if ($action === 'upload_image' || (isset($_POST['action']) && $_POST['action'] =
|
||||
// Update visitors table to ensure immediate visibility for both users and admins
|
||||
if ($sender === 'user') {
|
||||
$user_time = date('H:i:s');
|
||||
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, user_time) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP");
|
||||
$stmt->execute([$user_id, $ip, $user_time]);
|
||||
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, session_id, user_time) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, session_id = VALUES(session_id)");
|
||||
$stmt->execute([$user_id, $ip, $sid, $user_time]);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
@ -93,16 +99,17 @@ if ($action === 'get_messages') {
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
$target_user_id = $_GET['user_id'] ?? $user_id;
|
||||
$target_ip = $_GET['ip'] ?? getRealIP();
|
||||
$target_sid = $_GET['session_id'] ?? '';
|
||||
|
||||
// If admin is requesting, we use the provided user_id and ip
|
||||
// If admin is requesting, we use the provided user_id and ip/session
|
||||
if (isset($_SESSION['admin_id'])) {
|
||||
$stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (ip_address = ? AND ip_address != '') ORDER BY created_at ASC");
|
||||
$stmt->execute([$target_user_id, $target_ip]);
|
||||
$stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (session_id = ? AND session_id != '') OR (ip_address = ? AND ip_address != '' AND session_id = '') ORDER BY created_at ASC");
|
||||
$stmt->execute([$target_user_id, $target_sid, $target_ip]);
|
||||
} else {
|
||||
// User requesting their own messages
|
||||
// If logged in, get by user_id. Also get by IP to catch visitor history.
|
||||
$user_id = (int)($_SESSION['user_id'] ?? 0);
|
||||
$ip = getRealIP();
|
||||
$sid = session_id();
|
||||
|
||||
// Fallback: If user_id is 0 but we find a user with this IP, associate it
|
||||
if ($user_id === 0) {
|
||||
@ -111,8 +118,8 @@ if ($action === 'get_messages') {
|
||||
$user_id = (int)($stmt->fetchColumn() ?: 0);
|
||||
}
|
||||
|
||||
$stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (ip_address = ? AND ip_address != '') ORDER BY created_at ASC");
|
||||
$stmt->execute([$user_id, $ip]);
|
||||
$stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (session_id = ?) OR (ip_address = ? AND ip_address != '' AND session_id = '') ORDER BY created_at ASC");
|
||||
$stmt->execute([$user_id, $sid, $ip]);
|
||||
}
|
||||
|
||||
$messages = $stmt->fetchAll();
|
||||
@ -126,6 +133,7 @@ if ($action === 'send_message') {
|
||||
|
||||
$user_id = (int)($_SESSION['user_id'] ?? 0);
|
||||
$ip = getRealIP();
|
||||
$sid = session_id();
|
||||
|
||||
// Fallback: If user_id is 0 but we find a user with this registration IP, associate it
|
||||
if ($user_id === 0) {
|
||||
@ -134,14 +142,14 @@ if ($action === 'send_message') {
|
||||
$user_id = (int)($stmt->fetchColumn() ?: 0);
|
||||
}
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, 'user', $message, $ip]);
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, 'user', $message, $ip, $sid]);
|
||||
$newId = db()->lastInsertId();
|
||||
|
||||
// Also update visitors table to ensure immediate visibility
|
||||
$user_time = date('H:i:s');
|
||||
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, user_time) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP");
|
||||
$stmt->execute([$user_id, $ip, $user_time]);
|
||||
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, session_id, user_time) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, session_id = VALUES(session_id)");
|
||||
$stmt->execute([$user_id, $ip, $sid, $user_time]);
|
||||
|
||||
echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => 'user', 'message' => $message, 'created_at' => date('Y-m-d H:i:s')]]);
|
||||
exit;
|
||||
@ -153,16 +161,21 @@ if ($action === 'admin_send') {
|
||||
$message = $_POST['message'] ?? '';
|
||||
$user_id = (int)($_POST['user_id'] ?? 0);
|
||||
$target_ip = $_POST['ip_address'] ?? '';
|
||||
$target_sid = $_POST['session_id'] ?? '';
|
||||
if ($target_ip === '---') $target_ip = '';
|
||||
|
||||
if (!$message) exit(json_encode(['success' => false, 'error' => 'Empty message']));
|
||||
|
||||
// Robust IP matching: if target_ip is empty but user_id is set, try to find their last IP
|
||||
if (empty($target_ip)) {
|
||||
// Robust IP/Session matching: if target is empty but user_id is set, try to find their last known details
|
||||
if (empty($target_ip) && empty($target_sid)) {
|
||||
if ($user_id != 0) {
|
||||
$stmt = db()->prepare("SELECT ip_address FROM messages WHERE user_id = ? AND ip_address != '' ORDER BY id DESC LIMIT 1");
|
||||
$stmt = db()->prepare("SELECT ip_address, session_id FROM messages WHERE user_id = ? AND ip_address != '' AND ip_address != '---' ORDER BY id DESC LIMIT 1");
|
||||
$stmt->execute([$user_id]);
|
||||
$target_ip = $stmt->fetchColumn() ?: getRealIP();
|
||||
} else {
|
||||
$row = $stmt->fetch();
|
||||
$target_ip = $row['ip_address'] ?? '';
|
||||
$target_sid = $row['session_id'] ?? '';
|
||||
}
|
||||
if (empty($target_ip)) {
|
||||
$target_ip = getRealIP();
|
||||
}
|
||||
}
|
||||
@ -170,8 +183,8 @@ if ($action === 'admin_send') {
|
||||
$admin_id = $_SESSION['admin_id'] ?? 1;
|
||||
$sender = 'admin';
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $admin_id, $sender, $message, $target_ip]);
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $admin_id, $sender, $message, $target_ip, $target_sid]);
|
||||
$newId = db()->lastInsertId();
|
||||
echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => $sender, 'message' => $message, 'created_at' => date('Y-m-d H:i:s')]]);
|
||||
exit;
|
||||
@ -181,9 +194,10 @@ if ($action === 'admin_send') {
|
||||
if ($action === 'ping') {
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
$ip = getRealIP();
|
||||
$sid = session_id();
|
||||
$user_time = $_GET['user_time'] ?? '';
|
||||
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, user_time) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, user_time = ?");
|
||||
$stmt->execute([$user_id, $ip, $user_time, $user_time]);
|
||||
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, session_id, user_time) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, user_time = ?, session_id = VALUES(session_id)");
|
||||
$stmt->execute([$user_id, $ip, $sid, $user_time, $user_time]);
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
@ -196,18 +210,20 @@ if ($action === 'admin_get_all') {
|
||||
exit;
|
||||
}
|
||||
try {
|
||||
// Robust query to get all active chat sessions, merging guest sessions into user sessions if IP matches
|
||||
// Robust query to get all active chat sessions, merging guest sessions into user sessions if IP or Session matches
|
||||
// Grouping by (final_user_id, effective_sid, effective_ip) to ensure "One ID, One Conversation"
|
||||
$stmt = db()->query("
|
||||
SELECT
|
||||
v.final_user_id as user_id,
|
||||
v.ip_address,
|
||||
v.effective_ip as ip_address,
|
||||
v.effective_sid as session_id,
|
||||
CASE
|
||||
WHEN m.message LIKE '<img%' THEN '[图片消息]'
|
||||
WHEN (m.message IS NULL OR m.message = '') AND v.has_recharge = 1 THEN '[充值申请]'
|
||||
ELSE COALESCE(m.message, '新会话')
|
||||
END as message,
|
||||
COALESCE(m.created_at, v.last_activity) as created_at,
|
||||
COALESCE(u.username, u.email, CONCAT('访客 ', COALESCE(v.ip_address, '0.0.0.0'))) as username,
|
||||
COALESCE(u.username, u.email, CONCAT('访客 ', COALESCE(NULLIF(v.effective_ip, ''), '0.0.0.0'))) as username,
|
||||
IFNULL(u.uid, '---') as uid,
|
||||
IFNULL(u.registration_ip, '---') as registration_ip,
|
||||
IFNULL(r.remark, '') as remark,
|
||||
@ -215,47 +231,55 @@ if ($action === 'admin_get_all') {
|
||||
FROM (
|
||||
SELECT
|
||||
final_user_id,
|
||||
MAX(ip_address) as ip_address,
|
||||
effective_ip,
|
||||
effective_sid,
|
||||
MAX(last_activity) as last_activity,
|
||||
MAX(user_time) as user_time,
|
||||
MAX(has_recharge) as has_recharge
|
||||
FROM (
|
||||
SELECT
|
||||
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = messages.ip_address LIMIT 1), 0) as final_user_id,
|
||||
IFNULL(ip_address, '') as ip_address,
|
||||
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = m.ip_address AND m.ip_address != '---' AND m.ip_address != '' LIMIT 1), 0) as final_user_id,
|
||||
CASE WHEN ip_address = '---' THEN '' ELSE IFNULL(ip_address, '') END as effective_ip,
|
||||
IFNULL(session_id, '') as effective_sid,
|
||||
created_at as last_activity,
|
||||
NULL as user_time,
|
||||
0 as has_recharge
|
||||
FROM messages
|
||||
FROM messages m
|
||||
UNION ALL
|
||||
SELECT
|
||||
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = chat_visitors.ip_address LIMIT 1), 0) as final_user_id,
|
||||
IFNULL(ip_address, '') as ip_address,
|
||||
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = cv.ip_address AND cv.ip_address != '---' AND cv.ip_address != '' LIMIT 1), 0) as final_user_id,
|
||||
CASE WHEN ip_address = '---' THEN '' ELSE IFNULL(ip_address, '') END as effective_ip,
|
||||
IFNULL(session_id, '') as effective_sid,
|
||||
last_ping as last_activity,
|
||||
user_time,
|
||||
0 as has_recharge
|
||||
FROM chat_visitors
|
||||
FROM chat_visitors cv
|
||||
UNION ALL
|
||||
SELECT
|
||||
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = finance_requests.ip_address LIMIT 1), 0) as final_user_id,
|
||||
IFNULL(ip_address, '') as ip_address,
|
||||
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = fr.ip_address AND fr.ip_address != '---' AND fr.ip_address != '' LIMIT 1), 0) as final_user_id,
|
||||
CASE WHEN ip_address = '---' THEN '' ELSE IFNULL(ip_address, '') END as effective_ip,
|
||||
'' as effective_sid,
|
||||
created_at as last_activity,
|
||||
NULL as user_time,
|
||||
1 as has_recharge
|
||||
FROM finance_requests
|
||||
FROM finance_requests fr
|
||||
) t1
|
||||
GROUP BY (CASE WHEN final_user_id = 0 THEN 0 ELSE final_user_id END), (CASE WHEN final_user_id = 0 THEN ip_address ELSE '0' END)
|
||||
GROUP BY final_user_id, (CASE WHEN final_user_id = 0 THEN effective_sid ELSE '' END), (CASE WHEN final_user_id = 0 AND effective_sid = '' THEN effective_ip ELSE '' END)
|
||||
) v
|
||||
LEFT JOIN (
|
||||
SELECT m1.* FROM messages m1
|
||||
SELECT m1.*, m2.final_user_id as m_final_user_id, m2.effective_ip as m_effective_ip, m2.effective_sid as m_effective_sid FROM messages m1
|
||||
INNER JOIN (
|
||||
SELECT MAX(id) as max_id FROM (
|
||||
SELECT id, COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = messages.ip_address LIMIT 1), 0) as final_user_id, ip_address FROM messages
|
||||
) t2 GROUP BY (CASE WHEN final_user_id = 0 THEN 0 ELSE final_user_id END), (CASE WHEN final_user_id = 0 THEN ip_address ELSE '0' END)
|
||||
SELECT MAX(id) as max_id, final_user_id, effective_ip, effective_sid FROM (
|
||||
SELECT id,
|
||||
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = messages.ip_address AND messages.ip_address != '---' AND messages.ip_address != '' LIMIT 1), 0) as final_user_id,
|
||||
CASE WHEN ip_address = '---' THEN '' ELSE IFNULL(ip_address, '') END as effective_ip,
|
||||
IFNULL(session_id, '') as effective_sid
|
||||
FROM messages
|
||||
) t2 GROUP BY final_user_id, (CASE WHEN final_user_id = 0 THEN effective_sid ELSE '' END), (CASE WHEN final_user_id = 0 AND effective_sid = '' THEN effective_ip ELSE '' END)
|
||||
) m2 ON m1.id = m2.max_id
|
||||
) m ON (v.final_user_id = COALESCE(NULLIF(m.user_id, 0), (SELECT id FROM users WHERE registration_ip = m.ip_address LIMIT 1), 0))
|
||||
) m ON (v.final_user_id = m.m_final_user_id AND (v.final_user_id != 0 OR (v.effective_sid != '' AND v.effective_sid = m.m_effective_sid) OR (v.effective_sid = '' AND v.effective_ip = m.m_effective_ip)))
|
||||
LEFT JOIN users u ON (v.final_user_id = u.id AND v.final_user_id != 0)
|
||||
LEFT JOIN chat_remarks r ON (v.final_user_id = COALESCE(r.user_id, 0) AND (v.final_user_id != 0 OR IFNULL(v.ip_address, '') = IFNULL(m.ip_address, '')))
|
||||
LEFT JOIN chat_remarks r ON (v.final_user_id = r.user_id AND (v.final_user_id != 0 OR (v.effective_sid != '' AND v.effective_sid = r.session_id) OR (v.effective_sid = '' AND v.effective_ip = r.ip_address)))
|
||||
ORDER BY created_at DESC
|
||||
");
|
||||
echo json_encode($stmt->fetchAll());
|
||||
@ -271,10 +295,12 @@ if ($action === 'save_remark') {
|
||||
|
||||
$user_id = $_POST['user_id'] ?? 0;
|
||||
$ip = $_POST['ip_address'] ?? '';
|
||||
$sid = $_POST['session_id'] ?? '';
|
||||
if ($ip === '---') $ip = '';
|
||||
$remark = $_POST['remark'] ?? '';
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO chat_remarks (user_id, ip_address, remark) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE remark = ?");
|
||||
$stmt->execute([$user_id, $ip, $remark, $remark]);
|
||||
$stmt = db()->prepare("INSERT INTO chat_remarks (user_id, ip_address, session_id, remark) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE remark = ?, session_id = VALUES(session_id)");
|
||||
$stmt->execute([$user_id, $ip, $sid, $remark, $remark]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
@ -285,9 +311,11 @@ if ($action === 'get_remark') {
|
||||
|
||||
$user_id = $_GET['user_id'] ?? 0;
|
||||
$ip = $_GET['ip'] ?? '';
|
||||
$sid = $_GET['session_id'] ?? '';
|
||||
if ($ip === '---') $ip = '';
|
||||
|
||||
$stmt = db()->prepare("SELECT remark FROM chat_remarks WHERE user_id = ? AND ip_address = ?");
|
||||
$stmt->execute([$user_id, $ip]);
|
||||
$stmt = db()->prepare("SELECT remark FROM chat_remarks WHERE user_id = ? AND (session_id = ? AND session_id != '') OR (user_id != 0 AND user_id = ?) OR (ip_address = ? AND ip_address != '' AND session_id = '')");
|
||||
$stmt->execute([$user_id, $sid, $user_id, $ip]);
|
||||
$remark = $stmt->fetchColumn() ?: '';
|
||||
|
||||
echo json_encode(['success' => true, 'remark' => $remark]);
|
||||
@ -298,6 +326,8 @@ if ($action === 'admin_delete_user') {
|
||||
if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized']));
|
||||
$user_id = $_POST['user_id'] ?? 0;
|
||||
$ip = $_POST['ip_address'] ?? '';
|
||||
$sid = $_POST['session_id'] ?? '';
|
||||
if ($ip === '---') $ip = '';
|
||||
|
||||
if ($user_id != 0) {
|
||||
$stmt = db()->prepare("DELETE FROM messages WHERE user_id = ?");
|
||||
@ -307,12 +337,12 @@ if ($action === 'admin_delete_user') {
|
||||
$stmt = db()->prepare("DELETE FROM chat_remarks WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
} else {
|
||||
$stmt = db()->prepare("DELETE FROM messages WHERE user_id = 0 AND ip_address = ?");
|
||||
$stmt->execute([$ip]);
|
||||
$stmt = db()->prepare("DELETE FROM chat_visitors WHERE user_id = 0 AND ip_address = ?");
|
||||
$stmt->execute([$ip]);
|
||||
$stmt = db()->prepare("DELETE FROM chat_remarks WHERE user_id = 0 AND ip_address = ?");
|
||||
$stmt->execute([$ip]);
|
||||
$stmt = db()->prepare("DELETE FROM messages WHERE user_id = 0 AND (session_id = ? OR ip_address = ?)");
|
||||
$stmt->execute([$sid, $ip]);
|
||||
$stmt = db()->prepare("DELETE FROM chat_visitors WHERE user_id = 0 AND (session_id = ? OR ip_address = ?)");
|
||||
$stmt->execute([$sid, $ip]);
|
||||
$stmt = db()->prepare("DELETE FROM chat_remarks WHERE user_id = 0 AND (session_id = ? OR ip_address = ?)");
|
||||
$stmt->execute([$sid, $ip]);
|
||||
}
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
|
||||
@ -32,7 +32,7 @@ if ($action === 'get_orders') {
|
||||
|
||||
if ($tab === 'binary') {
|
||||
// Auto-settle expired orders
|
||||
$stmt = $db->prepare("SELECT o.*, u.win_loss_control as user_control FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.user_id = ? AND o.status = 0 AND DATE_ADD(o.created_at, INTERVAL o.duration SECOND) <= NOW()");
|
||||
$stmt = $db->prepare("SELECT o.*, u.win_loss_control as user_control FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.user_id = ? AND o.status = 'pending' AND DATE_ADD(o.created_at, INTERVAL o.duration SECOND) <= NOW()");
|
||||
$stmt->execute([$user_id]);
|
||||
$expired = $stmt->fetchAll();
|
||||
|
||||
@ -88,10 +88,10 @@ if ($action === 'get_orders') {
|
||||
'pnl' => $o['status'] === 'won' ? (float)($o['amount'] * $o['profit_rate'] / 100) : ($o['status'] === 'lost' ? -(float)$o['amount'] : 0),
|
||||
'total' => $o['status'] === 'won' ? (float)($o['amount'] + ($o['amount'] * $o['profit_rate'] / 100)) : ($o['status'] === 'lost' ? 0.00 : '---'),
|
||||
'status' => ($o['status'] === 'won' ? __('won') : ($o['status'] === 'lost' ? __('loss') : __('executing'))),
|
||||
'status_type' => $o['status'] === 0 ? 'executing' : $o['status'],
|
||||
'status_type' => $o['status'] === 'pending' ? 'executing' : $o['status'],
|
||||
'profitRate' => $o['profit_rate']
|
||||
];
|
||||
if ($o['status'] === 0) {
|
||||
if ($o['status'] === 'pending') {
|
||||
$row['status'] = __('executing');
|
||||
$row['totalSeconds'] = $o['duration'];
|
||||
$elapsed = time() - strtotime($o['created_at']);
|
||||
|
||||
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->execute([$user_id, $symbol, $side, $price, $amount, $amount, getRealIP()]);
|
||||
|
||||
// ...
|
||||
// Add receive balance
|
||||
$receive_symbol = ($side === 'buy') ? $symbol : 'USDT';
|
||||
$receive_amount = ($side === 'buy') ? $amount : ($price * $amount);
|
||||
|
||||
$stmt = $db->prepare("SELECT id FROM user_balances WHERE user_id = ? AND symbol = ?");
|
||||
$stmt->execute([$user_id, $receive_symbol]);
|
||||
if ($stmt->fetch()) {
|
||||
$db->prepare("UPDATE user_balances SET available = available + ? WHERE user_id = ? AND symbol = ?")
|
||||
->execute([$receive_amount, $user_id, $receive_symbol]);
|
||||
} else {
|
||||
$db->prepare("INSERT INTO user_balances (user_id, symbol, available) VALUES (?, ?, ?)")
|
||||
->execute([$user_id, $receive_symbol, $receive_amount]);
|
||||
}
|
||||
|
||||
// Record transaction
|
||||
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status, ip_address) VALUES (?, ?, ?, ?, 'completed', ?)")
|
||||
|
||||
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,
|
||||
`entry_price` decimal(20,8) NOT NULL,
|
||||
`close_price` decimal(20,8) DEFAULT NULL,
|
||||
`status` enum(0,'won','lost','cancelled') DEFAULT 0,
|
||||
`status` enum('pending','won','lost','cancelled') DEFAULT 'pending',
|
||||
`control_status` tinyint(4) DEFAULT 0 COMMENT '0: normal, 1: force win, 2: force loss',
|
||||
`created_at` timestamp NULL DEFAULT current_timestamp(),
|
||||
`ip_address` varchar(45) DEFAULT NULL,
|
||||
@ -117,10 +117,11 @@ CREATE TABLE `chat_remarks` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) DEFAULT 0,
|
||||
`ip_address` varchar(45) DEFAULT NULL,
|
||||
`session_id` varchar(255) DEFAULT NULL,
|
||||
`remark` text DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_id` (`user_id`,`ip_address`)
|
||||
UNIQUE KEY `user_id` (`user_id`,`ip_address`,`session_id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
@ -146,10 +147,12 @@ CREATE TABLE `chat_visitors` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) DEFAULT 0,
|
||||
`ip_address` varchar(45) DEFAULT NULL,
|
||||
`session_id` varchar(255) DEFAULT NULL,
|
||||
`last_ping` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
`user_time` varchar(50) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_id` (`user_id`,`ip_address`)
|
||||
UNIQUE KEY `user_id` (`user_id`,`ip_address`),
|
||||
KEY `idx_session` (`session_id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=57 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
@ -310,8 +313,10 @@ CREATE TABLE `messages` (
|
||||
`sender` enum('user','admin') DEFAULT NULL,
|
||||
`message` text DEFAULT NULL,
|
||||
`ip_address` varchar(45) DEFAULT NULL,
|
||||
`session_id` varchar(255) DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`)
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_session` (`session_id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
|
||||
@ -403,6 +403,16 @@ $translations = [
|
||||
'lost' => '亏损',
|
||||
4 => '已拒绝',
|
||||
'cancelled' => '已取消',
|
||||
'hosting' => '托管中',
|
||||
'today_profit' => '今日收益',
|
||||
'total_profit' => '累计收益',
|
||||
'order_count' => '订单数',
|
||||
'btc_mining_pool' => 'BTC 矿池',
|
||||
'eth_mining_pool' => 'ETH 矿池',
|
||||
'usdt_mining_pool' => 'USDT 矿池',
|
||||
'bnb_mining_pool' => 'BNB 矿池',
|
||||
'sol_mining_pool' => 'SOL 矿池',
|
||||
'avax_mining_pool' => 'AVAX 矿池',
|
||||
'app_store' => '苹果商店',
|
||||
'google_play' => '谷歌商店',
|
||||
'android_apk' => '安卓应用',
|
||||
@ -1001,6 +1011,16 @@ $translations = [
|
||||
'lost' => 'Lost',
|
||||
4 => 'Rejected',
|
||||
'cancelled' => 'Cancelled',
|
||||
'hosting' => 'Hosting',
|
||||
'today_profit' => 'Today\'s Profit',
|
||||
'total_profit' => 'Total Profit',
|
||||
'order_count' => 'Order Count',
|
||||
'btc_mining_pool' => 'BTC Mining Pool',
|
||||
'eth_mining_pool' => 'ETH Mining Pool',
|
||||
'usdt_mining_pool' => 'USDT Mining Pool',
|
||||
'bnb_mining_pool' => 'BNB Mining Pool',
|
||||
'sol_mining_pool' => 'SOL Mining Pool',
|
||||
'avax_mining_pool' => 'AVAX Mining Pool',
|
||||
'app_store' => 'App Store',
|
||||
'google_play' => 'Google Play',
|
||||
'android_apk' => 'Android APK',
|
||||
|
||||
67
mining.php
@ -1,6 +1,33 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/lang.php';
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
$stakingAmount = 0;
|
||||
$todayProfit = 0;
|
||||
$totalProfit = 0;
|
||||
$orderCount = 0;
|
||||
|
||||
if ($user) {
|
||||
// 托管中
|
||||
$stmt = db()->prepare("SELECT SUM(amount) FROM staking_records WHERE user_id = ? AND status = 'running'");
|
||||
$stmt->execute([$user['id']]);
|
||||
$stakingAmount = $stmt->fetchColumn() ?: 0;
|
||||
|
||||
// 今日收益
|
||||
$stmt = db()->prepare("SELECT SUM(amount) FROM transactions WHERE user_id = ? AND type = 'mining' AND DATE(created_at) = CURDATE()");
|
||||
$stmt->execute([$user['id']]);
|
||||
$todayProfit = $stmt->fetchColumn() ?: 0;
|
||||
|
||||
// 累计收益
|
||||
$stmt = db()->prepare("SELECT SUM(amount) FROM transactions WHERE user_id = ? AND type = 'mining'");
|
||||
$stmt->execute([$user['id']]);
|
||||
$totalProfit = $stmt->fetchColumn() ?: 0;
|
||||
|
||||
// 订单数
|
||||
$stmt = db()->prepare("SELECT COUNT(*) FROM staking_records WHERE user_id = ?");
|
||||
$stmt->execute([$user['id']]);
|
||||
$orderCount = $stmt->fetchColumn() ?: 0;
|
||||
}
|
||||
?>
|
||||
<main class="container py-5">
|
||||
<div class="text-center mb-5">
|
||||
@ -10,15 +37,43 @@ require_once __DIR__ . '/includes/header.php';
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Stats Section -->
|
||||
<div class="row g-3 mb-5">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="bg-surface border border-secondary p-3 rounded-4 shadow-sm text-center">
|
||||
<div class="text-white-50 small mb-1"><?= __('hosting') ?></div>
|
||||
<div class="fs-5 fw-bold text-white"><?= number_format($stakingAmount, 2) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="bg-surface border border-secondary p-3 rounded-4 shadow-sm text-center">
|
||||
<div class="text-white-50 small mb-1"><?= __('today_profit') ?></div>
|
||||
<div class="fs-5 fw-bold text-success">+<?= number_format($todayProfit, 4) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="bg-surface border border-secondary p-3 rounded-4 shadow-sm text-center">
|
||||
<div class="text-white-50 small mb-1"><?= __('total_profit') ?></div>
|
||||
<div class="fs-5 fw-bold text-primary"><?= number_format($totalProfit, 4) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="bg-surface border border-secondary p-3 rounded-4 shadow-sm text-center">
|
||||
<div class="text-white-50 small mb-1"><?= __('order_count') ?></div>
|
||||
<div class="fs-5 fw-bold text-warning"><?= $orderCount ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4 mb-5">
|
||||
<?php
|
||||
$pools = [
|
||||
['symbol' => 'BTC', 'name' => __('bitcoin') . __('mining_pool'), 'apy' => '12.5%', 'min' => '0.01 ' . __('BTC'), 'term' => '30 ' . __('day'), 'hot' => true],
|
||||
['symbol' => 'ETH', 'name' => __('ethereum') . __('mining_pool'), 'apy' => '8.2%', 'min' => '0.1 ' . __('ETH'), 'term' => __('flexible'), 'hot' => false],
|
||||
['symbol' => 'USDT', 'name' => __('tether') . __('mining_pool'), 'apy' => '15.0%', 'min' => '100 ' . __('USDT'), 'term' => '90 ' . __('day'), 'hot' => true],
|
||||
['symbol' => 'BNB', 'name' => __('binance_coin') . __('mining_pool'), 'apy' => '22.0%', 'min' => '1 ' . __('BNB'), 'term' => '180 ' . __('day'), 'hot' => false],
|
||||
['symbol' => 'SOL', 'name' => __('solana') . __('mining_pool'), 'apy' => '14.2%', 'min' => '5 ' . __('SOL'), 'term' => '60 ' . __('day'), 'hot' => false],
|
||||
['symbol' => 'AVAX', 'name' => __('avalanche') . __('mining_pool'), 'apy' => '18.5%', 'min' => '10 ' . __('AVAX'), 'term' => '120 ' . __('day'), 'hot' => false],
|
||||
['symbol' => 'BTC', 'name' => __('btc_mining_pool'), 'apy' => '12.5%', 'min' => '0.01 ' . __('BTC'), 'term' => '30 ' . __('day'), 'hot' => true],
|
||||
['symbol' => 'ETH', 'name' => __('eth_mining_pool'), 'apy' => '8.2%', 'min' => '0.1 ' . __('ETH'), 'term' => __('flexible'), 'hot' => false],
|
||||
['symbol' => 'USDT', 'name' => __('usdt_mining_pool'), 'apy' => '15.0%', 'min' => '100 ' . __('USDT'), 'term' => '90 ' . __('day'), 'hot' => true],
|
||||
['symbol' => 'BNB', 'name' => __('bnb_mining_pool'), 'apy' => '22.0%', 'min' => '1 ' . __('BNB'), 'term' => '180 ' . __('day'), 'hot' => false],
|
||||
['symbol' => 'SOL', 'name' => __('sol_mining_pool'), 'apy' => '14.2%', 'min' => '5 ' . __('SOL'), 'term' => '60 ' . __('day'), 'hot' => false],
|
||||
['symbol' => 'AVAX', 'name' => __('avax_mining_pool'), 'apy' => '18.5%', 'min' => '10 ' . __('AVAX'), 'term' => '120 ' . __('day'), 'hot' => false],
|
||||
];
|
||||
|
||||
foreach ($pools as $pool): ?>
|
||||
|
||||
@ -305,11 +305,14 @@ $kycStatusColor = [
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mobile Transaction List -->
|
||||
<div class="d-md-none p-3">
|
||||
<div class="d-md-none p-3 mobile-transaction-list" style="max-height: 400px; overflow-y: auto;">
|
||||
<?php if (empty($transactions)): ?>
|
||||
<div class="text-center py-4 text-white-50 small"><?= __('no_records_found') ?></div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($transactions as $t):
|
||||
<?php
|
||||
$count = 0;
|
||||
foreach ($transactions as $t):
|
||||
$count++;
|
||||
$typeName = __($t['type']);
|
||||
$typeColor = (strpos($t['type'], 'win') !== false || $t['type'] === 'deposit' || $t['type'] === 'recharge') ? 'text-success' : ((strpos($t['type'], 'loss') !== false || $t['type'] === 'withdraw' || $t['type'] === 'withdrawal') ? 'text-danger' : 'text-primary');
|
||||
$statusText = ($t['status'] === 'completed' || $t['status'] === 3) ? __(3) : ($t['status'] === 4 ? __(4) : __(0));
|
||||
@ -335,6 +338,8 @@ $kycStatusColor = [
|
||||
|
||||
<style>
|
||||
.x-small { font-size: 11px; }
|
||||
.mobile-transaction-list::-webkit-scrollbar { display: none; }
|
||||
.mobile-transaction-list { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
@media (max-width: 991px) {
|
||||
.col-lg-3 { margin-bottom: 1rem; }
|
||||
}
|
||||
|
||||