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

如何实现Contest模型published_at字段设置后30分钟触发邮件任务?

Alright, let's figure out how to fix this problem. The main issue with your current code is that it schedules the email job immediately when a Contest is created—even if published_at is blank. And since published_at might be added later via an update, we need a way to only trigger the job when published_at is actually set, not just on record creation. Here are a few solid solutions:

Solution 1: Use after_commit with conditional checks for published_at

Instead of after_create, switch to an after_commit callback that triggers only when published_at is either set on creation or added later via an update. We'll add a helper method to check if published_at was just set:

class Contest < ApplicationRecord
  after_commit :schedule_contest_email, on: [:create, :update], if: :published_at_just_set?

  private

  def published_at_just_set?
    # Two valid scenarios:
    # 1. The contest was just created with a non-blank published_at
    # 2. The contest was updated, and published_at changed from nil to a value
    published_at.present? && (new_record? || saved_change_to_published_at?(from: nil))
  end

  def schedule_contest_email
    SendContestJob.set(wait: 30.minutes).perform_later(self)
  end
end

This way, the job only gets scheduled when published_at is actually present, whether that's on creation or a later update.

Solution 2: Add a flag to prevent duplicate job scheduling

If you're worried about users accidentally updating published_at multiple times (which could trigger multiple job schedules), add a boolean flag to track if the email has already been scheduled:

First, generate a migration to add the flag:

rails generate migration AddEmailScheduledToContests email_scheduled:boolean default:false
rails db:migrate

Then update the model:

class Contest < ApplicationRecord
  after_commit :schedule_contest_email, on: [:create, :update], if: :should_schedule_email?

  private

  def should_schedule_email?
    published_at.present? && !email_scheduled && (new_record? || saved_change_to_published_at?(from: nil))
  end

  def schedule_contest_email
    SendContestJob.set(wait: 30.minutes).perform_later(self)
    # Use update_column to skip callbacks and avoid infinite loops
    update_column(:email_scheduled, true)
  end
end

This ensures the email job is only scheduled once, even if published_at is modified again later.

Solution 3: Add a final check in the Job itself

As an extra layer of protection, add a check directly in your SendContestJob to make sure published_at is still present before sending the email. This guards against edge cases (like published_at being cleared after the job was scheduled):

class SendContestJob < ApplicationJob
  queue_as :default

  def perform(contest)
    # Refresh the contest record from the database to get the latest state
    contest.reload
    if contest.published_at.present?
      # Execute your email sending logic here
      ContestMailer.notify_contest(contest).deliver_now
    end
  end
end

For the most robust setup, combine Solution 1 (conditional callback scheduling) with Solution 3 (final job check). This ensures the job is only scheduled when needed, and even if something slips through, the job won't send an email if published_at is missing.

内容的提问来源于stack exchange,提问作者Alex Zakruzhetskyi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:41:34