用户封禁系统(核心实现)
数据层与核心服务
user 表新增 ban_type/ban_reason/ban_admin_uid/ban_time 4 字段,复用 banned_until 表示封禁截止时间(0=未封禁/9999999999=永久) [配置不同步]
新增 bbs_user_ban_log 表(封禁历史:uid/admin_uid/action/ban_type/reason/duration/create_time) [缓存失效]
新增 bbs_banned_ip 表(IP 段黑名单:ip_start/ip_end/reason/admin_uid/create_time/expire_time) [边界未校验]
UpgradeService::upgradeUserBanSystem() 升级项:4 字段 ALTER + 2 表 CREATE + 旧数据 ban_type=0 初始化 [状态不同步]
conf.default.php 新增 ban_show_public_list/ban_inherit_to_same_ip 配置 [配置不同步]
创建 model/ban_log.func.php(6 函数:ban_log_create/find_by_uid/count_by_uid/find_all/delete_by_uid/find_recent_unbanned) [SQL N+1]
创建 model/banned_ip.func.php(6 函数:banned_ip_create/delete/find/count/check/read,IP 用 ip2long+sprintf 处理 32 位溢出) [类型误用]
创建 lib/XnEvent.php 轻量事件机制(on/once/trigger/off/hasListeners,回调异常不中断主流程) [异步竞态]
XnEvent 已同步到 xiunophp.php 和 xiunophp.min.php,check_min_sync.sh 通过 [状态不同步]
创建 lib/UserBanService.php 核心服务(ban/unban/checkBan/checkBanByScene/clearContent/getBanStatus + 6 事件触发) [逻辑错误]
UserBanService 不能封禁管理员组(gid=1,2)/不能封禁自己,永久封禁用 9999999999 避免 32 位溢出 [边界未校验]
通知用 notify_create(uid, 0, 'system', ...) 而非 notice_send(Xiuno 实际函数名) [配置不同步]
后台 UI
admin/route/user.php 新增 ban/unban/clear_content/ban_log 4 个 action(POST 在 header include 之前) [状态不同步]
user_list.htm 新增状态列徽章/按 ban_type 筛选/批量封禁解禁 Modal/行内一键禁言7天按钮 [配置不同步]
user_update.htm 新增封禁设置区(类型/时长/原因,已封禁可修改)+ 解封按钮 + 清空内容按钮 + 封禁历史链接 [语言包不同步]
新建 admin/view/htm/user_ban_log.htm 封禁历史页(时间/操作/类型/时长/原因/管理员) [配置不同步]
创建 admin/route/banned_ip.php + banned_ip_list.htm IP 黑名单管理后台 [边界未校验]
admin/menu.conf.php + sidebar.inc.htm 新增 IP 黑名单菜单入口 [配置不同步]
IP 输入用 filter_var(FILTER_VALIDATE_IP) 校验,param 第3参数 FALSE 关闭 htmlspecialchars [类型误用]
核心检查点
index.inc.php 全局封禁检查:禁止访问/锁定用户跳转封禁提示页(admin 豁免用 SCRIPT_NAME/管理员 gid=1,2 豁免/AJAX 返回 JSON) [HTMX事件冲突]
创建 view/htm/banned_notice.htm 封禁提示页(原因/解封时间/倒计时/申诉/退出登录) [配置不同步]
route/thread.php + route/post.php create/update 发帖入口检查 ban_type>0(管理员豁免+hook 点预留) [边界未校验]
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) [状态不同步]
前台内容显示
model/misc.func.php 新增 user_ban_badge_html()/user_ban_hidden_notice_html() 复用函数 [配置不同步]
post_list.inc.htm/thread_main.inc.htm/thread_list.inc.htm 被封用户内容隐藏+状态徽章(管理员可见原始内容/作者本人可见) [变量覆盖]
thread_list.inc.htm 帖子标题显示 [已隐藏] 标记 [语言包不同步]
user_info_card.inc.htm 用户主页状态徽章+本人显示完整封禁原因+清空内容提示 [配置不同步]
封禁公示页
创建 route/banned.php + view/htm/banned.htm 双栏布局(当前封禁/近期释放30天) [配置不同步]
受 ban_show_public_list 配置控制,关闭返回 404 [边界未校验]
触发 XnEvent::trigger('UserBanService.bannedListDisplay') + 预留 banned_list_display hook 点 [异步竞态]
版主封禁入口
route/mod.php 新增 ban_user action(校验 allowbanuser 权限+三重校验:版主仅禁言1-7天/不能永久/不能禁止访问) [逻辑错误]
thread_main.inc.htm + post_list.inc.htm 作者卡片新增封禁按钮(版主/管理员可见) [配置不同步]
封禁弹窗 Modal(管理员全选项/版主限禁言+1-7天) [状态不同步]
model/route.func.php 新增 mod_ban_user 路由 + mod_ban_user_url() 助手函数 [配置不同步]
xnx_report 举报系统接入 UserBanService
改造 ReportService::handle_report() 的 ban / delete_ban 分支:由旧的 user_update(gid=0) 永久封禁改为调用 UserBanService::ban() 默认禁言7天(BAN_TYPE_SILENCE, duration=604800),封禁原因取自举报内容(reason_type + reason_text) [状态不同步]
改造 ReportService::batch_handle() 的 ban / delete_ban 分支同步接入 UserBanService [状态不同步]
ban 单独操作失败时(如被举报人为管理员组)回滚举报状态为待处理,避免误标记已处理 [逻辑错误]
delete_ban 操作中封禁失败时内容已删不可恢复,保持举报状态已处理并提示管理员「内容已删除,但封禁失败:xxx」 [逻辑错误]
新增 ReportService::execute_ban() 私有方法封装 UserBanService 调用(include_once + 构造原因 + 调用),handle_report 与 batch_handle 复用 [配置不同步]
新增 ReportService::build_ban_reason() 根据举报记录构造封禁原因,截断 200 字符 [边界未校验]
复用现有「封禁用户」按钮和「删除并封禁」按钮 UI / JS / CSRF 流程,不新增重复按钮,避免管理员困惑





