Xiuno BBS 用户封禁系统
1. 概述
用户封禁系统是 Xiuno BBS 4.5+ 的核心安全模块,提供 4 档用户状态管理,用于对付恶意用户(小黑子)。系统采用「核心实现 + 事件扩展」架构:核心逻辑在 lib/UserBanService.php,插件通过 lib/XnEvent.php 事件机制接入。
核心特性:
- 4 档封禁状态:正常 / 禁言 / 禁止访问 / 锁定
- 场景化检查:login / browse / post / password 四种入口分别拦截不同 ban_type
- 到期自动解封:访问时检查
banned_until,过期自动重置字段 - 封禁历史:完整记录 ban / unban / auto_unban / clear_content 操作
- IP 黑名单:支持单 IP 与 IP 段(范围)封禁
- 版主权限分级:版主仅能禁言 1-7 天,管理员可永久 / 锁定
- 内容清空:保留账号、清空回帖/主题索引/附件/通知
- 前台内容隐藏:被封用户发布的内容显示占位提示,管理员可见原始内容
- 封禁公示页:双栏展示当前封禁与近期释放用户
- 事件机制:6 个 XnEvent 事件 + 3 个 PHP hook 点供插件扩展
设计原则:
- 永久封禁时间戳用
9999999999(约 2286 年),避免 32 位系统PHP_INT_MAX溢出 - 不可封禁管理员组(gid=1,2)和封禁操作者自己
ban_type等字段不在USER_UPDATE_PROTECTED_FIELDS中,可用user_update()而非user__update(),前者会自动清缓存 + 触发 hook- 通知函数是
notify_create()(非notice_send()),用type='system'+from_uid=0允许系统通知自己
2. 数据库设计
2.1 user 表扩展字段
sql
ALTER TABLE `bbs_user`
ADD COLUMN `ban_type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '封禁类型 0正常/1禁言/2禁止访问/3锁定',
ADD COLUMN `ban_reason` varchar(255) NOT NULL DEFAULT '' COMMENT '封禁原因',
ADD COLUMN `ban_admin_uid` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '封禁操作管理员uid',
ADD COLUMN `ban_time` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '封禁时间';
-- 复用已有 banned_until 字段:0=未封禁,9999999999=永久,其他=到期时间戳
| 字段 | 类型 | 说明 |
|---|---|---|
| ban_type | tinyint(1) | 0 正常 / 1 禁言 / 2 禁止访问 / 3 锁定 |
| banned_until | int(11) unsigned | 复用已有字段。0=未封禁,9999999999=永久,其他=到期 Unix 时间戳 |
| ban_reason | varchar(255) | 封禁原因,显示给用户和后台 |
| ban_admin_uid | int(11) unsigned | 执行封禁的管理员 uid(系统自动解封时为 0) |
| ban_time | int(11) unsigned | 最近一次封禁时间 |
2.2 bbs_user_ban_log 表(封禁历史)
sql
CREATE TABLE `bbs_user_ban_log` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '被操作用户uid',
`admin_uid` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '操作管理员uid(0=系统自动)',
`action` varchar(16) NOT NULL DEFAULT '' COMMENT 'ban/unban/auto_unban/clear_content',
`ban_type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '封禁类型(unban/auto_unban/clear_content时为0)',
`reason` varchar(255) NOT NULL DEFAULT '' COMMENT '原因',
`duration` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '封禁时长秒数(0=永久)',
`create_time` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '操作时间',
PRIMARY KEY (id),
KEY uid (uid),
KEY action_time (action, create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
action 取值:
ban--- 手动封禁unban--- 手动解封auto_unban--- 到期系统自动解封(不发通知)clear_content--- 清空用户内容
2.3 bbs_banned_ip 表(IP 黑名单)
sql
CREATE TABLE `bbs_banned_ip` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`ip_start` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '起始IP(ip2long整型)',
`ip_end` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '结束IP(ip2long整型)',
`reason` varchar(255) NOT NULL DEFAULT '' COMMENT '封禁原因',
`admin_uid` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '操作管理员uid',
`create_time` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '创建时间',
`expire_time` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '过期时间戳(0=永久)',
PRIMARY KEY (id),
KEY ip_range (ip_start, ip_end)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
注意 :ip_start/ip_end 字段为整型,写入必须用 ip2long() + sprintf('%u', ...) 处理 32 位系统溢出。读取显示时用 long2ip() 还原。
3. 配置项
在 conf/conf.default.php 中:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ban_show_public_list |
int | 1 | 是否开启封禁公示页(/banned)。0 关闭(访问返回 404),1 开启 |
ban_inherit_to_same_ip |
int | 0 | 预留:是否对同 IP 用户继承封禁状态。当前未启用,供插件实现 |
4. UserBanService API
文件位置:lib/UserBanService.php
4.1 常量
php
UserBanService::BAN_TYPE_NORMAL = 0; // 正常
UserBanService::BAN_TYPE_SILENCE = 1; // 禁言(可浏览,不能发帖回帖)
UserBanService::BAN_TYPE_BAN_ACCESS = 2; // 禁止访问(不能登录、不能浏览)
UserBanService::BAN_TYPE_LOCK = 3; // 锁定(不能登录、不能改密找密)
UserBanService::PERMANENT_BAN = 9999999999; // 永久封禁时间戳(约2286年)
UserBanService::ADMIN_GIDS = [1, 2]; // 管理员组 gid(不可被封禁/清空内容)
4.2 静态方法
ban($uid, $banType, $duration, $reason, $adminUid) --- 封禁用户
php
$result = UserBanService::ban(
$uid, // 被封禁用户 uid
UserBanService::BAN_TYPE_SILENCE, // 封禁类型 1/2/3
86400 * 7, // 时长秒数,0=永久
'广告灌水', // 封禁原因
$adminUid // 操作管理员 uid
);
// 返回 ['code'=>0 成功, 'message'=>错误信息]
校验:
$uid > 0且$adminUid > 0$banType必须是 1/2/3$duration >= 0(0=永久)- 不能封禁自己(
$uid === $adminUid) - 不能封禁管理员组(gid ∈ ADMIN_GIDS)
- user 表必须有
ban_type字段(升级后才有)
流程 :触发 beforeBan 事件 → 更新 user 表 → 写 ban_log(action=ban)→ 发送通知 → 触发 afterBan 事件。
unban($uid, $adminUid, $reason = '') --- 解封用户
php
$result = UserBanService::unban($uid, $adminUid, '申诉通过');
流程 :触发 beforeUnban → 重置 5 个封禁字段为 0/空 → 写 ban_log(action=unban)→ 发送通知 → 触发 afterUnban。
checkBan($uid) --- 检查封禁状态(含到期自动解封)
php
$status = UserBanService::checkBan($uid);
// 返回 ['banned'=>bool, 'ban_type'=>int, 'ban_reason'=>string, 'expire_time'=>int, 'expire_formatted'=>string]
自动解封 :若 banned_until > 0 且 <= time(),自动调用 autoUnban() 重置字段并写日志(action=auto_unban,不发通知),返回 banned=false。
checkBanByScene($uid, $scene) --- 按场景检查(用于入口拦截)
php
$check = UserBanService::checkBanByScene($uid, 'post');
if(!$check['allowed']) {
message(-1, $check['message']);
}
场景规则:
| Scene | 拒绝的 ban_type | 用途 |
|---|---|---|
login |
2, 3 | 禁止访问 / 锁定 不能登录 |
browse |
2, 3 | 禁止访问 / 锁定 不能浏览 |
post |
1, 2, 3 | 禁言及以上不能发帖回帖编辑 |
password |
3 | 锁定 不能改密找密 |
| 其他 | 1, 2, 3 | 未知场景保守拒绝所有 |
clearContent($uid, $adminUid) --- 清空用户内容(保留账号)
php
$result = UserBanService::clearContent($uid, $adminUid);
清理范围:
- 删除所有回帖(
post_delete_by_uid) - 删除主题索引(
mythread_delete_by_uid,不删 thread 表主题记录,已知天花板见代码注释) - 删除附件(
attach_delete_by_uid) - 删除通知(
notify_delete_by_uid) - 重置
threads/posts计数为 0
流程 :触发 beforeClearContent → 删除内容 → 重置计数 → 写 ban_log(action=clear_content)→ 触发 afterClearContent。不发通知。
getBanStatus($uid) --- 获取格式化封禁状态(前端显示用)
php
$status = UserBanService::getBanStatus($uid);
// 返回 ['ban_type', 'ban_reason', 'ban_time', 'banned_until',
// 'expire_formatted', 'status_label', 'status_color']
status_color 用于 Bootstrap 徽章:success(正常)/ warning(禁言)/ danger(禁止访问)/ dark(锁定)。
辅助方法
php
UserBanService::getBanTypeLabel($banType); // 返回 ['label'=>..., 'color'=>...]
UserBanService::formatDuration($duration); // "7天" / "永久" / "3小时"
UserBanService::formatExpireTime($banned_until); // "2026-07-09 12:00:00" / "永久"
5. 模型函数
5.1 ban_log.func.php(封禁历史)
php
ban_log_create($data) // 创建记录(自动补 create_time)
ban_log_find_by_uid($uid, $page=1, $ps=20) // 按 uid 查询
ban_log_count_by_uid($uid) // 按 uid 计数
ban_log_find_all($cond=[], $page=1, $ps=50) // 后台查询所有
ban_log_delete_by_uid($uid) // 删除某用户所有记录(用户被彻底删除时调用)
ban_log_find_recent_unbanned($days=30, $lim=20) // 近 N 天解封记录(公示页用)
5.2 banned_ip.func.php(IP 黑名单)
php
banned_ip_create($ip_start, $ip_end, $reason, $admin_uid, $expire_time=0) // 创建
banned_ip_delete($id) // 删除
banned_ip_find($page=1, $pagesize=50) // 列表
banned_ip_count() // 计数
banned_ip_check($ip) // 检查 IP 是否命中(已废弃,转发到 IpBlacklistService::is_blacklisted())
banned_ip_read($id) // 单条查询
新代码请直接用 IpBlacklistService::is_blacklisted($ip)(位于 lib/security/IpBlacklistService.php),banned_ip_check() 仅保留兼容。
6. 后台管理
6.1 用户管理扩展(admin/route/user.php)
| Action | 用途 |
|---|---|
list |
用户列表,支持按 ban_type 筛选,显示状态徽章、批量操作 Modal、行内一键禁言7天按钮 |
update |
用户编辑页含「封禁设置」区(类型/时长/原因),已封禁显示当前状态并支持修改 |
ban |
POST 封禁(POST 处理在 header include 之前) |
unban |
POST 解封 |
clear_content |
POST 清空内容 |
ban_log |
查看某用户封禁历史 |
6.2 IP 黑名单(admin/route/banned_ip.php)
后台菜单「其他 → IP 黑名单」,支持单 IP / IP 段新增、过期时间设置、删除。
6.3 升级
部署后必须到 /admin/?upgrade.htm 执行「用户封禁系统」升级项,会:
- 为 user 表添加 4 个新字段
- 创建 bbs_user_ban_log 表
- 创建 bbs_banned_ip 表
- 为已存在用户初始化
ban_type=0
7. 核心检查点
| 入口 | 文件 | 检查内容 |
|---|---|---|
| 全局 | index.inc.php |
ban_type=2,3 跳转封禁提示页(admin 豁免用 SCRIPT_NAME,管理员组豁免,AJAX 返回 JSON) |
| 登录 | route/user.php login |
ban_type=2,3 拒绝登录 + IP 黑名单 |
| 注册 | route/user.php create |
IP 黑名单 |
| 找密 | route/user.php resetpw |
ban_type=3 锁定拒绝 |
| 改密 | route/my.php password |
ban_type=3 锁定拒绝(改密在 my.php 不在 user.php) |
| 发主题 | route/thread.php create |
ban_type>0 拒绝 + IP 黑名单 |
| 发回帖 | route/post.php create |
ban_type>0 拒绝 |
| 编辑 | route/post.php update |
ban_type>0 拒绝 |
封禁提示页 :view/htm/banned_notice.htm 显示封禁原因、解封时间、倒计时、申诉链接、退出登录按钮。
8. 前台内容显示
被封用户发布的内容会被隐藏,显示占位提示「该用户被关禁闭,内容被隐藏」。涉及的模板:
| 模板 | 处理 |
|---|---|
view/htm/thread_main.inc.htm |
楼主主题内容隐藏 + 状态徽章 + 封禁按钮(版主/管理员可见) |
view/htm/post_list.inc.htm |
回帖内容隐藏 + 状态徽章 + 封禁按钮 |
view/htm/thread_list.inc.htm |
帖子标题前 [已隐藏] 标记 |
view/htm/user_info_card.inc.htm |
用户主页状态徽章 + 本人显示完整封禁信息 + 清空内容提示 |
复用函数 (model/misc.func.php):
user_ban_badge_html($banType)--- 输出状态徽章 HTMLuser_ban_hidden_notice_html()--- 输出占位提示 HTML
特殊规则:
- 管理员(gid=1,2)可见被封用户原始内容
- 被封用户本人可见自己的内容
- 解封后内容自动恢复显示(
unban()只重置字段,不删内容) - 清空内容后用户主页显示「该用户已被管理员清空内容」提示
9. 封禁公示页
- 路由:
/banned(route/banned.php) - 模板:
view/htm/banned.htm,双栏布局(当前封禁中 / 近期释放30天) - 受
ban_show_public_list配置控制,关闭时返回 404 - 预留 hook 点:
banned_list_display.php+XnEvent::trigger('UserBanService.bannedListDisplay')
10. 版主封禁入口
- 路由:
route/mod.phpban_useraction - 权限:管理员(gid=1,2)直接放行;版主需
forum_access_mod($fid, $gid, 'allowbanuser') - 版主权限限制(三重校验) :
ban_type必须为 1(禁言)duration不能为 0(不能永久)duration必须在[86400, 604800](1-7天)
- 管理员可永久封禁和锁定
- 前台帖子页作者卡片「封禁」按钮(
thread_main.inc.htm+post_list.inc.htm)+ 封禁弹窗 Modal - 路由 URL:
mod_ban_user_url()(model/route.func.php)
11. xnx_report 举报系统联动
lib/security/ReportService.php 的 handle_report() / batch_handle() 中:
ban分支:调用UserBanService::ban($target_uid, BAN_TYPE_SILENCE, 604800, $reason, $handler_uid)默认禁言 7 天delete_ban分支:先删内容再封禁(封禁失败时保持已处理状态,提示「内容已删除,但封禁失败」)- 封禁失败回滚:纯
ban操作失败时回滚举报状态为待处理 - 复用现有「封禁用户」「删除并封禁」按钮 UI,不新增重复按钮
12. 插件扩展接口
12.1 XnEvent 事件机制
文件位置:lib/XnEvent.php,提供 on / once / trigger / off / hasListeners 五个静态方法。
事件名约定 :ClassName.methodName,如 UserBanService.beforeBan。
回调签名 :function(&$args)(参数按引用传递,可修改后传递给主流程)。
异常处理 :回调抛出异常不会中断主流程,仅记录 xn_log('error')。
12.2 UserBanService 触发的 7 个事件
| 事件名 | 触发时机 | 可修改参数 |
|---|---|---|
UserBanService.beforeBan |
封禁前 | banType / duration / reason(引用,可修改) |
UserBanService.afterBan |
封禁后 | 只读,含 bannedUntil |
UserBanService.beforeUnban |
解封前 | reason(引用,可修改) |
UserBanService.afterUnban |
解封后 | 只读 |
UserBanService.beforeClearContent |
清空前 | uid / adminUid |
UserBanService.afterClearContent |
清空后 | 只读 |
UserBanService.bannedListDisplay |
公示页渲染时 | current_list / recent_list(引用,可修改) |
12.3 PHP hook 点
| Hook 文件 | 位置 | 用途 |
|---|---|---|
user_ban_check.php |
route/user.php / route/thread.php / route/post.php / route/my.php / index.inc.php |
自定义封禁检查(如第三方风控插件判定) |
banned_ip_check.php |
route/user.php login/create / route/thread.php create |
自定义 IP 检查(如外接 IP 信誉库) |
banned_list_display.php |
route/banned.php |
修改封禁公示页列表数据 |
12.4 插件调用示例
示例 1:监听封禁事件,记录到第三方日志系统
php
// plugin/my_audit/hook/model_inc_start.php
<?php exit;
if(!class_exists('UserBanService')) { include_once APP_PATH.'lib/UserBanService.php'; }
XnEvent::on('UserBanService.afterBan', 'my_audit', function(&$args) {
// $args 含 uid/banType/duration/reason/adminUid/bannedUntil
// 写入第三方审计系统(如 Elasticsearch)
my_audit_log_write('user_ban', array(
'uid' => $args['uid'],
'ban_type' => $args['banType'],
'duration' => $args['duration'],
'reason' => $args['reason'],
'admin_uid' => $args['adminUid'],
'expire_at' => $args['bannedUntil'],
));
});
示例 2:封禁前修改封禁时长(如风控插件自动延长)
php
// plugin/risk_control/hook/model_inc_start.php
<?php exit;
XnEvent::on('UserBanService.beforeBan', 'risk_control', function(&$args) {
// 检查用户风险等级
$risk = RiskControlService::getRiskLevel($args['uid']);
if($risk === 'high' && $args['duration'] > 0 && $args['duration'] < 86400 * 30) {
// 高风险用户:自动延长到 30 天
$args['duration'] = 86400 * 30;
$args['reason'] .= '(风控自动延长至30天)';
}
});
示例 3:插件中调用 UserBanService 封禁用户
php
// plugin/my_plugin/route/my_plugin.php
<?php
!defined('DEBUG') AND exit('Access Denied');
// 必须先 include_once(生产环境 min.php 类加载顺序不可预测)
if(!class_exists('UserBanService')) {
include_once APP_PATH.'lib/UserBanService.php';
}
if($method == 'POST' && $action == 'auto_ban') {
CsrfService::check();
$target_uid = intval(param('uid'));
$reason = param('reason', '', FALSE); // 第3参数 FALSE 关闭 htmlspecialchars
// 调用核心封禁服务(禁言 3 天)
$result = UserBanService::ban(
$target_uid,
UserBanService::BAN_TYPE_SILENCE,
86400 * 3,
$reason,
$uid // 当前登录管理员 uid
);
if($result['code'] != 0) {
message(-1, $result['message']);
}
message(0, lang('user_ban_success'));
}
示例 4:解封时清理插件自身数据
php
// plugin/my_plugin/hook/model_inc_start.php
<?php exit;
XnEvent::on('UserBanService.afterUnban', 'my_plugin', function(&$args) {
// 清理插件中存储的该用户封禁期间数据
db_delete('my_plugin_ban_data', array('uid' => $args['uid']));
});
示例 5:自定义 IP 检查(外接 IP 信誉库)
php
// plugin/ip_reputation/hook/banned_ip_check.php
<?php exit;
// 此 hook 在 banned_ip_check($ip) 落地后调用
// 如需更严格的检查,可直接调用 IpBlacklistService::is_blacklisted($ip) 或自建查询
$reputation = IpReputationService::query($ip);
if($reputation['score'] < -50) {
// 自动加入本地黑名单
if(!class_exists('IpBlacklistService')) {
include_once APP_PATH.'lib/security/IpBlacklistService.php';
}
IpBlacklistService::add($ip, $ip, 'IP信誉库自动拦截', 0, 86400 * 7);
message(-1, lang('user_ban_ip_banned'));
}
示例 6:修改封禁公示页列表
php
// plugin/my_plugin/hook/model_inc_start.php
<?php exit;
XnEvent::on('UserBanService.bannedListDisplay', 'my_plugin', function(&$args) {
// $args['current_list'] / $args['recent_list'] 是引用
// 添加插件标识的徽章
foreach($args['current_list'] as &$row) {
$row['plugin_badge'] = '⚠️';
}
});
12.5 插件卸载时清理事件监听器
php
// plugin/my_plugin/uninstall.php
<?php
!defined('DEBUG') AND exit('Access Denied');
if(!class_exists('XnEvent')) {
include_once APP_PATH.'lib/XnEvent.php';
}
// 移除本插件注册的所有事件监听器
XnEvent::off(null, 'my_plugin');
13. 多语言包
封禁系统涉及的语言包 key(zh-cn / zh-tw / en-us 三语已同步):
- 前台 (
lang/{lang}/bbs_common.php,约 61 个user_ban_*key):状态标签、拒绝消息、通知文案、时长格式 - 后台 (
lang/{lang}/bbs_admin.php,约 88 个admin_user_ban_*/admin_banned_ip_*key):表单标签、按钮文案、提示信息
带占位符的 key:
user_ban_notice_ban--- 含{type}{reason}{duration}{expire}user_ban_duration_minutes/user_ban_duration_hours/user_ban_duration_days--- 含{n}
新增功能时必须同步三种语言包,否则用户看到原始 key 名。
14. 安全规范
- 所有 POST 表单包含
CsrfService::input()+ 后端CsrfService::check() - 后台路由 POST 处理在
header.inc.htminclude 之前(message() 303 要求) - IP 字段写入用
ip2long()转整型,禁用intval("ip字符串")(只返回第一段) - IP / 原因等参数
param()第 3 参数传FALSE关闭 htmlspecialchars ban_type等字段不在USER_UPDATE_PROTECTED_FIELDS中,用user_update()而非user__update()(前者自动 cache_delete + 触发 hook)- 删除操作幂等:对已删除记录返回成功而非 404
- admin 检测用
SCRIPT_NAME(兼容子目录安装),非REQUEST_URI
15. 缓存与编译同步
XnEvent.php已同步到xiunophp/xiunophp.min.php- 修改
xiunophp/xiunophp.php后必须运行bash scripts/check_min_sync.sh(退出码 0 才算通过) - 修改模板/PHP 后清理
tmp/缓存
16. 关键文件清单
| 类型 | 文件 |
|---|---|
| 核心服务 | lib/UserBanService.php |
| 事件机制 | lib/XnEvent.php |
| 升级服务 | lib/UpgradeService.php(upgradeUserBanSystem() 方法) |
| 模型 | model/ban_log.func.php、model/banned_ip.func.php |
| IP 黑名单服务 | lib/security/IpBlacklistService.php |
| 全局检查 | index.inc.php |
| 路由检查 | route/user.php、route/thread.php、route/post.php、route/my.php |
| 封禁提示页 | view/htm/banned_notice.htm |
| 公示页 | route/banned.php、view/htm/banned.htm |
| 版主入口 | route/mod.php(ban_user action) |
| 后台用户管理 | admin/route/user.php、admin/view/htm/user_list.htm、user_update.htm、user_ban_log.htm |
| 后台 IP 黑名单 | admin/route/banned_ip.php、admin/view/htm/banned_ip_list.htm |
| 举报联动 | lib/security/ReportService.php |
| 复用函数 | model/misc.func.php(user_ban_badge_html / user_ban_hidden_notice_html) |
| 数据库 | install/install.sql |
| 配置 | conf/conf.default.php |
17. 测试要点
部署后建议测试以下场景:
- 基础封禁:后台编辑用户 → 选择禁言 7 天 → 提交 → 用户登录前台发帖被拒
- 场景隔离:禁言用户可登录浏览,但发帖/回帖/编辑被拒;禁止访问用户登录被拒
- 自动解封:设置封禁时长 1 分钟 → 等待过期 → 再次访问 → 自动解封
- 永久封禁:duration=0 → banned_until=9999999999 → 永不解封
- 管理员豁免:尝试封禁 gid=1,2 用户 → 拒绝;管理员自己访问被封 IP 不受影响
- 不能封禁自己:管理员尝试封禁自己 → 拒绝
- 版主权限:版主尝试永久封禁 → 拒绝;尝试禁言 8 天 → 拒绝;尝试禁止访问 → 拒绝
- 内容隐藏:被封用户发的帖子,普通用户看到占位提示,管理员看到原始内容
- 解封恢复:解封后帖子内容自动恢复显示
- 清空内容:清空操作 → 该用户主页显示「已被管理员清空内容」
- IP 黑名单:添加 IP → 用该 IP 访问 → 登录/注册/发帖三个入口全部被拒
- 封禁公示页 :开启
ban_show_public_list→ 访问/banned看到双栏列表 - 封禁历史 :多次封禁解封后,后台
user-ban_log-{uid}.htm显示完整历史 - xnx_report 联动:举报 → 选择「封禁用户」→ 用户被禁言 7 天 + 通知
- 生产环境 :DEBUG=0 模式下访问各入口,确认
UserBanService类加载正常(无 Class not found)