AutomaticRewardGrantService.php 2.4 KB

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