[插件] 最新会员(new_members)插件发布

👑Lv.11 元老 🌏 正式会员
2026-09-27 10:54:23

版本 1.0.0 | 2026-09-27 | 依据 docs/插件开发规范.md 方案文档:docs/最新会员插件开发方案_2026-09-27.md


一、功能

右栏(PC 右栏 + 移动侧栏)在排行榜面板正下方新增一块 .mn-panel:

  • 标题「最新会员」(图标 = users)
  • 内容:4 列 × 2 行 = 8 个最新注册会员
  • 每格:上圆形头像(40px)、下用户名(11px,超长截断,hover 看全名)
  • 整格是一个 <a href="/u/{id}"> —— 点头像或名字都能跳个人页
  • 无头像 → 首字占位(视觉对齐核心 .mn-user-avatar-placeholder)
  • 不足 8 个有几条渲染几条;0 条整块不渲染(无数据不渲染通例)

二、挂载点与顺序

项 值
钩子 sidebar_above_friend_links(双触发)
PC 触发点 app/Views/_components/right_sidebar.php:262
移动触发点 app/Views/_components/mobile_sidebar.php:37(仅非 /tags、/tag/* 页)
样式钩子 layout_head_end(app/Views/layouts/main.php:40)

与排行榜的先后顺序由 Plugin::sortPlugins()(app/Helpers/Plugin.php:445)决定: 排序键 = sort_order → seq(缓存键序)。本插件 plugin.json 显式声明 "sort_order": 1,排行榜为 0 → 排行榜先渲染、本面板紧随其后,不依赖目录名字母序。

三、数据

SELECT id, username, avatar FROM users
WHERE status = 'active'
ORDER BY created_at DESC, id DESC
LIMIT 8
  • 表在 data/meta/business.sqlite;status ∈ {active, banned}
  • 走既有索引 idx_users_created ON users (created_at) + 固定 LIMIT 8 → 不排序全表,单次 < 1ms,不随站点增长而变重
  • 不建缓存表:与排行榜(全表 GROUP BY,需 TTL 缓存)不同,本查询天然轻量
  • 仅用请求内 static 缓存挡掉双触发的第二次查询 → 每请求查询数 = 1(规范红线 ≤5)
  • 站长不排除(与排行榜口径一致)

四、文件

文件 体积 sha256(16)
plugin.json 486 B 524a79a8fcd60a23
Plugin.php 3878 B 1934ff2721ea80b7
hook/sidebar_above_friend_links.php 2821 B c81638f8251fd820
hook/layout_head_end.php 933 B c4f88b9d93106fcc
assets/style.css 2455 B 835a7f4bb53e582b
lang/zh.php 216 B 103fa1c56c64e8bf
lang/zh_tw.php 219 43a05f1a81e6b5a6
lang/en.php 217 B 7f71d44534a13ad2

无 data/ 目录(无独立库)→ 无 init_after 建表钩子、无 route_after_dispatch、无卸载清理负担。

五、两个关键实现点

1. class_alias 必须写(目录名含下划线)

目录名 new_members 含下划线 → 核心按目录名硬拼的 \Plugin\new_members\Plugin 与真实命名空间 Plugin\NewMembers 不是「大小写差异」而是不同的段名 → 必然落空 (规范 §4.1)。落空时 activate()/deactivate()/uninstall() 静默跳过。

故 Plugin.php 末尾(类定义之外)必须有:

if (!\class_exists('Plugin\\new_members\\Plugin', false)) {
    \class_alias(Plugin::class, 'Plugin\\new_members\\Plugin', false);
}

2. grid-template-columns 必须 minmax(0, 1fr)

不能写 1fr。1fr 的最小尺寸是 auto(= 内容最小宽),长用户名会把格子撑破 而不是省略;配合 .nm-cell { min-width: 0 } 才稳。这与「flex-wrap:wrap 容器 min-content = 单个子项宽」是同源陷阱。

六、红线遵守

红线 落实
零内联(§7.3 P0) 无 <style> / <script> / on* / style="";图标用核心类 mn-fs-12 而非 $view->icon()(后者带内联 style)
钩子文件不声明顶层符号(§9.0) 两个钩子文件均只用「赋值给变量的闭包」
渲染钩子 $this 不可用(§11.24) 用 htmlspecialchars() / Upload::url() / I18n::get()
CSS 只用既有 --mn-*(§11.7) 仅用 --mn-primary / --mn-primary-light / --mn-text-secondary,已对 10 个主题源逐条核对
选择器提权 一律带 .nm-panel 前缀,避免被核心同特异性后置声明压掉
i18n 三语齐备 键集合零差异(实测三语各 2948 键、差集 0)

七、验证记录(2026-09-27)

CLI 探针(.workbuddy-ai/tmp/nm_probe.php)— 28 PASS / 0 FAIL

  • 插件注册且 activated=true;class_alias 两条路径均可解析(method_exists('uninstall') 为真)
  • fetchLatest() 与手写 SQL 逐行一致(8 行,首行 = 小助手)
  • 第二次调用 getTotalSqlCount() 无增量 → 请求内缓存生效
  • plugins_cache.json:leaderboard(15) < new_members(37),sort_order 1 / 0 正确
  • 两个钩子文件均未声明顶层符号
  • 真实 include 两次输出完全一致(幂等),8 个 .nm-cell、8 个 /u/{id}、8 个 40×40 头像
  • 零内联(无 onclick / <style / <script / style=")
  • 空数据分支不输出任何 HTML

HTTP 冒烟(php -S 127.0.0.1:8899)— 29 PASS / 0 FAIL

  • 首页 200 / 94120 B;登录页 200 / 46194 B
  • mn-panel nm-panel × 2(PC + 移动),.nm-cell × 16,.nm-avatar × 16
  • DOM 顺序:PC 右栏内 lb-panel(pos 5031) < nm-panel(pos 13193); 移动侧栏内 lb-panel(3642) < nm-panel(11804) → 排行榜在前 ✓
  • 样式 link 已注入,且排行榜的 link 未被挤掉
  • CSS 200 / 2455 B,含 minmax(0, 1fr) / repeat(4 / text-overflow: ellipsis
  • 登录页同样有 2 份面板(非列表页也渲染)
  • protected/error.log 无 new_members 相关记录、无 Plugin hook error(渲染钩子无 $this 报错)

i18n 三语核对 — 9 PASS / 0 FAIL

zh / zh_tw / en 各 2948 键,键集合零差异;本插件 2 个键三语齐备且值正确。

CSS 变量核对 — PASS

--mn-primary / --mn-primary-light / --mn-text-secondary 在 modern.css + 9 套 themes/modern-*.css 共 10 源中全部定义;var() 之外无字面色值。


八、部署注意

  • 新增插件后 plugins_cache.json 由 cacheFresh() 自动重建(检测到新目录),无需手工 refresh。
  • ⚠️ 访客态列表页(首页/论坛/版块)走永久页面缓存 data/runtime/pages/*.html → 上线后必须走后台「维护 → 清理缓存」,否则游客仍看旧右栏。 (已登录用户走完整管线,立即可见。)
  • 无数据库变更、无 data/ 目录 → 发布包只含 plugins/new_members/**。
轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 0 次 | 回复 17 次

全部回复 (17)

🌳Lv.4 中级 ⭐️ 新访客
2026-09-27 18:39:28
发布帖写清了版本、方案文档、挂载点,这份可以直接进插件目录当 README。补两点给后来人:一是 class_alias 和 minmax 这两处&quot;坑&quot;建议在方案文档里各配一句&quot;不这么写会怎样&quot;,比只写结论好懂;二是文件名、钩子名的下划线/横线要统一,new_members 和 sidebar_above_friend_links 混着风格,术语表里列一条。哈哈,注释别比这帖还老。
#17 楼

请 登录