用户数据归档导出失败:DiscourseDataExplorer::QueryGroup 实例没有 'url' 方法

完全披露:我在撰写此错误报告前,曾通过 ask.discourse 进行过相关讨论。另外,我知道我的版本更新滞后了,但我希望趁记忆还清晰时发布此问题。

我已在两个不同的用户账户上复现了该问题。

摘要

对于已书签(Bookmark)Data Explorer 组报告的用户,请求用户数据存档(偏好设置 → 导出您的数据 → 请求存档)会失败。Jobs::ExportUserArchive 后台作业在生成书签 CSV 时崩溃,导致导出无法完成,且未发送下载链接。

环境信息

  • Discourse 版本:2026.8.0-latest.1
  • Data Explorer:随核心捆绑(版本跟随核心)
  • 自托管 / 托管:自托管
  • 根据回溯显示的 Ruby/Rails 技术栈:activemodel/activerecord 8.0.5.1, Sidekiq 7.3.10

复现步骤

  1. 以管理员身份创建一个 Data Explorer 查询,并授予某个组的访问权限。
  2. 在该组的 Reports(报告)选项卡中,点击查询旁的 Bookmark(书签)按钮,使其成为已书签的组报告。
  3. 使用该同一用户,前往 偏好设置 → 导出您的数据 → 请求存档。
  4. 观察作业失败情况(检查 Admin → Logs 或 Sidekiq 日志)。

预期行为

应生成用户存档,并且用户应收到包含其书签在内的下载链接。

实际行为

导出作业抛出异常,导致存档失败。回溯显示崩溃发生在 bookmarks_export 中,该作业对已书签对象调用了 .url:

Job exception: undefined method 'url' for an instance of DiscourseDataExplorer::QueryGroup

回溯(仅保留相关帧):

activemodel-8.0.5.1/lib/active_model/attribute_methods.rb:512:in 'ActiveModel::AttributeMethods#method_missing'
activerecord-8.0.5.1/lib/active_record/attribute_methods.rb:495:in 'ActiveRecord::AttributeMethods#method_missing'
/var/www/discourse/app/jobs/regular/export_user_archive.rb:325:in 'block in Jobs::ExportUserArchive#bookmarks_export'
activerecord-8.0.5.1/lib/active_record/relation/delegation.rb:101:in 'Array#each'
activerecord-8.0.5.1/lib/active_record/relation/delegation.rb:101:in 'ActiveRecord::Delegation#each'
/var/www/discourse/app/jobs/regular/export_user_archive.rb:319:in 'Jobs::ExportUserArchive#bookmarks_export'
/var/www/discourse/app/jobs/regular/export_user_archive.rb:170:in 'Kernel#public_send'
/var/www/discourse/app/jobs/regular/export_user_archive.rb:170:in 'block (2 levels) in Jobs::ExportUserArchive#execute'
/var/www/discourse/app/jobs/regular/export_user_archive.rb:168:in 'block in Jobs::ExportUserArchive#execute'

分析

书签是多态的。导出作业调用 bookmarkable.url,这对核心可书签类型(Topic、Post)有效,但 DiscourseDataExplorer::QueryGroup 未实现 url 方法,因此作业在处理第一个此类书签时会引发 NoMethodError。

看起来 Data Explorer 将查询组注册为可书签对象(通过组报告的 Bookmark 按钮),但该模型缺少 url 方法(或者核心导出作业需要针对未提供 url 方法的书签类型添加保护机制)。

建议修复方案

要么在 DiscourseDataExplorer::QueryGroup 上实现 url 方法,要么使核心的 bookmarks_export 更具防御性(例如使用 bookmarkable.respond_to?(:url) / 逐行 rescue),以便单个不受支持的书签类型不会导致整个导出失败。

临时解决方案

删除有问题的书签(即受影响用户的 bookmarks 表中 bookmarkable_type = 'DiscourseDataExplorer::QueryGroup' 的行)可使导出正常完成。