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

基于Ionic、PHP、MySQL实现赛事通知定时触发方案咨询

精准定时推送赛事通知的可行方案(适配PHP+MySQL+Firebase栈)

嘿,这个需求我之前帮朋友处理过类似的——CronJob确实搞不定指定某一精准日期时间的触发,毕竟它是按固定间隔轮询的。结合你用的Ionic+PHP+MySQL+Firebase技术栈,给你几个落地性拉满的方案:

方案1:改进版CronJob + MySQL任务队列(最易上手)

这是成本最低的方案,不用额外加服务,直接复用现有技术栈:

  1. 创建任务表:在MySQL里建一个event_notification_tasks表,用来存待推送的任务:
CREATE TABLE event_notification_tasks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    event_id INT NOT NULL,
    push_time DATETIME NOT NULL,
    participant_tokens TEXT NOT NULL, -- 存储参与者的FCM令牌,用逗号分隔
    status ENUM('pending', 'executed', 'failed', 'cancelled') DEFAULT 'pending',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (event_id) REFERENCES events(id)
);
  1. 赛事创建时写入任务:当用户创建赛事并设置举办日期时,把推送任务插入这个表,push_time设为赛事举办当日的目标通知时间(比如赛事开始前30分钟)。

  2. 编写PHP执行脚本:写一个process_notifications.php脚本,核心逻辑是:

    • 查询当前时间前后5分钟内的pending任务(时间范围可根据精度需求调整)
    • 遍历任务,调用FCM API给所有参与者推送通知
    • 更新任务状态为executed,推送失败则标记为failed(后续可加重试逻辑)

核心SQL查询示例:

$pdo = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass');
$stmt = $pdo->prepare("
    SELECT * FROM event_notification_tasks 
    WHERE push_time BETWEEN DATE_SUB(NOW(), INTERVAL 5 MINUTE) AND DATE_ADD(NOW(), INTERVAL 5 MINUTE) 
    AND status = 'pending'
");
$stmt->execute();
$tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
  1. 配置CronJob:设置CronJob每隔5分钟跑一次这个脚本:
*/5 * * * * /usr/bin/php /path/to/process_notifications.php

注意:一定要确保数据库和服务器的时区一致,最好统一用UTC时间存储,避免时区误差导致推送时间不准。

方案2:Redis延迟队列(更高精度)

如果你的服务器能安装Redis,这个方案的精度比CronJob高很多(可做到秒级触发):

  1. 存储延迟任务:赛事创建时,把推送任务(包含赛事ID、参与者令牌、推送时间)作为JSON字符串存入Redis的ZSet(有序集合),score设为推送时间的时间戳:
$redis = new Redis();
$redis->connect('localhost', 6379);
$taskData = json_encode([
    'event_id' => $eventId,
    'participant_tokens' => $tokens,
    'push_time' => $pushTimestamp
]);
$redis->zAdd('delayed_notification_tasks', $pushTimestamp, $taskData);
  1. 常驻脚本轮询执行:写一个PHP常驻脚本,不断从ZSet中取出score小于等于当前时间戳的任务,执行推送后删除该任务:
while (true) {
    $redis = new Redis();
    $redis->connect('localhost', 6379);
    $currentTime = time();
    // 取出一条待执行的任务
    $tasks = $redis->zRangeByScore('delayed_notification_tasks', 0, $currentTime, ['limit' => [0, 1]]);
    if (!empty($tasks)) {
        $taskData = json_decode($tasks[0], true);
        // 调用FCM推送通知
        sendFCMNotification($taskData['participant_tokens'], '赛事即将开始!');
        // 删除已执行的任务
        $redis->zRem('delayed_notification_tasks', $tasks[0]);
    }
    // 每秒轮询一次
    sleep(1);
}
  1. 用Supervisor管理常驻脚本:防止脚本意外退出,用Supervisor配置守护进程:
[program:event_notification_worker]
command=/usr/bin/php /path/to/redis_worker.php
autostart=true
autorestart=true
user=www-data
stdout_logfile=/var/log/event_notification_worker.log
stderr_logfile=/var/log/event_notification_worker.err.log

方案3:利用FCM自带的调度推送功能(最省心)

既然你已经在用Firebase,FCM本身就支持调度推送,完全不用自己维护任务队列,直接让Firebase帮你精准触发:

  1. 调用FCM调度API:赛事创建时,通过FCM的send API,指定sendTime参数为推送的UTC时间戳,FCM会在指定时间自动推送:
$apiKey = 'your-fcm-server-key';
$url = 'https://fcm.googleapis.com/fcm/send';

$payload = [
    'message' => [
        'topic' => 'event_' . $eventId, // 用topic群发更适合多人场景,提前让参与者订阅对应赛事topic
        'notification' => [
            'title' => '赛事提醒',
            'body' => '你参与的赛事今天开始啦!'
        ],
        'android' => [
            'notification' => [
                'channel_id' => 'sports-event-channel'
            ]
        ],
        'apns' => [
            'payload' => [
                'aps' => [
                    'alert' => [
                        'title' => '赛事提醒',
                        'body' => '你参与的赛事今天开始啦!'
                    ]
                ]
            ]
        ],
        'sendTime' => $pushTimestamp // UTC时间戳,比如赛事开始前30分钟的时间戳
    ]
];

$headers = [
    'Authorization: key=' . $apiKey,
    'Content-Type: application/json'
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));

$response = curl_exec($ch);
curl_close($ch);

注意:FCM调度推送最多支持提前28天设置,如果你的赛事有超过28天的,可能需要结合方案1或2来处理。另外,如果赛事被修改或取消,记得调用FCM的API取消对应的调度任务。

额外注意事项

  • 失败重试:不管用哪个方案,都要加重试机制——比如推送失败的任务重新加入队列,或者标记为failed后隔一段时间重试。
  • 任务取消:当赛事被修改日期或者取消时,要及时删除对应的待推送任务,避免误推。
  • 时区处理:所有时间统一用UTC存储和传递,避免不同时区导致的时间偏差。

内容的提问来源于stack exchange,提问作者Felipe Cortés

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:54:36