自定义主题过滤器:URL参数未传递到 `TopicQuery.options`

在使用URL查询参数(例如 ...?market_item_statuses=Available)构建具有自定义主题列表过滤器(如按价格、位置)的插件时,URL正确更新,但参数没有显示在服务器端的 topic_query.options 中。

设置:

  1. 客户端参数注册(tecenc-discovery-params.js):

    // tecenc-discovery-params.js
    import { apiInitializer } from "discourse/lib/api";
    
    export default apiInitializer("1.37.3", (api) => {
      const MARKET_PARAMS_KEYS = [
        "market_price_min", "market_price_max", "market_location",
        "market_conditions", "market_warranty", "market_item_statuses"
      ];
      MARKET_PARAMS_KEYS.forEach(paramKey => {
        api.addDiscoveryQueryParam(paramKey, { replace: true, refreshModel: true });
      });
    });
    
  2. 服务器端白名单尝试(plugin.rb):

    # plugin.rb
    module ::Tecenc
      MARKET_PARAMS_KEYS = [
        :market_price_min, :market_price_max, :market_location,
        :market_conditions, :market_warranty, :market_item_statuses
      ].freeze
    end
    
    # after_initialize do
      if SiteSetting.tecenc_enabled?
        if defined?(::TopicQuery.add_custom_param_handler)
          ::Tecenc::MARKET_PARAMS_KEYS.each do |param_key|
            ::TopicQuery.add_custom_param_handler(param_key) { |value| value } # 简化示例
          end
          Rails.logger.info "[Tecenc] 已注册 with add_custom_param_handler."
        elsif defined?(::TopicQuery) && ::TopicQuery.methods.include?(:extra_options_whitelist)
          current_whitelist = ::TopicQuery.extra_options_whitelist || []
          new_whitelist = (current_whitelist + ::Tecenc::MARKET_PARAMS_KEYS).uniq
          ::TopicQuery.extra_options_whitelist(*new_whitelist)
          Rails.logger.info "[Tecenc] 扩展了 extra_options_whitelist."
        else
          Rails.logger.warn "[Tecenc] 警告: 未找到允许参数白名单的方法。"
        end
    
  3. 服务器端过滤逻辑(plugin.rb):

    # plugin.rb (在 after_initialize / 启用后)
    ::TopicQuery.add_custom_filter(:"tecenc_filters") do |topics, topic_query|
      opts = topic_query.options
      Rails.logger.info "[Tecenc_TopicQuery] 选项: #{opts.inspect}" # 关键日志
    
      # market_params_present = ::Tecenc::MARKET_PARAMS_KEYS.any? { |p| opts[p].present? }
      # if market_params_present
      #   # ... 使用 opts[key] 进行过滤逻辑 ...
      # end
      topics # 或过滤后的话题
    end
    

问题(日志):

  1. 参数白名单失败:

    [Tecenc] PLUGIN_WARN: 找不到适合的方法(add_custom_param_handler 或者 extra_options_whitelist)为 TopicQuery 白名单自定义参数。
    
  2. TopicQuery.add_custom_filter 中,opts 缺少我们的自定义参数:
    当URL为 ...?market_item_statuses=Available时,日志显示:

    [Tecenc_TopicQuery] 选项: {:category=>5, :filter=>"default", :topic_ids=>nil, :category_id=>5}
    

我们的 market_item_statuses(以及其他自定义参数)不存在。

我们的环境:

问题:

  1. 在最新的 Discourse 版本中,确保自定义 URL 查询参数到达 topic_query.options 的目前最佳实践是什么?
  2. 为什么我们尝试使用 add_custom_param_handlerextra_options_whitelist 时会出现 “Could not find a suitable method” 的警告,操作失败?
  3. 是否有其他的参数注册方法可以用于 TopicQuery,我们应该考虑使用?

任何帮助将不胜感激!

刚刚注意到 add_custom_param_handler 甚至不是 TopicQuery 的方法。在较新版本的 discourse 中,是否有其他方法可以为主题构建自定义筛选器?

TopicQuery 单例方法:[:add_custom_filter, :apply_custom_filters, :new_filter, :public_valid_options, :remove_custom_filter, :remove_muted_tags, :results_filter_callbacks, :results_filter_callbacks=, :tracked_filter, :unread_filter, :valid_options, :validate?, :validators, :yaml_tag]

{“progress_report”: “到目前为止的进展:\n\n1. 确认在我的 Discourse 版本中,像 add_custom_param_handlerextra_options_whitelist 这样的静态 TopicQuery 白名单方法不可用,因此放弃了这些方法。\n\n2. 对 ListController#build_topic_list_options 进行了补丁,以在调用 TopicQuery.new 之前,将我自定义的 URL 参数(例如 market_item_statusesmarket_price_min)注入到 opts 哈希中。\n\n3. 这部分现在工作正常! 当我发出像 /c/market/5/l/latest.json?filter=defaultmarket_item_statuses=Available 这样的请求时,我的服务器日志确认了注入:\n\n \n [TecencMarket_ListControllerExt_BuildOptsV3] 注入到 opts 中:market_item_statuses = Available\n \n 所以,TopicQuery.new(user, opts) 现在接收到的 opts 包含我自定义的参数。\n\n当前遇到的问题: 500 错误 & 过滤器块未执行\n\n虽然参数现在被正确传递到 TopicQuery 的初始化器,但我仍然在请求包含这些自定义市场参数时遇到 500 内部服务器错误。\n\n为排查问题,我简化我的 TopicQuery.add_custom_filter(:"tecenc_market_filters") 块到最基本的形式。\n\nruby\n# plugin.rb - 当前简化的自定义过滤器块\nif ::TopicQuery.respond_to?(:add_custom_filter)\n ::TopicQuery.add_custom_filter(:"tecenc_market_filters") do |topics, topic_query| original_topics_relation = topics \n opts = topic_query.options\n log_prefix_query = "[TecencMarket_SimplifiedFilter_V1.1_Test]" # 我的调试前缀\n\n Rails.logger.info "#{log_prefix_query} 通过 TopicQuery 接收的 Opts:#{opts.inspect}"\n\n if opts[:market_item_statuses].present?\n Rails.logger.info "#{log_prefix_query} 'market_item_statuses' 存在于 opts 中:#{opts[:market_item_statuses]}"\n else\n Rails.logger.info "#{log_prefix_query} 'market_item_statuses' 不存在于 opts 中。"\n end\n \n Rails.logger.info "#{log_prefix_query} 返回原始话题关系。"\n original_topics_relation # 隐式返回\n end\n Rails.logger.info "[TecencMarket] 应用了简化的自定义过滤器 (V1.1_Test)。"\nend\n\n\n使用此简化过滤器的观察:\n\n* 没有我自定义过滤器参数的请求(例如,仅 /c/market/5/l/latest.json?filter=default):\n\n * 简化的过滤块日志正确:\n\n \n [TecencMarket_SimplifiedFilter_V1.1_Test] 通过 TopicQuery 接收的 Opts: {:category=>5, :filter=>"default", ...} \n [TecencMarket_SimplifiedFilter_V1.1_Test] 'market_item_statuses' 不存在于 opts。\n [TecencMarket_SimplifiedFilter_V1.1_Test] 返回原始话题关系。\n \n\n * 页面加载正常,返回 200 OK。\n\n* 带有我自定义过滤器参数的请求(例如 /c/market/5/l/latest.json?filter=defaultmarket_item_statuses=Available):\n\n * 观察到 ListController 补丁日志:[TecencMarket_ListControllerExt_BuildOptsV3] 注入到 opts:market_item_statuses = Available\n”}{“content”: "* 在 … 毫秒内立即发生的 Completed 500 Internal Server Error

  • 至关重要的是,没有任何来自简化过滤器块内部的日志(没有 [TecencMarket_SimplifiedFilter_V1.1_Test]... 的行)出现在此失败请求的日志中。

这表明 500 错误发生在 TopicQuery 使用 opts 哈希(现在包含我自定义参数)初始化之后,但在或刚刚当 Discourse 核心的 TopicQuery#apply_custom_filters 机制试图执行我注册的(现在非常简单的)过滤器块时。

我在尝试从 development.log 中孤立导致请求失败的 Ruby 异常和完整的回溯信息方面遇到困难(我收集到的日志片段显示了 500 的行本身,但没有显示前面详细的错误信息)。

后续问题:

鉴于:

  1. 现已成功将自定义参数注入到 TopicQuery.options,这是通过 ListController 更新实现的。

  2. 当这些自定义参数存在于 opts 中时,发生了 500 错误。

  3. 这个 500 错误发生在甚至执行极其简化的自定义过滤器块(只记录日志并返回原始关系)之前。

  4. 这是在 Discourse 3.5.0.beta3-dev 版本上。

什么可能导致 TopicQuery 本身,或其 apply_custom_filters 方法,在调用注册的自定义过滤器块之前,因 options 哈希中包含这些插件特定的键而出错?我们之前怀疑的 LocalJumpError 仍然在处理过滤器块迭代或调用的底层有关联,即使我简化的块只是隐式返回?

关于在 TopicQuery 行为中检查这些类型的自定义选项的下一步建议,或关于如何更稳健地获取导致 500 错误的完整回溯信息的指导,将非常有帮助。" }