本文目录导读:

- 方案一:数据库查询 + 前端/页面拦截(被动触发,简单高效)
- 方案二:Linux Crontab + PHP CLI 脚本(主动推送,最常用)
- 方案三:Redis 过期键通知(高并发、实时性高)
- 方案四:Chronos / Laravel Task Scheduling(框架自带定时任务)
- 必须处理的关键坑点
- 总结建议
在PHP项目中实现效期预警,通常有被动触发(用户访问时检查)和主动推送(定时任务或消息队列)两种模式,最常见的场景是会员过期提醒、商品保质期预警、合同到期提醒等。
以下是几种主流、实用的实现方案:
数据库查询 + 前端/页面拦截(被动触发,简单高效)
适用场景: 用户每次登录或访问特定页面时进行检查(登录后提示会员即将过期、后台管理系统首页展示即将到期的订单)。
实现逻辑:
- 数据表设计:存储实体的
end_time(到期时间)和status(状态)。 - SQL查询:在用户请求时,查询
WHERE end_time BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 30 DAY),这里的30 DAY就是预警期。 - 代码示例(ThinkPHP/Laravel 风格):
<?php
// 控制器中
public function index()
{
$userId = session('user_id');
// 查询即将在7天内过期的会员
$expiringMembers = Db::table('member')
->where('user_id', $userId)
->where('status', 1)
->where('end_time', '>=', date('Y-m-d H:i:s'))
->where('end_time', '<=', date('Y-m-d H:i:s', strtotime('+7 days')))
->select();
if (!empty($expiringMembers)) {
// 设置警告信息到视图
view()->share('warning', '您的会员将在 ' . $expiringMembers[0]['end_time'] . ' 到期,请及时续费!');
}
return view('index');
}
Linux Crontab + PHP CLI 脚本(主动推送,最常用)
适用场景: 需要定时发送邮件、短信、站内信(每天早上10点给快过期用户发短信),这是企业级系统最推荐的方案。
实现步骤:
- 编写PHP脚本(
expire_warning.php),运行于命令行(CLI模式)。
<?php
// expire_warning.php (放在项目根目录或cli目录)
require_once 'vendor/autoload.php'; // 引入框架或自动加载
use App\Models\Member;
use App\Services\SmsService;
// 1. 查询所有3天后过期的用户
$threeDaysLater = date('Y-m-d H:i:s', strtotime('+3 days'));
$members = Member::where('status', 1)
->where('end_time', '<=', $threeDaysLater)
->where('end_time', '>', date('Y-m-d H:i:s')) // 确保未过期
->where('warning_sent', 0) // 避免重复发送(重要!)
->get();
// 2. 循环处理
foreach ($members as $member) {
try {
// 发送短信或邮件
SmsService::sendExpireWarning($member->phone, $member->end_time);
// 3. 标记已发送(防止重复预警)
$member->warning_sent = 1;
$member->save();
// 记录日志
Log::info("已向用户 {$member->id} 发送过期预警");
} catch (\Exception $e) {
Log::error("发送失败: " . $e->getMessage());
}
}
- 设置Linux定时任务(Crontab)
在服务器终端执行 crontab -e,添加如下规则(每天上午9点执行):
# 每天 9:00 执行 0 9 * * * /usr/bin/php /var/www/html/expire_warning.php >> /var/log/expire_warning.log 2>&1 # 如果是生产环境,建议使用绝对路径 # 或者使用 docker exec 如果是容器化部署
关键点:
- 防重复:永远要在数据库里加一个
warning_sent字段或last_warning_time字段,避免同一个用户一天收到10条短信。 - 时区:确保服务器时区设置正确(
date.timezone = Asia/Shanghai)。
Redis 过期键通知(高并发、实时性高)
适用场景: 需要实时推送(如聊天消息、订单自动关闭提醒)。
实现逻辑:
- 当会员创建/续费时,向Redis设置一个过期键。
// 会员到期时间戳 $expireTimestamp = strtotime('2025-06-01 00:00:00'); // 设置一个键,在到期时自动删除并触发通知 $redis->setex('member_expire_'.$userId, $expireTimestamp - time(), $userId); - 配置Redis的
notify-keyspace-events为Ex(开启过期事件通知)。 - PHP订阅频道:
// 开启一个常驻进程或使用Swoole/Workerman $redis->subscribe(['__keyevent@0__:expired'], function($instance, $channel, $key) { // $key 格式为 member_expire_123 preg_match('/member_expire_(\d+)/', $key, $matches); $userId = $matches[1]; // 发送消息或处理过期逻辑 echo "用户 {$userId} 已过期!"; });优点: 精准、实时。 缺点: 依赖Redis、需要常驻进程(Swoole/Workerman)、不适合复杂逻辑。
Chronos / Laravel Task Scheduling(框架自带定时任务)
适用场景: 使用Laravel、ThinkPHP 6.0+等现代框架的项目。
Laravel示例(App\Console\Kernel.php):
protected function schedule(Schedule $schedule)
{
// 每天凌晨2点执行过期预警
$schedule->command('members:expire-warning')
->dailyAt('02:00')
->withoutOverlapping(); // 防止重复执行
}
// 然后定义命令 php artisan make:command MembersExpireWarning
public function handle()
{
// 查询即将过期的会员并发送通知
}
运行:需要在服务器上设置一个每分钟执行的Crontab:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
必须处理的关键坑点
-
精度问题:
- 日级别:还有3天过期”,使用
DATE_DIFF或strtotime计算天数差。 - 小时/分钟级别:必须使用
timestamp或datetime类型,精确计算秒差。
- 日级别:还有3天过期”,使用
-
时间长短:
- 会员过期:一般是提前 3天、1天、1小时。
- 商品质保:可能提前 30天、15天。
- 建议将预警规则配置化(保存在数据库或配置文件),不要写死在代码里。
-
状态判断:
- 已过期(
status=0或end_time < now)的逻辑通常和“即将过期” (end_time > now且end_time < now + 预警期)分开处理。 - 用户已过期,登录时直接跳转到续费页面;用户即将过期,在页面顶部显示黄条警告。
- 已过期(
-
高并发下的重复执行:
- 定时任务脚本中,使用
DB::raw('UPDATE ... WHERE warned=0')加affected_rows判断,或者使用 Redis锁。// 加锁防止并发 $lockKey = 'expire_warning_lock'; if (Redis::setnx($lockKey, time())) { Redis::expire($lockKey, 60); // 60秒超时 // ... 执行逻辑 Redis::del($lockKey); }
- 定时任务脚本中,使用
-
测试与监听:
- 先对少量测试用户发送预警,观察日志无误后再全量推送。
- 监控脚本执行时间,如果数据量过大(10万+条),请使用批量处理(
chunk)或队列。
总结建议
| 项目阶段 | 推荐方案 | 理由 |
|---|---|---|
| 小型项目/创业初期 | 方案一(页面拦截) + 简单Crontab | 成本低、容易实现 |
| 中型企业项目 | 方案二(Crontab+PHP CLI脚本) | 稳定性高、可控性强 |
| 高并发/微服务 | 方案三(Redis过期通知)+ 方案四(Laravel任务) | 实时性好、架构优雅 |
最佳实践组合:
- Web端:使用 方案一 做被动提醒(用户查看页面时显示倒计时)。
- 后端:使用 方案二 + 方案四 做主动推送(发短信、写日志)。
- 防逃逸:在数据库层面使用
event或job标记预警状态,避免用户刷新页面时重复触发通知。