本文目录导读:

我来帮你详细解析PHP项目中群公告的发布、编辑和展示功能实现方案。
数据库设计
-- 群公告表
CREATE TABLE `group_announcements` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` int(11) NOT NULL COMMENT '群组ID',
`user_id` int(11) NOT NULL COMMENT '发布者ID', varchar(200) NOT NULL COMMENT '公告标题',
`content` text NOT NULL COMMENT '公告内容',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:1显示 0隐藏',
`is_top` tinyint(1) DEFAULT '0' COMMENT '是否置顶:1是 0否',
`view_count` int(11) DEFAULT '0' COMMENT '浏览次数',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_group_id` (`group_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
核心功能实现
发布公告
<?php
class AnnouncementController {
public function publish($request) {
// 验证用户权限
$userId = $_SESSION['user_id'];
$groupId = $request['group_id'];
if (!$this->checkPermission($userId, $groupId)) {
return json_encode(['code' => 403, 'msg' => '无权发布公告']);
}
// 验证数据
$validator = new Validator();
$rules = [
'title' => 'required|max:200',
'content' => 'required',
'group_id' => 'required|integer'
];
if (!$validator->validate($request, $rules)) {
return json_encode(['code' => 400, 'msg' => $validator->getErrors()]);
}
// 处理内容(过滤XSS等安全风险)
$data = [
'group_id' => intval($request['group_id']),
'user_id' => $userId,
'title' => htmlspecialchars($request['title']),
'content' => $this->filterContent($request['content']),
'is_top' => isset($request['is_top']) ? intval($request['is_top']) : 0
];
// 如果是置顶公告,取消其他置顶
if ($data['is_top']) {
$this->cancelOtherTop($groupId);
}
// 发布公告
$announcementId = Announcement::create($data);
// 发送通知
$this->sendNotification($groupId, $data['title']);
return json_encode(['code' => 200, 'msg' => '发布成功', 'data' => ['id' => $announcementId]]);
}
private function filterContent($content) {
// 使用HTML Purifier过滤XSS
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.Allowed', 'p,b,i,u,a[href],img[src]');
$purifier = new HTMLPurifier($config);
return $purifier->purify($content);
}
private function checkPermission($userId, $groupId) {
// 检查用户是否为群管理员或群主
$groupMember = GroupMember::where('user_id', $userId)
->where('group_id', $groupId)
->first();
return $groupMember && in_array($groupMember->role, ['owner', 'admin']);
}
}
编辑公告
<?php
class AnnouncementController {
public function edit($request) {
$announcementId = $request['id'];
$userId = $_SESSION['user_id'];
// 获取公告信息
$announcement = Announcement::find($announcementId);
if (!$announcement) {
return json_encode(['code' => 404, 'msg' => '公告不存在']);
}
// 权限验证(发布者或群管理员可编辑)
$isPublisher = $announcement->user_id == $userId;
$isAdmin = $this->checkPermission($userId, $announcement->group_id);
if (!$isPublisher && !$isAdmin) {
return json_encode(['code' => 403, 'msg' => '无权编辑']);
}
// 如果已经发布超过24小时,限制编辑
if (time() - strtotime($announcement->created_at) > 86400 && !$isAdmin) {
return json_encode(['code' => 403, 'msg' => '仅限管理员编辑超过24小时的公告']);
}
// 更新数据
$updateData = [];
if (isset($request['title'])) {
$updateData['title'] = htmlspecialchars($request['title']);
}
if (isset($request['content'])) {
$updateData['content'] = $this->filterContent($request['content']);
}
if (isset($request['is_top']) && $isAdmin) {
$updateData['is_top'] = intval($request['is_top']);
}
if (!empty($updateData)) {
$announcement->update($updateData);
}
// 记录编辑历史
$this->saveEditHistory($announcementId, $userId, $updateData);
return json_encode(['code' => 200, 'msg' => '编辑成功']);
}
private function saveEditHistory($announcementId, $userId, $changes) {
AnnouncementHistory::create([
'announcement_id' => $announcementId,
'user_id' => $userId,
'changes' => json_encode($changes),
'created_at' => date('Y-m-d H:i:s')
]);
}
}
展示公告
<?php
class AnnouncementController {
public function list($request) {
$groupId = $request['group_id'];
$page = isset($request['page']) ? intval($request['page']) : 1;
$pageSize = isset($request['page_size']) ? intval($request['page_size']) : 10;
// 查询公告列表
$query = Announcement::where('group_id', $groupId)
->where('status', 1)
->whereNull('deleted_at');
// 置顶公告排在前面
$query->orderBy('is_top', 'desc')
->orderBy('created_at', 'desc');
$total = $query->count();
$announcements = $query->skip(($page - 1) * $pageSize)
->take($pageSize)
->get();
// 格式化输出
$list = $announcements->map(function($item) {
return [
'id' => $item->id,
'title' => $item->title,
'summary' => mb_substr(strip_tags($item->content), 0, 200),
'is_top' => $item->is_top,
'publisher' => $this->getPublisherInfo($item->user_id),
'view_count' => $item->view_count,
'created_at' => $item->created_at,
'updated_at' => $item->updated_at
];
});
return json_encode([
'code' => 200,
'data' => [
'list' => $list,
'total' => $total,
'page' => $page,
'page_size' => $pageSize
]
]);
}
public function detail($request) {
$announcementId = $request['id'];
// 增加浏览次数
Announcement::where('id', $announcementId)->increment('view_count');
$announcement = Announcement::find($announcementId);
if (!$announcement) {
return json_encode(['code' => 404, 'msg' => '公告不存在']);
}
return json_encode([
'code' => 200,
'data' => [
'id' => $announcement->id,
'title' => $announcement->title,
'content' => $announcement->content,
'publisher' => $this->getPublisherInfo($announcement->user_id),
'view_count' => $announcement->view_count,
'created_at' => $announcement->created_at,
'updated_at' => $announcement->updated_at,
'can_edit' => $this->checkEditPermission($announcementId)
]
]);
}
}
前端展示
HTML结构
<!-- 公告列表 -->
<div class="announcement-list">
<div class="announcement-item" v-for="item in announcements" :key="item.id">
<div class="announcement-header">
<span class="announcement-title" v-text="item.title"></span>
<span v-if="item.is_top" class="top-tag">置顶</span>
<span class="announcement-time" v-text="formatTime(item.created_at)"></span>
</div>
<div class="announcement-summary" v-html="item.summary"></div>
<div class="announcement-footer">
<span class="publisher" v-text="item.publisher.nickname"></span>
<span class="view-count">浏览 {{item.view_count}} 次</span>
</div>
</div>
</div>
<!-- 发布/编辑弹窗 -->
<div class="modal" id="announcementModal">
<div class="modal-content">
<div class="modal-header">
<h3>{{modalTitle}}</h3>
<button class="close" @click="closeModal">×</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>公告标题</label>
<input type="text" v-model="form.title" maxlength="200"
class="form-control" placeholder="请输入公告标题">
</div>
<div class="form-group">
<label>公告内容</label>
<textarea v-model="form.content" rows="10"
class="form-control" placeholder="请输入公告内容"></textarea>
</div>
<div class="form-group" v-if="isAdmin">
<label>
<input type="checkbox" v-model="form.is_top"> 置顶公告
</label>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" @click="closeModal">取消</button>
<button class="btn btn-primary" @click="submitForm">发布公告</button>
</div>
</div>
</div>
JavaScript示例
// Vue组件示例
new Vue({
el: '#app',
data: {
announcements: [],
showModal: false,
isEdit: false,
currentId: null,
form: {
title: '',
content: '',
is_top: false
}
},
computed: {
modalTitle() {
return this.isEdit ? '编辑公告' : '发布公告';
},
isAdmin() {
// 检查用户权限
}
},
methods: {
fetchAnnouncements() {
axios.get('/api/announcements', {
params: { group_id: this.groupId }
}).then(response => {
this.announcements = response.data.data.list;
});
},
showCreateModal() {
this.isEdit = false;
this.form = { title: '', content: '', is_top: false };
this.showModal = true;
},
showEditModal(id) {
this.isEdit = true;
this.currentId = id;
axios.get(`/api/announcement/${id}`).then(response => {
const data = response.data.data;
this.form = {
title: data.title,
content: data.content,
is_top: data.is_top
};
this.showModal = true;
});
},
submitForm() {
const url = this.isEdit ?
`/api/announcement/${this.currentId}/edit` :
'/api/announcement/publish';
axios.post(url, this.form).then(response => {
if (response.data.code === 200) {
this.closeModal();
this.fetchAnnouncements();
alert('操作成功');
}
}).catch(error => {
alert(error.response.data.msg);
});
},
closeModal() {
this.showModal = false;
},
formatTime(time) {
// 时间格式化函数
}
},
mounted() {
this.fetchAnnouncements();
}
});
安全注意事项
- 权限验证:严格验证用户发布、编辑权限
- XSS防护:使用HTML Purifier过滤内容
- SQL注入防护:使用参数绑定或ORM
- 频率限制:限制频繁发布操作审核**:敏感词过滤机制
扩展功能建议
- 公告分类:支持紧急、重要等分类
- 消息推送:新公告即时通知群成员
- 已读统计:查看哪些成员已阅读
- 定时发布:设置定时发布公告
- 评论功能:允许成员在公告下评论
需要我详细解释某个具体实现或提供其他功能支持吗?