| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364 |
- <?php
- namespace Longyi\Gift\Services;
- use Carbon\Carbon;
- use Illuminate\Support\Facades\DB;
- use Longyi\Gift\Models\GiftCards;
- /**
- * 礼品卡过期处理。
- *
- * 判定规则:
- * - 只处理「未使用」(giftcard_status = 1)的礼品卡,已使用(2)的卡不会被覆盖;
- * - expirationdate 不为空且 <= 判定时间,才视为过期;
- * - expirationdate 为空的卡视为永久有效,不处理;
- * - 幂等:状态已是「已过期」(3)的卡不会再被命中,重复执行不会产生副作用。
- */
- class GiftCardExpiryService
- {
- /**
- * 默认每批处理条数。
- */
- public const DEFAULT_BATCH_SIZE = 500;
- /**
- * 把已过期的未使用礼品卡标记为「已过期」。
- *
- * @param Carbon|null $at 过期判定基准时间,默认当前时间
- * @param bool $dryRun true 时只统计不写库
- * @param int $batchSize 每批处理条数
- * @return int 被更新(dry-run 时为被判定过期)的礼品卡数量
- */
- public function expire(?Carbon $at = null, bool $dryRun = false, int $batchSize = self::DEFAULT_BATCH_SIZE): int
- {
- $at = $at ?: now();
- $batchSize = $batchSize > 0 ? $batchSize : self::DEFAULT_BATCH_SIZE;
- $pending = fn () => DB::table('gift_cards')
- ->where('giftcard_status', GiftCards::STATUS_UNUSED)
- ->whereNotNull('expirationdate')
- ->where('expirationdate', '<=', $at);
- if ($dryRun) {
- return (int) $pending()->count();
- }
- $total = 0;
- // 按 id 分批,避免一次性锁定/更新过多行。
- $pending()
- ->select('id')
- ->chunkById($batchSize, function ($rows) use (&$total, $at) {
- $total += DB::table('gift_cards')
- ->whereIn('id', $rows->pluck('id')->all())
- ->update([
- 'giftcard_status' => GiftCards::STATUS_EXPIRED,
- 'updated_at' => $at,
- ]);
- });
- return $total;
- }
- }
|