本文目录导读:

我来帮你详细解析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
- 频率限制:限制频繁发布操作审核**:敏感词过滤机制
扩展功能建议
- 公告分类:支持紧急、重要等分类
- 消息推送:新公告即时通知群成员
- 已读统计:查看哪些成员已阅读
- 定时发布:设置定时发布公告
- 评论功能:允许成员在公告下评论
需要我详细解释某个具体实现或提供其他功能支持吗?
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播