[开发]碎语时光(suiyu)· 插件说明

👑Lv.11 元老 🌞 骨干会员
2026-10-25 01:36:58

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/plugins/suiyu/说明.md。

碎语时光(suiyu)· 插件说明

短文字 + 图片动态流。全部自建,零插件依赖(不读不写任何其他插件的数据表,也不调用其他插件的类方法)。


一、它能干什么

功能说明
发布纯文字 / 文字 + 图片(最多 9 张,可在后台调);表情 shortcode(huaji 等)自动还原
流三个 tab:最新 / 热门(按点赞数,不加权)/ 关注
我的/suiyu/mine
个人页/suiyu/u/{id}(含关注按钮、关注数/粉丝数)
互动点赞(可取消)、评论(含楼中楼)、转发(可附一句,repost_of 指向最初原创,禁止套娃)
关注自建 sy_follows,不碰 user_profile 的 user_follows
搜索自建 LIKE 搜索(%/_ 已转义 + ESCAPE '\'),支持「只搜我的」;不接入核心全站搜索
审核敏感词库(拦截 / 替换 两种动作)
举报同一人同一帖只报一次(UNIQUE(post_id,user_id) + INSERT OR IGNORE);后台可「删碎语」或「忽略」
通知点赞 / 评论 / 关注 → 走核心 Notification(type = suiyu_like / suiyu_comment / suiyu_follow)
后台碎语列表 / 回收站 / 敏感词库 / 举报处理 / 设置

v1.0.0 明确不做:@提及(@用户名)、碎语进全站搜索、热门加权排序、核心个人主页上的关注按钮。


二、入口

端位置实现
PC左侧栏「应用」面板hook/nav_plugin_links.php
移动端顶栏「应用」下拉同一个钩子、同一份输出(双触发)
全站右栏「碎语广场」面板(最近 5 条)hook/right_sidebar_after.php

移动端不是第三个触发点:.mn-mobile-icon-apps 与 .mn-apps-dropdown 是同一份 DOM,

靠 @media (min-width:769px){ .mn-nav-right .mn-mobile-icon-apps{display:none} } 切换显隐。

⚠️ 右栏在 @media (max-width:1024px) 起整块 display:none → 面板只是辅助入口,

真正的导航入口是「应用」。


三、目录结构

plugins/suiyu/
├── plugin.json                   # name=suiyu · admin_url=/admin/suiyu
│                                 # permissions=["route:admin","system:settings"]  ← ★ 缺一不可
├── Plugin.php                    # 建表 + 全部业务静态方法(前台/后台/探针共用)
├── FrontController.php           # 前台 13 条路由
├── AdminController.php           # 后台 13 条路由(extends app\Controllers\Admin\BaseController)
├── hook/
│   ├── init_after.php            # 惰性建表兜底(门控 = md5(ddl()) **内容哈希**,不是「文件是否存在」)
│   ├── route_register.php        # 前台路由(**写全路径**)
│   ├── admin_route_register.php  # 后台路由(**不写 /admin**)
│   ├── layout_head_end.php       # 前台注入 style.css(全站)+ script.js(仅本插件页)
│   ├── nav_plugin_links.php      # ★ 唯一导航入口(双触发 · 禁顶层符号)
│   └── right_sidebar_after.php   # 右栏「碎语广场」面板
├── views/
│   ├── stream.php                # 流页(全站 / 我的 / 个人页 共用)
│   ├── _list.php                 # htmx 翻页片段(⚠️ 不带 /


四、数据表(6 张,全部在 plugins/suiyu/data/suiyu.sqlite)

表用途关键约束
sy_posts碎语status:0 正常 / 1 回收站;repost_of 指向最初原创
sy_comments评论reply_to 跨帖会被归零(防挂载到别的帖子下)
sy_likes点赞明细UNIQUE(post_id, user_id)
sy_follows关注UNIQUE(follower_id, followee_id)
sy_blocked_words敏感词word UNIQUE;action:block / replace
sy_reports举报UNIQUE(post_id, user_id) → 同一人同一帖只能报一次

计数一致性(红线)

like_count / comment_count / repost_count 与明细行永远在同一个事务里改;

取消点赞时先看 rowCount() > 0 才 -1,并统一用 MAX(x-1, 0) 兜底防负。

图片删除策略

操作图片文件
软删(前台删除 / 后台「删除」)保留(可恢复)
彻底删除(后台「彻底删除」)一并删除(Upload::delete())


五、路由

前台(route_register,写全路径)

方法路径响应
GET/suiyu页面(`?tab=new\
GET/suiyu/mine页面
GET/suiyu/u/{id}页面
GET/suiyu/post/{id}页面
GET/suiyu/search页面(?q=&mine=1)
GET/suiyu/listhtmx 片段(viewRaw)
POST/suiyu/postPRG
POST/suiyu/post/{id}/deletePRG
POST/suiyu/repostPRG
POST/suiyu/likeXHR → JSON
POST/suiyu/commentXHR → JSON(回包含渲染好的评论 HTML)
POST/suiyu/followXHR → JSON
POST/suiyu/reportXHR → JSON

后台(admin_route_register,不写 /admin)

/admin/suiyu · /admin/suiyu/trash · /admin/suiyu/words · /admin/suiyu/reports · /admin/suiyu/settings

(另有 delete/{id}、restore/{id}、purge/{id}、words/{add,edit,delete}、reports/{id}/handle)


六、后台设置项

设置键默认范围说明
suiyu_per_page151–50前台列表每页条数
suiyu_max_len30010–2000单条碎语 / 评论最大字数
suiyu_max_img90–9单条最多图片数(0 = 关闭图片)
suiyu_like_points00–100每被点赞一次作者获得的积分(0 = 不奖励)
suiyu_report_enabled10/1是否显示举报入口

⚠️ 保存走 Settings::update(),需要 plugin.json 声明 system:settings 权限。

缺声明时表现为「提示保存成功、回读 DB 值没变」(只写 error.log 后静默 return)。

⚠️ 首次启用后请到后台「维护 → 清理缓存」清一次设置缓存 —— settings_cache.php

不会自动补新键,旧站点读不到这 5 个新键(后台不显示、config.php 读不到)。


七、几个刻意的设计取舍

  1. nav_plugin_links 用字面量图标 <i class="fa mn-fs-16">&#xf075;</i>,不用 $view->icon()

—— 核心 TemplateCompiler::icon() 的返回值恒带 style="font-size:Npx" 内联样式,

违反插件零内联红线(plugin_hygiene.py 的口径是「任意 style 属性都算」)。

  1. 右栏面板「无数据不渲染」:一条碎语都没有时直接 return,不输出空占位卡。

想改成「空态也显示引导」,把 hook/right_sidebar_after.php 里的

if (empty($syItems)) { return; } 去掉即可(一行)。

  1. 后台表格列序刻意避开核心的按列序硬编码

(assets/css/admin.css:180 给 td:nth-child(4) 限宽省略号、:182-189 给 1/3/5/6/7 列加 nowrap)。

本插件列表统一用 ID | 内容 | 作者 | 互动 | 状态 | 时间 | 操作 —— 长文本落在第 2 列(不受限),

短文本落在受限列 → 零特异性战争、零 !important。

  1. 缩略图两档:thumb_(400×300,列表九宫格)+ md_(800×600,详情页)。

⚠️ 一律走 Upload::thumbUrl($rel, $prefix),禁止手拼 'thumb_' . $rel

—— makeThumbnail() 把前缀加在 basename 上,而 makeRelPath() 返回 ns/YYYY-MM/<hex>.ext

(namespace 是前导目录),手拼必 404。

  1. 图片灯箱靠 data-fullurl:assets/js/lightbox.js:45 只收集 [data-fullurl], .post-text-content img。

碎语图不在 .post-text-content 里 → 不写 data-fullurl 就点图毫无反应(完全静默)。

  1. 评论走 XHR 而非整页刷新:/suiyu/comment 返回 JSON,其中 html 字段是服务端渲染好的评论片段

(复用 views/_comment.php)→ 前端就地插入,楼层/回复关系与刷新后一致。

  1. 加载更多用 htmx(hx-get="/suiyu/list" + hx-swap="outerHTML"),

因此 views/_list.php 绝不能出现 <link> / <script>(否则每点一次 DOM 就多一个节点,视觉无感但会累积)。


八、卸载

后台「插件」页点卸载 → 调 Plugin::uninstall(),DROP 掉本插件独立库里的 6 张表,

不触碰核心库、不删除其他插件数据。上传的图片文件不会被自动删除(如需清理请手动处理 assets/uploads/)。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| Views 5 | Replies 0

All Replies (0)

No replies yet

Please Log in