AutomaticRewardGrantService.php 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. <?php
  2. namespace App\Services;
  3. use App\Models\Backflow;
  4. use App\Models\Rebate;
  5. use App\Support\BackflowPeriod;
  6. use Carbon\Carbon;
  7. use Illuminate\Support\Facades\DB;
  8. use Illuminate\Support\Facades\Log;
  9. use Throwable;
  10. class AutomaticRewardGrantService
  11. {
  12. public function run(): array
  13. {
  14. $result = [];
  15. foreach (RewardGrantSettingsService::TYPES as $type) {
  16. $result[$type] = ['granted' => 0, 'skipped' => 0, 'failed' => 0];
  17. $setting = DB::table('reward_grant_settings')->where('type', $type)->first();
  18. $scheduled = RewardGrantSettingsService::dueAt($setting, Carbon::now());
  19. if (!$scheduled) continue;
  20. $model = $type === 'rebate' ? Rebate::class : Backflow::class;
  21. $cutoff = $this->cutoff($type, $scheduled);
  22. $query = $model::whereIn('status', $model::OPEN_STATUSES)->where('date', '<', $cutoff);
  23. if ($type === 'backflow') {
  24. $dailyFrom = BackflowPeriod::dailyFrom();
  25. if ($dailyFrom === null || $cutoff < $dailyFrom) $query->where('date', '<', substr($cutoff, 0, 7));
  26. }
  27. $query
  28. ->chunkById(100, function ($rewards) use ($type, &$result) {
  29. foreach ($rewards as $reward) {
  30. try {
  31. $outcome = DB::transaction(function () use ($type, $reward) {
  32. // Configuration changes wait for the current single-row payout only.
  33. $setting = DB::table('reward_grant_settings')->where('type', $type)->lockForUpdate()->first();
  34. $scheduled = RewardGrantSettingsService::dueAt($setting, Carbon::now());
  35. if (!$scheduled) return 'stopped';
  36. return RewardGrantService::grant($type, (int) $reward->id, 'system:auto-grant', true, $this->cutoff($type, $scheduled))
  37. ? 'granted' : 'skipped';
  38. }, 3);
  39. if ($outcome === 'stopped') return false;
  40. ++$result[$type][$outcome];
  41. } catch (Throwable $e) {
  42. ++$result[$type]['failed'];
  43. Log::error('automatic_reward_grant_failed', ['type' => $type, 'id' => $reward->id, 'exception' => $e]);
  44. }
  45. }
  46. });
  47. }
  48. return $result;
  49. }
  50. private function cutoff(string $type, \Carbon\CarbonInterface $scheduled): string
  51. {
  52. return $type === 'rebate' ? $scheduled->copy()->setTimezone('America/New_York')->format('Y-m-d')
  53. : $scheduled->copy()->setTimezone(BackflowPeriod::TIMEZONE)->toDateString();
  54. }
  55. }