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
相关产品推荐
相关产品推荐

