2026-07-12 更新日志
[XSS风险] 修复 xnx_fields 插件 hook 文件 json_encode 缺少 JSON_HEX_TAG 安全标志
文件: plugin/xnx_fields/hook/post_ref_thread_after.htm (window.FIELDS_LANG)
文件: plugin/xnx_fields/hook/thread_js.htm (window.XNX_FIELDS)
与同插件 admin 模板(json_encode + JSON_HEX_TAG|JSON_HEX_AMP|...)保持一致
必测: 发帖页自定义字段渲染 + 帖子详情页付费查看按钮
[变量未定义] 修复 admin_hidden.php 中 post[subject] 未定义键报错(3 处统一加 ?? 空合并)
必测: admin/?xnx_hidden-list.htm / admin/?xnx_hidden-logs.htm / admin/?xnx_hidden-purchases.htm
[逻辑错误] 调整 xnx_hidden admin tab 顺序:设置放第一个(原为最后)
同步修改: hidden_admin.htm + setting.htm
[CSRF防护缺失] 修复 xnx_fields 后台版块绑定/分类删除/字段删除 AJAX 请求未传 csrf_token 导致 CSRF 校验失败
根因: ajaxPost 函数中用普通 object 传参的 3 处调用(单行版块绑定/分类删除/字段删除)未注入 csrf_token,而 admin/index.inc.php 对所有 POST 请求做中央化 CSRF 校验
修复: 在 ajaxPost 函数中统一注入 csrf_token(从页面 form 的 csrf_token 字段获取),FormData 传参的调用已由表单 CsrfService::input() 包含无需额外处理
必测: admin/?xnx_fields-forum.htm (选择绑定分类) / admin/?xnx_fields-category.htm (删除分类) / admin/?xnx_fields-field-{id}.htm (删除字段)
回归: admin/?xnx_fields-category.htm (保存分类) / admin/?xnx_fields-field-{id}.htm (保存字段+拖拽排序)
[逻辑错误] 修复 xnx_tag 话题帖子列表未过滤已软删除帖子
根因: tag.php 用 db_find 直接查询 thread 表,绕过了核心 thread__find() 自动添加 is_deleted=0 的过滤逻辑;且 onThreadDelete 钩子只挂在物理删除(thread_delete),软删除(thread_soft_delete)不清理话题关联
修复: 查询条件加 is_deleted=>0,与核心 thread_find_by_tids 做法一致
必测: 软删除一个带话题的帖子后访问 topic-{tagid}.htm,确认该帖子不再出现在话题列表
[逻辑错误] 个人信息区块积分显示改为按配置类型显示
首页/我的页面:credits 替换为 golds,label 从后台 golds_name 获取
必测: 首页右侧栏登录用户卡片、我的页面用户卡片
xnx_invite 侧边栏「获得积分」按 inviter_credits_type 动态显示 label 和对应 reward 字段
必测: 邀请中心侧边栏(配置 inviter_credits_type=credits/golds 分别验证)
xnx_dice 侧边栏 label 三元判断补全 rmbs 分支(原 rmbs 回退显示「积分」)
必测: 摇骰子页(配置 credits_type=rmbs 验证 label 显示「人民币」)
xnx_invite setting.php creditsTypes 改用系统配置名称(credits_name/golds_name),未补 rmbs(后端日志统计/表结构不支持)
回归: 后台邀请插件设置页积分类型下拉显示名称
[GET表单路由丢失] 修复 xnx_lottery 后台筛选搜索 404(GET 表单提交覆盖路由 query,改为 JS 构建 URL 跳转)
必测: admin/?plugin-setting-xnx_lottery.htm 各 tab 的筛选搜索
[组件混用] xnx_lottery 后台 tab 结构重组:5 个平级 tab 改为 3 主 tab + 子 tab
主Tab1: 基本设置
主Tab2: 侧边栏抽奖管理(子tab: 奖品管理+抽奖记录)
主Tab3: 帖子管理(子tab: 抽奖活动+中奖记录)
必测: admin/?plugin-setting-xnx_lottery.htm#tab-sidebar / #tab-thread
回归: 各子 tab 切换、搜索后自动回到对应子 tab
[语言包不同步] 同步 zh-cn/zh-tw/en-us 三语言包 xnx_lottery_admin_tab_thread 文案
[组件混用] 统一全部插件翻页样式为全局右对齐样式(27个文件、37处翻页)
统一为: nav.m-2[aria-label=lang(page_navigation)] > ul.pagination.pagination-boxed.justify-content-end.flex-wrap
C类(手写翻页重构为pagination()函数): xnx_magic/history.php+magic_history.htm、xnx_report/setting.php+setting.htm
A-2类(裸echo/div补nav/ul包裹): xnx_hidden(3处)、xnx_duel(5文件)、xnx_attach_access、xnx_tag(2文件)
A-1类(仅改nav/ul class): xnx_lottery(3处)、xnx_appcenter、xnx_checkin(2文件)、xnx_medal(3文件)、xnx_verify(2文件)、xnx_dice、xnx_invite(2处)、xnx_ai_reply、xnx_friendlink(2处)、xnx_ad_selfbuy、xnx_oauth、xnx_tag/tags_all
修复 xnx_oauth 外层 d-flex.justify-content-center 与 ul 右对齐冲突(移除外层div)
修复 xnx_tag/tags_all 外层 div.mt-3 多余间距(移除外层div)
同步补充 page_navigation 语言键到 zh-cn/zh-tw/en-us/bbs_common.php(核心 pagination.inc.htm 也引用此键但原未定义)
必测: 各插件前台/后台翻页页面(xnx_magic/history-1、xnx_report/setting-list-pending-1、xnx_checkin/rank、xnx_medal/setting 等)
回归: 核心翻页 view/htm/pagination.inc.htm 未改动
[安全合规] 批量修复 27 个 xnx_ 插件安全合规问题(3 严重 + 10 中等 + 6 轻微)
严重: xnx_edgeone 敏感配置(secret_id/secret_key/auth_key) param() 未传 FALSE 关闭 htmlspecialchars,密钥特殊字符被转义导致 API 鉴权失败
严重: xnx_medal/uninstall.php 遗漏 DROP xnx_forum_medal 表,卸载后残留孤儿表
严重: xnx_hidden/uninstall.php 遗漏 DROP xnx_hidden_log 表 + setting_delete(xnx_hidden),卸载后残留审计表和配置
中等: xnx_checkin/setting.php+admin.php 补管理员权限校验(gid != 1 && gid != 2)
中等: xnx_status/route/admin_status.php 补管理员权限校验
中等: xnx_friendlink/setting.php 移除 html_entity_decode 反转义 param() 默认 htmlspecialchars(view 端已有 esc_html 二次转义,此层反转义多余且不规范)
中等: xnx_magic/magic_history.htm keyword/interpretation/lucky_tip 补 esc_html(管理员词库数据未转义存储,输出需转义防 XSS)
中等: xnx_attach_access/AttachAccessService.php purchase 方法补 credits_type 白名单校验(credits/golds/rmbs)
中等: xnx_edgeone/setting.htm JS 中 zoneName/Target 拼接 innerHTML 补 eoEscapeHtml 转义防 XSS(数据来自腾讯云 API 返回)
中等: xnx_edgeone/hook/footer_end.htm+header_start.htm htmlspecialchars 替换为 esc_html/esc_attr
中等: xnx_edgeone/install.php + xnx_demo_data/install.php 末尾补清理 model.min.php 编译缓存
轻微: xnx_oauth/my_oauth.htm 4 处 htmlspecialchars 替换为 esc_html/esc_attr
轻微: xnx_hidden/hook/model_post_format_end.php 删除 DEBUG 诊断块(会向访客泄露 hidden_id/权限类型/购买数等内部信息)
轻微: xnx_maintenance/setting.php custom_css param() 传 FALSE 关闭 htmlspecialchars(CSS 中 > < & 不应被转义,会导致选择器失效)
轻微: xnx_duel/DuelService.php + xnx_tag/install.php 补「保留 db_sql_find」注释
必测: 后台各插件设置页保存功能、xnx_edgeone API 连接测试、xnx_magic 历史记录页、xnx_hidden 隐藏内容渲染
[状态不同步] 修复插件开发手册 docs/ 与当前源码不一致(40+ 处问题)
删除 07-example-nx_tag.md(整篇基于虚构 nx_tag,实际为 xnx_tag)
README.md: 去掉 07 导航,nx_tag→xnx_tag 命名前缀示例
01-architecture.md: 修正 6 处行号/依赖版本比较(实际有语义化版本)/install 流程补并发锁+扫描器/补 plugin_hook 运行时分发/upgrade 机制/protected_paths 白名单/模板安全检测/bbs_plugin.version 字段
02-plugin-structure.md: plugin_read_by_dir 行号/补 capabilities 字段/修正 hooks_rank 排序方向(降序值大先跑)/nx_tag→xnx_tag 真实文件清单/minimal 示例字段类型
03-hooks-catalog.md: 源码扫描重建 607 个 hook,删除虚构 header_theme_/body_end/theme_ 与 nxn_hook 引用,补录 model_route_table_end.php/post_list_inc_*(22个)/forum/sidebar/post/my/user_create 等遗漏 hook
04-api-cheatsheet.md: 补 CacheHelper 完整 API(remember/pluginKey/pluginDeletePrefix)/http_location 第二参数/forum_create/setting_delete/cache_truncate flushdb 警告/esc_js 实现/CSRF token 存 cookie 非 session
05-frontend-security.md: anime.js 从 CSS 移到 JS/bbs.js 路径修正/补 flash toast+cron_run/xiuno-modern.js 行数 679→816/CSRF 服务端读取来源澄清
06-ai-collaboration.md: 扫描器规则重写为 5 级(Fatal 11 条)/bbs_version 阈值 1.0.2 非 4.5 且为 error 非 fatal/补 protected_paths/plugin_hook 运行时分发/模板安全检测/upgrade.php 机制
[配置不同步] 实施插件版本号规范:version 三位制 + bbs_version 两位制
扫描器逻辑(lib/PluginScanner.php):bbs_version 必须两位制 X.Y 且 <= 核心主次版本(XIUNOX_VERSION 取前两段),version 必须三位制 X.Y.Z
扫描规则(lib/PluginScannerRules.php):新增 plugin_version_format(warning 级),更新 conf_version 描述
核心默认值(model/plugin.func.php:569-570):version 默认 1.0→1.0.0,bbs_version 默认 4.0→1.0
批量更新 30 个插件 conf.json:bbs_version 统一改 1.0(匹配当前核心 1.0.9),version 统一改三位制
文档更新:docs/02-plugin-structure.md(conf.json 字段表+示例),docs/06-ai-collaboration.md(扫描器规则)
必测: 后台插件列表扫描、新插件安装时扫描拦截、现有插件重新启用
回归: admin/?plugin-list.htm、admin/?plugin-read-*.htm
[逻辑错误] xnx_checkin 右侧栏「今日排名」原显示「1天」无意义,改为显示签到时间 HH:MM
必测: ?xnx_checkin(切到「今日排名」tab,确认显示时:分;切「连续/累计签到」仍显示 N 天)
数据来源: getTodayCheckinUsers 已返回 checkin_time 字段,前端未取用
根因: today 分支 map 时硬编码 days=1 未保留 checkin_time,渲染未按 tab 区分
[逻辑错误] 修复 xnx_tag 话题详情页帖子数统计未过滤未审核帖子
根因: total用dbcount(′xnxthreadtag′)仅数关联表,未JOINthread表过滤isdeleted和auditstatus;话题卡片显示的tag['threads'] 是 xnx_tag 表缓存计数,发帖 +1 时不考虑审核状态
修复: total改为JOINthread表实时计数,按权限过滤(管理员看所有/普通用户auditstatus=1OR自己的/游客仅auditstatus=1),与列表显示逻辑一致;tag['threads'] 覆盖为 $total(不写库),话题卡片显示数 + SEO description 同步生效
必测: topic-{tagid}.htm(管理员/普通用户/游客分别验证显示数与列表数一致;待审帖子作者能在话题页看到自己的帖子且总数包含)
[配置不同步] 修复 xnx_lottery/install.php 末尾缺少 model.min.php 缓存清理(违反插件 install.php 必须清理编译缓存硬约束)
根因: upgrade.php 有 plugin_clear_tmp_dir() 但 install.php 末尾完全没有清理逻辑,新装用户在生产环境 model.min.php 合并加载下 install 后未重新启用时旧缓存不含本插件 Service 类会白屏
修复: 末尾添加 @xn_unlink($conf[\x27tmp_path\x27].\x27model.min.php\x27) 与 xnx_duel/xnx_icon/xnx_status 三个插件保持一致
必测: 生产环境(model.min.php 存在时)全新安装 xnx_lottery 后访问帖子抽奖/侧边栏抽奖不白屏
回归: 已升级用户重新禁用启用插件、后台插件列表扫描
[配置不同步] 检查 4 个含 upgrade.php 的 xnx 插件 install/upgrade 一致性(xnx_duel/xnx_icon/xnx_status 一致,xnx_lottery 已修复)
[变量未定义] 修复 xnx_dice 投注提示余额不足(800金币投10金币被前端拦下)
根因: dice.js getBalance() 通过 getElementById("dice-balance") 获取余额元素,但 dice_index.htm 模板中无此 id 的元素(该 id 仅在投注成功后由后端 OOB swap 输出),首次进入页面时 balanceEl=null → getBalance() 返回 0 → betAmount(10) > 0 为 true → 前端拦下并 toast 余额不足
修复: dice_index.htm 投注档位信息行加 <span id="dice-balance"> 显示初始余额;后端 OOB swap 输出带 class=fw-bold text-body 保持替换后样式一致
必测: ?dice 页面投注(余额充足时不再误报余额不足;投注后余额实时刷新)
[组件混用] xnx_dice 骰子点数从 CSS radial-gradient 改为 Tabler Icons 图标
每个 dice-face 内加 <i class="ti ti-dice-N">(N=16),移除 face-16 的 background-image radial-gradient 点数绘制
CSS 改为 flex 居中 + font-size 控制图标大小(桌面 48px / 移动 36px),保留 3D 立方体翻滚动画
必测: ?dice 页面骰子显示图标 + 摇骰子翻滚动画 + 定格到结果面
[HTMX事件冲突] 修复 xnx_dice 骰子摇不完(结果已显示但骰子永不停止)
根因: htmx 4 移除了 evt.detail.target(改用 evt.detail.ctx),dice.js 4 个事件监听器(htmx:after:swap/response:error/after:request)都依赖 evt.detail.target 判断是否为 #dice-result,htmx 4 中该字段为 undefined → 每次都 return → stopRolling() 永远不被调用
修复: 移除对 evt.detail.target 的依赖,改为检查 state.isAnimating + DOM 中 #dice-result 是否有 data-dice1 属性;htmx:after:request 的 failed 检查增加 ctx.failed 回退
必测: ?dice 投注后骰子翻滚动画在 ~1.2s 后停止并定格到结果面
[组件混用] xnx_dice 骰子点数改回 radial-gradient(回退上一版 Tabler Icons 图标方案),同步优化 UI
恢复: 移除 <i class="ti ti-dice-N"> 标签,恢复 face-1~6 的 background-image radial-gradient 点数绘制
UI 优化: 骰子面圆角 12→14px、内阴影增强、点数 10→11px、定格过渡用弹性曲线 cubic-bezier(0.34,1.56,0.64,1)、perspective 1000→1200 视角上移
UI 优化: 押大小按钮加 py-2 增大触控区、投注信息条用 bg-body-tertiary 包裹分列布局、档位按钮选中态抬升+阴影、摇骰子按钮 hover 抬升+阴影、历史记录项圆角
必测: ?dice 页面骰子点数显示 + 翻滚动画 + 押大小按钮 + 档位按钮选中态
[新功能] xnx_dice 庄家操控:大额投注按概率强制让用户输(管理员豁免)
后台基本设置 Tab 加「庄家操控」区块:开关 + 阈值 + 概率(0-100%)
DiceService::shouldManipulate() 判断逻辑:开启 && 非管理员(gid=1,2) && 投注≥阈值 && random_int(1,100)≤概率
DiceService::generateLosingDice() 生成必输骰子组合:押大→小或豹子、押小→大或豹子、押单字N→不含N
play() 方法加 $gid 参数,route/dice.php 调用时传入
默认关闭(house_edge_enabled=0),需后台手动开启
必测: 后台设置页保存操控配置 + 普通用户大额投注输了 + 管理员大额投注不受影响
[新功能] xnx_dice 右侧栏加盈亏排行榜
DiceService::getTopPlayers(10) 聚合 SUM(profit) 降序,缓存 5 分钟,批量查用户名避免 N+1
dice_index.htm 右侧栏用户卡下方加排行榜卡,Top3 显示奖牌图标,盈亏正数绿色负数红色
必测: ?dice 右侧栏显示排行榜 + 投注后排行榜更新(缓存 5 分钟延迟)
[优化] xnx_dice 输赢反馈体验增强
中奖:骰子舞台绿色光晕脉冲 1.5s + 浮动 +N 数字飞出 + 余额高亮 + 移动端长振
输了:骰子舞台红色闪烁 0.8s + 浮动 -N 数字飞出 + 余额高亮 + 移动端短振
必测: ?dice 投注后观察舞台光效 + 浮动数字 + 余额变化动画
[逻辑错误] 优化 cache_debug 日志:只记录缓存未命中(MISS),不再记录 HIT/SET
根因: CacheHelper.php:366 DEBUG>1 时记录每次缓存 HIT/MISS/SET,首页一次请求产生几十条日志,cache_debug.php 一天 1.6MB/1073 行,99% 是无价值的 HIT/SET 噪声
修复: 增加 $type === 'miss' 条件,只记录 MISS(未命中需回源查询的事件),HIT/SET 是正常行为不记录
预期: 日志量从 1073 行/天 降到约 50 行/天(仅首次缓存未命中),减少 95%+
必测: 首页/话题页/帖子详情页访问后查看 log/202607/cache_debug.php,确认只有 MISS 无 HIT/SET
[UI优化] xnx_dice 结果区重构:从按钮下方 alert 改为骰子正下方大号结果组件
移动: #dice-result 从摇骰子按钮下方移到骰子舞台正下方,不再覆盖押注区域
重构: 后端返回的 alert-secondary 改为专属 .dice-outcome 组件(大号状态图标3rem + 大号盈亏数字2.2rem + 点数和 + 左边框颜色条 + 渐变背景)
视觉: 中奖绿色渐变+奖杯图标+加号金额、输了红色渐变+悲伤图标+减号金额、错误橙色边框
动画: 结果出现时弹性放大 cubic-bezier(0.34,1.56,0.64,1),移动端字号缩小
必测: ?dice 投注后骰子正下方弹出大号结果组件,押注区域不被覆盖
[信息泄露] 删除 message() 函数遗留的调试 error_log,清理 PHP errors.log 噪声
根因: model/misc.func.php:289-294 有一段调试代码,每次 message() 被调用都 error_log 一条请求头信息到 PHP 全局 errors.log。message() 是 Xiuno 提示消息函数(登录/发帖/回复/点赞等都调用),导致 ServBay 的 /Applications/ServBay/logs/php/8.5/errors.log 膨胀到 3.9MB,最近 100 行全是 "[message()] is_htmx= HX-Request=(none) X-Requested-With=XMLHttpRequest HX-Boosted=(none) code=0" 这种无调试价值的噪声,真正的 PHP 错误被淹没
修复: 删除 if(DEBUG){error_log(...)} 整段调试代码块
清缓存: tmp/model_misc.func.php(misc.func.php 通过 _include() 加载,有编译缓存)
必测: 任意触发 message() 的操作(登录/发帖/回复),确认 errors.log 不再新增 "[message()]" 行
[bug修复] xnx_dice 结果区显示太快:after:swap 时先隐藏 #dice-result(visibility:hidden),骰子停止后才显示
[bug修复] xnx_dice 今日盈亏显示为0:sidebar_stats value 传带+前缀字符串被 intval 变0,改为传纯数字;去掉无效的 #dice-stats/#dice-today-profit OOB(sidebar 用 data-key 非 id),改为 data-* 属性 + JS 更新 sidebar [data-key=today_count/today_profit]
[bug修复] xnx_dice 排行榜头像不显示:user_find_by_uids 返回的 avatar 是原始数字非 URL,改用 avatar_component($uid, sm) 生成头像
[优化] xnx_dice 排行榜加总投注次数显示 + 标题旁加「每5分钟更新」提示
[优化] xnx_dice 玩法说明去掉押单字相关内容(保留大/小/豹子),后台设置去掉 odds_number_1/2/3 赔率配置
必测: ?dice 投注后骰子先停再显示结果 + 今日盈亏正确更新 + 排行榜头像正常 + 玩法说明无押单字
必测: 后台插件设置页赔率只显示押大小一项
[bug修复] xnx_dice play action 中 $today_count 未定义错误:统计计算在 echo 之后,提前到 echo 之前
[bug修复] xnx_dice 今日盈亏显示:JS 更新用纯数字+class 控制颜色,与 sidebar 模板 intval 输出一致
