• one
    主题参与者摘要 · 增强
    ID topic_participant_summary_plus版本 1.6.1插件制作者 one免费325 行 / 22.3 KBHook / 后台页
    在主题详情页以头像墙展示本贴参与者,直观呈现每位成员的发言次数与参与顺序,并可一键跳转到其最近楼层。后台可设置头像墙人数上限、参与者排序(最先参与或最近参与优先)以及头像尺寸档位,自适应不同规模的讨论。
    app/plugins/topic_participant_summary_plus/plugin.php
    开发日志已有 7 条
    协作者
    • one
      one
      版本 1.6.1 2553d17cf5a7 更新:### Fixed - 后台保存配置时对「头像墙最多显示人数」增加服务端上限钳制(≤500,与表单上限一致),修复伪造 POST 可写入超限值的安全隐患。 - 前端重建头像 `<img>` 时补回 `loading="lazy"` 与 `decoding="async"`,与服务端 `avatar_tag()` 的懒加载行为一致,外域头像不再阻塞首屏渲染。 ### Changed - 头像底色 `release
    • one
      one
      版本 1.6.0 9cf797d79697 更新:### Changed - 渲染注入机制从红区钩子 `topic.after_render`(配合 `topic.after_view` 预热)切换为核心专用非红区钩子 `topic.content_after`(仅主题详情页主楼触发),渲染位置不变;回调遵循管道追加语义(拼接 `$value` 返回),与同钩子的其他插件(如 topic_manage)共存不互相覆盖。 - 前端挂载逻辑改为锚定插
    • one
      one
      版本 1.5.1 a496fa92a12a 更新:1、细节优化 2、再次按 .ai-rules.md AI开发规则对代码做最严格的全量审查,并做修复。@350 再次帮检查
    • one
      one
      版本 1.5.0 1ca3ea5fc557 更新:感谢 @350,现修复以下内容: # 排序 bug(真 bug) - plugin.php:89-93 的 usort 从字符串比较 strcmp($a['first_at'], $b['first_at']) 改为整数比较 created_at 是 uint 整数时间戳,之前转字符串后逐位比较,遇到不相等位数(尤其极早期 9 位时间戳或空值)会错排。现在强制 (int) 比较,稳健。 # 渲染钩
    • one
      one
      版本 1.3.9 237559ea0dc5 更新:细节调整:大头像时参与者头像之间更宽松,小头像时更紧凑,视觉比例协调。 漂亮,这效果把自己都美哭了
    • one
      one
      版本 1.3.8 7e3c479e7539 更新:- 每个参与者卡片固定宽度,头像和用户名都垂直居中对齐 - 所有头像顶部对齐、底部对齐 - 用户名过长自动截断显示省略号(text-overflow:ellipsis) - 三档尺寸各自有合适的列宽
    • one
      one
      版本 1.3.6 a1a792dd4b8c 更新:原始作者:307 原贴地址:https://bbs1.org/topic/428 **一、安全问题修复** 1. CSRF 防护:保存设置前校验 hash_equals(csrf_token(), $_POST['_csrf']),杜绝跨站请求伪造 2. 参数边界校验:max_participants≥0、size_lg_max 1100、size_sm_min 2200,非法值不落库 3. 数值
    主楼
  • 质量报告

    插件质量报告

    插件:topic_participant_summary_plus

    Hook功能范围循环使用频率文件读写修改系统表读写系统表读写自己的表
    topic.content_after非系统hook否1--app_replies, app_topics, app_users-

    颜色说明:黄色表示有数据读写,红色表示 Hook 在系统循环中执行,浅蓝色表示有文件写入。“列表跳过”表示回调在列表路径会提前返回,“回调内”表示操作位于回调自身或其帮助函数的循环体内。“文件批处理”表示逐个处理用户一次提交的文件,属于必要操作,不标记修正。只有操作实际处于需要优化的循环路径时,才会标记“修正❗️”。

    数据字典

    未检测到插件数据表声明。

    #1
  • one

    版本 1.3.6 更新:
    原始作者:307
    原贴地址:https://bbs1.org/topic/428

    一、安全问题修复

    1. CSRF 防护:保存设置前校验 hash_equals(csrf_token(), $_POST['_csrf']),杜绝跨站请求伪造
    2. 参数边界校验:max_participants≥0、size_lg_max 1100、size_sm_min 2200,非法值不落库
    3. 数值关联约束:保证大尺寸上限 < 小尺寸下限,保存时自动调整为"下限 − 1"
    4. 展示顺序白名单:order 非 latest 一律回落为 earliest,拒绝非法值注入
    5. 匿名身份保护:匿名主楼/回帖不计入摘要、不渲染,避免暴露作者
    6. XSS 防护:JSON 输出 JSON_HEX_TAG/APOS/QUOT 转义,DOM 文本用 textContent,链接用 href 赋值
    7. 最小权限:无 exec 等危险函数,不写文件,仅声明 1 个钩子 + 后台管理页

    二、性能问题修复

    1. 消除 N+1:无循环内查询,全部改为两次独立 SQL + 内存聚合
    2. 单次全量汇总:UNION ALL 合并主楼 + 全部回帖,一次分组统计全贴参与者
    3. 配置请求级缓存:static $cfg 缓存,单请求只解析一次配置
    4. 聚合结果缓存:按 topic_id:max:order 静态缓存,避免重复渲染重复查询
    5. 服务端零 DOM 拼接:只输出 JSON 数据,DOM 由前端 JS 构建
    6. 防重复渲染:页面已有摘要时跳过,仅渲染一次

    三、新增功能

    1. 后台设置面板:新增管理页,可配置头像墙人数上限、展示顺序、尺寸阈值
    2. 头像三档自适应:按展示人数自动切换 48px / 36px / 24px,含移动端适配
    3. 发言次数徽标:头像右下角显示该用户发言次数
    4. 楼层直达链接:点击头像跳转到该用户最近回复楼层(主楼作者显示"主贴")
    5. 悬停提示:显示"用户名 · 发言 N 次 · 楼层"
    6. 截断计数文案:"显示前 N 位,共 M 位"
    7. 头像兜底:图片加载失败自动替换为首字母占位
    8. 无侵入渲染:仅主题详情页渲染,列表页不显示,不存储用户数据

    01.png

    最后由 one 编辑于 2026-08-17 20:13
    #2
  • bbs1org

    头像排列可以优化一下,改成纵向也对齐

    #3
  • one

    更新了,再试一下

    #5
  • one

    版本 1.3.8 更新:

    • 每个参与者卡片固定宽度,头像和用户名都垂直居中对齐
    • 所有头像顶部对齐、底部对齐
    • 用户名过长自动截断显示省略号(text-overflow:ellipsis)
    • 三档尺寸各自有合适的列宽
    #4
  • one

    版本 1.3.9 更新:
    细节调整:大头像时参与者头像之间更宽松,小头像时更紧凑,视觉比例协调。
    漂亮,这效果把自己都美哭了

    #6
  • bbs1org

    很不错

    #7
  • one

    感谢之前作者307和你的付出。我修改捡漏的。

    还有,你那个积分规则,我一直用不成功。下午又全新重装了论坛测试,安装积分规则后,就出错:
    自动停用原因
    2026-08-17 22:44:27 PDOException: SQLSTATE[HY000]: General error: 1 cannot start a transaction within a transaction (index.php:316)

    #8
  • bbs1org

    我后续看看。你让ai分析一下也可以

    #9
  • one

    一分析就想加功能,我这是有病🤣

    #10
  • bbs1org

    那就自己写个更完美的

    #11
  • one

    没太理解原理。我研究下

    #12
  • 350

    🔍 插件审查报告(对照《AI 开发规则》与开发者文档)

    🔒 安全问题

    未发现明显问题。JSON 载荷用 JSON_HEX_TAG/APOS/QUOT 转义后放入 <script type="application/json">,头像由核心 avatar_tag()(内部已 h 转义)生成,姓名用 textContent 输出;匿名主楼被排除。

    ⚡ 性能问题

    1. topic.after_render 属红区钩子,但 topic_participant_summary_plus_after_render(plugin.php:114)直接调用 ..._participants(),内部执行两次 q()(plugin.php:47-58、61-64)查库。虽靠静态缓存和“仅主楼”判断把开销限制在每主题页 2 次,仍违反“渲染循环零数据库读取”。建议把聚合查询移到 topic.after_view 预加载,渲染钩子只读内存。

    🐛 功能/规范缺陷

    1. 参与者按 strcmp($a['first_at'], $b['first_at'])(plugin.php:89-93)排序,但 first_at 是整数时间戳(plugin.php:83 被转成字符串),不等长数字会排错。请保留 int 并用 <=> 比较。
    2. CSS 中 border-radius:4px(plugin.php:152 多处)写死,建议改用 var(--radius) 派生值。

    ✅ 修复优先级

    先改红区 DB 读为预加载 → 修正时间戳比较 → CSS 圆角变量。

    #13
  • one

    版本 1.5.0 更新:
    感谢 @350,现修复以下内容:

    排序 bug(真 bug)

    • plugin.php:89-93 的 usort 从字符串比较 strcmp($a['first_at'], $b['first_at']) 改为整数比较

    created_at 是 uint 整数时间戳,之前转字符串后逐位比较,遇到不相等位数(尤其极早期 9 位时间戳或空值)会错排。现在强制 (int) 比较,稳健。

    渲染钩子零查库(规范层)

    • 新增 topic_participant_summary_plus_after_view($ctx):在 topic.after_view 钩子触发后(主题数据加载完、渲染前),提前调用 participants() 并写入 static $cache。
    • return 数组注册 'topic.after_view' => '...after_view'。
    • 渲染钩子 after_render 调用 participants() 时直接命中静态缓存,渲染阶段零数据库读取。

    #其他安全性和细节的修复。

    #14
  • 350

    🔍 复核报告(v1.5.0,回应 1.3.9 版审查)· @one

    ✅ 已确认修复

    1. 排序 bug:usort 已从 strcmp 改为 (int) 整数比较 + id 决胜(plugin.php:91-93),修复正确。
    2. 后台 lg≥sm 改为显式报错不落库、CSRF hash_equals 校验——均正确。

    ❌ 红区修复未生效(关键,需再改一行)

    新增的 topic.after_view 预热回调是单参签名 (?array $ctx),但核心 hook()/fire() 对回调固定传两参 ($value=null, $ctx)(index.php:697)——第一个参数 null 会绑定到 $ctx,真正的上下文数组被丢弃,函数在 is_array(null) 处直接 return,预热从未执行。结果 topic.after_render 首次渲染时仍会在渲染链内执行 2 条 DB 查询(participants() 两处 q()),红区违规与原状相同。

    一行修复(规范②:fire 回调必须两参):
    function topic_participant_summary_plus_after_view($value, array $ctx): void

    💡 顺带优化

    预热的楼层计算会全量拉取该主题所有回帖(仅用于算每人最后楼层),万楼主题下每页视图 O(N) 行读取;可只 SELECT user_id,或复用 ctx['replies'] 与 reply_count 推导。

    其余(JSON_HEX 转义防 XSS、命名前缀、assets、description)均合规。签名改完这个插件就干净了。

    #15
  • one

    版本 1.5.1 更新:
    1、细节优化
    2、再次按 .ai-rules.md AI开发规则对代码做最严格的全量审查,并做修复。@350 再次帮检查

    #16
  • 350

    🔍 复核报告(v1.5.1,回应 1.5.0 版审查)· @one

    ✅ 已确认修复

    1. 排序 bug:usort 已改为 (int) 整数比较 + id 决胜(plugin.php:87-88),latest 倒序正确——修复正确。
    2. lg≥sm 后台显式报错不落库(plugin.php:276-278)、CSRF hash_equals 校验(plugin.php:268)——均正确。
    3. 红区关键项:topic.after_view 回调已改为两参签名(plugin.php:102),核心 fire('topic.after_view')(index.php:2951)把上下文正确传入 $ctx,预热 participants()(plugin.php:109)在 topic.after_render(plugin.php:112)之前填充静态缓存,渲染链零 DB 读——修复生效。

    ❌ 未生效 / 新问题

    未发现明显问题。

    💡 顺带建议

    聚合查询改用窗口函数 ROW_NUMBER() OVER (...)(plugin.php:56),MySQL 8.0 / MariaDB 10.2 以下不支持;规则要求兼容 SQLite/MySQL/PostgreSQL,建议确认目标库版本或改用兼容写法。

    这个插件现在干净了。

    #17
  • 399

    🔍 插件审查报告(AI 自动审查,对照《AI 开发规则》与开发者文档;本插件未被 8/18 楼上 350 的报告覆盖,属独立首次审查)

    插件:主题参与者摘要 · 增强(topic_participant_summary_plus)v1.5.1

    结论:已完成命名规范、生命周期(install/uninstall 幂等)、数据库跨库兼容、红区 Hook 零 DB 读、Hook 真实性(含核心与跨插件依赖核对)、SQL 注入风险、CSRF/权限校验、CSS 变量规范等项审查,未发现安全、性能或功能性问题。

    #18
  • one

    版本 1.6.0 更新:

    Changed

    • 渲染注入机制从红区钩子 topic.after_render(配合 topic.after_view 预热)切换为核心专用非红区钩子 topic.content_after(仅主题详情页主楼触发),渲染位置不变;回调遵循管道追加语义(拼接 $value 返回),与同钩子的其他插件(如 topic_manage)共存不互相覆盖。
    • 前端挂载逻辑改为锚定插件自身的 payload <script>(服务端已注入到主楼 post-content 末尾)相邻插入,不再依赖核心内部 DOM 结构选择器(ul.post-list.topic-post-list、li.post-item.post-entry),符合核心 data-slot 定位规范。
    • CSS 圆角规范修正:面板圆角由不存在的 var(--radius-md,8px)(fallback 恒生效,等同写死 8px)改为 var(--radius);设置页提示 border-radius:4px 改为 var(--radius-sm);清理已定义变量的冗余 fallback。

    Fixed

    • 修复性能缺陷:原 topic.after_view 预热在主题页每个分页(p≥2)都会执行一次全帖聚合 SQL,但主楼仅在 p=1 渲染、摘要也仅在 p=1 显示,第 2 页起查询全部浪费;切换钩子后聚合查询仅在真正渲染摘要时执行一次。

    Removed

    • 删除无引用的死代码:CSS 规则 -title-placement、-order-row(非 settings- 版)、-order-label,移动端媒体查询中与基础规则重复的 size-sm 头像尺寸;JS 辅助函数 mainPost() 及对应的列表查询、fallback 插入分支。
    • 删除无理由的 !important(实测核心 CSS 无任何 label 元素选择器,不存在需要对抗的冲突)。
    #19
  • one

    版本 1.6.1 更新:

    Fixed

    • 后台保存配置时对「头像墙最多显示人数」增加服务端上限钳制(≤500,与表单上限一致),修复伪造 POST 可写入超限值的安全隐患。
    • 前端重建头像 <img> 时补回 loading="lazy" 与 decoding="async",与服务端 avatar_tag() 的懒加载行为一致,外域头像不再阻塞首屏渲染。

    Changed

    • 头像底色 color-mix() 前置 var(--brand-soft) 回退声明,不支持 color-mix 的旧浏览器不再出现透明底色。
    • 前端尺寸档位判定(sizeClass)的异常回退默认值与 PHP 归一化口径对齐(lg=8、sm=21),保证三档不退化。
    • 合并前端三处首字母回退元素的重复构造为局部辅助函数,消除重复代码。
    #20

发表回复

登录后回复