本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:暂存20260913/插件开发计划.md。
FlintHub 社区治理模块(举报中心 + 通知联动 + 小黑屋)开发计划
插件名:
mod_system目标:用户举报内容 → 后台治理(删除/封禁/驳回)→ 小黑屋封禁 → 三方通知
本文档为开发计划,仅规划,不落地代码。待你确认后启动实施。
一、系统机制认知(基于现状源码)
在动手前,我已核对 WWW 当前这套源码(非 xiunox)的关键机制:
| 机制 | 现状 | 对本插件的影响 |
|---|---|---|
| 插件系统 | app/Helpers/Plugin.php,插件目录 plugins/{name}/,plugin.json 声明 hooks | 按 plugin.json 注册的钩子挂载逻辑,Plugin.php 提供 activate()/deactivate()/uninstall() |
| 插件独立库 | Plugin::db($name) 返回 plugins/{name}/data/{name}.sqlite,自动生成 .htaccess/web.config 防护 | 举报/封禁/日志表放插件独立库,卸载即清库 |
| 建表 | Plugin::ensureSchema($name, $ddl) 幂等 DDL | 用 CREATE TABLE IF NOT EXISTS |
| 前台路由 | index.php 中 Plugin::hook('route_register', ['router'=>$router]) | 注册举报/通知前台路由 |
| 后台路由 | /admin 分组内 Plugin::hook('admin_route_register', ['router'=>$r]) | 注册后台治理/小黑屋路由 |
| 后台鉴权 | 后台控制器继承 app\Controllers\Admin\BaseController(自动 Auth::isAdmin() + 二次验证) | 本插件后台控制器直接继承它 |
| 全局拦截点 | Plugin::hook('route_before_dispatch')(index.php 分发前) | 用于封禁用户全局动作拦截 |
| 用户 | users.group_id;user_groups 默认 1普通/2VIP/3版主/4管理员;role='admin' && group_id==4 才是管理员 | 封禁=记入小黑屋表+迁入封禁组(组 ID 你在后台建,作为配置项,不硬编码) |
| 认证 | Auth::isLoggedIn() / isAdmin() / requireAdmin();Controller::requireLogin() | 前台按钮仅登录用户看,后台用 BaseController 保护 |
| 登录流 | AuthController::login(),成功后 auth_login_after 钩子(登录后) | 需在写入 Session 前拦截,新增 auth_login_check 前置钩子 |
| 帖子存储 | SplitDB 分片:topic(桶) + topic_index(main_index);Thread::softDelete($id) 软删进回收站 | 删除内容一律调 Thread::softDelete(),内容进已存在的 回收站 |
| 回复 | posts(退役表)/reply(桶) + reply_index;Post 模型有删除逻辑 | 回复删除复用核心 Post 删除逻辑 |
| 博客/评论 | blogs、blog_comments、BlogController | 博客删除复用核心删除逻辑 |
| 用户组迁移 | User::updateByAdmin() 可改 group_id | 封禁/解封改 group_id |
| 设置 | app\Helpers\Settings::get($key, $default)(settings 表) | 全局配置存 settings 表(前缀 modsystem_,与 seo 插件同模式) |
| 通知中心 | 系统无独立"通知中心",仅私信 messages 表(收件箱带未读角标) | 通知走插件自带轻量通知表 + 导航钩子显示红点(详见 §九) |
| 前端 | 内置 mn- CSS、Alpine.js、htmx,禁止第三方框架 | 弹窗/反馈用 Alpine + htmx + mn- 样式 |
| 多语言 | plugins/{name}/lang/{zh,en,zh_tw}.php 自动合并 | 三语种键值对 |
| 定时任务 | 有 cron_trigger.php、cli/worker.php | 自动解封用"请求时懒检测"(最简单,无需定时任务) |
| 页面缓存 | data/runtime/pages/*.html 游客列表缓存,PageCache::invalidate() | 删除内容后需失效,保证见效果 |
二、插件目录结构
plugins/mod_system/
├── plugin.json # 声明钩子
├── Plugin.php # 激活/停用/卸载、配置读写、封禁服务
├── FrontController.php # 前台:举报提交、通知中心
├── AdminController.php # 后台:举报列表/处理、小黑屋、配置
├── hook/
│ ├── route_register.php # 前台路由
│ ├── admin_route_register.php # 后台路由
│ ├── admin_sidebar_links.php # 后台侧边栏入口
│ ├── route_before_dispatch.php # 封禁全局动作拦截
│ ├── auth_login_check.php # 登录拦截(封禁用户不得登录)
│ ├── nav_user_menu_items.php # 前台导航"举报通知红点"
│ ├── thread_operation_after.php # 帖子详情主帖操作区 → 举报按钮
│ ├── post_operation_after.php # 帖子详情回复楼层操作区 → 举报按钮
│ ├── blog_detail_operation_after.php # 博客详情正文操作区 → 举报按钮
│ ├── blog_comment_operation_after.php # 博客详情评论操作区 → 举报按钮
│ └── profile_user_card_after.php # 个人主页用户卡片 → 举报按钮
├── views/
│ ├── admin_list.php # 举报列表(分页+筛选+批量+预警高亮)
│ ├── admin_ban.php # 小黑屋列表(解封入口)
│ ├── admin_config.php # 全局配置页
│ ├── report_modal.php # 前台举报弹窗(被 htmx 拉取 or 内联)
│ └── notifications.php # 前台通知中心
├── assets/
│ └── style.css # 插件专属样式(mn- 基础上微量扩展)
├── lang/
│ ├── zh.php ├── en.php └── zh_tw.php
└── data/ # 运行时自动生成 .htaccess/web.config + mod_system.sqlite三、数据表设计(插件独立库 mod_system.sqlite)
1. mod_reports(举报记录)
| 列 | 类型 | 说明 |
|---|---|---|
| id | INTEGER PK | |
| reporter_id | INTEGER | 举报人(匿名举报仍记 ID,仅展示时脱敏) |
| target_type | TEXT | thread / post / blog / blog_comment / user |
| target_id | INTEGER | 目标内容 ID |
| target_uid | INTEGER | 被举报用户 ID(预存,防内容删除后可查) |
| category | TEXT | 分类:spam/porn/illegal/attack/flood/fake/other |
| note | TEXT | 补充说明,≤200 字 |
| is_anonymous | INTEGER | 1=匿名举报 |
| ip | TEXT | 举报 IP(辅助防刷) |
| status | INTEGER | 0 待处理 / 1 已处理 / 2 已驳回 |
| handle_action | TEXT | dismiss/delete/ban/delete_ban |
| handler_id | INTEGER | 处理人 |
| handled_at | TEXT | |
| created_at | TEXT |
- 索引:
(target_type, target_id)、(status)、(reporter_id, created_at)
2. mod_bans(小黑屋)
| 列 | 类型 | 说明 |
|---|---|---|
| id | INTEGER PK | |
| user_id | INTEGER | 被封禁用户 |
| reason | TEXT | 封禁原因 |
| banned_by | INTEGER | 处理人 |
| banned_at | TEXT | |
| expires_at | TEXT NULL | 空=永久封禁;否则限时解封时间 |
| original_group_id | INTEGER | 封禁前用户组,解封恢复用 |
| status | INTEGER | 1 生效 / 0 已解封 |
| unbanned_by | INTEGER | 解封人 |
| unbanned_at | TEXT |
3. mod_logs(处理日志,可追溯)
| 列 | 类型 | 说明 |
|---|---|---|
| id | INTEGER PK | |
| report_id | INTEGER | 关联举报 |
| user_id | INTEGER | 处理人 |
| target_uid | INTEGER | 被处理用户 |
| action | TEXT | dismiss/delete/ban/delete_ban |
| detail | TEXT | 摘要 |
| created_at | TEXT |
4. mod_notifications(插件通知中心)
| 列 | 类型 | 说明 |
|---|---|---|
| id | INTEGER PK | |
| to_uid | INTEGER | 接收者(被举报人/管理员/举报人) |
| type | TEXT | report_new / auto_audit / report_result / ban_penalty |
| title | TEXT | |
| content | TEXT | |
| url | TEXT | 跳转链接 |
| is_read | INTEGER | 0 未读 |
| created_at | TEXT |
四、全局配置项(settings 表,前缀 modsystem_)
| 键 | 默认 | 说明 |
|---|---|---|
modsystem_auto_audit_count | 3 | 同一内容被举报 N 次后台自动标红预警 |
modsystem_daily_limit | 5 | 每用户每日最多举报次数 |
modsystem_cooldown | 300 | 同一用户对同一内容举报冷却秒数 |
modsystem_notify_enabled | 1 | 通知中心联动开关(0=关闭不发通知) |
modsystem_ban_group_id | `` | 封禁用户组 ID(你在后台建好小组后填,不硬编码) |
modsystem_ban_days | 7 | 小黑屋默认"封禁天数"(0 或空=永久,可在小黑屋列表逐条改) |
modsystem_moderator_group_ids | `` | (可选,预留)可处理举报的高管用户组 ID 多选框(默认空=仅管理员) |
配置页展示全部 7 项,保存后 Settings::clearCache()。
五、前台用户功能(举报流程)
举报入口(共五类目标)
- 帖子详情主帖:
thread_operation_after钩子在主帖操作区(show.php编辑/删除按钮附近)注入"举报"按钮 →target_type=thread - 帖子详情回复楼层:
post_operation_after在每个回复操作区注入"举报"按钮 →target_type=post - 博客详情正文:
blog_detail_operation_after注入"举报"按钮 →target_type=blog - 博客详情评论文楼层:每个评论操作区注入"举报"按钮 →
target_type=blog_comment(复用post_operation_after同一套弹窗/提交逻辑,仅 target_type 不同) - 个人主页:
profile_user_card_after在用户卡片注入"举报用户"按钮 →target_type=user - 仅登录用户展示(
Auth::isLoggedIn()) - 前端用 htmx 拉取弹窗 HTML,Alpine 控制开关
举报弹窗表单
- 分类单选(7 种):spam垃圾广告 / porn色情低俗 / illegal违法违规 / attack人身攻击 / flood恶意刷屏 / fake虚假信息 / other其他
- 补充说明:textarea 选填,
maxlength=200 - 勾选"匿名举报"(默认不勾)
匿名举报实现(重要:前端脱敏 ≠ 后端隐藏)
- 数据库始终存
reporter_id(mod_reports.reporter_id+mod_logs都不为空),后台管理员列表/详情可查真实举报人 is_anonymous=1只影响对外展示:后台列表对"举报人"列显示"匿名",通知/邮件不携带举报人身份- 后端永不因匿名而减少防刷校验:cooldown / daily_limit / IP 限流对匿名举报者照样生效,防止借匿名校验绕过
提交限制(FrontController::submit 内校验)
- 未登录 → 拒绝
- 不能举报自己
- Cooldown:同用户对同目标(target_type+target_id)在
cooldown秒内重复 → "您已举报过该内容,请稍后再试" - 每日
daily_limit上限(按 reporter_id 当日次数)→ "今日举报次数已达上限,请明日再试" - 目标必须存在(thread/post/blog/blog_comment/user 各自校验)
- 同一用户对同一目标仅计一条待处理举报(重复提交合并;不同用户累计才触发预警)
无感反馈
- 成功 → "举报成功,感谢您的监督"(htmx 局部提示)
- 匿名与否不影响任何限制:cooldown/daily_limit 按真实 reporter_id 计,匿名只是展示层脱敏
六、后台管理功能(治理流程)
继承
app\Controllers\Admin\BaseController,天然具备管理员权限保护
1. 举报列表 GET /admin/mod-system/reports
- 字段:内容摘要、被举报人、举报分类、举报人(匿名则显示"匿名")、提交时间、状态、处理结果
- 筛选:全部 / 待处理 / 已处理 / 已驳回(
?status=) - 预警高亮:目标累计举报次数 ≥
auto_audit_count时该行背景高亮(admin.css 扩展类) - 分页:
?page=,per_page默认 20
2. 单条处理 POST /admin/mod-system/reports/{id}/handle
dismiss驳回:标记已驳回,不惩罚delete删除:调核心软删/删除逻辑删帖/回复/博客/博客评论,内容进回收站ban封禁:目标用户迁入封禁组 + 记小黑屋(可带封禁天数)delete_ban删除+封禁:先删内容再封禁用户- 每次处理写
mod_logs(处理人/时间/结果) - 处理表单带"封禁天数"输入框(
ban_days,0/空=永久),ACTION=ban/delete_ban 时生效
3. 批量处理 POST /admin/mod-system/reports/batch
- 勾选多条 → 批量 驳回 / 删除 / 封禁
- 合并通知(同目标只封一次,去重)
4. 处理日志
- 后台单独 tab 查看
mod_logs,或列表内联展示最近动作
七、小黑屋(封禁模块)GET/POST /admin/mod-system/bans
数据逻辑
mod_bans独立表:用户 ID、原因、封禁时间、解封时间、原用户组- 封禁动作:
INSERT mod_bans+UPDATE users SET group_id = :ban_group_id(ban_group_id为空则仅记表不迁组,由管理员手动设置) - 封禁方式:永久(
expires_at = NULL)或限时(expires_at = banned_at + N 天)
小黑屋列表(后台)
- 列:用户名、封禁原因、封禁人、封禁时间、解封时间、状态
- 新建/编辑封禁提供"封禁天数"输入框(默认取
modsystem_ban_days,0/空=永久)→ 对轻微违规可"关几天" - 状态展示剩余解封时间;已解封行置灰
封禁判断(Plugin::isBanned($uid))
- 查
mod_bans.status=1且(expires_at为空 或expires_at > now) - 懒解封:若
expires_at <= now,顺手执行自动解封(删除记录 + 恢复original_group_id),并返回未封禁
拦截机制
- 登录拦截:
auth_login_check钩子(放在 Session 建立前)→ 封禁用户提示"您的账号已被封禁",拒绝登录 - 操作拦截(力度 = 只拦写操作):
route_before_dispatch全局钩子
- 只拦截写操作:发帖、回帖、发博客、博客评论、回复、上传(按
POST方法 + 目标路由白名单判定) - 读操作一律放行:看帖、看博客、看通知、看个人主页、浏览列表 → 被封禁者仍能"看自己是否解封/看通知",保留申诉与自查能力
- 拦截时返回明确提示(如"您的账号已被封禁,无法执行此操作")
- 内容冻结:仅对当前处于封禁状态的用户内容加"该用户已被封禁"提示(数据层不改,仅展示)。解封后提示自动消失;被举报但未封禁的内容不加任何提示(避免误伤/虚假负面形象)
解封机制
- 自动:请求时懒检测到期自动恢复(无需定时任务)
- 手动:后台"小黑屋列表"点"解除封禁" → 删
mod_bans记录 + 恢复original_group_id - 封禁生效(新增/修改)后统一
PageCache::invalidate():即使用户内容未删除仅迁组,也强制失效游客列表缓存,确保前台"该用户已被封禁"提示即时一致(简单粗暴但保证一致性)
八、通知中心联动(mod_notifications 表)
系统无独立通知中心,故插件自带轻量通知表,并在前台导航钩子显示未读红点。
已确认:不复用现有
messages私信表(收件箱 JOIN 依赖 sender_id>0,系统通知 sender 为 0 会丢显示、造成展示错误)。
| 时机 | 接收者 | 内容 |
|---|---|---|
| 举报提交后 | 被举报人 | "您的内容因【分类】被举报,请及时整改。若多次被举报,将面临封禁处理。"(不暴露举报人,匿名/实名均不暴露) |
| 新举报产生 | 全体管理员 | "有新的举报待处理"(前台通知 + 后台列表可见) |
| 达 auto_audit_count | 管理员 | "【内容摘要】已被举报多次,请及时处理" |
| 处理完毕 | 举报人 | "您举报的内容已被处理(删除/封禁/驳回)" |
- 由
modsystem_notify_enabled开关控制(默认开) - 管理员范围(已确认):通知/预警只发给
role='admin' && group_id==4;后续如需让版主等高级组处理,可通过配置页modsystem_moderator_group_ids多选框开放(默认空=仅管理员) - 前台导航通过
nav_user_menu_items/nav_plugin_links钩子显示未读数红点,点击进/mod-system/notifications查看
九、权限控制
- 后台三页全部继承
BaseController(自动管理员鉴权 + 二次验证) - 前台举报按钮仅登录用户可见(
Auth::isLoggedIn()) - 提交接口重复校验登录态 + CSRF
- CSRF:所有 POST 用
Csrf::verifyOrDie($_POST['csrf'] ?? '')
十、多语言(lang/zh.php lang/en.php lang/zh_tw.php)
覆盖键:
plugin.mod_system.*:侧边栏导航、配置项标签、按钮- 举报分类 7 项
- 提示文案:已上限 / 冷却 / 成功 / 已封禁 / 处理结果
- 后台:列表、筛选、批量、预警、处理动作、小黑屋、解封
十一、需新增的核心钩子点(最小改动)
为在现有视图注入按钮 + 登录拦截,需在核心文件加少量钩子帧(所有逻辑都在插件内,核心仅加一行 Plugin::hook(...)):
| 核心文件 | 新增钩子 | 位置 |
|---|---|---|
app/Views/thread/show.php | thread_operation_after | 主帖操作区 |
app/Views/thread/show.php | post_operation_after | 每个回复操作区 |
app/Views/blog/detail.php | blog_detail_operation_after | 博客正文操作区 |
app/Views/blog/detail.php | blog_comment_operation_after | 每个博客评论操作区 |
app/Views/profile/index.php | profile_user_card_after | 用户卡片 |
app/Controllers/AuthController.php | auth_login_check | 登录成功校验后、写 Session 前 |
app/Views/layouts/main.php | 复用已有 nav_user_menu_items | 通知红点(免加新钩子) |
index.php | 复用已有 route_before_dispatch | 全局写操作拦截(免加新钩子) |
十二、实施顺序
- 建目录骨架 +
plugin.json+Plugin.php(配置读写、建表、isBanned/封禁/解封服务) - 三语种
lang/ - 前台路由 +
FrontController(举报提交 + 拦截 + 通知) - 视图钩子 + 前台举报弹窗(Alpine + htmx)
- 后台路由 +
AdminController(列表/处理/批量/日志) - 后台视图(列表、小黑屋、配置)
- 通知中心(表 + 前台通知视图 + 红点)
- 核心钩子帧注入
- 联调:封禁登录拦截 / 全局动作拦截 / 删除进回收站 / PageCache 失效
- 验证 + 清理游客缓存
十三、决策结论(已确认)
基于你的要求,对原计划中的待确认项给出以下定论,已回填到上文各章节:
- 通知中心:采用插件自带
mod_notifications表 + 导航红点方案。不复用现有messages私信表(系统通知 sender 为 0,收件箱 JOINsender_id>0会丢显示,造成展示错误)。 - 封禁组 ID:默认留空,由管理员在后台配置页手动填入自己建的"封禁用户组" ID(如 5)。不硬编码,后续改组长/组名无需动代码。
- 管理员范围:通知/预警只发给
role='admin' && group_id==4。后续如需开放给版主等高级组,通过配置页modsystem_moderator_group_ids多选框(默认空=仅管理员)。 - 内容冻结提示:只对"当前处于封禁状态"的用户内容加提示;解封后自动消失;被举报但未封禁的内容不加提示(避免误伤和虚假负面形象)。
本次调整响应的补充建议
- 目标覆盖补上博客评论(
target_type=blog_comment,新增blog_comment_operation_after钩子) - 匿名举报仅前端脱敏、后端始终存
reporter_id,且不豁免任何防刷校验 - 小黑屋支持限时封禁天数(默认
modsystem_ban_days,可逐条改),轻微违规"关几天" - 全局拦截只拦写操作(POST 发帖/回帖/发博客/评论/上传),读操作全放行,保留被封禁者自查/申诉能力
- 封禁生效后统一
PageCache::invalidate(),保证前台"该用户已被封禁"提示即时一致
确认无误后,我按计划开工实施。