where('iStatus', 1) ->whereColumn('iYearLikeTarget', '>', 'iYearLikeAdded') ->select('id')->chunkById(100, function ($articles) use (&$total) { foreach ($articles as $article) { $total += $this->syncArticle($article->id); } }); return $total; } public function syncArticle(int $id): int { return DB::transaction(function () use ($id) { $article = DB::table('articles')->where('id', $id)->lockForUpdate()->first(); if (! $article || (int) $article->iStatus !== 1 || $article->iYearLikeTarget <= $article->iYearLikeAdded) { return 0; } $now = CarbonImmutable::now(); if (! $article->sYearLikeStartedAt) { DB::table('articles')->where('id', $id)->update(['sYearLikeStartedAt' => $now]); return 0; } $start = CarbonImmutable::parse($article->sYearLikeStartedAt); $end = $start->addYearNoOverflow(); $duration = $end->getTimestamp() - $start->getTimestamp(); $elapsed = max(0, min($duration, $now->getTimestamp() - $start->getTimestamp())); $due = intdiv((int) $article->iYearLikeTarget * $elapsed, $duration); $add = max(0, $due - (int) $article->iYearLikeAdded); if ($add > 0) { // Query-builder arithmetic preserves real/other automatic likes and content timestamps. DB::table('articles')->where('id', $id)->update([ 'iLikeCount' => DB::raw('iLikeCount + '.$add), 'iYearLikeAdded' => DB::raw('iYearLikeAdded + '.$add), 'sLastLikedAt' => $now, ]); } return $add; }, 3); } }