本文目录导读:

我来详细介绍如何用 PHP 实现一个公告系统。
数据库设计
首先创建公告表:
CREATE TABLE announcements (
id INT PRIMARY KEY AUTO_INCREMENT,VARCHAR(200) NOT NULL COMMENT '公告标题',
content TEXT NOT NULL COMMENT '公告内容',
type TINYINT DEFAULT 1 COMMENT '公告类型:1-通知 2-活动 3-紧急',
status TINYINT DEFAULT 1 COMMENT '状态:1-发布 0-草稿 2-下架',
priority INT DEFAULT 0 COMMENT '优先级,越大越靠前',
start_time DATETIME COMMENT '开始展示时间',
end_time DATETIME COMMENT '结束展示时间',
created_by INT COMMENT '创建人ID',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_status_time (status, start_time, end_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
公告核心类
<?php
class Announcement {
private $db;
public function __construct($db) {
$this->db = $db;
}
// 发布公告
public function create($data) {
$sql = "INSERT INTO announcements (title, content, type, status, priority, start_time, end_time, created_by)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $this->db->prepare($sql);
return $stmt->execute([
$data['title'],
$data['content'],
$data['type'] ?? 1,
$data['status'] ?? 1,
$data['priority'] ?? 0,
$data['start_time'] ?? date('Y-m-d H:i:s'),
$data['end_time'] ?? null,
$data['created_by'] ?? 0
]);
}
// 获取有效公告列表
public function getActiveList($page = 1, $limit = 10) {
$offset = ($page - 1) * $limit;
$now = date('Y-m-d H:i:s');
$sql = "SELECT * FROM announcements
WHERE status = 1
AND (start_time IS NULL OR start_time <= ?)
AND (end_time IS NULL OR end_time >= ?)
ORDER BY priority DESC, created_at DESC
LIMIT ? OFFSET ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$now, $now, $limit, $offset]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 获取公告详情
public function getById($id) {
$sql = "SELECT * FROM announcements WHERE id = ? AND status = 1";
$stmt = $this->db->prepare($sql);
$stmt->execute([$id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// 更新公告
public function update($id, $data) {
$sql = "UPDATE announcements SET
title = ?, content = ?, type = ?, status = ?, priority = ?,
start_time = ?, end_time = ? WHERE id = ?";
$stmt = $this->db->prepare($sql);
return $stmt->execute([
$data['title'],
$data['content'],
$data['type'],
$data['status'],
$data['priority'],
$data['start_time'],
$data['end_time'],
$id
]);
}
// 删除公告(软删除)
public function delete($id) {
$sql = "UPDATE announcements SET status = 2 WHERE id = ?";
$stmt = $this->db->prepare($sql);
return $stmt->execute([$id]);
}
}
前端页面示例
公告列表页面
<?php
// 引入数据库连接
require_once 'db.php';
require_once 'Announcement.php';
$announcement = new Announcement($db);
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$list = $announcement->getActiveList($page, 10);
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">公告系统</title>
<style>
.announcement-list { max-width: 800px; margin: 0 auto; }
.announcement-item {
border: 1px solid #ddd;
margin: 10px 0;
padding: 15px;
border-radius: 5px;
}
.announcement-title { font-weight: bold; font-size: 1.2em; color: #333; }
.announcement-meta { color: #999; font-size: 0.8em; margin-top: 10px; }
.announcement-type {
display: inline-block;
padding: 2px 8px;
border-radius: 3px;
font-size: 0.8em;
background: #e3f2fd;
color: #1976d2;
}
.type-urgent { background: #ffebee; color: #c62828; }
.type-activity { background: #f3e5f5; color: #8e24aa; }
</style>
</head>
<body>
<div class="announcement-list">
<h1>最新公告</h1>
<?php foreach ($list as $item): ?>
<div class="announcement-item">
<div class="announcement-title">
<?php echo htmlspecialchars($item['title']); ?>
</div>
<div class="announcement-type type-<?php echo $item['type']; ?>">
<?php echo getTypeName($item['type']); ?>
</div>
<div class="announcement-content">
<?php echo mb_substr(strip_tags($item['content']), 0, 100) . '...'; ?>
</div>
<div class="announcement-meta">
发布于: <?php echo date('Y-m-d', strtotime($item['created_at'])); ?>
</div>
</div>
<?php endforeach; ?>
</div>
</body>
</html>
<?php
function getTypeName($type) {
$types = [1 => '通知', 2 => '活动', 3 => '紧急'];
return $types[$type] ?? '通知';
}
?>
公告管理后台
<?php
// 后台管理页面(仅为简化示例)
require_once 'db.php';
require_once 'Announcement.php';
$announcement = new Announcement($db);
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($_POST['action'])) {
switch ($_POST['action']) {
case 'create':
$announcement->create($_POST);
break;
case 'update':
$announcement->update($_POST['id'], $_POST);
break;
case 'delete':
$announcement->delete($_POST['id']);
break;
}
}
}
?>
核心功能实现
公告缓存优化(Redis)
<?php
class AnnouncementCache {
private $redis;
public function __construct($redis) {
$this->redis = $redis;
}
// 获取公告列表(带缓存)
public function getListWithCache($page = 1, $limit = 10) {
$cacheKey = "announcement:list:{$page}:{$limit}";
// 尝试从缓存获取
$cached = $this->redis->get($cacheKey);
if ($cached) {
return json_decode($cached, true);
}
// 从数据库获取
$list = $this->getFromDB($page, $limit);
// 写入缓存,设置过期时间10分钟
$this->redis->setex($cacheKey, 600, json_encode($list));
return $list;
}
// 清除缓存
public function clearCache() {
$keys = $this->redis->keys('announcement:list:*');
if ($keys) {
$this->redis->del($keys);
}
}
}
?>
高级功能实现
公告推送功能(WebSocket)
<?php
// 使用简单的长轮询或定期检查
class AnnouncementPusher {
private $db;
// 检查新公告
public function checkNewAnnouncements($lastCheckTime) {
$sql = "SELECT * FROM announcements
WHERE created_at > ? AND status = 1
ORDER BY created_at DESC";
$stmt = $this->db->prepare($sql);
$stmt->execute([$lastCheckTime]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 推送公告到前端
public function pushToClient() {
$newAnnouncements = $this->checkNewAnnouncements($this->getLastCheckTime());
if (!empty($newAnnouncements)) {
echo json_encode([
'status' => 'new',
'data' => $newAnnouncements
]);
} else {
echo json_encode(['status' => 'no_new']);
}
}
}
?>
阅读记录功能
CREATE TABLE announcement_reads (
id INT PRIMARY KEY AUTO_INCREMENT,
announcement_id INT NOT NULL,
user_id INT NOT NULL,
read_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_announcement (user_id, announcement_id)
);
<?php
class AnnouncementRead {
private $db;
// 标记为已读
public function markAsRead($announcementId, $userId) {
$sql = "INSERT INTO announcement_reads (announcement_id, user_id)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE read_at = CURRENT_TIMESTAMP";
$stmt = $this->db->prepare($sql);
return $stmt->execute([$announcementId, $userId]);
}
// 获取用户未读公告数
public function getUnreadCount($userId) {
$sql = "SELECT COUNT(*) FROM announcements a
WHERE a.status = 1
AND a.id NOT IN (
SELECT announcement_id FROM announcement_reads WHERE user_id = ?
)";
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId]);
return $stmt->fetchColumn();
}
}
?>
安全建议
- XSS防护:输出时使用
htmlspecialchars() - SQL注入防护:使用PDO预处理语句
- 权限控制:区分管理员和普通用户
- CSRF防护:添加Token验证
- 数据验证:输入数据严格验证
性能优化建议
- 使用Redis缓存公告数据
- 对大列表进行分页处理
- 建立合适的数据库索引字段使用TEXT类型,避免过大
- 定期清理过期的公告
这个公告系统实现了基本的功能,你可以根据实际需求进行扩展,比如添加附件上传、公告分类、全文搜索等功能。