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

FastAPI实现精准定时提醒任务:PostgreSQL取数+FCM通知方案咨询

精确闹钟提醒系统实现方案

核心思路

放弃轮询方案,采用基于事件的精准定时调度:直接计算每个提醒的下次触发时间,将通知任务精准绑定到对应时间执行;触发后自动处理重复提醒的后续调度,确保时间零偏差。

具体实现步骤

1. 选用精准的定时调度框架

使用APScheduler(Python生态)这类支持精准时间触发的调度框架,它支持单机/分布式部署,能保证任务在指定时间精确执行。如果是集群部署,可搭配Redis作为分布式调度存储,避免多实例重复触发。

2. 系统启动时加载待触发任务

系统初始化时,从数据库筛选所有未完成、且有有效下次触发时间的提醒,将对应的通知任务添加到调度器:

from apscheduler.schedulers.background import BackgroundScheduler
from sqlalchemy.orm import Session
from datetime import datetime
from models import Reminder

# 全局调度器实例
scheduler = BackgroundScheduler()

def init_reminder_scheduler(db: Session):
    # 筛选需要调度的活跃提醒
    active_reminders = db.query(Reminder).filter(
        Reminder.is_finished == False,
        Reminder.next_date.isnot(None),
        Reminder.next_date > datetime.now()
    ).all()

    for reminder in active_reminders:
        # 为每个提醒添加精准触发任务
        scheduler.add_job(
            trigger_reminder_notification,
            trigger='date',
            run_date=reminder.next_date,
            args=[reminder.id],
            id=f"reminder_task_{reminder.id}",
            replace_existing=True
        )
    scheduler.start()

3. 实现通知触发与重复提醒逻辑

当任务触发时,执行FCM通知发送,然后自动处理重复提醒的后续调度:

def trigger_reminder_notification(reminder_id: int, db: Session):
    reminder = db.query(Reminder).get(reminder_id)
    if not reminder or reminder.is_finished:
        return

    # 执行FCM通知发送逻辑
    # 此处替换为你的FCM发送代码
    # send_fcm_message(user_id=reminder.user_id, content=reminder.content)

    # 更新提醒状态
    reminder.last_reminded_at = datetime.now()
    reminder.is_notified = True

    # 计算下一次触发时间
    next_trigger_date = reminder.next_date
    if next_trigger_date:
        # 检查是否超过重复结束时间
        if reminder.repeat_end_date is None or next_trigger_date <= reminder.repeat_end_date:
            # 添加下一次触发任务
            scheduler.add_job(
                trigger_reminder_notification,
                trigger='date',
                run_date=next_trigger_date,
                args=[reminder.id],
                id=f"reminder_task_{reminder.id}",
                replace_existing=True
            )
        else:
            # 已到重复截止时间,标记为完成
            reminder.is_finished = True
    else:
        # 无后续提醒,标记为完成
        reminder.is_finished = True

    db.commit()

4. 处理新增/修改提醒的实时调度

当用户新增或修改提醒时,同步更新调度任务:

def create_reminder(db: Session, reminder_data):
    reminder = Reminder(**reminder_data)
    db.add(reminder)
    db.commit()

    # 新增提醒时添加调度任务
    if reminder.next_date and not reminder.is_finished:
        scheduler.add_job(
            trigger_reminder_notification,
            trigger='date',
            run_date=reminder.next_date,
            args=[reminder.id],
            id=f"reminder_task_{reminder.id}",
            replace_existing=True
        )
    return reminder

def update_reminder(db: Session, reminder_id: int, update_data):
    reminder = db.query(Reminder).get(reminder_id)
    if not reminder:
        return None

    # 先移除原调度任务
    job_id = f"reminder_task_{reminder_id}"
    if scheduler.get_job(job_id):
        scheduler.remove_job(job_id)

    # 更新提醒数据
    for key, value in update_data.items():
        setattr(reminder, key, value)
    db.commit()

    # 添加新的调度任务(如果有效)
    if reminder.next_date and not reminder.is_finished:
        scheduler.add_job(
            trigger_reminder_notification,
            trigger='date',
            run_date=reminder.next_date,
            args=[reminder.id],
            id=job_id,
            replace_existing=True
        )
    return reminder

5. 数据库层面的时间计算优化

可以直接在PostgreSQL中计算下次触发时间,减少应用层逻辑复杂度:

-- 查询未来1小时内需要触发的提醒
SELECT * FROM reminders
WHERE is_finished = false
AND (
    -- 单次提醒:首次触发时间在未来1小时内
    (repeat_type = 'NEVER' AND date BETWEEN NOW() AND NOW() + INTERVAL '1 hour')
    OR
    -- 重复提醒:计算下次触发时间在未来1小时内,且未超过结束时间
    (repeat_type != 'NEVER' 
     AND (last_reminded_at + repeat_interval) BETWEEN NOW() AND NOW() + INTERVAL '1 hour'
     AND (repeat_end_date IS NULL OR (last_reminded_at + repeat_interval) <= repeat_end_date))
);

关键注意事项

  • 时区一致性:确保数据库、应用服务器、调度器使用同一时区,避免时间偏差
  • 任务持久化:配置APScheduler的任务存储(如SQLAlchemy),避免系统重启后丢失未触发任务
  • 异常重试:在FCM发送逻辑中添加异常捕获与重试机制,避免因网络问题导致通知失败
  • 分布式锁:集群部署时,用Redis分布式锁确保同一提醒任务只被执行一次

内容的提问来源于stack exchange,提问作者Dmitriy Lunev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 03:57:48