| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 |
- <?php
- namespace App\Services;
- use App\Models\Backflow;
- use App\Models\Rebate;
- use Carbon\Carbon;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Log;
- use Throwable;
- class AutomaticRewardGrantService
- {
- public function run(): array
- {
- $result = [];
- foreach (RewardGrantSettingsService::TYPES as $type) {
- $result[$type] = ['granted' => 0, 'skipped' => 0, 'failed' => 0];
- $setting = DB::table('reward_grant_settings')->where('type', $type)->first();
- $scheduled = RewardGrantSettingsService::dueAt($setting, Carbon::now());
- if (!$scheduled) continue;
- $model = $type === 'rebate' ? Rebate::class : Backflow::class;
- $cutoff = $this->cutoff($type, $scheduled);
- $model::where('status', 0)->where('date', '<', $cutoff)
- ->chunkById(100, function ($rewards) use ($type, &$result) {
- foreach ($rewards as $reward) {
- try {
- $outcome = DB::transaction(function () use ($type, $reward) {
- // Configuration changes wait for the current single-row payout only.
- $setting = DB::table('reward_grant_settings')->where('type', $type)->lockForUpdate()->first();
- $scheduled = RewardGrantSettingsService::dueAt($setting, Carbon::now());
- if (!$scheduled) return 'stopped';
- return RewardGrantService::grant($type, (int) $reward->id, 'system:auto-grant', true, $this->cutoff($type, $scheduled))
- ? 'granted' : 'skipped';
- }, 3);
- if ($outcome === 'stopped') return false;
- ++$result[$type][$outcome];
- } catch (Throwable $e) {
- ++$result[$type]['failed'];
- Log::error('automatic_reward_grant_failed', ['type' => $type, 'id' => $reward->id, 'exception' => $e]);
- }
- }
- });
- }
- return $result;
- }
- private function cutoff(string $type, \Carbon\CarbonInterface $scheduled): string
- {
- return $type === 'rebate' ? $scheduled->copy()->setTimezone('America/New_York')->format('Y-m-d')
- : $scheduled->copy()->setTimezone('Asia/Shanghai')->format('Y-m');
- }
- }
|