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

Laravel框架下每月仅执行一次数据库操作的实现问题问询

Laravel 函数内逻辑每月仅执行一次实现方案

你需要通过持久化存储执行标记的方式实现需求,无需额外配置CRON任务,以下是两种常用实现方案:

方案1:使用Laravel缓存实现(最简便)

利用Laravel自带的缓存系统存储当月执行标记,缓存有效期设置为当月最后一天,到期自动失效即可触发下月判断。

use Carbon\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Cache\Lock; // 处理并发请求重复执行问题

public function getAllOrder()
{
    $today = Carbon::today();
    // 替换为你从业务表中读取的指定日期逻辑
    $specifiedDate = Carbon::parse(/* 表中存储的指定日期字段值 */);
    // 按年月生成唯一缓存键
    $monthCacheKey = 'monthly_order_create_' . $today->format('Y_m');

    // 同时满足日期条件、当月未执行过两个条件才运行逻辑
    if ($today->gt($specifiedDate) && !Cache::has($monthCacheKey)) {
        // 加10分钟原子锁,避免并发请求同时触发逻辑
        $lock = Cache::lock('monthly_order_create_lock', 600);
        if ($lock->get()) {
            try {
                // 你已经编写完成的数据库操作逻辑
                $this->createOrder();

                // 标记当月已执行,缓存有效期到当月最后一天23:59:59
                $expireTime = $today->copy()->endOfMonth();
                Cache::put($monthCacheKey, true, $expireTime);
            } finally {
                $lock->release();
            }
        }
    }

    // 此处保留你原本返回所有订单数据的逻辑
}

private function createOrder()
{
    // 你已实现的数据库操作代码
}

该方案优势是无需额外建表,实现成本极低;如果你的业务允许缓存清空后当月重复执行一次,完全可以满足需求。

方案2:使用数据库存储执行记录(可靠性更高)

如果需要严格保证每月仅执行一次,不受缓存清空影响,可以单独建表存储执行记录,利用唯一索引避免重复执行。

第一步:创建任务执行记录表

生成迁移文件并执行:

// 迁移文件代码
Schema::create('system_task_records', function (Blueprint $table) {
    $table->id();
    $table->string('task_key')->comment('任务唯一标识');
    $table->string('execute_year_month')->comment('执行年月,格式Y-m');
    $table->timestamps();
    // 联合唯一索引,保证每个任务每月仅存一条记录
    $table->unique(['task_key', 'execute_year_month']);
});

第二步:编写业务逻辑

use Carbon\Carbon;
use App\Models\SystemTaskRecord;
use Illuminate\Support\Facades\DB;

public function getAllOrder()
{
    $today = Carbon::today();
    $specifiedDate = Carbon::parse(/* 表中存储的指定日期字段值 */);
    $currentYm = $today->format('Y-m');
    $taskKey = 'monthly_order_create';

    if ($today->gt($specifiedDate)) {
        $hasExecuted = SystemTaskRecord::where('task_key', $taskKey)
            ->where('execute_year_month', $currentYm)
            ->exists();
        
        if (!$hasExecuted) {
            DB::beginTransaction();
            try {
                // 先插入执行记录,利用唯一索引拦截并发重复插入请求
                SystemTaskRecord::create([
                    'task_key' => $taskKey,
                    'execute_year_month' => $currentYm
                ]);

                // 执行你的数据库操作逻辑
                $this->createOrder();

                DB::commit();
            } catch (\Illuminate\Database\UniqueConstraintViolationException $e) {
                // 捕获唯一索引冲突,说明其他请求已经执行过,直接回滚即可
                DB::rollBack();
            } catch (\Exception $e) {
                // 其他业务异常回滚,下次请求会自动重试
                DB::rollBack();
                throw $e;
            }
        }
    }

    // 此处保留你原本返回所有订单数据的逻辑
}

补充注意事项

  • 所有日期判断需要和Laravel配置文件中app.timezone设置的时区保持一致,避免跨月时间判断异常。
  • 如果要求逻辑哪怕执行失败当月也不能重试,只需在捕获普通异常时不回滚执行记录的插入操作即可。

内容的提问来源于stack exchange,提问作者chablis-omoba

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 01:18:00