feat: 添加开发者功能模块和审核系统
- 新增开发者注册、登录、仪表盘和登出功能 - 实现应用上传、编辑和管理功能 - 添加管理员审核应用和管理开发者功能 - 完善数据库结构支持开发者系统 - 增加错误日志记录功能 - 更新.gitignore忽略上传目录和系统文件
This commit is contained in:
@@ -16,7 +16,7 @@ if (isset($_GET['logout'])) {
|
||||
}
|
||||
|
||||
// 获取App列表
|
||||
$sqlApps = "SELECT * FROM apps ORDER BY created_at DESC";
|
||||
$sqlApps = "SELECT * FROM apps WHERE status = 'approved' ORDER BY created_at DESC";
|
||||
$resultApps = $conn->query($sqlApps);
|
||||
|
||||
if (!$resultApps) {
|
||||
@@ -56,7 +56,13 @@ if (!$resultApps) {
|
||||
<a class="nav-link active" aria-current="page" href="index.php">App列表</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="addapp.php">添加App</a>
|
||||
<a class="nav-link" href="addapp.php">添加App</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="review_apps.php">审核APP</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="manage_developers.php">管理开发者</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="?logout=true">退出登录</a>
|
||||
|
||||
246
admin/manage_developers.php
Normal file
246
admin/manage_developers.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
require_once '../config.php';
|
||||
// 检查管理员权限
|
||||
// 设置会话cookie路径为根目录以确保跨目录访问
|
||||
session_set_cookie_params(0, '/');
|
||||
// 检查会话是否已启动,避免重复启动
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
if (!session_start()) {
|
||||
error_log('会话启动失败');
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
|
||||
error_log('会话启动失败');
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
// 从数据库验证用户角色,确保权限检查准确性
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
$userId = $_SESSION['user_id'];
|
||||
$stmt = $conn->prepare("SELECT role FROM users WHERE id = ?");
|
||||
if (!$stmt) {
|
||||
error_log('Database prepare failed: ' . $conn->error);
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
$stmt->bind_param("i", $userId);
|
||||
if (!$stmt->execute()) {
|
||||
error_log('Query execution failed: ' . $stmt->error);
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
$result = $stmt->get_result();
|
||||
if (!$result) {
|
||||
error_log('Failed to get result: ' . $stmt->error);
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
$user = $result->fetch_assoc();
|
||||
|
||||
if (!$user || $user['role'] !== 'admin') {
|
||||
error_log('用户 ' . $userId . ' 不是管理员,拒绝访问');
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
error_log('未找到用户会话,重定向到登录页');
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理删除用户请求
|
||||
if (isset($_POST['delete_user'])) {
|
||||
$userId = $_POST['user_id'];
|
||||
$stmt = $conn->prepare("DELETE FROM users WHERE id = ? AND role = 'developer'");
|
||||
$stmt->bind_param("i", $userId);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
header("Location: manage_developers.php?deleted=true");
|
||||
exit;
|
||||
}
|
||||
|
||||
// 处理更新用户请求
|
||||
if (isset($_POST['update_user'])) {
|
||||
$userId = $_POST['user_id'];
|
||||
$username = $_POST['username'];
|
||||
$email = $_POST['email'];
|
||||
|
||||
$stmt = $conn->prepare("UPDATE users SET username = ?, email = ? WHERE id = ? AND role = 'developer'");
|
||||
$stmt->bind_param("ssi", $username, $email, $userId);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
header("Location: manage_developers.php?updated=true");
|
||||
exit;
|
||||
}
|
||||
|
||||
// 获取所有开发者用户
|
||||
$developers = [];
|
||||
$result = $conn->query("SELECT id, username, email, created_at FROM users WHERE role = 'developer' ORDER BY created_at DESC");
|
||||
if (!$result) {
|
||||
error_log('Failed to fetch developers: ' . $conn->error);
|
||||
die('获取开发者列表失败,请稍后重试');
|
||||
}
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$developers[] = $row;
|
||||
}
|
||||
|
||||
// 获取要编辑的用户信息
|
||||
$editUser = null;
|
||||
if (isset($_GET['edit'])) {
|
||||
$editUserId = $_GET['edit'];
|
||||
$stmt = $conn->prepare("SELECT id, username, email FROM users WHERE id = ? AND role = 'developer'");
|
||||
$stmt->bind_param("i", $editUserId);
|
||||
$stmt->execute();
|
||||
$editUser = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>管理开发者用户 - 应用商店管理</title>
|
||||
<link rel="stylesheet" href="../styles.css">
|
||||
<style>
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.user-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.user-table th, .user-table td {
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.user-table th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
}
|
||||
.action-btn {
|
||||
padding: 6px 12px;
|
||||
margin: 0 5px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.edit-btn {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
.delete-btn {
|
||||
background-color: #f44336;
|
||||
color: white;
|
||||
}
|
||||
.edit-form {
|
||||
background-color: #f9f9f9;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.submit-btn {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.message {
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.success {
|
||||
background-color: #dff0d8;
|
||||
color: #3c763d;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>管理开发者用户</h1>
|
||||
|
||||
<?php if (isset($_GET['deleted'])): ?>
|
||||
<div class="message success">用户已成功删除</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['updated'])): ?>
|
||||
<div class="message success">用户信息已成功更新</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($editUser): ?>
|
||||
<div class="edit-form">
|
||||
<h2>编辑开发者用户</h2>
|
||||
<form method="post" action="manage_developers.php">
|
||||
<input type="hidden" name="user_id" value="<?php echo $editUser['id']; ?>">
|
||||
<div class="form-group">
|
||||
<label for="username">用户名</label>
|
||||
<input type="text" id="username" name="username" value="<?php echo htmlspecialchars($editUser['username']); ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">邮箱</label>
|
||||
<input type="email" id="email" name="email" value="<?php echo htmlspecialchars($editUser['email']); ?>" required>
|
||||
</div>
|
||||
<button type="submit" name="update_user" class="submit-btn">更新用户</button>
|
||||
<a href="manage_developers.php" class="action-btn">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<table class="user-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>用户名</th>
|
||||
<th>邮箱</th>
|
||||
<th>注册时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($developers as $developer): ?>
|
||||
<tr>
|
||||
<td><?php echo $developer['id']; ?></td>
|
||||
<td><?php echo htmlspecialchars($developer['username']); ?></td>
|
||||
<td><?php echo htmlspecialchars($developer['email']); ?></td>
|
||||
<td><?php echo $developer['created_at']; ?></td>
|
||||
<td>
|
||||
<a href="manage_developers.php?edit=<?php echo $developer['id']; ?>" class="action-btn edit-btn">编辑</a>
|
||||
<form method="post" action="manage_developers.php" style="display: inline-block;" onsubmit="return confirm('确定要删除这个用户吗?');">
|
||||
<input type="hidden" name="user_id" value="<?php echo $developer['id']; ?>">
|
||||
<button type="submit" name="delete_user" class="action-btn delete-btn">删除</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($developers)): ?>
|
||||
<tr>
|
||||
<td colspan="5" style="text-align: center;">暂无开发者用户</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
208
admin/review_apps.php
Normal file
208
admin/review_apps.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
require_once '../config.php';
|
||||
|
||||
session_start();
|
||||
// 检查管理员登录状态
|
||||
if (!isset($_SESSION['admin'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
|
||||
// 处理审核操作
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['review_action'])) {
|
||||
$appId = $_POST['app_id'];
|
||||
$action = $_POST['review_action'];
|
||||
$rejectionReason = $_POST['rejection_reason'] ?? '';
|
||||
|
||||
// 验证应用ID
|
||||
if (!is_numeric($appId)) {
|
||||
$error = '无效的应用ID';
|
||||
} else {
|
||||
// 检查数据库连接
|
||||
if (!($conn instanceof mysqli)) {
|
||||
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
|
||||
$error = '数据库连接错误,请检查配置';
|
||||
} else {
|
||||
// 更新应用状态
|
||||
$status = $action === 'approve' ? 'approved' : 'rejected';
|
||||
$stmt = $conn->prepare("UPDATE apps SET status = ?, rejection_reason = ? WHERE id = ?");
|
||||
if (!$stmt) {
|
||||
$error = "数据库错误: " . $conn->error;
|
||||
} else {
|
||||
$stmt->bind_param("ssi", $status, $rejectionReason, $appId);
|
||||
if ($stmt->execute()) {
|
||||
$success = '应用审核已更新';
|
||||
} else {
|
||||
$error = '更新审核状态失败: ' . $conn->error;
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取待审核应用列表
|
||||
$pendingApps = [];
|
||||
if (!($conn instanceof mysqli)) {
|
||||
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
|
||||
$error = '数据库连接错误,请检查配置';
|
||||
} else {
|
||||
$stmt = $conn->prepare("SELECT a.id, a.name, a.description, a.status, a.created_at
|
||||
FROM apps a
|
||||
WHERE a.status = 'pending'
|
||||
ORDER BY a.created_at DESC");
|
||||
if (!$stmt) {
|
||||
$error = "数据库错误: " . $conn->error;
|
||||
} else {
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$pendingApps = $result->fetch_all(MYSQLI_ASSOC);
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>应用审核 - <?php echo APP_STORE_NAME; ?></title>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- 自定义CSS -->
|
||||
<link rel="stylesheet" href="../styles.css">
|
||||
<!-- Fluent Design 模糊效果 -->
|
||||
<style>
|
||||
.blur-bg {
|
||||
backdrop-filter: blur(10px);
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.app-card {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.app-card:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航栏 -->
|
||||
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.php">App列表</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="addapp.php">添加App</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="review_apps.php">应用审核</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="?logout=true">退出登录</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-4">
|
||||
<?php if (!empty($success)): ?>
|
||||
<div class="alert alert-success"><?php echo $success; ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2>应用审核</h2>
|
||||
<p class="text-muted">待审核应用: <?php echo count($pendingApps); ?></p>
|
||||
|
||||
<?php if (empty($pendingApps)): ?>
|
||||
<div class="alert alert-info">没有待审核的应用</div>
|
||||
<?php else: ?>
|
||||
<div class="row">
|
||||
<?php foreach ($pendingApps as $app): ?>
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card app-card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="card-title mb-0"><?php echo htmlspecialchars($app['name']); ?></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text"><strong>开发者:</strong> <?php echo htmlspecialchars($app['username']); ?></p>
|
||||
<p class="card-text"><strong>提交时间:</strong> <?php echo htmlspecialchars($app['created_at']); ?></p>
|
||||
<p class="card-text"><strong>描述:</strong> <?php echo nl2br(htmlspecialchars($app['description'])); ?></p>
|
||||
|
||||
<!-- 获取应用图片 -->
|
||||
<?php
|
||||
$images = [];
|
||||
$stmt = $conn->prepare("SELECT image_path FROM app_images WHERE app_id = ?");
|
||||
$stmt->bind_param("i", $app['id']);
|
||||
$stmt->execute();
|
||||
$imgResult = $stmt->get_result();
|
||||
while ($img = $imgResult->fetch_assoc()) {
|
||||
$images[] = $img['image_path'];
|
||||
}
|
||||
$stmt->close();
|
||||
?>
|
||||
|
||||
<?php if (!empty($images)): ?>
|
||||
<div class="mb-3">
|
||||
<strong>预览图片:</strong><br>
|
||||
<img src="<?php echo htmlspecialchars($images[0]); ?>" alt="应用截图" class="img-thumbnail" style="max-width: 200px;">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" class="mt-3">
|
||||
<input type="hidden" name="app_id" value="<?php echo $app['id']; ?>">
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" name="review_action" value="approve" class="btn btn-success flex-grow-1">通过</button>
|
||||
<button type="button" class="btn btn-danger flex-grow-1" data-bs-toggle="modal" data-bs-target="#rejectModal<?php echo $app['id']; ?>">拒绝</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 拒绝原因模态框 -->
|
||||
<div class="modal fade" id="rejectModal<?php echo $app['id']; ?>" tabindex="-1" aria-labelledby="rejectModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="rejectModalLabel">拒绝应用: <?php echo htmlspecialchars($app['name']); ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="app_id" value="<?php echo $app['id']; ?>">
|
||||
<div class="mb-3">
|
||||
<label for="rejection_reason<?php echo $app['id']; ?>" class="form-label">拒绝原因</label>
|
||||
<textarea class="form-control" id="rejection_reason<?php echo $app['id']; ?>" name="rejection_reason" rows="3" required></textarea>
|
||||
<div class="form-text">请详细说明拒绝原因,帮助开发者改进应用</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="submit" name="review_action" value="reject" class="btn btn-danger">确认拒绝</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user