# 附件缺少链接计数器

**URL:** <https://meta.discourse.org/t/link-counter-missing-for-attachments/398453>\
**Category:** Support\
**Created:** [2026 年3 月 14 日 20:19 UTC](https://meta.discourse.org/t/link-counter-missing-for-attachments/398453 "2026-03-14T20:19:18Z")\
**Posts on this page:** 1\
**Showing post:** 3

<div class="post-metadata">

**Author:** ![Zalunda](https://avatars.discourse-cdn.com/v4/letter/z/57b2e6/32.png) [@Zalunda](https://meta.discourse.org/u/Zalunda)\
**Post date:** [2026 年10 月 3 日 18:29 UTC](https://meta.discourse.org/t/link-counter-missing-for-attachments/398453/3 "2026-10-03T18:29:13Z")

</div>

我已经调试了这个问题，并找到了一个潜在的修复方案（在 Gemini 的帮助下）。

我在版本 3.4.5（我知道该版本可以正常工作）和最新版本上分别创建了这条消息（拖拽文件的内容并不重要）：

 ![image](https://global.discourse-cdn.com/meta/original/4X/e/6/4/e647d9a794dea0f8ac5bd274bcf8cf3e2cd67c15.png)

然后我使用了另外两个账户下载了这两个文件，并刷新了页面。

对于“without-attachement”（无附件），我没有看到下载计数器（尽管当我使用源版本 v3.4.5 时它是有效的）。“with-attachment”（有附件）从未显示下载计数器，我只是为了“完整性”而添加了它。

当在调试器中执行以下代码时：

```plaintext
post = Post.last
TopicLink.counts_for(Guardian.new(post.user), post.topic, [post])

```

我得到了：

```plaintext
=> {8 =>
  [{url: "/uploads/short-url/iMUBjeLNs0eRWZhdYnh24XGXY0f.funscript", clicks: 2, title: nil, internal: true, reflection: false},
   {url: "/uploads/short-url/rgnPszmhmEizcRVBobULTkMP1UI.funscript", clicks: 0, title: nil, internal: true, reflection: false}]}

```

所以点击次数是存在的，但 UI 没有考虑它们，因为它们被用于防止用户查看指向私有/隐藏主题的链接点击次数的规则过滤掉了。问题在于，新规则没有考虑到内部链接可能是一个主题，也可能是一个内部文件。目前，任何 `internal` 且 `not a topic` 的链接都会被丢弃。

我通过修改 topic\_link.rb 中的代码解决了这个问题：

```plaintext
def self.apply_link_visibility_filters(builder, link:, target_topic:, target_posts:)
    builder.where(<<\~SQL)
      #{target_topic}.deleted_at IS NULL
      AND (#{target_topic}.id IS NULL OR #{target_topic}.visible = true)
      AND (#{link}.internal = false OR #{target_topic}.id IS NOT NULL)
      AND (#{link}.link_post_id IS NULL OR (#{link_posts}.id IS NOT NULL AND #{link_posts}.deleted_at IS NULL))
    SQL
  end

```

改为：

```plaintext
def self.apply_link_visibility_filters(builder, link:, target_topic:, target_posts:)
    builder.where(<<\~SQL)
      #{target_topic}.deleted_at IS NULL
      AND (#{target_topic}.id IS NULL OR #{target_topic}.visible = true)
      AND (#{link}.internal = false OR #{link}.link_topic_id IS NULL OR #{target_topic}.id IS NOT NULL)
      AND (#{link}.link_post_id IS NULL OR (#{link_posts}.id IS NOT NULL AND #{link_posts}.deleted_at IS NULL))
    SQL
  end

```

> **Gemini 完整解释**
>
> 你找到了确凿的证据！通过提供 topic\_link.rb 的两个版本，现在非常清楚是什么破坏了 Discourse 最新版本中的此功能。
> 
> 这个问题完全是由添加到 counts\_for 数据库查询中的新安全过滤器引起的。
> 
> ### 罪魁祸首：新的 apply\_link\_visibility\_filters 方法
> 
> 如果你查看新的 topic\_link.rb 文件底部附近，你会发现这个新方法：
> 
> codeRuby
> 
> ```plaintext
> def self.apply_link_visibility_filters(builder, link:, target_topic:, target_posts:)
> builder.where(<<~SQL)
> #{target_topic}.deleted_at IS NULL
> AND (#{target_topic}.id IS NULL OR #{target_topic}.visible = true)
> AND (#{link}.internal = false OR #{target_topic}.id IS NOT NULL) <--- 这是 BUG
> AND (#{link}.link_post_id IS NULL OR (#{link_posts}.id IS NOT NULL AND #{link_posts}.deleted_at IS NULL))
> SQL
> end
> 
> ```
> 
> ### 为什么这会破坏标准的文件上传
> 
> Discourse 开发者添加这条 SQL 规则是为了防止用户查看指向他们没有权限查看的私有/隐藏主题的链接的点击次数。然而，他们意外地波及了文件上传。
> 
> 以下是 SQL 引擎如何评估你的 without-attachment.funscript 上传链接的：
> 
> 1. **#{link}.internal = false** ：当保存上传文件时，Discourse 将其标记为 internal = true（因为文件托管在你的论坛上）。所以这评估为 **FALSE** 。
> 2. **#{target\_topic}.id IS NOT NULL** ：因为这是一个文件上传，而不是指向另一个论坛帖子的链接，所以它没有目标主题。主题 ID 为 NULL。所以这也评估为 **FALSE** 。
> 3. **FALSE OR FALSE = FALSE** 。
> 
> 由于这两个条件都失败，SQL 查询完全过滤掉了你的文件上传，并且 counts\_for 方法向序列化器返回了一个空数组。
> 
> _（注意：|attachment 仍然有效的原因是，前端通过一个完全独立的 API 端点/机制处理 .attachment 类以进行下载跟踪，从而完全绕过了 TopicLink！）_
> 
> ### 如何修复代码
> 
> 要在你的本地环境中修复此 bug，你需要告诉 SQL 查询：_“如果内部链接没有目标主题是可以的，只要它一开始就不应该有目标主题（例如文件上传）。"_
> 
> 在你的 Dev Container 中打开 app/models/topic\_link.rb，找到 apply\_link\_visibility\_filters 方法（大约在第 185 行），并将损坏的行更改为：
> 
> **将此更改：**
> 
> ```plaintext
> AND (#{link}.internal = false OR #{target_topic}.id IS NOT NULL)
> 
> ```
> 
> **改为：**
> 
> ```plaintext
> AND (#{link}.internal = false OR #{link}.link_topic_id IS NULL OR #{target_topic}.id IS NOT NULL)
> 
> ```
> 
> **为什么这能修复它：**  
> 通过添加 #{link}.link\_topic\_id IS NULL，你是在告诉数据库允许纯粹是文件/上传的内部链接。它仍然会成功阻止那些 _确实_ 有 link\_topic\_id 但 target\_topic 缺失/隐藏的情况（这满足了 Discourse 开发者试图实现的安全要求）。

---

_[View the full topic](https://meta.discourse.org/t/link-counter-missing-for-attachments/398453)._
