You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sidekiq Worker报错‘no implicit conversion of Symbol into Integer’求助

问题分析与解决方案

错误原因

你遇到的no implicit conversion of Symbol into Integer错误,核心问题出在参数传递和文件处理逻辑上:

  1. 直接传递完整的params对象给Sidekiq Worker:Rails的ActionDispatch::Http::UploadedFile(上传文件对象)无法被Sidekiq序列化(Sidekiq仅支持JSON兼容的数据类型),序列化后参数结构异常,导致你在Worker中用符号键访问时,实际访问的是数组,触发类型转换错误。
  2. 临时文件生命周期问题:请求结束后Rails会自动删除上传的临时文件,Worker运行时根本无法读取到原文件。
  3. 异常捕获被注释:无法记录完整错误信息,排查难度增大。

修复步骤

1. 修改控制器代码:正确传递参数并保存上传文件

不要直接传递整个params,先将上传文件保存到永久位置,再提取必要参数传递给Worker:

def create
  # 保存上传文件到永久目录
  uploaded_file = params[:coupons_import][:file]
  upload_dir = Rails.root.join('public', 'coupon_uploads')
  FileUtils.mkdir_p(upload_dir) # 确保目录存在
  file_path = upload_dir.join(uploaded_file.original_filename)
  File.open(file_path, 'wb') { |f| f.write(uploaded_file.read) }

  # 提取需要的参数,排除无法序列化的文件对象
  worker_params = params[:coupons_import].permit(
    :product, :percent, :amount, :promo_name, :promo_desc,
    :usage_limit, :starts_at, :expires_at, :option_values
  ).to_h
  worker_params[:file_path] = file_path.to_s

  CouponsImportWorker.perform_async(worker_params)
  @status = Spree.t('coupons_import.in_progress')
  flash[:notice] = "CSV processing is in progress. You will be notified upon completion."
  redirect_back fallback_location: spree.admin_promotions_path
end

2. 修改Worker代码:读取永久文件并修复参数访问

调整Worker逻辑,使用保存后的文件路径,同时恢复异常捕获:

require 'csv'
require 'json'

class CouponsImportWorker
  include Sidekiq::Worker
  
  def perform(worker_params)
    Rails.logger.info("Worker running with params: #{worker_params}")
    
    products = []
    worker_params[:product].split(',').each do |product_id|
      products << Spree::Product.find(product_id)
    end

    filename = worker_params[:file_path]
    Rails.logger.info("Reading file: #{filename}")
    csv_table = CSV.parse(File.read(filename), headers: true, col_sep: ';')
    codes = csv_table.map(&:to_h).map { |e| e['code'] }
     
    percent = worker_params[:percent].to_i
    amount = worker_params[:amount].to_d
    
    if percent > 0 || amount > 0
      codes.each do |code|
        promotion = Spree::Promotion.create(
          name: worker_params[:promo_name],
          description: worker_params[:promo_desc],
          match_policy: 'all',
          usage_limit: worker_params[:usage_limit],
          starts_at: worker_params[:starts_at],
          expires_at: worker_params[:expires_at],
          code: code,
          store_ids: 1
        )

        if amount > 0
          promotion.promotion_actions << Spree::Promotion::Actions::CreateAdjustment.create(
            calculator: Spree::Calculator::FlatRate.new(preferred_amount: amount)
          )
        else
          promotion.promotion_actions << Spree::Promotion::Actions::CreateItemAdjustments.create(
            calculator: Spree::Calculator::PercentOnLineItem.new(preferred_percent: percent)
          )
        end
  
        if worker_params[:option_values].blank?
          create_promotion_rule(promotion, products)
        else
          create_promotion_option_value_rule(promotion, products, worker_params[:option_values])
        end
      end
    end 
    
    Rails.logger.info("CSV processing completed")
    ActionCable.server.broadcast('coupons_channel', message: 'CSV processing completed.')
    
    # 可选:处理完后删除临时文件
    File.delete(filename) if File.exist?(filename)
  rescue StandardError => e
    Rails.logger.error("CouponsImportWorker error: #{e.message}")
    Rails.logger.error(e.backtrace.join("\n"))
    # 可选:添加错误通知,比如发邮件给管理员
  end
  
  private
  
  def create_promotion_rule(promotion, products)
    rule = Spree::Promotion::Rules::Product.create(promotion: promotion)
    rule.products = products
    rule.save!
  end
  
  def create_promotion_option_value_rule(promotion, products, option_value)
    Spree::Promotion::Rules::OptionValue.create(
      promotion_id: promotion.id,
      type: 'Spree::Promotion::Rules::OptionValue',
      preferences: {
        eligible_values: { products.last.id => [option_value] },
        match_policy: 'any'
      }
    )
  end
end

3. 额外优化点

  • 确保public/coupon_uploads目录有写入权限,可通过命令创建:mkdir -p public/coupon_uploads && chmod 755 public/coupon_uploads
  • create_promotion_option_value_rule中仅使用products.last.id,如果业务需要关联多个产品的选项值,需调整该逻辑
  • 可添加文件重命名逻辑,避免同名文件覆盖

内容的提问来源于stack exchange,提问作者Abhishek Panda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 16:15:15