初始提交 - FutureOSS v1.0 插件化运行时框架

一切皆为插件的开发者工具运行时框架

🧩 核心特性:
  - 插件热插拔 (importlib 动态加载)
  - 依赖自动解析 (拓扑排序 + 循环检测)
  - 企业级稳定 (熔断/降级/重试/隔离)
  - 事件驱动 (发布/订阅事件总线)
  - 完整配置 (YAML 配置 + 热重载)
This commit is contained in:
Falck
2026-04-06 09:57:10 +08:00
commit 76147bae94
174 changed files with 15626 additions and 0 deletions

117
website/community/post.php Normal file
View File

@@ -0,0 +1,117 @@
<?php
require_once __DIR__ . '/includes/Database.php';
$id = (int)($_GET['id'] ?? 0);
if (!$id) { header('Location: index.php'); exit; }
$db = Database::getInstance();
$post = $db->fetchOne(
"SELECT p.*, u.username, u.avatar, u.role, 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.id = ?",
[$id]
);
if (!$post) { header('HTTP/1.0 404 Not Found'); exit('帖子不存在'); }
$db->query("UPDATE posts SET views = views + 1 WHERE id = ?", [$id]);
$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.created_at ASC",
[$id]
);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($post['title']) ?> - OSS Community</title>
<meta name="description" content="<?= htmlspecialchars(mb_substr(strip_tags($post['content']), 0, 150)) ?>..." />
<meta name="keywords" content="<?= htmlspecialchars($post['title']) ?>, 技术讨论, 开发者社区, 经验分享" />
<meta name="author" content="Falck" />
<meta property="og:title" content="<?= htmlspecialchars($post['title']) ?>" />
<meta property="og:description" content="<?= htmlspecialchars(mb_substr(strip_tags($post['content']), 0, 150)) ?>" />
<meta property="og:type" content="article" />
<link rel="canonical" href="https://oss-runtime.dev/community/post.php?id=<?= $post['id'] ?>" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="../css/main.css" />
<link rel="stylesheet" href="../css/dock.css" />
<style>
/* 帖子详情页样式 - 对齐官网 */
:root { --bg: #030712; --bg-card: rgba(255, 255, 255, 0.02); --border: rgba(255, 255, 255, 0.05); --cyan: #06b6d4; --cyan-light: #22d3ee; --text: #fff; --text-secondary: #9ca3af; --text-muted: #6b7280; }
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); line-height: 1.6; }
a { color: var(--cyan); text-decoration: none; }
body::before { content: ''; position: fixed; inset: 0; background-image: linear-gradient(rgba(6, 182, 212, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(6, 182, 212, 0.03) 1px, transparent 1px); background-size: 60px 60px; pointer-events: none; z-index: -1; }
.header { padding: 20px 24px 20px 80px; display: flex; align-items: center; gap: 16px; }
.back-link { color: var(--text-muted); transition: all 0.3s; display: flex; align-items: center; gap: 4px; }
.back-link:hover { color: var(--cyan-light); transform: translateX(-4px); }
.container { max-width: 800px; margin: 0 auto; padding: 24px; }
.post-header { margin-bottom: 32px; animation: fadeInUp 0.6s ease forwards; opacity: 0; }
.post-title { font-size: 32px; font-weight: 800; margin-bottom: 16px; line-height: 1.2; }
.post-meta { display: flex; align-items: center; gap: 12px; color: var(--text-muted); font-size: 14px; }
.avatar { width: 44px; height: 44px; border-radius: 12px; background: linear-gradient(135deg, var(--cyan), #3b82f6); display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 18px; color: #fff; }
.post-content { font-size: 16px; line-height: 1.8; margin-bottom: 32px; white-space: pre-wrap; background: var(--bg-card); border: 1px solid var(--border); border-radius: 16px; padding: 24px; }
.post-stats { display: flex; gap: 16px; color: var(--text-muted); font-size: 13px; margin-bottom: 40px; font-weight: 500; }
.replies-header { font-size: 20px; font-weight: 700; margin-bottom: 24px; padding-top: 24px; border-top: 1px solid var(--border); }
.reply-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 16px; padding: 24px; margin-bottom: 16px; transition: all 0.3s; animation: cardEnter 0.5s ease forwards; opacity: 0; }
.reply-card:hover { border-color: var(--border-hover); }
.reply-content { margin-top: 12px; white-space: pre-wrap; color: var(--text-secondary); line-height: 1.7; }
.empty-replies { color: var(--text-muted); text-align: center; padding: 60px 0; }
@keyframes fadeInUp { to { opacity: 1; transform: translateY(0); } }
@keyframes cardEnter { to { opacity: 1; transform: translateY(0); } }
</style>
</head>
<body>
<?php require_once 'includes/dock.php'; ?>
<header class="header">
<a href="index.php" class="back-link">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="M10 19l-7-7m0 0l7-7m-7 7h18"/></svg>
返回列表
</a>
</header>
<main class="container">
<article>
<div class="post-header">
<h1 class="post-title"><?= htmlspecialchars($post['title']) ?></h1>
<div class="post-meta">
<div class="avatar"><?= $post['username'][0] ?></div>
<div>
<div style="color: var(--text); font-weight: 600;"><?= htmlspecialchars($post['username']) ?></div>
<div><?= date('Y-m-d H:i', strtotime($post['created_at'])) ?> · <span style="color:var(--cyan)"><?= htmlspecialchars($post['category_name']) ?></span></div>
</div>
</div>
</div>
<div class="post-content"><?= nl2br(htmlspecialchars($post['content'])) ?></div>
<div class="post-stats">
<span>👁️ <?= $post['views'] ?> 浏览</span>
<span>❤️ <?= $post['likes'] ?> 点赞</span>
<span>💬 <?= count($replies) ?> 回复</span>
</div>
</article>
<section>
<h2 class="replies-header">回复 (<?= count($replies) ?>)</h2>
<?php if (empty($replies)): ?>
<div class="empty-replies">暂无回复,抢沙发吧!</div>
<?php else: ?>
<?php foreach ($replies as $reply): ?>
<div class="reply-card" style="animation-delay: <?= $reply['id'] * 0.05 ?>s">
<div class="post-meta">
<div class="avatar" style="width:36px;height:36px;font-size:15px;border-radius:10px;"><?= $reply['username'][0] ?></div>
<div>
<div style="color:var(--text);font-weight:600;"><?= htmlspecialchars($reply['username']) ?></div>
<div><?= date('Y-m-d H:i', strtotime($reply['created_at'])) ?></div>
</div>
</div>
<div class="reply-content"><?= nl2br(htmlspecialchars($reply['content'])) ?></div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</section>
</main>
<script src="../js/particles.js"></script>
</body>
</html>