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

生产环境下Spree状态机complete状态转换回调缺失问题咨询

问题原因排查
  • 加载顺序冲突:Rails生产环境默认开启类缓存(config.cache_classes = true)和类预加载(config.eager_load = true),你将state_machine回调的注册逻辑写在装饰器模块外部,若Spree::Order类在装饰器加载前已经完成预加载和state_machine初始化,后续注册的回调不会被写入已固化的状态机配置。开发环境默认关闭类缓存,每次请求都会重载类,因此每次加载装饰器时都会重新注册回调,不会出现异常。
  • prepend判断逻辑错误:你使用::Spree::Order.included_modules.exclude?(Spree::OrderDecorator)作为prepend的前置判断,但included_modules仅返回通过include引入的模块,通过prepend引入的模块不会出现在该列表中,会导致重复prepend的潜在问题,极端情况下会覆盖原有逻辑导致回调失效。
  • 装饰器未被正确加载:Rails生产环境的自动加载策略与开发环境不同,若没有显式配置装饰器的加载规则,app/models/spree/order_decorator.rb可能不会被主动加载,导致回调逻辑完全没有被执行。
解决方法
  1. 调整装饰器逻辑,将状态机回调注册移入prepended钩子中,保证每次模块被prepend时都会执行回调注册,修正后的装饰器代码如下:
module Spree
  module OrderDecorator
    def self.prepended(base)
      base.whitelisted_ransackable_attributes += %w[synced_with_erp]
      # 将状态机回调移到此处,绑定到当前被prepend的类
      base.state_machine.after_transition to: :complete, do: :update_user_was_purchased
    end

    def update_user_was_purchased
      user.update_column(:was_purchase, true) if user
    end
  end
end
# 修正prepend判断逻辑,用ancestors判断是否已经引入模块
::Spree::Order.prepend Spree::OrderDecorator unless ::Spree::Order.ancestors.include?(Spree::OrderDecorator)
  1. 配置装饰器自动加载规则,在config/application.rb中添加如下配置,保证生产环境预加载阶段会主动加载所有装饰器:
config.to_prepare do
  Dir.glob(Rails.root.join('app', 'models', '**', '*_decorator.rb')).each do |decorator|
    require_dependency decorator
  end
end
  1. 完全重启生产环境的Rails服务(Puma/Unicorn等),不要仅重载控制台进程,避免类缓存导致修改不生效,重启后再执行校验代码验证回调是否存在。

内容的提问来源于stack exchange,提问作者Виктор

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 07:36:06