38320-vm/admin.php
Flatlogic Bot fd1a7fb782 11
2026-02-10 12:44:05 +00:00

460 lines
22 KiB
PHP

<?php
session_start();
require_once __DIR__ . '/db/config.php';
$pdo = db();
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
if (!$user) {
session_destroy();
header('Location: index.php?error=user_not_found');
exit;
}
// Ensure role is admin
if ($user['role'] !== 'admin') {
// Check if this is the ONLY user, if so, force admin
$count = $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn();
if ($count == 1) {
$pdo->query("UPDATE users SET role = 'admin' WHERE id = " . $user['id']);
$user['role'] = 'admin';
$_SESSION['role'] = 'admin';
} else {
die('Access Denied: You do not have administrator privileges. Your role is: ' . htmlspecialchars($user['role']) . '. Please logout and login as admin.');
}
}
$action = $_GET['action'] ?? 'dashboard';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($action === 'confirm_recharge') {
$id = $_POST['id'];
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare("SELECT * FROM recharges WHERE id = ? AND status = 'pending'");
$stmt->execute([$id]);
$recharge = $stmt->fetch();
if ($recharge) {
$stmt = $pdo->prepare("UPDATE recharges SET status = 'completed' WHERE id = ?");
$stmt->execute([$id]);
$stmt = $pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?");
$stmt->execute([$recharge['amount'], $recharge['user_id']]);
$pdo->commit();
} else {
$pdo->rollBack();
}
} catch (Exception $e) {
$pdo->rollBack();
}
header('Location: admin.php?action=recharges');
exit;
}
if ($action === 'update_settings') {
foreach ($_POST['settings'] as $key => $value) {
$stmt = $pdo->prepare("UPDATE settings SET setting_value = ?, updated_at = NOW() WHERE setting_key = ?");
$stmt->execute([$value, $key]);
}
header('Location: admin.php?action=settings&success=1');
exit;
}
if ($action === 'update_user') {
$id = $_POST['id'];
$balance = $_POST['balance'];
$role = $_POST['role'];
$stmt = $pdo->prepare("UPDATE users SET balance = ?, role = ? WHERE id = ?");
$stmt->execute([$balance, $role, $id]);
header('Location: admin.php?action=users');
exit;
}
}
$settings = $pdo->query("SELECT setting_key, setting_value FROM settings")->fetchAll(PDO::FETCH_KEY_PAIR);
// Fetch stats for dashboard
$stats = [
'total_users' => $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn(),
'total_recharge' => $pdo->query("SELECT SUM(amount) FROM recharges WHERE status = 'completed'")->fetchColumn() ?: 0,
'total_orders' => $pdo->query("SELECT COUNT(*) FROM sms_orders")->fetchColumn(),
'pending_recharges' => $pdo->query("SELECT COUNT(*) FROM recharges WHERE status = 'pending'")->fetchColumn()
];
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>后台管理 - <?= htmlspecialchars($settings['site_name'] ?? '全球接码') ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<style>
:root {
--primary: #3b82f6;
--bg-body: #f8fafc;
--sidebar-width: 260px;
}
body { background: var(--bg-body); font-family: system-ui, -apple-system, sans-serif; }
.sidebar {
width: var(--sidebar-width);
height: 100vh;
position: fixed;
left: 0; top: 0;
background: #1e293b;
color: white;
padding: 20px;
}
.main-content { margin-left: var(--sidebar-width); padding: 40px; }
.nav-link { color: #cbd5e1; padding: 12px 15px; border-radius: 8px; margin-bottom: 5px; }
.nav-link:hover, .nav-link.active { background: #334155; color: white; }
.nav-link i { width: 20px; margin-right: 10px; }
.card { border: none; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.stat-card { padding: 24px; }
.stat-value { font-size: 24px; font-weight: 700; margin: 8px 0; }
.stat-label { color: #64748b; font-size: 14px; text-transform: uppercase; }
</style>
</head>
<body>
<div class="sidebar">
<div class="mb-4 px-3">
<h5 class="fw-bold mb-0">管理后台</h5>
<small class="text-muted">ADMIN PANEL</small>
</div>
<nav class="nav flex-column">
<a class="nav-link <?= $action === 'dashboard' ? 'active' : '' ?>" href="admin.php?action=dashboard"><i class="fas fa-home"></i> 控制台</a>
<a class="nav-link <?= $action === 'users' ? 'active' : '' ?>" href="admin.php?action=users"><i class="fas fa-users"></i> 用户管理</a>
<a class="nav-link <?= $action === 'recharges' ? 'active' : '' ?>" href="admin.php?action=recharges"><i class="fas fa-wallet"></i> 充值管理</a>
<a class="nav-link <?= $action === 'orders' ? 'active' : '' ?>" href="admin.php?action=orders"><i class="fas fa-shopping-cart"></i> 订单记录</a>
<a class="nav-link <?= $action === 'support' ? 'active' : '' ?>" href="admin.php?action=support"><i class="fas fa-headset"></i> 客服消息</a>
<a class="nav-link <?= $action === 'settings' ? 'active' : '' ?>" href="admin.php?action=settings"><i class="fas fa-cog"></i> 系统设置</a>
<hr class="my-3 border-secondary">
<a class="nav-link" href="dashboard.php"><i class="fas fa-arrow-left"></i> 返回前台</a>
<a class="nav-link text-danger" href="auth.php?action=logout"><i class="fas fa-sign-out-alt"></i> 退出登录</a>
</nav>
</div>
<div class="main-content">
<?php if ($action === 'dashboard'): ?>
<h4 class="fw-bold mb-4">数据概览</h4>
<div class="row g-4">
<div class="col-md-3">
<div class="card stat-card">
<div class="stat-label">总用户数</div>
<div class="stat-value"><?= $stats['total_users'] ?></div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card">
<div class="stat-label">总充值金额</div>
<div class="stat-value">$<?= number_format($stats['total_recharge'], 2) ?></div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card">
<div class="stat-label">总订单数</div>
<div class="stat-value"><?= $stats['total_orders'] ?></div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card">
<div class="stat-label">待审核充值</div>
<div class="stat-value text-warning"><?= $stats['pending_recharges'] ?></div>
</div>
</div>
</div>
<?php elseif ($action === 'users'): ?>
<h4 class="fw-bold mb-4">用户管理</h4>
<div class="card p-3">
<table class="table align-middle">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>余额</th>
<th>角色</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php
$users = $pdo->query("SELECT * FROM users ORDER BY id DESC")->fetchAll();
foreach ($users as $u): ?>
<tr>
<td><?= $u['id'] ?></td>
<td><?= htmlspecialchars($u['username']) ?></td>
<td>$<?= number_format($u['balance'], 2) ?></td>
<td><span class="badge bg-<?= $u['role'] === 'admin' ? 'danger' : 'info' ?>"><?= $u['role'] ?></span></td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick='editUser(<?= json_encode($u) ?>)'>编辑</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="modal fade" id="userModal" tabindex="-1">
<div class="modal-dialog">
<form class="modal-content" method="POST" action="admin.php?action=update_user">
<div class="modal-header">
<h5 class="modal-title">编辑用户</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" name="id" id="userId">
<div class="mb-3">
<label class="form-label">余额</label>
<input type="number" step="0.01" class="form-control" name="balance" id="userBalanceInput">
</div>
<div class="mb-3">
<label class="form-label">角色</label>
<select class="form-select" name="role" id="userRoleSelect">
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">保存修改</button>
</div>
</form>
</div>
</div>
<script>
function editUser(user) {
document.getElementById('userId').value = user.id;
document.getElementById('userBalanceInput').value = user.balance;
document.getElementById('userRoleSelect').value = user.role;
new bootstrap.Modal(document.getElementById('userModal')).show();
}
</script>
<?php elseif ($action === 'recharges'): ?>
<h4 class="fw-bold mb-4">充值管理</h4>
<div class="card p-3">
<table class="table align-middle">
<thead>
<tr>
<th>ID</th>
<th>用户</th>
<th>金额</th>
<th>TXID / 备注</th>
<th>时间</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php
$recharges = $pdo->query("SELECT r.*, u.username FROM recharges r JOIN users u ON r.user_id = u.id ORDER BY r.created_at DESC")->fetchAll();
foreach ($recharges as $r): ?>
<tr>
<td><?= $r['id'] ?></td>
<td><?= htmlspecialchars($r['username']) ?></td>
<td class="fw-bold">$<?= number_format($r['amount'], 2) ?></td>
<td><small class="text-muted"><?= htmlspecialchars($r['txid']) ?></small></td>
<td><?= $r['created_at'] ?></td>
<td>
<span class="badge bg-<?= $r['status'] === 'completed' ? 'success' : ($r['status'] === 'pending' ? 'warning' : 'secondary') ?>">
<?= $r['status'] ?>
</span>
</td>
<td>
<?php if ($r['status'] === 'pending'): ?>
<form method="POST" action="admin.php?action=confirm_recharge" style="display:inline;">
<input type="hidden" name="id" value="<?= $r['id'] ?>">
<button type="submit" class="btn btn-sm btn-success" onclick="return confirm('确定手动确认此笔充值并增加用户余额?')">确认入账</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php elseif ($action === 'orders'): ?>
<h4 class="fw-bold mb-4">订单记录</h4>
<div class="card p-3">
<table class="table align-middle">
<thead>
<tr>
<th>ID</th>
<th>用户</th>
<th>项目</th>
<th>国家</th>
<th>号码</th>
<th>费用</th>
<th>状态</th>
<th>时间</th>
</tr>
</thead>
<tbody>
<?php
$orders = $pdo->query("SELECT o.*, u.username FROM sms_orders o JOIN users u ON o.user_id = u.id ORDER BY o.id DESC LIMIT 100")->fetchAll();
foreach ($orders as $o): ?>
<tr>
<td><?= $o['id'] ?></td>
<td><?= htmlspecialchars($o['username']) ?></td>
<td><?= htmlspecialchars($o['service_name']) ?></td>
<td><?= htmlspecialchars($o['country_name']) ?></td>
<td><?= $o['number'] ?></td>
<td>$<?= number_format($o['cost'], 2) ?></td>
<td><span class="badge bg-<?= $o['status'] === 'received' ? 'success' : ($o['status'] === 'pending' ? 'warning' : 'secondary') ?>"><?= $o['status'] ?></span></td>
<td><small><?= $o['created_at'] ?></small></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php elseif ($action === 'support'): ?>
<h4 class="fw-bold mb-4">客服消息</h4>
<div class="row">
<div class="col-md-4">
<div class="card p-3" style="height: 600px; overflow-y: auto;">
<div id="chatUserList">
<div class="text-center py-5 text-muted">正在加载对话...</div>
</div>
</div>
</div>
<div class="col-md-8">
<div class="card d-flex flex-column" style="height: 600px;">
<div class="card-header bg-white fw-bold" id="chatTitle">请选择一个对话</div>
<div class="card-body p-3 flex-grow-1" id="chatContent" style="overflow-y: auto; background: #f1f5f9;">
</div>
<div class="card-footer bg-white p-3">
<div class="input-group">
<input type="text" id="msgInput" class="form-control" placeholder="输入回复内容...">
<button class="btn btn-primary" onclick="sendMessage()">发送</button>
</div>
</div>
</div>
</div>
</div>
<script>
let currentChatUser = null;
async function loadChatUsers() {
const res = await fetch('ajax_handler.php?action=get_chat_users');
const data = await res.json();
if (data.code === 0) {
const list = document.getElementById('chatUserList');
list.innerHTML = '';
data.data.forEach(u => {
const div = document.createElement('div');
div.className = `p-3 border-bottom cursor-pointer \${currentChatUser === u.id ? 'bg-light' : ''}`;
div.style.cursor = 'pointer';
div.innerHTML = `
<div class="d-flex justify-content-between align-items-center">
<strong>\${u.username}</strong>
<small class="text-muted">\${u.last_time.split(' ')[1]}</small>
</div>
<div class="text-truncate small \${u.unread_count > 0 ? 'fw-bold text-primary' : 'text-muted'}">\${u.last_message}</div>
`;
div.onclick = () => selectUser(u.id, u.username);
list.appendChild(div);
});
}
}
function selectUser(id, name) {
currentChatUser = id;
document.getElementById('chatTitle').textContent = '正在与 ' + name + ' 对话';
loadMessages();
loadChatUsers();
}
async function loadMessages() {
if (!currentChatUser) return;
const res = await fetch('ajax_handler.php?action=get_messages&user_id=' + currentChatUser);
const data = await res.json();
if (data.code === 0) {
const content = document.getElementById('chatContent');
content.innerHTML = '';
data.data.forEach(m => {
const isMe = m.sender === 'admin';
content.innerHTML += `
<div class="mb-3 d-flex \${isMe ? 'justify-content-end' : ''}">
<div class="p-2 px-3 rounded-4 shadow-sm" style="max-width: 80%; background: \${isMe ? '#3b82f6; color: white;' : 'white;'}">
<div>\${m.message}</div>
<small class="opacity-75" style="font-size: 10px;">\${m.created_at}</small>
</div>
</div>
`;
});
content.scrollTop = content.scrollHeight;
}
}
async function sendMessage() {
const input = document.getElementById('msgInput');
const msg = input.value.trim();
if (!msg || !currentChatUser) return;
const formData = new FormData();
formData.append('message', msg);
formData.append('user_id', currentChatUser);
const res = await fetch('ajax_handler.php?action=send_message', { method: 'POST', body: formData });
const data = await res.json();
if (data.code === 0) {
input.value = '';
loadMessages();
loadChatUsers();
}
}
loadChatUsers();
setInterval(() => { loadChatUsers(); loadMessages(); }, 5000);
</script>
<?php elseif ($action === 'settings'): ?>
<h4 class="fw-bold mb-4">系统设置</h4>
<div class="card p-4">
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success">设置已更新</div>
<?php endif; ?>
<form method="POST" action="admin.php?action=update_settings">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">网站名称</label>
<input type="text" class="form-control" name="settings[site_name]" value="<?= htmlspecialchars($settings['site_name'] ?? '') ?>">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">网站Logo URL</label>
<input type="text" class="form-control" name="settings[site_logo]" value="<?= htmlspecialchars($settings['site_logo'] ?? '') ?>">
</div>
<div class="col-md-12 mb-3">
<label class="form-label">系统公告</label>
<textarea class="form-control" name="settings[notice_text]" rows="3"><?= htmlspecialchars($settings['notice_text'] ?? '') ?></textarea>
</div>
<div class="col-md-12 mb-3">
<label class="form-label">Luban SMS API Key</label>
<input type="text" class="form-control" name="settings[lubansms_apikey]" value="<?= htmlspecialchars($settings['lubansms_apikey'] ?? '') ?>">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">USDT (TRC20) 收款地址</label>
<input type="text" class="form-control" name="settings[usdt_trc20_address]" value="<?= htmlspecialchars($settings['usdt_trc20_address'] ?? '') ?>">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">USDT (ERC20) 收款地址</label>
<input type="text" class="form-control" name="settings[usdt_erc20_address]" value="<?= htmlspecialchars($settings['usdt_erc20_address'] ?? '') ?>">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">汇率 (1 USDT = ? 余额)</label>
<input type="number" step="0.01" class="form-control" name="settings[exchange_rate]" value="<?= htmlspecialchars($settings['exchange_rate'] ?? '1.0') ?>">
</div>
</div>
<button type="submit" class="btn btn-primary px-4">保存设置</button>
</form>
</div>
<?php endif; ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>