38350-vm/admin/chat_iframe.php
2026-02-14 05:11:30 +00:00

330 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require_once "auth.php";
require_once '../db/config.php';
require_once '../includes/currency_helper.php';
$pdo = db();
$user_id = $_GET['user_id'] ?? null;
if (!$user_id) die("需要用户 ID");
// Initial mark as read
$pdo->prepare("UPDATE messages SET is_read = 1 WHERE user_id = ? AND sender = 'user'")->execute([$user_id]);
// 处理消息发送
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['message'])) {
$msg = $_POST['message'];
$pdo->prepare("INSERT INTO messages (user_id, sender, message, type) VALUES (?, 'admin', ?, 'text')")->execute([$user_id, $msg]);
exit;
}
// 处理充值/提现操作
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['action'])) {
$oid = $_POST['order_id'];
if ($_POST['action'] == 'match_deposit') {
$bank = $_POST['bank_name'] ?? '';
$name = $_POST['account_name'] ?? '';
$number = $_POST['account_number'] ?? '';
$remarks = $_POST['remarks'] ?? '';
$info = "Bank$bank\nAccount Name$name\nBank Acc$number\nRemarks$remarks";
$pdo->prepare("UPDATE fiat_orders SET status = 'matched', bank_account_info = ? WHERE id = ?")->execute([$info, $oid]);
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, $info]);
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, "✅ 匹配成功收款账户已下发请在转账后上传凭证。clearly"]);
echo "<script>parent.location.reload();</script>";
exit;
} elseif ($_POST['action'] == 'send_withdraw_format') {
$format = "Bank\nBank Acc\nAccount Name";
$pdo->prepare("UPDATE fiat_orders SET status = 'matched', bank_account_info = ? WHERE id = ?")->execute([$format, $oid]);
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, $format]);
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, "✅ 请按上述格式回复您的收款详情。"]);
echo "<script>parent.location.reload();</script>";
exit;
} elseif ($_POST['action'] == 'complete') {
$stmt = $pdo->prepare("SELECT order_type, usdt_amount, currency FROM fiat_orders WHERE id = ?");
$stmt->execute([$oid]);
$order = $stmt->fetch();
$pdo->beginTransaction();
try {
if ($order['order_type'] === 'deposit') {
$pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$order['usdt_amount'], $user_id]);
$msg = "🎉 您的充值已确认到账!";
} else {
$msg = "🎉 您的提现申请已处理完成,请查收!";
}
$pdo->prepare("UPDATE fiat_orders SET status = 'completed' WHERE id = ?")->execute([$oid]);
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, $msg]);
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
die($e->getMessage());
}
echo "<script>parent.location.reload();</script>";
exit;
} elseif ($_POST['action'] == 'reject') {
$stmt = $pdo->prepare("SELECT order_type, usdt_amount FROM fiat_orders WHERE id = ?");
$stmt->execute([$oid]);
$order = $stmt->fetch();
$pdo->beginTransaction();
try {
if ($order['order_type'] === 'withdrawal') {
$pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$order['usdt_amount'], $user_id]);
}
$pdo->prepare("UPDATE fiat_orders SET status = 'rejected' WHERE id = ?")->execute([$oid]);
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, "❌ 您的申请已被拒绝,如有疑问请咨询客服。"]);
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
die($e->getMessage());
}
echo "<script>parent.location.reload();</script>";
exit;
}
}
$user = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$user->execute([$user_id]);
$userData = $user->fetch();
$orders = $pdo->prepare("SELECT * FROM fiat_orders WHERE user_id = ? AND status IN ('matching', 'matched', 'paid') ORDER BY id DESC");
$orders->execute([$user_id]);
$pending_orders = $orders->fetchAll();
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
body { margin: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #0B0E11; color: white; display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
.chat-header { padding: 12px 20px; background: #1E2329; border-bottom: 1px solid #2B3139; display: flex; flex-direction: column; gap: 5px; font-size: 13px; }
.header-top { display: flex; justify-content: space-between; align-items: center; }
.header-bottom { display: flex; justify-content: space-between; font-size: 12px; color: #848E9C; }
#chat-box { flex: 1; padding: 20px; display: flex; flex-direction: column; gap: 15px; overflow-y: auto; scroll-behavior: smooth; background: #0b0e11; }
.msg { max-width: 80%; padding: 12px 16px; border-radius: 16px; font-size: 13.5px; line-height: 1.6; position: relative; }
.msg.admin { align-self: flex-end; background: #f0b90b; color: black; border-bottom-right-radius: 4px; }
.msg.user { align-self: flex-start; background: #2B3139; color: #EAECEF; border-bottom-left-radius: 4px; border: 1px solid #3b424d; }
.msg-time { font-size: 10px; opacity: 0.5; margin-top: 5px; display: block; text-align: right; }
.input-area { padding: 15px 20px; background: #1E2329; border-top: 1px solid #2B3139; display: flex; gap: 12px; align-items: center; }
input[type="text"] { flex: 1; background: #0B0E11; border: 1px solid #2B3139; color: white; padding: 12px 15px; border-radius: 10px; outline: none; transition: 0.3s; }
input[type="text"]:focus { border-color: #f0b90b; }
button { background: #f0b90b; border: none; color: black; padding: 10px 20px; border-radius: 10px; cursor: pointer; font-weight: bold; transition: 0.3s; }
button:hover { background: #fcd535; }
.order-panel { background: #1E2329; border-bottom: 1px solid #2B3139; padding: 15px; max-height: 250px; overflow-y: auto; }
.order-card { background: #2B3139; border: 1px solid #3b424d; padding: 15px; border-radius: 12px; margin-bottom: 10px; }
.order-card input { background: #0B0E11; border: 1px solid #3b424d; color: white; padding: 8px; border-radius: 6px; font-size: 12px; margin-bottom: 5px; width: 100%; box-sizing: border-box; }
.status-tag { padding: 2px 8px; border-radius: 4px; font-size: 10px; font-weight: bold; text-transform: uppercase; }
.status-matching { background: rgba(240, 185, 11, 0.2); color: #f0b90b; }
.status-matched { background: rgba(0, 192, 135, 0.2); color: #00c087; }
.status-paid { background: rgba(14, 203, 129, 0.2); color: #0ecb81; }
.sending { opacity: 0.7; }
.sending::after { content: '...'; position: absolute; right: 5px; bottom: 5px; font-size: 10px; }
.icon-btn { background: #2b3139; border: 1px solid #3b424d; width: 40px; height: 40px; border-radius: 8px; color: #f0b90b; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; }
.icon-btn:hover { background: #3b424d; }
</style>
</head>
<body>
<div class="chat-header">
<div class="header-top">
<span><i class="fas fa-user-circle"></i> <b><?php echo htmlspecialchars($userData['username'] ?? ''); ?></b> (UID: <?php echo $userData['uid'] ?? ''; ?>)</span>
<span style="color: #00c087; font-weight: bold;">余额: <?php echo number_format($userData['balance'] ?? 0, 2); ?> USDT</span>
</div>
<div class="header-bottom">
<span><i class="fas fa-network-wired"></i> 实时 IP: <?php echo htmlspecialchars($userData['last_ip'] ?? '未知'); ?></span>
<span><i class="fas fa-clock"></i> 当前时间: <?php echo date('Y-m-d H:i:s'); ?></span>
</div>
</div>
<div style="flex: 1; display: flex; flex-direction: column; overflow: hidden;">
<?php if ($pending_orders): ?>
<div class="order-panel">
<div style="font-size: 11px; color: #848E9C; margin-bottom: 10px; font-weight: bold;">待处理申请 (ACTIVE REQUESTS)</div>
<?php foreach($pending_orders as $o):
$orderTypeDisplay = $o['order_type'] === 'deposit' ? '充值' : '提现';
$statusDisplay = '';
if ($o['status'] === 'matching') $statusDisplay = '待受理';
elseif ($o['status'] === 'matched') $statusDisplay = '已下发';
elseif ($o['status'] === 'paid') $statusDisplay = '待审核';
?>
<div class="order-card">
<div style="display:flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<div>
<span style="font-weight: 800; color: #f0b90b; font-size: 14px;"><?php echo $orderTypeDisplay; ?>: <?php echo $o['amount']; ?> <?php echo $o['currency']; ?></span>
<span style="color: #848E9C; font-size: 12px; margin-left: 10px;">≈ <?php echo number_format($o['usdt_amount'], 2); ?> USDT</span>
</div>
<span class="status-tag status-<?php echo $o['status']; ?>"><?php echo $statusDisplay; ?></span>
</div>
<?php if($o['status'] == 'matching'): ?>
<?php if($o['order_type'] == 'deposit'): ?>
<form method="POST">
<input type="hidden" name="order_id" value="<?php echo $o['id']; ?>">
<input type="hidden" name="action" value="match_deposit">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
<input type="text" name="bank_name" placeholder="银行名称" required>
<input type="text" name="account_name" placeholder="收款姓名" required>
<input type="text" name="account_number" placeholder="收款账号 / IBAN" required style="grid-column: span 2;">
<input type="text" name="remarks" placeholder="备注说明 (可选)" style="grid-column: span 2;">
<button type="submit" style="grid-column: span 2; background: #00c087; color: white;">下发收款账户</button>
</div>
</form>
<?php else: ?>
<form method="POST">
<input type="hidden" name="order_id" value="<?php echo $o['id']; ?>">
<input type="hidden" name="action" value="send_withdraw_format">
<button type="submit" style="width: 100%; background: #4facfe; color: white;">发送提现信息格式模板</button>
</form>
<?php endif; ?>
<?php else: ?>
<div style="display: flex; gap: 10px;">
<form method="POST" style="flex: 1;">
<input type="hidden" name="order_id" value="<?php echo $o['id']; ?>">
<input type="hidden" name="action" value="complete">
<button type="submit" style="width: 100%; background: #00c087; color: white;">同意 (Approve)</button>
</form>
<form method="POST" style="flex: 1;">
<input type="hidden" name="order_id" value="<?php echo $o['id']; ?>">
<input type="hidden" name="action" value="reject">
<button type="submit" style="width: 100%; background: #f6465d; color: white;">拒绝 (Reject)</button>
</form>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div id="chat-box"></div>
</div>
<form class="input-area" id="msg-form">
<button type="button" class="icon-btn" id="upload-btn" onclick="document.getElementById('image-input').click()">
<i class="fas fa-plus"></i>
</button>
<input type="file" id="image-input" accept="image/*" style="display: none;" onchange="uploadImage(this)">
<input type="text" id="msg-input" placeholder="输入消息..." autocomplete="off">
<button type="submit"><i class="fas fa-paper-plane"></i></button>
</form>
<audio id="notif-sound" src="https://assets.mixkit.co/active_storage/sfx/2869/2869-preview.mp3" preload="auto"></audio>
<script>
const chatBox = document.getElementById('chat-box');
const msgInput = document.getElementById('msg-input');
const uploadBtn = document.getElementById('upload-btn');
const notifSound = document.getElementById('notif-sound');
let lastMsgId = 0;
let lastMessagesHtml = '';
let isSending = false;
async function loadMessages() {
if (isSending) return;
try {
const resp = await fetch('../api/get_messages.php?user_id=<?php echo $user_id; ?>');
const res = await resp.json();
if (res.success) {
let html = '';
let hasNewUserMsg = false;
res.data.forEach(m => {
if (m.id > lastMsgId) {
if (m.sender === 'user' && lastMsgId > 0) hasNewUserMsg = true;
lastMsgId = m.id;
}
html += `
<div class="msg ${m.sender}">
${m.type === 'image' ? `<img src="../${m.message}" style="max-width:100%; border-radius:8px; cursor:pointer;" onclick="window.open(this.src)">` : m.message.replace(/\n/g, '<br>')}
<span class="msg-time">${new Date(m.created_at).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
</div>
`;
});
if (hasNewUserMsg) {
try { notifSound.play(); } catch(e) {}
}
if (html !== lastMessagesHtml) {
const isAtBottom = chatBox.scrollHeight - chatBox.scrollTop <= chatBox.clientHeight + 100;
chatBox.innerHTML = html;
lastMessagesHtml = html;
if (isAtBottom) chatBox.scrollTop = chatBox.scrollHeight;
// Mark as read
fetch('../api/get_messages.php?action=mark_read&user_id=<?php echo $user_id; ?>&reader=admin');
}
}
} catch (e) {}
}
document.getElementById('msg-form').onsubmit = async (e) => {
e.preventDefault();
const msg = msgInput.value.trim();
if (!msg || isSending) return;
msgInput.value = '';
isSending = true;
// Optimistic UI
const tempMsg = document.createElement('div');
tempMsg.className = 'msg admin sending';
tempMsg.innerHTML = `${msg.replace(/\n/g, '<br>')}<span class="msg-time">${new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>`;
chatBox.appendChild(tempMsg);
chatBox.scrollTop = chatBox.scrollHeight;
const formData = new FormData();
formData.append('message', msg);
try {
await fetch(window.location.href, { method: 'POST', body: formData });
isSending = false;
await loadMessages();
} catch (e) {
isSending = false;
tempMsg.style.background = '#f6465d';
tempMsg.innerHTML += ' (发送失败)';
}
};
function uploadImage(input) {
if (!input.files || !input.files[0]) return;
const formData = new FormData();
formData.append('image', input.files[0]);
formData.append('is_admin', '1'); // Optional, to distinguish sender if needed
uploadBtn.disabled = true;
uploadBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
// We need a separate API or same API for admin image upload
// api/upload_chat_image.php already handles sending message as 'user'
// I should probably create a separate one or modify it.
// Let's modify api/upload_chat_image.php to support admin sender.
fetch('../api/upload_chat_image.php?sender=admin&user_id=<?php echo $user_id; ?>', { method: 'POST', body: formData })
.then(res => res.json())
.then(res => {
if (res.success) {
loadMessages();
} else {
alert(res.error || '上传失败');
}
})
.finally(() => {
uploadBtn.disabled = false;
uploadBtn.innerHTML = '<i class="fas fa-plus"></i>';
input.value = '';
});
}
loadMessages();
setInterval(loadMessages, 1000);
</script>
</body>
</html>