生产环境下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可能不会被主动加载,导致回调逻辑完全没有被执行。
解决方法
- 调整装饰器逻辑,将状态机回调注册移入
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)
- 配置装饰器自动加载规则,在
config/application.rb中添加如下配置,保证生产环境预加载阶段会主动加载所有装饰器:
config.to_prepare do Dir.glob(Rails.root.join('app', 'models', '**', '*_decorator.rb')).each do |decorator| require_dependency decorator end end
- 完全重启生产环境的Rails服务(Puma/Unicorn等),不要仅重载控制台进程,避免类缓存导致修改不生效,重启后再执行校验代码验证回调是否存在。
内容的提问来源于stack exchange,提问作者Виктор
相关产品推荐
相关产品推荐

