459 lines
22 KiB
PHP
459 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';
|
|
} 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>
|