• bbs1org
    新回复通知楼主
    ID reply_author_notice版本 1.1.2插件制作者 bbs1org免费131 行 / 6.7 KBHook
    回复未提及用户时,每个主题在未读期间仅通知楼主一次;浏览到对应楼层时自动清除通知并重置计数。
    app/plugins/reply_author_notice/plugin.php
    开发日志已有 2 条

    此项目接受所有登录用户协助维护和提交更新。

    • elm
      elm
      版本 1.1.2 3ba8c1167d0d 更新:浏览到对应楼层时自动清除通知提醒;修复已读后不再通知的问题。release
    • bbs1org
      bbs1org
      版本 1.1.1 770e994f1337 更新:旧格式迁移:已将插件代码转存到开发日志,主题正文改为基础介绍。
    主楼
  • 质量报告

    插件质量报告

    插件:reply_author_notice

    Hook功能范围循环使用频率文件读写修改系统表读写系统表读写自己的表
    topic.after_view主题查看页完成加载否4-app_notifications, app_usersapp_notifications, app_replies, app_users写 plugin_reply_author_notice_topics
    reply.after_save回帖保存后否3--app_topics写 plugin_reply_author_notice_topics
    user.profile_tab_header用户资料页标签内容顶部否3---写 plugin_reply_author_notice_topics
    topic.before_delete主题删除操作否2---写 plugin_reply_author_notice_topics

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

    数据字典

    plugin_reply_author_notice_topics

    字段类型可空默认值约束
    user_iduint否-主键
    topic_iduint否-主键
    updated_atuint否--
    #1
  • bbs1org

    回复未包含 @ 时自动通知主题楼主
    同一主题在楼主未读通知期间只通知一次
    楼主查看通知后自动恢复下一次提醒
    回复者是楼主本人时不发送通知
    含 @ 的回复保留原有提及通知流程

    #2
  • 350

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

    🔒 安全问题

    未发现明显问题。reply.after_save 回调为两参签名,写入在 tx() 内原子完成;create_notification() 不自行开启事务,无 SQLite 嵌套事务风险。

    ⚡ 性能问题

    未发现明显问题。仅在回帖保存后执行一次去重查询与通知写入,无循环逐条查询。

    🐛 功能/规范缺陷

    未发现明显问题。已按 MySQL 用 INSERT IGNORE、SQLite/PostgreSQL 用 ON CONFLICT 分别处理防重,去重键与表主键 (user_id,topic_id) 一致;topic.before_delete 清理逻辑正确。

    ✅ 修复优先级

    无需修复项。

    #3
  • 399

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

    插件:新回复通知楼主(reply_author_notice)v1.1.1

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

    #4
  • bbs1org

    旧格式迁移:已将插件代码转存到开发日志,主题正文改为基础介绍。

    #5
  • elm

    这个通知有个逻辑bug,一个帖子有回复了,我的通知里会有消息。但如果不管通知,直接点开去看了帖子中的新消息新回复,通知提醒并不会消失,还得打开我的通知,提醒才会消失。

    #6
  • bbs1org

    看主题是个高频操作,再去关联通知,效率太低了。自己DIY下吧

    #7
  • elm

    Atria的回复

    两个关键问题:

    1. 是不是只改 unread_topic_notice 插件就行?
    2. 修改前后系统负担会不会变大?

    问题 1:只改插件够吗?

    bug 在核心 index.php 的通知系统(mark_notifications_read 只在 notifications tab 调用,topic.after_view 不清通知)。

    你的思路是:只改 unread_topic_notice 插件,因为插件已经挂在 topic.after_view 上了(unread_topic_notice_after_view)。

    这是可行的!因为:

    • 插件已经有 topic.after_view hook 的注册
    • 插件可以在 after_view 里调用核心的 q() 发 UPDATE 清理 app_notifications
    • 不需要改核心 index.php

    但要确认:

    • 插件能否访问 app_notifications 表?(能,q() 是全局函数,插件里 q() 直接用)
    • 插件能否访问 app_users.unread_notifications?(能)
    • 插件能否访问 me() / $GLOBALS['__me_cache']?(能,都是全局的)

    所以是的,只改插件就够了——这是最干净的方案,完全不动核心代码(符合 AGENTS.md 的稳定模块保护原则)。

    但有一个细节:插件目前只在登录用户且 ctx['topic'] 有效时才处理(after_view 里 if (!uid() || !is_array($ctx['topic'] ?? null)) return;)。这正好符合需求。

    问题 2:系统负担会不会变大?

    让我仔细分析修改前后的负担:

    修改前(现状):

    • 看主题时:topic.after_view → unread_topic_notice_set_viewed() → 写一个 cookie(setcookie,不落库)
    • 通知清理:只在"我的通知"页(低频)

    修改后(方向1,在插件的 after_view 里加通知清理):

    • 看主题时:topic.after_view → 写 cookie(不变)+ 查一次 app_notifications 的 COUNT(带索引,微秒级)
    • 如果没有相关未读通知(99% 情况)→ 就一条 COUNT,然后返回
    • 如果有 → 再两条 UPDATE(标记已读 + 减计数)

    负担分析:

    • 每次看主题多一次 COUNT 查询(有索引的话是 log(n) 或常数级)
    • 只有真正有未读通知时才写(UPDATE)
    • 相比"看主题"本身的代价(查主题、查回复、渲染、写 cookie),多一条 COUNT 几乎可以忽略

    但要注意一个重要点:topic.after_view 是每次看主题都触发的(高频)。所以:

    • 加一条 COUNT 查询:这是每次看主题都多一次 DB 查询
    • 虽然单次很轻,但确实是"高频操作 × 每次多一次查询"

    你担心的正是这个。让我量化:

    • 假设站点每天 10000 次主题浏览(小站)
    • 每次多一条带索引的 COUNT(~0.1-0.5ms)
    • 每天多 1-5 秒 DB 时间,完全可忽略

    优化空间:

    • 可以短路:先检查 cookie 里的状态,如果用户最近根本没收到通知(unread_notifications=0),连 COUNT 都不用发
    • me()['unread_notifications'] 在内存缓存里(notifications_unread_total 优先读 me() 缓存),读它是零开销的
    • 如果 unread_notifications == 0 → 直接 return,连 COUNT 都不查
    • 这样:没有未读通知时,完全零额外 DB 开销(只多读一个内存变量)

    这是关键优化!让我确认 me() 的缓存机制——notifications_unread_total 第 1013 行确实优先读 me() 缓存:

    function notifications_unread_total(int $uid): int {
        $m = me();
        if ($m && (int)$m['id'] === $uid) return (int)($m['unread_notifications'] ?? 0);
        ...
    }

    所以 me()['unread_notifications'] 是内存缓存的,读它零开销。可以用来短路。

    结论:

    • 只改插件即可(不动核心)
    • 负载:无未读通知时零额外开销(读内存缓存短路);有未读时多 1 次 COUNT + 2 次 UPDATE
    • 比起"看主题"本身的代价,新增开销可忽略

    让我验证一下 me() 缓存的机制,确认 me()['unread_notifications'] 确实是缓存的(不是每次查库)。这决定了短路优化是否真的零开销。

    让我看 me() 函数。

    另外要确认:插件的 after_view 里能不能拿到当前用户的通知计数(me() 是全局函数,能用)。

    让我验证 me() 的实现。这是回答"负担会不会变大"的关键——如果 me() 是查库的,那短路就没意义;如果是缓存的,短路就是零开销。

    让我读 me() 函数。

    同时,我注意到一个更精细的点:插件的 after_view 已经在做 unread_topic_notice_set_viewed(写 cookie)。在这个函数里,我们已经知道用户登录了。所以加一段:

    function unread_topic_notice_after_view($value, array $ctx): void {
        if (!uid() || !is_array($ctx['topic'] ?? null)) return;
        $topic_id = (int)($ctx['topic']['id'] ?? 0);
        unread_topic_notice_set_viewed($topic_id, (int)($ctx['topic']['reply_count'] ?? 0));
        // 新增:清理该主题相关的未读通知
        unread_topic_notice_clear_topic_notifications($topic_id);
    }

    让我先验证 me() 缓存机制,再给出准确回答。

    关于回答的篇幅:德耶问两个具体问题,我应该简洁准确地回答,不要长篇大论。重点:

    1. 只改插件即可 + 为什么
    2. 负载:有短路优化,无未读时零开销

    让我验证 me()。德耶,这两个问题都很关键。让我先验证一个决定答案的点——me() 的未读计数是否内存缓存(这决定了能不能做零开销短路)。

    确认了:me() 用 $GLOBALS['__me_cache'] 缓存(第 1411 行),整个请求周期只查一次库。读 me()['unread_notifications'] 是纯内存操作,零 DB 开销。

    这就确认了短路优化可行。现在我能准确回答德耶的两个问题。

    让我整理回答:

    问题 1:只改 unread_topic_notice 够吗?
    够。因为:

    • 插件已经挂在 topic.after_view(看主题时必经)
    • 插件里能用全局的 q()、me()、now(),可以直接操作 app_notifications 和 app_users
    • 完全不动核心 index.php(符合稳定模块保护)

    问题 2:负担会变大吗?
    几乎不会,因为可以短路:

    • me()['unread_notifications'] 是内存缓存(me() 第 1411 行整个请求只查一次库)
    • 没有未读通知时 → 读一个内存变量就 return,零额外 DB 开销
    • 有未读时 → 1 次带索引 COUNT + 2 次 UPDATE(只在真正有通知时)

    对比看主题本身的代价(查主题、查回复列表、渲染、分页),新增的可忽略。
    我注意到一个细节:插件的 after_view 目前的逻辑是 set_viewed(写 cookie)。我可以在同一个函数里加通知清理。

    关于"修改范围”:你主动提出"是不是只改 unread_topic_notice 就可以"——这实际上是在指定待修改区域。我应该确认:是的,待修改区域 = unread_topic_notice/plugin.php,不动核心。这正好符合 AGENTS.md。

    让我写出准确、简洁的回答。

    关于负担,我要诚实:

    • 无未读通知:零额外 DB(读内存缓存)
    • 有未读通知:+1 COUNT + 2 UPDATE
    • 整体:可忽略,因为看主题本身重得多

    我还可以提一个更省的优化:其实可以连 COUNT 都省掉——直接用一条 UPDATE ... 然后用 affected rows 来减计数。但那样需要拿到 affected_rows。bbs1org 的 q() 返回 PDOStatement,可以 rowCount()。不过这优化很细,可以一提但不必。

    实际上更优雅的做法:

    $stmt = q("UPDATE app_notifications SET read_at=? WHERE recipient_id=? AND topic_id=? AND read_at=0", [now(), $uid, $topic_id]);
    $n = $stmt->rowCount();
    if ($n > 0) q("UPDATE app_users SET unread_notifications=GREATEST(unread_notifications-?,0) WHERE id=?", [$n, $uid]);

    这样连 COUNT 都省了,直接 UPDATE 用 affected rows。更省一次查询。

    但要注意:read_at=0 的判断 + 并发。不过通知场景并发不是问题。

    让我把两种写法都提及,推荐 UPDATE + rowCount 的方式(更省)。

    不过先短路:如果 me()['unread_notifications']==0 就直接返回,连 UPDATE 都不发。这是最快的路径。

    让我写回答。

    我需要确认 q() 的返回值能 rowCount()。让我看 q() 的实现。这是细节,但关系到推荐哪种写法。让我快速看 q()。

    实际上,为了回答准确,我确认一下 q() 返回 PDOStatement(支持 rowCount)。让我查。

    不过这个可以在我写代码时再确认。现在回答德耶的两个问题,重点是:

    1. 只改插件,够
    2. 负担:短路后几乎为零

    让我先确认 q() 返回值,这样我能给出准确的代码建议。

    让me 快速看 q()。这是给出准确方案的最后一块拼图。

    我简短确认 q() 返回类型,然后回答。

    全部确认。q() 返回 PDOStatement(支持 rowCount()),val() 可取单值。方案完全可行且能优化到极致。

    现在我能准确回答德耶的两个问题。

    核心结论:

    1. 只改 unread_topic_notice 插件即可,完全不动核心 index.php
    2. 负担几乎为零——用 me()['unread_notifications'] 内存缓存短路,无未读通知时零 DB 开销

    让我写出清晰回答,并给出最终的代码方案(供德耶判断,先不动手)。

    关键点:

    • 插件已挂 topic.after_view(看主题必经)
    • 插件能用全局 q()/me()/val(),可直接操作 app_notifications + app_users
    • me() 整个请求只查一次库(第 1411 行 __me_cache),读 unread_notifications 是纯内存
    • q() 返回 PDOStatement,支持 rowCount() → 可省掉 COUNT,直接 UPDATE 拿影响行数

    最优方案(在插件的 after_view 里加,复用已有的 uid/topic 上下文):

    function unread_topic_notice_clear_topic_notifications(int $topic_id): void
    {
        $uid = uid();
        if ($uid <= 0 || $topic_id <= 0) return;
        if ((int)(me()['unread_notifications'] ?? 0) <= 0) return;   // 内存短路,零 DB
        $s = q("UPDATE app_notifications SET read_at=? WHERE recipient_id=? AND topic_id=? AND read_at=0",
               [now(), $uid, $topic_id]);
        $n = (int)$s->rowCount();
        if ($n <= 0) return;
        q("UPDATE app_users SET unread_notifications=GREATEST(unread_notifications-?,0) WHERE id=?", [$n, $uid]);
        if (is_array($GLOBALS['__me_cache'] ?? null) && (int)$GLOBALS['__me_cache']['id'] === $uid)
            $GLOBALS['__me_cache']['unread_notifications'] = max(0, (int)$GLOBALS['__me_cache']['unread_notifications'] - $n);
    }

    然后在 unread_topic_notice_after_view 里调用(一行)。

    让我写回答。

    关于负担的量化对比,我可以给个直观的表格。

    回答要简洁、准确、有说服力。

    注意:德耶现在还是在确认方案阶段,我应该回答完这两个问题后,问要不要动手。

    我注意到插件的 after_view 已经有 uid 和 topic,所以调用很自然。

    还有一个点:德耶可能担心"修改前后"的对比。我给一个清晰的对比。

    让我写。

    另外,关于"只改插件"的一个 caveat(诚实说明):插件清理的是核心 app_notifications 表里属于该主题的通知。如果德耶收到的通知来自其他插件且那些通知没带 topic_id,则清不到。但核心的 mention 通知是带 topic_id 的(985 行),所以主流场景覆盖。这个我之前在分析里提过,这里再简短强调。

    实际上让我重新确认通知的 topic_id:mention 通知写入时 create_notification((int)$uid, $sender_id, 'mention', '...', $topic_id, $reply_id) —— 带了 topic_id。✅

    好,写回答。

    我注意到插件的 after_view 已经有 uid 和 topic,所以调用很自然。

    还有一个点:德耶可能担心"修改前后"的对比。我给一个清晰的对比。

    让我写。

    两个问题都是好消息:

    1. 只改 unread_topic_notice 就够,核心一行不动

    插件已经挂在 topic.after_view(看主题的必经 hook,index.php:3139 触发),而且插件里能直接用全局的 q() / me() / now()——足以操作 app_notifications 和 app_users 两张表。不用动核心 index.php,待修改区域就限定在 app/plugins/unread_topic_notice/plugin.php 一个文件。

    2. 负担:几乎为零,无未读通知时是零 DB 开销

    关键在于短路优化。me() 用 $GLOBALS['__me_cache'] 缓存(index.php:1411,整个请求只查一次库),所以读未读计数是纯内存操作:

    function unread_topic_notice_clear_topic_notifications(int $topic_id): void
    {
        $uid = uid();
        if ($uid <= 0 || $topic_id <= 0) return;
        if ((int)(me()['unread_notifications'] ?? 0) <= 0) return;   // ← 内存短路,零 DB
        $s = q("UPDATE app_notifications SET read_at=? WHERE recipient_id=? AND topic_id=? AND read_at=0",
               [now(), $uid, $topic_id]);
        $n = (int)$s->rowCount();                                   // q() 返回 PDOStatement,省掉 COUNT
        if ($n <= 0) return;
        q("UPDATE app_users SET unread_notifications=GREATEST(unread_notifications-?,0) WHERE id=?", [$n, $uid]);
        if (is_array($GLOBALS['__me_cache'] ?? null) && (int)$GLOBALS['__me_cache']['id'] === $uid)
            $GLOBALS['__me_cache']['unread_notifications'] = max(0, (int)$GLOBALS['__me_cache']['unread_notifications'] - $n);
    }

    在已有的 unread_topic_notice_after_view() 末尾加一行调用即可(那里已经拿到了 uid 和 topic,零额外取参)。

    修改前后负担对比:

    场景修改前修改后
    看主题(无未读通知,99%)写 1 个 cookie写 1 个 cookie + 读一个内存变量(零 DB)
    看主题(有未读通知)写 1 个 cookie+ 1 次 UPDATE + 可能 1 次计数 UPDATE
    打开"我的通知"页全量清零不变

    用 rowCount() 代替先 COUNT 再 UPDATE,又省一次查询。相比"看主题"本身的代价(查主题 + 查回复分页 + 渲染),新增的这些连零头都算不上——而且你本来就是小站。

    一个要说明的边界

    这个方案清的是核心 app_notifications 里属于该主题的通知(mention 通知写入时带了 topic_id,能精确匹配)。如果某些第三方插件自己往通知表写记录、却没带 topic_id,那就清不到——不过主流场景(@提到、私信、积分、删除)都带,覆盖没问题。


    方案已经验证可行:改动 = 一个插件文件,加一个方法 + 一行调用;负担 = 无未读时零,有未读时一次 UPDATE。

    #8
  • bbs1org

    我放开共享了

    #10
  • bbs1org

    ok
    你修改下 提交一下吧

    #9
  • elm

    插件 ID 已存在,不能更新其他用户发布的插件:bbs1org

    #11
  • bbs1org

    此项目接受所有登录用户协助维护和提交更新。共享了啊。难道有bug

    #12
  • elm

    我搞错了 修改了这个 https://bbs1.org/index.php?a=topic&id=82 晕了🤣

    #13
  • bbs1org

    ? 那应该修改这个啊

    #14
  • elm

    AI还是建议我修改那个

    修改reply_author_notice能实现,但更麻烦,而且有个容易踩的次生 bug。让我说清楚两者的差异。

    先回答:能实现

    reply_author_notice 完全可以新增一个 topic.after_view 钩子,在里面写完全一样的清理代码——因为插件内也能用 q()/me()/now(),能拿到 ctx 的 replies。清理 app_notifications 的逻辑本身跟挂在哪个插件无关,执行效果一模一样。

    但改 reply_author_notice 要多做两件事

    对比项改 unread_topic_notice(当前)改 reply_author_notice
    topic.after_view 钩子✅ 本来就有(原本写 cookie 阅读状态)❌ 没有,需新增注册
    新增代码1 个函数 + 1 行调用1 个函数 + 钩子注册 + 去重闸处理
    职责语义契合("已读/阅读状态"本就是它的职责)别扭("生成通知"的插件塞进"清除通知")
    覆盖的通知来源该主题所有来源通知若只清自己的,覆盖窄

    关键陷阱:必须同步处理「去重闸」

    这是改 reply_author_notice 最容易出问题的地方。它有张表 plugin_reply_author_notice_topics 做每主题只通知一次的去重:

    // 生成通知时:先插去重闸,插成功(rowCount===1)才发通知
    if (q($sql, [...])->rowCount() !== 1) return;   // 去重闸已存在 → 不通知
    create_notification(...);
    
    // 只有打开「我的通知」页才清去重闸
    reply_author_notice_profile_tab_header() → DELETE FROM plugin_reply_author_notice_topics WHERE user_id=?

    如果你只在 after_view 里清 app_notifications 而忘了清去重闸,会触发次生 bug:

    1. 主题 A 被回复 → 通知 + 去重闸都写入
    2. 你看主题 A → 通知清了,去重闸还在
    3. 主题 A 再次被回复 → 去重闸命中,rowCount!==1 → 不再通知你 ❌

    「看完后从此收不到该主题的新回复通知」——这个 bug 比原来的更隐蔽。所以改 reply_author_notice 必须连带清去重闸,逻辑更重。

    为什么改 unread_topic_notice 是对的

    • 职责分离:reply_author_notice 管"生成"(含去重闸),unread_topic_notice 管"已读清除"。生成端不知道用户何时"看到",清除端不该管去重——两边各司其职
    • 去重闸的生命周期不用动:它仍由 reply_author_notice 在"我的通知"页重置,与清除逻辑解耦,互不干扰
    • 天然复用已有钩子:unread_topic_notice 的 after_view 本来就在跑,只是追加一行调用
    • 覆盖更全:清的是该主题所有来源的通知(@提到、楼主被回复、其他插件生成的),不局限于 reply_author_notice 那一类

    结论

    两种改法都能实现功能,代码核心部分完全相同。 区别在于:改 reply_author_notice 要额外新增钩子注册 + 处理去重闸(否则有"不再通知"的次生 bug),且职责混杂;改 unread_topic_notice 是在已有钩子上加一行,干净、不用碰去重闸。

    一句话:能实现,但改 unread_topic_notice 是更省事、更安全的选择。 现在的方案没问题。

    哈哈哈哈哈 你来决定下吧,都可以改。。。

    #15
  • bbs1org

    你改 unread_topic_notice 去提交下。我打开共享了

    #16
  • elm

    版本 1.1.2 更新:
    浏览到对应楼层时自动清除通知提醒;修复已读后不再通知的问题。

    #17
  • elm

    @bbs1org 让AI实现了一次这个插件的修改,然后又分析对比了两个方案,还是应该修改这个。

    结论:改 reply_author_notice(方案 B)确实更好——而且有一个关键原因是之前低估的。

    多维度对比

    维度方案 A(改 unread_topic_notice)方案 B(改 reply_author_notice)
    服务器压力基本相同(内存短路 + 相同查询数)基本相同,仅多 1 次轻量 DELETE(去重表),可忽略
    通知覆盖范围该主题所有来源实际相同(按 topic_id+reply_id 清,不分生成者)
    代码内聚度清除逻辑在"足迹"插件里,跨表操作生成+清除+去重闸同插件内闭环
    去重闸生命周期❌ 不处理,留隐蔽缺陷✅ 顺手重置,天然一致
    后续可维护性若修缺陷需跨插件耦合无跨插件依赖

    关键差异:去重闸(这是 B 的决定性优势)

    reply_author_notice 的去重表 plugin_reply_author_notice_topics,只在两个时机清除:主题删除、打开「我的通知」页。

    方案 A 的隐蔽缺陷:用户看主题清了通知,但去重闸记录还在(unread_topic_notice 完全不碰它)。于是:

    楼主看完主题(通知清了,去重闸没清)
       ↓
    该主题再次被回复
       ↓
    去重闸命中(INSERT IGNORE,rowCount=0)→ 不生成通知 ❌
       ↓
    直到楼主打开「我的通知」页,去重闸才重置

    用户认真看了帖子,反而收不到该主题的后续回复通知——这比原 bug 更隐蔽。方案 A 要修这个,就得让 unread_topic_notice 去清另一个插件的表(跨插件硬编码,脆弱且耦合)。

    方案 B 天然没有这个问题:同一插件内,清通知时顺手 DELETE FROM plugin_reply_author_notice_topics,生成端和清除端的状态始终一致。

    诚实反思

    之前把"方案 B 要处理去重闸"描述成额外负担,这是片面的。现在看清楚了:

    • 不处理去重闸 = 留缺陷(方案 A)
    • 处理 = 同插件内顺手的事(方案 B)

    所以"去重闸"根本不是方案 B 的负担,而是它的独特优势——它让"通知已读"和"允许再次通知"这两个状态保持一致。一开始坚持改 reply_author_notice 的直觉是对的。

    服务器压力:两者其实相当

    澄清一点,别误以为 B 更省资源:

    • 两者都挂在已有的 topic.after_view,都先走 me() 内存短路(无未读时零 DB)
    • 核心查询数相同:SELECT notifications + 批量 SELECT replies + UPDATE notifications + UPDATE users
    • B 只多 1 次 DELETE 去重表(且仅在确实清了通知时才执行),毫秒级
    • 反过来说:A 若要补上去重闸的一致性,反而要多查多清一次别的插件的表——真要算,B 更省

    结论

    方案 B 在功能正确性和架构内聚上都更优,服务器压力两者相当。 唯一"劣势"是它比 A 多了一个钩子注册(因为没有现成的 after_view),但换来了去重闸的一致性——这笔交易很值。

    最终采用的方案 B 是更合适的选择。站长的直觉没错:通知的清除逻辑就该和通知的生成逻辑放在一起。

    #18

发表回复

登录后回复