xnx_related 相关帖子
在帖子详情页展示相关帖子推荐,支持标签匹配(软依赖 xnx_tag)与标题关键词降级匹配。纯读操作不建表,结果通过 CacheHelper 缓存。Bootstrap 5 + 原生 PHP,无 jQuery 依赖。
功能特性
- 双匹配策略:标签优先降级标题(默认)或仅标题关键词,可在后台切换
- 标签匹配:软依赖 xnx_tag,存在时通过
TagService::findByTid()取标签,再查xnx_thread_tag关联表找同标签帖 - 标题关键词降级:标签不足或无 xnx_tag 时,从标题提取关键词做
LIKE匹配,候选帖按命中数降序排序 - 三种展示位置:正文与评论区之间(全宽卡片)/ 右侧栏(紧凑卡片)/ 两者都显示
- 两种推荐范围:当前版块 / 全站
- 三种排序:发布时间(tid 倒序)/ 回复数 / 查看数
- 权限过滤:调用
thread_list_access_filter()按用户组过滤版块访问权限与待审/驳回帖 - 缓存隔离:缓存键含 gid,避免高权限用户缓存被低权限复用导致信息泄露
- 删帖级联:删帖后自动清全量推荐缓存
- 三语支持:zh_cn / zh_tw / en_us
安装说明
- 将插件目录放入
plugin/xnx_related/ - 进入后台「插件管理」找到「相关帖子」点击安装,自动初始化默认配置(不建表)
- 安装后默认启用,可在后台「插件设置」中调整
软依赖 xnx_tag
本插件对 xnx_tag 为软依赖(非强制):
conf.json的dependencies为空,安装时不强制要求 xnx_tag- 运行时通过
class_exists('TagService', false)检测,存在才启用标签匹配 - 未安装 xnx_tag 或帖子无标签时,自动降级到标题关键词匹配,功能不中断
- 标签匹配读取 xnx_tag 创建的
xnx_thread_tag关联表
使用指南
后台配置
访问 /admin/plugin-setting-xnx_related,仅管理员组(gid=1,2)可访问,所有 POST 操作经 CsrfService::check() 校验。
| 配置项 | 字段 | 取值范围 | 默认值 | 说明 | |---|---|---|---|---| | 启用相关帖子 | enabled | 0/1 | 1 | 全局开关 | | 推荐数量 | count | 1-20 | 6 | 最终展示条数 | | 展示位置 | position | 1/2/3 | 3 | 1=正文与评论区之间 2=右侧栏 3=两者都显示 | | 推荐范围 | scope | 1/2 | 2 | 1=当前版块 2=全站 | | 匹配方式 | match_method | 1/2 | 1 | 1=标签优先降级标题 2=仅标题关键词 | | 排序方式 | sort_order | 1/2/3 | 1 | 1=发布时间 2=回复数 3=查看数 | | 新窗口打开 | new_window | 0/1 | 1 | 帖子链接 target="_blank" | | 显示版块名称 | show_forum | 0/1 | 1 | 帖子项展示所属版块(可点击跳转) | | 显示回复/查看统计 | show_stats | 0/1 | 1 | 帖子项展示回复数 | | 缓存时间 | cache_ttl | 60-86400 秒 | 3600 | 推荐结果缓存存活秒数 |
保存设置后自动清旧缓存,新配置即时生效。
前台展示
通过两个 hook 注入帖子详情页:
thread_postlist_before.htm:位置一,正文与评论区之间(position=1或3时渲染),全宽卡片样式thread_user_after.htm:位置二,右侧栏末尾(position=2或3时渲染),紧凑卡片样式
卡片内每条帖子项包含:发帖人头像(调用 avatar_component_from_data,与头像组件兼容)、帖子标题(2 行截断)、用户名、版块名(可点击)、回复数。无推荐结果时不输出卡片。
技术细节
匹配算法
RelatedService::getRelated($tid, $fid, $gid) 主入口,按 match_method 分流:
1. 标签优先降级标题(match_method=1)
- 调
TagService::findByTid($tid)取当前帖标签 findByTags()按 tagIds 查xnx_thread_tag关联表(预取limit*5冗余),排除当前帖,scope=1时过滤非当前版块,按sort_order排序- 结果不足
count条时,降级到标题关键词匹配补足 - 合并去重:标签结果优先,标题结果按 tid 去重后追加
2. 仅标题关键词(match_method=2)
extractKeywords()从标题提取关键词(最多 3 个)
[\x{4e00}-\x{9fa5}]{2,} 匹配连续 2+ 汉字为一词 - 英文:正则 [a-zA-Z][a-zA-Z0-9]{2,} 匹配 3+ 字符英文词 - 过滤内置停用词表(中英文常见虚词)
findByKeyword()每个关键词做一次subject LIKE '%kw%'查询(无法走索引)- 候选数达到预取量时提前退出,多数情况只查 1-2 次
- 批量
user_preload()预加载用户数据消除 N+1 - 候选帖先按命中关键词数降序,再按
sort_order排序
> 朴素分词说明:extractKeywords 是正则分词,非真正中文分词(无词典无 HMM),帖子标题这类短文本足够用;长文本或专业领域需 jieba/scws。LIKE 匹配适合 BBS 中小流量场景,复杂场景需 MySQL 全文索引或 Elasticsearch。
预取与截断
预取量 preload = count * 3,预留冗余给后续权限过滤缩减,过滤后 array_slice 截断到 count。
缓存策略
- 使用 CacheHelper(存在时启用),缓存键注册:
thread_*=> 3600 - 缓存键格式
thread_{tid}_{gid},含 gid 做权限隔离 - TTL 取配置
cache_ttl(已 clamp 到 60-86400) - 清缓存时机:保存设置(
saveSettings)、删帖(onThreadDelete)、卸载(uninstall.php) - 删帖采用全量清前缀策略:删帖影响所有包含该帖的推荐列表,按 tid 精确清理成本高于全量清前缀,直接全量清最简
Hook 注入点
| Hook 文件 | 作用 | |---|---| | model_inc_file.php | 注册 model/RelatedService.php 到自动加载 | | lang_zh_cn_bbs.php / lang_zh_tw_bbs.php / lang_en_us_bbs.php | 三语语言包 | | thread_postlist_before.htm | 位置一渲染(正文与评论区之间) | | thread_user_after.htm | 位置二渲染(右侧栏) | | model_thread_delete_end.php | 删帖后清缓存 |
数据表
本插件为纯读操作插件,不创建任何数据表,仅写入 setting 表的 xnx_related 配置项。
标签匹配时读取 xnx_tag 插件创建的 xnx_thread_tag(标签-帖子关联表),无独立持久化存储。
兼容性
- Xiuno X 1.1+(
bbs_version1.1) - PHP 8.x(全局数组访问已加
isset兜底) - Bootstrap 5(卡片、栅格、表单组件)
- 无 jQuery 依赖
- 软依赖 xnx_tag:未安装时自动降级为标题关键词匹配
- 依赖 CacheHelper(存在时启用缓存,不存在时每次实时计算)
版本历史
- 1.0.0 (2026-07-18):初始版本。支持标签匹配(软依赖 xnx_tag)与标题关键词降级匹配;三种展示位置;当前版块/全站两种范围;发布时间/回复数/查看数三种排序;CacheHelper 缓存(gid 隔离防权限泄露);三语支持;删帖级联清缓存
版本 1.0.0 | 兼容 Xiuno 1.1+
版本:v1.0.0
适用版本:1.1
开发者:Twelve

