首页 > 解决方案 > 在 Rails 服务中处理数据的最佳方式

问题描述

我有一个提醒服务,它接受一些“更改请求”,然后将它们发送到工作队列以在以后执行。每个“更改请求”可以有“十分钟前”提醒和或“一小时后提醒”。未来还会有不同类型的提醒。

我正在寻找最优雅的红宝石处理方式,并将每个提醒发送给工人很多 if 语句感觉不对,但我可能错了!

class ReminderService

  # @data
  # ten_minute_before: data[:ten_minutes_before_reminder] bool,
  # one_hour_after: data[:one_hour_after_reminder] bool,
  # user: user,
  # price_change_ids: data[:change_requests],
  # user_hash: user_hash(user)

  attr_accessor :data, :user

  def initialize(user, data)
    {data[:price_change_ids]}
    @user = user
    @data = data
  end

  def run
    change_requests
  end

  def send
    reminderWorker.perform_at(action_reminder_at, price_change_id, reminder_type, user, user_hash)
  end

  def change_requests
    data[:price_change_ids]
  end

  def user_hash
    data[:user_hash]
  end

  def action_reminder_at
    DateTime.parse(price_change_at) - 11.minutes
  end

  def price_change_id
    data[:price_change_ids].first.id
  end

  def price_change_at
    data[:user_hash][:price_change_at_time]
  end
end

工人服务:

class PriceReminderWorker
  include Sidekiq::Worker

  sidekiq_options queue: 'price_reminders', retry: true

  def perform(price_change_id, reminder, user, data)
    id = price_change_id
    price_change = priceChangeRequest.find(id)

    if reminder[:ten_minutes_before]
      reminderMailer.notify(user, data).deliver_later unless price_change.cancelled_at
    end

    if reminder[:one_hour_after]
      Sms::priceChangeSms.new(build_hash(user)).submit unless price_change.cancelled_at
    end
  end

  def build_hash(user)
    {
        to: Phonelib.parse(user.mobile_number).full_e164,
        company_id: user&.company&.id,
        user_name: user.first_name,
        admin_user_name: data[:from_admin_user],
        body: Sms::Message::priceChangeMessage.new(data).build
    }
  end
end

标签: ruby-on-railsruby

解决方案


如果您真的想坚持当前的PriceChangeRequest结构,那么我想我会这样做:

class PriceReminderWorker
  include Sidekiq::Worker
  sidekiq_options queue: 'price_reminders', retry: true

  attr_accessor *%w( price_change_id user data ).freeze

  def perform(price_change_id, reminder, user, data)
    @price_change_id  = price_change_id
    @user             = user
    @data             = data 

    %i(
      ten_minutes_before
      one_hour_after
    ).each do |time_sym|
      send("send_#{time_sym}_reminder") if price_change_request.send("#{time_sym}_reminder")
    end if price_change_request

  end

private 

  def user_hash
    {
      to:               Phonelib.parse(user.mobile_number).full_e164,
      company_id:       user&.company&.id,
      user_name:        user.first_name,
      admin_user_name:  data[:from_admin_user],
      body:             Sms::Message::priceChangeMessage.new(data).build
    }
  end

  def send_ten_minutes_before_reminder
    reminderMailer.notify(user, data).deliver_later unless price_change.cancelled_at
  end

  def send_one_hour_after_reminder
    Sms::priceChangeSms.new(user_hash).submit unless price_change.cancelled_at
  end

  def price_change_request
    @price_change_request ||= PriceChangeRequest.find(price_change_id)
  end

end

但是,我认为在你的课堂上有一堆booleans喜欢ten_minutes_before_reminder的人并不是一个好主意。特别是如果您开始有许多提醒类型。您最终可能会得到稀疏填充的布尔字段,这不是那么好。而且,每次要添加新的提醒类型时,都必须运行新的迁移。等等等等等等。one_hour_after_reminderPriceChangeRequest

如果我是你,我想我会很想上一ReminderTime堂课,比如:

# == Schema Information
#
# Table name: reminder_times
#
#  id           :bigint           not null, primary key
#  system_name  :string           not null
#  created_at   :datetime         not null
#  updated_at   :datetime         not null
#
class ReminderTime < ApplicationRecord
  validates :system_name, presence: true
  has_many :price_change_request_reminder_times
  has_many :price_change_requests, through: :price_change_request_reminder_times
end

和类似的PriceChangeRequestReminderTime东西:

# == Schema Information
#
# Table name: price_change_request_reminder_times
#
#  id                       :bigint           not null, primary key
#  reminder_time_id         :integer          not null
#  price_change_request_id  :integer          not null
#  created_at               :datetime         not null
#  updated_at               :datetime         not null
#
class PriceChangeRequestReminderTime < ApplicationRecord
  belongs_to :reminder_time
  belongs_to :price_change_request
end

然后,在您的 中PriceChangeRequest,执行以下操作:

class PriceChangeRequest < ApplicationRecord
  has_many :price_change_request_reminder_times
  has_many :reminder_times, through: :price_change_request_reminder_times

  def set_reminder_times(*time_syms)
    time_syms.each do |time_sym|
      reminder_times << ReminderTime.find_or_create_by!(system_name: time_sym)
    end
  end

  def remove_reminder_times(*type_syms)
    price_change_request_reminder_times.
      where(reminder_time: ReminderTime.find_by(system_name: type_syms)).
      destroy_all
  end

  def reminder_time_names
    reminder_times.pluck(:system_name)
  end

end

要设置提醒时间,您可以执行以下操作:

@price_change_request.set_reminder_times :ten_minutes_before 

或者

@price_change_request.set_reminder_times :ten_minutes_before, :one_hour_after

要删除提醒时间,您可以执行以下操作:

@price_change_request.remove_reminder_times :ten_minutes_before

或者

@price_change_request.remove_reminder_times :one_hour_after, :ten_minutes_before

...这样您就可以执行以下操作:

class PriceReminderWorker
  include Sidekiq::Worker
  sidekiq_options queue: 'price_reminders', retry: true

  attr_accessor *%w( 
    price_change_id 
    user 
    data 
  ).freeze

  delegate *%w(
    reminder_time_names
  ), to: :price_change_request

  def perform(price_change_id, reminder, user, data)
    @price_change_id  = price_change_id
    @user             = user
    @data             = data 
    reminder_time_names.each do |reminder_time_name|
      send("send_#{reminder_time_name}_reminder")
    end if price_change_request
  end

private 

  def user_hash
    {
      to:               Phonelib.parse(user.mobile_number).full_e164,
      company_id:       user&.company&.id,
      user_name:        user.first_name,
      admin_user_name:  data[:from_admin_user],
      body:             Sms::Message::priceChangeMessage.new(data).build
    }
  end

  def send_ten_minutes_before_reminder
    reminderMailer.notify(user, data).deliver_later unless price_change.cancelled_at
  end

  def send_one_hour_after_reminder
    Sms::priceChangeSms.new(user_hash).submit unless price_change.cancelled_at
  end

  def price_change_request
    @price_change_request ||= PriceChangeRequest.find(price_change_id)
  end

end

推荐阅读