ThinkPHP6 依赖外部系统(如 Linux crontab)实现每年执行一次的定时任务,推荐使用 crontab 的 @yearly(即 0 0 1 1 *)在每年1月1日0点触发自定义命令,配合绝对路径 PHP 和日志记录确保稳定可靠。

ThinkPHP6 本身不内置“每年执行一次”的调度器(如 Laravel 的 ->yearly()),它依赖外部系统(如 Linux crontab)或第三方扩展来实现年粒度定时任务。
真正能稳定、可靠、可运维地做到“每年执行一次”,必须靠服务器级计划任务配合自定义指令。
用 crontab 实现每年执行一次
Linux 的 crontab 支持 `@yearly` 别名,等价于 `0 0 1 1 *`(每年 1 月 1 日 0 点 0 分执行)。这是最标准、最推荐的方式。示例:每年 1 月 1 日凌晨执行「年度数据归档」命令
# 编辑定时任务 crontab -e
添加这一行(请替换为你的实际路径和 PHP 命令):
0 0 1 1 * /usr/bin/php /www/wwwroot/your-project/think archive:yearly >> /www/wwwroot/your-project/runtime/log/yearly_archive.log 2>&1
✅ 关键说明:
-
/usr/bin/php:确保用绝对路径调用 PHP(可通过which php查看) -
/www/wwwroot/your-project/think:指向你的 ThinkPHP6 项目根目录下的think可执行脚本 -
archive:yearly:是你在app/command/下定义的命令类,比如ArchiveYearly,其setName('archive:yearly') -
>> ...log 2>&1:把标准输出和错误都记录到日志,方便排查(每年只写一次,体积极小)
自定义命令类示例(app/command/ArchiveYearly.php)
```php use think\console\Command; use think\console\Input; use think\console\Output;class ArchiveYearly extends Command { protected function configure() { $this->setName('archive:yearly') ->setDescription('Execute yearly data archiving and cleanup'); }
protected function execute(Input $input, Output $output)
{
// 示例:归档上一年订单、清空临时表、生成年报 PDF 等
$output->writeln('[INFO] Starting yearly archive at ' . date('Y-m-d H:i:s'));
// ✅ 这里写你的业务逻辑,例如:
// Db::name('order')->where('create_time', '<', strtotime('last year'))->chunk(500, function ($list) {
// // 批量归档到历史表
// });
$output->success('Yearly archive completed.');
}}
立即学习“PHP免费学习笔记(深入)”;
别忘了在 `config/console.php` 中注册:
```php
return [
'commands' => [
'archive:yearly' => 'app\command\ArchiveYearly',
],
];替代方案:用 shell 脚本 + 日期判断(不推荐用于生产)
虽然可行,但**易出错、难审计、不可靠**。仅作了解:# 每天检查是否是 1 月 1 日
if [ "$(date +\%m\%d)" = "0101" ]; then
/usr/bin/php /path/to/think archive:yearly >> /path/log/yearly_$(date +\%Y).log 2>&1
fi⚠️ 风险点:
- 若服务器在 1 月 1 日 0:00 附近宕机或脚本未运行,当年任务就丢失
- 多台服务器部署时可能重复执行(无分布式锁)
- 无法通过
crontab -l直观确认“是否配置了年度任务”
进阶建议:加防护与可观测性
- ✅ 添加防重入机制(如用 `flock` 包裹命令): ```bash 0 0 1 1 * flock -n /tmp/yearly_archive.lock -c "/usr/bin/php /path/think archive:yearly >> /path/log/yearly.log 2>&1" ``` - ✅ 日志按年切割(自动带年份): ```bash 0 0 1 1 * /usr/bin/php /path/think archive:yearly >> /path/log/archive_$(date +\%Y).log 2>&1 ``` - ✅ 配合宝塔面板:直接在「计划任务」中选择「每年」周期,填入命令,界面友好且自带执行记录不复杂但容易忽略



















