⚡ 初始提交 - FutureOSS v1.0 插件化运行时框架
一切皆为插件的开发者工具运行时框架
🧩 核心特性:
- 插件热插拔 (importlib 动态加载)
- 依赖自动解析 (拓扑排序 + 循环检测)
- 企业级稳定 (熔断/降级/重试/隔离)
- 事件驱动 (发布/订阅事件总线)
- 完整配置 (YAML 配置 + 热重载)
This commit is contained in:
286
website/community/api/auth.php
Normal file
286
website/community/api/auth.php
Normal file
@@ -0,0 +1,286 @@
|
||||
<?php
|
||||
/**
|
||||
* OSS Community 认证 API
|
||||
* 处理登录和注册请求
|
||||
*/
|
||||
|
||||
session_start();
|
||||
require_once __DIR__ . '/../includes/Database.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// 获取操作类型
|
||||
$action = $_GET['action'] ?? '';
|
||||
|
||||
// check 和 logout 允许 GET 请求,其他只允许 POST
|
||||
if (in_array($action, ['check', 'logout', 'current-user']) && $_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
// 允许 GET
|
||||
} elseif ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => '请求方法不允许']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'login') {
|
||||
handleLogin();
|
||||
} elseif ($action === 'register') {
|
||||
handleRegister();
|
||||
} elseif ($action === 'logout') {
|
||||
handleLogout();
|
||||
} elseif ($action === 'check') {
|
||||
handleCheck();
|
||||
} elseif ($action === 'my-post-count') {
|
||||
handleMyPostCount();
|
||||
} elseif ($action === 'current-user') {
|
||||
handleCurrentUser();
|
||||
} else {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => '无效的操作类型']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理登录
|
||||
*/
|
||||
function handleLogin() {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (empty($input['username']) || empty($input['password'])) {
|
||||
echo json_encode(['success' => false, 'message' => '用户名和密码不能为空']);
|
||||
return;
|
||||
}
|
||||
|
||||
$username = trim($input['username']);
|
||||
$password = $input['password'];
|
||||
$remember = $input['remember'] ?? false;
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
// 查询用户(支持用户名或邮箱登录)
|
||||
$user = $db->fetchOne(
|
||||
"SELECT id, username, email, password_hash, role, avatar FROM users WHERE username = ? OR email = ?",
|
||||
[$username, $username]
|
||||
);
|
||||
|
||||
if (!$user) {
|
||||
echo json_encode(['success' => false, 'message' => '用户名或密码错误']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (!password_verify($password, $user['password_hash'])) {
|
||||
echo json_encode(['success' => false, 'message' => '用户名或密码错误']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置 session
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
$_SESSION['avatar'] = $user['avatar'];
|
||||
|
||||
// 如果勾选记住我,设置更长的 session 生命周期
|
||||
if ($remember) {
|
||||
ini_set('session.gc_maxlifetime', 30 * 24 * 60 * 60); // 30天
|
||||
session_set_cookie_params(30 * 24 * 60 * 60);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => '登录成功',
|
||||
'user' => [
|
||||
'id' => $user['id'],
|
||||
'username' => $user['username'],
|
||||
'role' => $user['role'],
|
||||
'avatar' => $user['avatar']
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => '服务器错误:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理注册
|
||||
*/
|
||||
function handleRegister() {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (empty($input['username']) || empty($input['email']) || empty($input['password'])) {
|
||||
echo json_encode(['success' => false, 'message' => '所有字段都不能为空']);
|
||||
return;
|
||||
}
|
||||
|
||||
$username = trim($input['username']);
|
||||
$email = trim($input['email']);
|
||||
$password = $input['password'];
|
||||
|
||||
// 验证用户名格式
|
||||
if (!preg_match('/^[a-zA-Z0-9_]{3,50}$/', $username)) {
|
||||
echo json_encode(['success' => false, 'message' => '用户名只能包含字母、数字和下划线,长度 3-50 个字符']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证邮箱格式
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
echo json_encode(['success' => false, 'message' => '邮箱格式不正确']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证密码长度
|
||||
if (strlen($password) < 6) {
|
||||
echo json_encode(['success' => false, 'message' => '密码长度至少 6 个字符']);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
// 检查用户名是否已存在
|
||||
$existingUser = $db->fetchOne("SELECT id FROM users WHERE username = ?", [$username]);
|
||||
if ($existingUser) {
|
||||
echo json_encode(['success' => false, 'message' => '用户名已被使用']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
$existingEmail = $db->fetchOne("SELECT id FROM users WHERE email = ?", [$email]);
|
||||
if ($existingEmail) {
|
||||
echo json_encode(['success' => false, 'message' => '邮箱已被注册']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 密码哈希
|
||||
$passwordHash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
// 插入新用户
|
||||
$db->query(
|
||||
"INSERT INTO users (username, email, password_hash, role) VALUES (?, ?, ?, 'member')",
|
||||
[$username, $email, $passwordHash]
|
||||
);
|
||||
|
||||
$userId = $db->lastInsertId();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => '注册成功',
|
||||
'user' => [
|
||||
'id' => $userId,
|
||||
'username' => $username,
|
||||
'role' => 'member'
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => '服务器错误:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理登出
|
||||
*/
|
||||
function handleLogout() {
|
||||
session_destroy();
|
||||
echo json_encode(['success' => true, 'message' => '已成功退出']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查登录状态
|
||||
*/
|
||||
function handleCheck() {
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'logged_in' => true,
|
||||
'user' => [
|
||||
'id' => $_SESSION['user_id'],
|
||||
'username' => $_SESSION['username'],
|
||||
'role' => $_SESSION['role'] ?? 'member',
|
||||
'avatar' => $_SESSION['avatar'] ?? ''
|
||||
]
|
||||
]);
|
||||
} else {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'logged_in' => false
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户文章数量
|
||||
*/
|
||||
function handleMyPostCount() {
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
echo json_encode(['success' => false, 'message' => '未登录']);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
$count = $db->fetchOne(
|
||||
"SELECT COUNT(*) as count FROM posts WHERE user_id = ?",
|
||||
[$_SESSION['user_id']]
|
||||
)['count'];
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'count' => (int)$count
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => '服务器错误:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户信息(用于轮询)
|
||||
*/
|
||||
function handleCurrentUser() {
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
echo json_encode(['success' => false, 'message' => '未登录']);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
$user = $db->fetchOne(
|
||||
"SELECT id, username, email, avatar, role, title, bio, created_at FROM users WHERE id = ?",
|
||||
[$_SESSION['user_id']]
|
||||
);
|
||||
|
||||
if (!$user) {
|
||||
echo json_encode(['success' => false, 'message' => '用户不存在']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取统计数据
|
||||
$stats = $db->fetchOne(
|
||||
"SELECT
|
||||
(SELECT COUNT(*) FROM posts WHERE user_id = ?) as post_count,
|
||||
(SELECT COUNT(*) FROM replies WHERE user_id = ?) as reply_count",
|
||||
[$user['id'], $user['id']]
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'user' => $user,
|
||||
'stats' => [
|
||||
'post_count' => (int)$stats['post_count'],
|
||||
'reply_count' => (int)$stats['reply_count']
|
||||
],
|
||||
'permissions' => [
|
||||
'can_manage_users' => in_array($user['role'], ['admin']),
|
||||
'can_manage_posts' => in_array($user['role'], ['admin', 'moderator']),
|
||||
'can_pin_posts' => in_array($user['role'], ['admin', 'moderator']),
|
||||
'can_lock_posts' => in_array($user['role'], ['admin', 'moderator']),
|
||||
'can_delete_any_post' => in_array($user['role'], ['admin']),
|
||||
'can_manage_titles' => in_array($user['role'], ['admin'])
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => '服务器错误:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
112
website/community/api/index.php
Normal file
112
website/community/api/index.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
ini_set("display_errors", 1); error_reporting(E_ALL);
|
||||
require_once __DIR__ . '/../includes/Database.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$db = Database::getInstance();
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
case 'posts':
|
||||
$page = max(1, (int)($_GET['page'] ?? 1));
|
||||
$limit = 20;
|
||||
$offset = ($page - 1) * $limit;
|
||||
$categoryId = $_GET['category_id'] ?? '';
|
||||
|
||||
if ($categoryId) {
|
||||
$posts = $db->fetchAll(
|
||||
"SELECT p.*, u.username, u.avatar, c.name as category_name
|
||||
FROM posts p
|
||||
JOIN users u ON p.user_id = u.id
|
||||
JOIN categories c ON p.category_id = c.id
|
||||
WHERE p.category_id = ?
|
||||
ORDER BY p.is_pinned DESC, p.created_at DESC
|
||||
LIMIT ? OFFSET ?",
|
||||
[$categoryId, $limit, $offset]
|
||||
);
|
||||
$total = $db->fetchOne("SELECT COUNT(*) as count FROM posts WHERE category_id = ?", [$categoryId])['count'];
|
||||
} else {
|
||||
$posts = $db->fetchAll(
|
||||
"SELECT p.*, u.username, u.avatar, c.name as category_name
|
||||
FROM posts p
|
||||
JOIN users u ON p.user_id = u.id
|
||||
JOIN categories c ON p.category_id = c.id
|
||||
ORDER BY p.is_pinned DESC, p.created_at DESC
|
||||
LIMIT ? OFFSET ?",
|
||||
[$limit, $offset]
|
||||
);
|
||||
$total = $db->fetchOne("SELECT COUNT(*) as count FROM posts")['count'];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'posts' => $posts,
|
||||
'total' => $total,
|
||||
'pages' => ceil($total / $limit)
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'post':
|
||||
$id = (int)($_GET['id'] ?? 0);
|
||||
$post = $db->fetchOne(
|
||||
"SELECT p.*, u.username, u.avatar, u.role, c.name as category_name, c.slug as category_slug
|
||||
FROM posts p
|
||||
JOIN users u ON p.user_id = u.id
|
||||
JOIN categories c ON p.category_id = c.id
|
||||
WHERE p.id = ?",
|
||||
[$id]
|
||||
);
|
||||
|
||||
if (!$post) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => '帖子不存在']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 更新浏览数
|
||||
$db->query("UPDATE posts SET views = views + 1 WHERE id = ?", [$id]);
|
||||
$post['views']++;
|
||||
|
||||
// 获取回复
|
||||
$replies = $db->fetchAll(
|
||||
"SELECT r.*, u.username, u.avatar
|
||||
FROM replies r
|
||||
JOIN users u ON r.user_id = u.id
|
||||
WHERE r.post_id = ?
|
||||
ORDER BY r.is_solution DESC, r.created_at ASC",
|
||||
[$id]
|
||||
);
|
||||
|
||||
echo json_encode(['post' => $post, 'replies' => $replies]);
|
||||
break;
|
||||
|
||||
case 'categories':
|
||||
$categories = $db->fetchAll("SELECT * FROM categories ORDER BY sort_order ASC");
|
||||
echo json_encode(['categories' => $categories]);
|
||||
break;
|
||||
|
||||
case 'stats':
|
||||
$stats = [
|
||||
'posts' => $db->fetchOne("SELECT COUNT(*) as count FROM posts")['count'],
|
||||
'replies' => $db->fetchOne("SELECT COUNT(*) as count FROM replies")['count'],
|
||||
'users' => $db->fetchOne("SELECT COUNT(*) as count FROM users")['count'],
|
||||
'hot_posts' => $db->fetchAll("SELECT id, title, views, likes FROM posts ORDER BY views DESC LIMIT 5"),
|
||||
];
|
||||
echo json_encode($stats);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['error' => '未知操作']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
340
website/community/api/posts.php
Normal file
340
website/community/api/posts.php
Normal file
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
/**
|
||||
* OSS Community 帖子管理 API
|
||||
* 处理帖子创建、编辑、删除、置顶等操作
|
||||
*/
|
||||
|
||||
session_start();
|
||||
require_once __DIR__ . '/../includes/Database.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// 只允许 POST 请求
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => '请求方法不允许']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
|
||||
// 检查登录状态(除了查看操作)
|
||||
$requireAuth = in_array($action, ['create', 'update', 'delete', 'pin', 'lock']);
|
||||
if ($requireAuth && !isset($_SESSION['user_id'])) {
|
||||
echo json_encode(['success' => false, 'message' => '请先登录']);
|
||||
exit;
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'create':
|
||||
handleCreatePost();
|
||||
break;
|
||||
case 'update':
|
||||
handleUpdatePost();
|
||||
break;
|
||||
case 'delete':
|
||||
handleDeletePost();
|
||||
break;
|
||||
case 'pin':
|
||||
handlePinPost();
|
||||
break;
|
||||
case 'lock':
|
||||
handleLockPost();
|
||||
break;
|
||||
default:
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => '无效的操作类型']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建帖子
|
||||
*/
|
||||
function handleCreatePost() {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (empty($input['title']) || empty($input['content']) || empty($input['category_id'])) {
|
||||
echo json_encode(['success' => false, 'message' => '标题、内容和分类不能为空']);
|
||||
return;
|
||||
}
|
||||
|
||||
$title = trim($input['title']);
|
||||
$content = trim($input['content']);
|
||||
$categoryId = (int)$input['category_id'];
|
||||
$tags = $input['tags'] ?? [];
|
||||
|
||||
// 验证标题长度
|
||||
if (mb_strlen($title) < 5 || mb_strlen($title) > 200) {
|
||||
echo json_encode(['success' => false, 'message' => '标题长度必须在 5-200 个字符之间']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证内容长度
|
||||
if (mb_strlen($content) < 10) {
|
||||
echo json_encode(['success' => false, 'message' => '内容至少 10 个字符']);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
$userId = $_SESSION['user_id'];
|
||||
$userRole = $_SESSION['role'] ?? 'member';
|
||||
|
||||
// 验证分类是否存在
|
||||
$category = $db->fetchOne("SELECT * FROM categories WHERE id = ?", [$categoryId]);
|
||||
if (!$category) {
|
||||
echo json_encode(['success' => false, 'message' => '分类不存在']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 公告分类:禁止通过 API 发帖(只能通过 SQL 直接插入)
|
||||
if ($category['slug'] === 'announcements') {
|
||||
echo json_encode(['success' => false, 'message' => '公告不能通过发帖功能创建,请联系管理员通过数据库添加']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成 slug
|
||||
$slug = generateSlug($title);
|
||||
|
||||
// 检查 slug 是否重复
|
||||
$existing = $db->fetchOne("SELECT id FROM posts WHERE slug = ?", [$slug]);
|
||||
if ($existing) {
|
||||
$slug .= '-' . time();
|
||||
}
|
||||
|
||||
// 插入帖子
|
||||
$db->query(
|
||||
"INSERT INTO posts (user_id, category_id, title, slug, content) VALUES (?, ?, ?, ?, ?)",
|
||||
[$userId, $categoryId, $title, $slug, $content]
|
||||
);
|
||||
|
||||
$postId = $db->lastInsertId();
|
||||
|
||||
// 保存标签
|
||||
if (!empty($tags)) {
|
||||
saveTags($db, $postId, $tags);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => '发帖成功',
|
||||
'post_id' => $postId
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => '服务器错误:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新帖子
|
||||
*/
|
||||
function handleUpdatePost() {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (empty($input['id']) || empty($input['title']) || empty($input['content'])) {
|
||||
echo json_encode(['success' => false, 'message' => '标题和内容不能为空']);
|
||||
return;
|
||||
}
|
||||
|
||||
$postId = (int)$input['id'];
|
||||
$title = trim($input['title']);
|
||||
$content = trim($input['content']);
|
||||
$tags = $input['tags'] ?? [];
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
$userId = $_SESSION['user_id'];
|
||||
|
||||
// 检查帖子是否存在且属于当前用户
|
||||
$post = $db->fetchOne("SELECT user_id FROM posts WHERE id = ?", [$postId]);
|
||||
if (!$post) {
|
||||
echo json_encode(['success' => false, 'message' => '帖子不存在']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有作者可以编辑
|
||||
if ($post['user_id'] != $userId) {
|
||||
echo json_encode(['success' => false, 'message' => '无权编辑此帖子']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新帖子
|
||||
$db->query(
|
||||
"UPDATE posts SET title = ?, content = ?, updated_at = NOW() WHERE id = ?",
|
||||
[$title, $content, $postId]
|
||||
);
|
||||
|
||||
// 更新标签
|
||||
if (!empty($tags)) {
|
||||
saveTags($db, $postId, $tags);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => '更新成功'
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => '服务器错误:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除帖子
|
||||
*/
|
||||
function handleDeletePost() {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (empty($input['id'])) {
|
||||
echo json_encode(['success' => false, 'message' => '帖子 ID 不能为空']);
|
||||
return;
|
||||
}
|
||||
|
||||
$postId = (int)$input['id'];
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
$userId = $_SESSION['user_id'];
|
||||
$userRole = $_SESSION['role'] ?? 'member';
|
||||
|
||||
// 检查帖子是否存在
|
||||
$post = $db->fetchOne("SELECT user_id FROM posts WHERE id = ?", [$postId]);
|
||||
if (!$post) {
|
||||
echo json_encode(['success' => false, 'message' => '帖子不存在']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有作者或管理员可以删除
|
||||
if ($post['user_id'] != $userId && !in_array($userRole, ['admin', 'moderator'])) {
|
||||
echo json_encode(['success' => false, 'message' => '无权删除此帖子']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除帖子(外键级联删除回复和点赞)
|
||||
$db->query("DELETE FROM posts WHERE id = ?", [$postId]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => '删除成功'
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => '服务器错误:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 置顶/取消置顶
|
||||
*/
|
||||
function handlePinPost() {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (empty($input['id'])) {
|
||||
echo json_encode(['success' => false, 'message' => '帖子 ID 不能为空']);
|
||||
return;
|
||||
}
|
||||
|
||||
$postId = (int)$input['id'];
|
||||
$pinned = (bool)($input['pinned'] ?? false);
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
$userRole = $_SESSION['role'] ?? 'member';
|
||||
|
||||
// 只有管理员或版主可以置顶
|
||||
if (!in_array($userRole, ['admin', 'moderator'])) {
|
||||
echo json_encode(['success' => false, 'message' => '无权置顶帖子']);
|
||||
return;
|
||||
}
|
||||
|
||||
$db->query("UPDATE posts SET is_pinned = ? WHERE id = ?", [(int)$pinned, $postId]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => $pinned ? '已置顶' : '已取消置顶'
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => '服务器错误:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁定/解锁帖子
|
||||
*/
|
||||
function handleLockPost() {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (empty($input['id'])) {
|
||||
echo json_encode(['success' => false, 'message' => '帖子 ID 不能为空']);
|
||||
return;
|
||||
}
|
||||
|
||||
$postId = (int)$input['id'];
|
||||
$locked = (bool)($input['locked'] ?? false);
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
$userRole = $_SESSION['role'] ?? 'member';
|
||||
|
||||
// 只有管理员或版主可以锁定
|
||||
if (!in_array($userRole, ['admin', 'moderator'])) {
|
||||
echo json_encode(['success' => false, 'message' => '无权锁定帖子']);
|
||||
return;
|
||||
}
|
||||
|
||||
$db->query("UPDATE posts SET is_locked = ? WHERE id = ?", [(int)$locked, $postId]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => $locked ? '已锁定' : '已解锁'
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => '服务器错误:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存标签
|
||||
*/
|
||||
function saveTags($db, $postId, $tags) {
|
||||
// 先删除旧标签关联
|
||||
$db->query("DELETE FROM post_tags WHERE post_id = ?", [$postId]);
|
||||
|
||||
foreach ($tags as $tagName) {
|
||||
$tagName = trim($tagName);
|
||||
if (empty($tagName)) continue;
|
||||
|
||||
// 查找或创建标签
|
||||
$slug = strtolower(preg_replace('/[^a-zA-Z0-9\x{4e00}-\x{9fa5}]/u', '-', $tagName));
|
||||
$tag = $db->fetchOne("SELECT id FROM tags WHERE name = ?", [$tagName]);
|
||||
|
||||
if (!$tag) {
|
||||
$db->query(
|
||||
"INSERT INTO tags (name, slug) VALUES (?, ?)",
|
||||
[$tagName, $slug]
|
||||
);
|
||||
$tagId = $db->lastInsertId();
|
||||
} else {
|
||||
$tagId = $tag['id'];
|
||||
}
|
||||
|
||||
// 关联标签
|
||||
$db->query(
|
||||
"INSERT IGNORE INTO post_tags (post_id, tag_id) VALUES (?, ?)",
|
||||
[$postId, $tagId]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 slug
|
||||
*/
|
||||
function generateSlug($title) {
|
||||
// 简单处理:移除特殊字符,替换空格为连字符
|
||||
$slug = preg_replace('/[^\p{L}\p{N}\s]/u', '', $title);
|
||||
$slug = preg_replace('/\s+/', '-', $slug);
|
||||
$slug = mb_strtolower($slug, 'UTF-8');
|
||||
return mb_substr($slug, 0, 100);
|
||||
}
|
||||
Reference in New Issue
Block a user