laowu 2 недель назад
Родитель
Сommit
70485ca7d4
1 измененных файлов с 110 добавлено и 83 удалено
  1. 110 83
      app/Services/SuperballActivityService.php

+ 110 - 83
app/Services/SuperballActivityService.php

@@ -5,6 +5,7 @@ namespace App\Services;
 use App\Facade\TableName;
 use App\Game\Services\OuroGameService;
 use App\Http\helper\NumConfig;
+use App\Models\PrivateMail;
 use Carbon\Carbon;
 use Illuminate\Database\QueryException;
 use Illuminate\Support\Facades\DB;
@@ -44,18 +45,18 @@ class SuperballActivityService
 
         $rechargeToday = $this->getUserRechargeForDate($userId, $today);
         $turnoverToday = $this->getUserTotalBetForDate($userId, $today);
-        $turnoverProgress = (int) $turnoverToday;
+        $turnoverProgress = (int)$turnoverToday;
 
         $tierConfig = $userTask ? $this->getTierConfigByTier($userTask->tier) : null;
-        $rechargeRequired = $tierConfig ? (int) $tierConfig->recharge_required : 0;
-        $turnoverRequired = $tierConfig ? (int) $tierConfig->turnover_required : 0;
+        $rechargeRequired = $tierConfig ? (int)$tierConfig->recharge_required : 0;
+        $turnoverRequired = $tierConfig ? (int)$tierConfig->turnover_required : 0;
         $rechargeDisplay = $rechargeToday;
         $turnoverDisplay = $turnoverProgress / NumConfig::NUM_VALUE;
         $taskCompleted = $tierConfig && $rechargeDisplay >= $rechargeRequired && $turnoverDisplay >= $turnoverRequired;
         if ($userTask && $userTask->status == 1) {
             $taskCompleted = $userTask->complete;
         }
-        $canClaim = $taskCompleted && $userTask && (int) $userTask->status == 0;
+        $canClaim = $taskCompleted && $userTask && (int)$userTask->status == 0;
         $canUpgrade = $userTask && $userTask->tier !== self::TIER_MAX && $taskCompleted;
         // 能升级自动升
         if ($canUpgrade && $userTask->status != 1) {
@@ -78,49 +79,63 @@ class SuperballActivityService
             if ($res['success']) {
                 $tierConfig = $this->getTierConfigByTier($res['new_tier']);
                 $userTask = $this->getUserTask($userId, $today);
-                $rechargeRequired = $tierConfig ? (int) $tierConfig->recharge_required : 0;
-                $turnoverRequired = $tierConfig ? (int) $tierConfig->turnover_required : 0;
+                $rechargeRequired = $tierConfig ? (int)$tierConfig->recharge_required : 0;
+                $turnoverRequired = $tierConfig ? (int)$tierConfig->turnover_required : 0;
                 $taskCompleted = $tierConfig && $rechargeDisplay >= $rechargeRequired && $turnoverDisplay >= $turnoverRequired;
-                $canClaim = $taskCompleted && $userTask && (int) $userTask->status === 0;
+                $canClaim = $taskCompleted && $userTask && (int)$userTask->status === 0;
             }
         }
-
+        $rechargeDisplay = min($rechargeDisplay, $tierConfig->recharge_required);
+        $turnoverDisplay = min($turnoverDisplay, $tierConfig->turnover_required);
 
         $yesterdayBalls = $this->getUserBalls($userId, $yesterday);
-        $yesterdayLucky = (int) ($yesterdayDaily->lucky_number ?? 0);
+        $yesterdayLucky = (int)($yesterdayDaily->lucky_number ?? 0);
         $yesterdayPrizeLog = $this->getUserPrizeLog($userId, $yesterday);
         $yesterdayBasePerBall = 0;
         if ($yesterdayDaily->total_balls > 0 && $yesterdayDaily->pool_amount > 0) {
-            $yesterdayBasePerBall = (int) ($yesterdayDaily->pool_amount / $yesterdayDaily->total_balls);
+            $yesterdayBasePerBall = (int)($yesterdayDaily->pool_amount / $yesterdayDaily->total_balls);
         }
-        $yesterdayMyPrize = $yesterdayPrizeLog ? (int) $yesterdayPrizeLog->total_amount : 0;
-        $yesterdayMultiplier = $yesterdayPrizeLog ? (float) $yesterdayPrizeLog->multiplier : 1.0;
+        $yesterdayMyPrize = $yesterdayPrizeLog ? (int)$yesterdayPrizeLog->total_amount : 0;
+        $yesterdayMultiplier = $yesterdayPrizeLog ? (float)$yesterdayPrizeLog->multiplier : 1.0;
         $canClaimYesterday = false;
         $yesterdayPendingPrize = 0;
         if (!$yesterdayPrizeLog && count($yesterdayBalls) > 0) {
             $canClaimYesterday = true;
             $yesterdayPendingPrize = $this->calculateYesterdayPrizeForUser($userId, $yesterday);
         }
+        if (count($yesterdayBalls) > 0) {
+            $daily = DB::table(TableName::agent() . 'superball_daily')->where('pool_date', $yesterday)->first();
+            $hitCount = $baseAmount = 0;
+            if ($daily) {
+                $basePerBall = (int)($daily->pool_amount / $daily->total_balls);
+                $hitCount = count(array_filter($yesterdayBalls, function ($item) use ($yesterdayLucky) {
+                    return $item->number == $yesterdayLucky;
+                }));
+                $baseAmount = $basePerBall * count($yesterdayBalls);
+            }
+
+            self::sendOpenRewardMailOnce($userId, count($yesterdayBalls), $yesterdayLucky, $yesterdayMultiplier, $hitCount, $baseAmount);
+        }
         // 用户昨日未领取球
         $yesterdayNotClaimedBallCount = $this->getNotClaimBallByUserIdDate($userId, $yesterday);
 
         $todayBasePerBall = 0;
         if ($todayDaily->total_balls > 0 && $todayDaily->pool_amount > 0) {
-            $todayBasePerBall = (int) ($todayDaily->pool_amount / $todayDaily->total_balls);
+            $todayBasePerBall = (int)($todayDaily->pool_amount / $todayDaily->total_balls);
         }
 
         $todayBalls = $this->getUserBalls($userId, $today);
         $todayMyBallsList = array_map(function ($b) {
-            return ['ball_index' => (int) $b->ball_index, 'number' => (int) $b->number];
+            return ['ball_index' => (int)$b->ball_index, 'number' => (int)$b->number];
         }, $todayBalls);
         $todayNumberCounts = [];
         foreach ($todayBalls as $b) {
-            $n = (int) $b->number;
+            $n = (int)$b->number;
             $todayNumberCounts[$n] = ($todayNumberCounts[$n] ?? 0) + 1;
         }
 
         // 0 点到 1 点前,前端展示的今日整体数据全部置为 0(不影响实际统计与任务进度)
-        $hour = (int) Carbon::now()->format('G'); // 0-23
+        $hour = (int)Carbon::now()->format('G'); // 0-23
         if ($hour < 1) {
             $todayDisplayPoolAmount = 0;
             $todayDisplayCompleted = 0;
@@ -129,9 +144,9 @@ class SuperballActivityService
             $todayDisplayMyBalls = $todayMyBallsList;
             $todayDisplayNumberCounts = $todayNumberCounts;
         } else {
-            $todayDisplayPoolAmount = (int) $todayDaily->pool_amount;
-            $todayDisplayCompleted = (int) ($todayDaily->completed_count ?? 0);
-            $todayDisplayTotalBalls = (int) ($todayDaily->total_balls ?? 0);
+            $todayDisplayPoolAmount = (int)$todayDaily->pool_amount;
+            $todayDisplayCompleted = (int)($todayDaily->completed_count ?? 0);
+            $todayDisplayTotalBalls = (int)($todayDaily->total_balls ?? 0);
             $todayDisplayBasePerBall = $todayBasePerBall;
             $todayDisplayMyBalls = $todayMyBallsList;
             $todayDisplayNumberCounts = $todayNumberCounts;
@@ -141,16 +156,16 @@ class SuperballActivityService
 
         $data = [
             'yesterday' => [
-                'pool_amount' => (int) $yesterdayDaily->pool_amount,
-                'pool_amount_display' => (int) $yesterdayDaily->pool_amount / NumConfig::NUM_VALUE,
+                'pool_amount' => (int)$yesterdayDaily->pool_amount,
+                'pool_amount_display' => (int)$yesterdayDaily->pool_amount / NumConfig::NUM_VALUE,
                 'base_reward_per_ball' => $yesterdayBasePerBall,
                 'base_reward_per_ball_display' => $yesterdayBasePerBall / NumConfig::NUM_VALUE,
                 'lucky_number' => $yesterdayLucky,
-                'completed_count' => (int) ($yesterdayDaily->completed_count ?? 0),
-                'total_balls' => (int) ($yesterdayDaily->total_balls ?? 0),
+                'completed_count' => (int)($yesterdayDaily->completed_count ?? 0),
+                'total_balls' => (int)($yesterdayDaily->total_balls ?? 0),
                 'my_balls' => $yesterdayBalls,
                 'my_balls_with_lucky' => array_map(function ($b) use ($yesterdayLucky) {
-                    return ['number' => (int) $b->number, 'is_lucky' => (int) $b->number === $yesterdayLucky];
+                    return ['number' => (int)$b->number, 'is_lucky' => (int)$b->number === $yesterdayLucky];
                 }, $yesterdayBalls),
                 'my_prize' => $yesterdayMyPrize,
                 'my_prize_display' => $yesterdayMyPrize / NumConfig::NUM_VALUE,
@@ -178,14 +193,14 @@ class SuperballActivityService
                 'recharge_progress' => $rechargeDisplay,
                 'turnover_progress' => $turnoverDisplay,
                 'task_completed' => $taskCompleted,
-                'status' => (int) $userTask->status,
+                'status' => (int)$userTask->status,
                 'can_claim' => $canClaim,
                 'can_upgrade' => $canUpgrade,
-                'ball_count' => $tierConfig ? (int) $tierConfig->ball_count : 0,
+                'ball_count' => $tierConfig ? (int)$tierConfig->ball_count : 0,
             ] : null,
             'multiplier' => [
-                'value' => (float) ($multiplierRow->multiplier ?? 1.0),
-                'consecutive_days' => (int) ($multiplierRow->consecutive_days ?? 0),
+                'value' => (float)($multiplierRow->multiplier ?? 1.0),
+                'consecutive_days' => (int)($multiplierRow->consecutive_days ?? 0),
                 'min' => self::MULTIPLIER_MIN,
                 'max' => self::MULTIPLIER_MAX,
                 'step' => self::MULTIPLIER_STEP,
@@ -212,11 +227,11 @@ class SuperballActivityService
         }
 
         // 从 YN_VIPAccount 读取累计充值金额,交由 VipService 计算 VIP 等级
-        $userRecharge = (int) DB::table('QPAccountsDB.dbo.YN_VIPAccount')
+        $userRecharge = (int)DB::table('QPAccountsDB.dbo.YN_VIPAccount')
             ->where('UserID', $userId)
             ->value('Recharge');
 
-        return (int) VipService::calculateVipLevel($userId, $userRecharge);
+        return (int)VipService::calculateVipLevel($userId, $userRecharge);
     }
 
     /**
@@ -259,7 +274,7 @@ class SuperballActivityService
         if (!$task) {
             return ['success' => false, 'message' => ['web.superball.no_task_today', 'No task for today']];
         }
-        if ((int) $task->status === 1) {
+        if ((int)$task->status === 1) {
             return ['success' => false, 'message' => ['web.superball.already_claimed', 'Already claimed']];
         }
 
@@ -273,8 +288,8 @@ class SuperballActivityService
         $rechargeToday = $this->getUserRechargeForDate($userId, $today);
         $turnoverToday = $this->getUserTotalBetForDate($userId, $today);
         $newConfig = $this->getTierConfigByTier($task->tier);
-        $rechargeOk = $rechargeToday >= (int) $newConfig->recharge_required;
-        $turnoverOk = ($turnoverToday / NumConfig::NUM_VALUE) >= (int) $newConfig->turnover_required;
+        $rechargeOk = $rechargeToday >= (int)$newConfig->recharge_required;
+        $turnoverOk = ($turnoverToday / NumConfig::NUM_VALUE) >= (int)$newConfig->turnover_required;
 
 //        var_dump($rechargeToday,$turnoverToday,$newConfig);
         if (!$rechargeOk || !$turnoverOk) {
@@ -285,7 +300,7 @@ class SuperballActivityService
             ->where('user_id', $userId)
             ->where('task_date', $today)
             ->update(['tier' => $newTier, 'updated_at' => now()->format('Y-m-d H:i:s')]);
-        return ['success' => true,'user_id' => $userId, 'new_tier' => $newTier,'task_date'=>$today];
+        return ['success' => true, 'user_id' => $userId, 'new_tier' => $newTier, 'task_date' => $today];
     }
 
     /**
@@ -315,7 +330,7 @@ class SuperballActivityService
                 'message' => 'Claim success, please select numbers for your balls',
             ];
         }
-        if ((int) $task->status === 1) {
+        if ((int)$task->status === 1) {
             return ['success' => false, 'message' => ['web.superball.already_claimed', 'Already claimed']];
         }
 
@@ -325,8 +340,8 @@ class SuperballActivityService
         }
         $rechargeToday = $this->getUserRechargeForDate($userId, $today);
         $turnoverToday = $this->getUserTotalBetForDate($userId, $today);
-        $rechargeOk = ($rechargeToday) >= (int) $tierConfig->recharge_required;
-        $turnoverOk = ($turnoverToday / NumConfig::NUM_VALUE) >= (int) $tierConfig->turnover_required;
+        $rechargeOk = ($rechargeToday) >= (int)$tierConfig->recharge_required;
+        $turnoverOk = ($turnoverToday / NumConfig::NUM_VALUE) >= (int)$tierConfig->turnover_required;
         $complete = 1;
         if (!$rechargeOk || !$turnoverOk) {
             // 本档没完成
@@ -337,7 +352,7 @@ class SuperballActivityService
         if ($complete) {
             $ballCount = $tierConfig->ball_count;
         } else if ($tierConfig->tier !== 'E') { // 没完成获取上一级奖励
-            $idx = $tierConfig->sort_index+1;
+            $idx = $tierConfig->sort_index + 1;
             $configs = $this->getTierConfig();
             foreach ($configs as $config) {
                 if ($config['sort_index'] == $idx) {
@@ -355,7 +370,7 @@ class SuperballActivityService
                 ->where('user_id', $userId)
                 ->where('task_date', $today)
                 ->update(['status' => 1, 'complete' => $complete, 'updated_at' => now()->format('Y-m-d H:i:s')]);
-
+            $this->getOrCreateDaily($today);
             DB::connection('write')->table(TableName::agent() . 'superball_daily')
                 ->where('pool_date', $today)
                 ->update([
@@ -382,6 +397,7 @@ class SuperballActivityService
             return false;
         }
         $today = Carbon::today()->format('Y-m-d');
+        $this->getOrCreateDaily($today);
         DB::connection('write')->table(TableName::agent() . 'superball_daily')
             ->where('pool_date', $today)
             ->update([
@@ -400,7 +416,7 @@ class SuperballActivityService
     public function submitNumbers(int $userId, array $numbers): array
     {
         $key = sprintf('claim_vip_reward_%s_%s', $userId, date('Ymd'));
-        $vipFreeBalls = (int) Redis::get($key);
+        $vipFreeBalls = (int)Redis::get($key);
         $vipLevel = $this->getUserVipLevel($userId);
         $level = VipService::getVipByField('VIP', $vipLevel);
         $vipFreeBalls2 = $level ? ($level->SuperballNum ?? 0) : 0;
@@ -414,7 +430,7 @@ class SuperballActivityService
             if ($task->complete == 1) {
                 $ballCount = $tierConfig->ball_count;
             } else if ($tierConfig->tier !== 'E') { // 没完成获取上一级奖励
-                $idx = $tierConfig->sort_index+1;
+                $idx = $tierConfig->sort_index + 1;
                 $configs = $this->getTierConfig();
                 foreach ($configs as $config) {
                     if ($config['sort_index'] == $idx) {
@@ -444,7 +460,7 @@ class SuperballActivityService
         DB::connection('write')->transaction(function () use ($userId, $today, $numbers) {
             // 写入用户今日所有球号
             foreach ($numbers as $index => $num) {
-                $n = (int) $num;
+                $n = (int)$num;
                 if ($n < 0 || $n > 9) {
                     throw new \RuntimeException('Number must be 0-9');
                 }
@@ -464,10 +480,10 @@ class SuperballActivityService
                 ->first();
 
             if ($daily) {
-                $luckyNumber = (int) $daily->lucky_number;
+                $luckyNumber = (int)$daily->lucky_number;
                 $matched = 0;
                 foreach ($numbers as $num) {
-                    if ((int) $num === $luckyNumber) {
+                    if ((int)$num === $luckyNumber) {
                         $matched++;
                     }
                 }
@@ -497,7 +513,7 @@ class SuperballActivityService
             ->orderBy('ball_index')
             ->get();
         return array_map(function ($r) {
-            return ['ball_index' => (int) $r->ball_index, 'number' => (int) $r->number];
+            return ['ball_index' => (int)$r->ball_index, 'number' => (int)$r->number];
         }, $rows->all());
     }
 
@@ -520,26 +536,26 @@ class SuperballActivityService
         }
 
         $daily = DB::table(TableName::agent() . 'superball_daily')->where('pool_date', $yesterday)->first();
-        if (!$daily || (int) $daily->total_balls <= 0) {
+        if (!$daily || (int)$daily->total_balls <= 0) {
             return ['success' => false, 'message' => ['web.superball.pool_not_ready', 'Yesterday pool not ready']];
         }
 
-        $basePerBall = (int) ($daily->pool_amount / $daily->total_balls);
-        $luckyNumber = (int) $daily->lucky_number;
+        $basePerBall = (int)($daily->pool_amount / $daily->total_balls);
+        $luckyNumber = (int)$daily->lucky_number;
         $multiplierRow = $this->getUserMultiplier($userId);
-        $multiplier = (float) ($multiplierRow->multiplier ?? 1.0);
+        $multiplier = (float)($multiplierRow->multiplier ?? 1.0);
 
         // 选号可重复:每个球单独比对幸运号,中奖球数 = 号码等于幸运号的球个数(同一号码可多球)
         $matched = 0;
         foreach ($balls as $b) {
-            if ((int) $b->number === $luckyNumber) {
+            if ((int)$b->number === $luckyNumber) {
                 $matched++;
             }
         }
         $baseAmount = $basePerBall * count($balls);
         $luckyAmountDisplay = self::LUCKY_REWARD_PER_BALL * $matched;
         $luckyAmountInternal = $luckyAmountDisplay * NumConfig::NUM_VALUE;
-        $totalAmount = (int) round($baseAmount * $multiplier) + $luckyAmountInternal;
+        $totalAmount = (int)round($baseAmount * $multiplier) + $luckyAmountInternal;
 
         DB::connection('write')->table(TableName::agent() . 'superball_prize_log')->insert([
             'user_id' => $userId,
@@ -567,27 +583,27 @@ class SuperballActivityService
     public function calculateYesterdayPrizeForUser(int $userId, string $yesterday): int
     {
         $daily = DB::table(TableName::agent() . 'superball_daily')->where('pool_date', $yesterday)->first();
-        if (!$daily || (int) $daily->total_balls <= 0) {
+        if (!$daily || (int)$daily->total_balls <= 0) {
             return 0;
         }
         $balls = $this->getUserBalls($userId, $yesterday);
         if (count($balls) === 0) {
             return 0;
         }
-        $basePerBall = (int) ($daily->pool_amount / $daily->total_balls);
-        $luckyNumber = (int) $daily->lucky_number;
+        $basePerBall = (int)($daily->pool_amount / $daily->total_balls);
+        $luckyNumber = (int)$daily->lucky_number;
         $multiplierRow = $this->getUserMultiplier($userId);
-        $multiplier = (float) ($multiplierRow->multiplier ?? 1.0);
+        $multiplier = (float)($multiplierRow->multiplier ?? 1.0);
         // 选号可重复:按球逐个比对幸运号,中奖球数 = 号码等于幸运号的球个数
         $matched = 0;
         foreach ($balls as $b) {
-            if ((int) $b->number === $luckyNumber) {
+            if ((int)$b->number === $luckyNumber) {
                 $matched++;
             }
         }
         $baseAmount = $basePerBall * count($balls);
         $luckyAmountInternal = self::LUCKY_REWARD_PER_BALL * $matched * NumConfig::NUM_VALUE;
-        return (int) round($baseAmount * $multiplier) + $luckyAmountInternal;
+        return (int)round($baseAmount * $multiplier) + $luckyAmountInternal;
     }
 
     /**
@@ -644,27 +660,21 @@ class SuperballActivityService
      * @param $date
      * @return int
      */
-    public function getNotClaimBallByUserIdDate($userId, $date) :int
+    public function getNotClaimBallByUserIdDate($userId, $date): int
     {
         $cacheKey = sprintf('superball_yesterday_not_claim_%d_%s', $userId, $date);
         if (Redis::exists($cacheKey)) {
             $ball = Redis::get($cacheKey);
-            return (int) $ball;
+            return (int)$ball;
         }
         $ball = 0;
         $task = $this->getUserTask($userId, $date);
         if ($task && $task->status == 0) {
+            $recharge = $this->getUserRechargeForDate($userId, $date);
+            $turnover = $this->getUserTotalBetForDate($userId, $date);
             $config = $this->getTierConfigByTier($task->tier);
-            if ($task->complete) {
+            if ($recharge >= $config->recharge_required && $turnover / NumConfig::NUM_VALUE >= $config->turnover_required) {
                 $ball = $config->ball_count;
-            } else if ($task->tier != 'E') {
-                $idx = $config->sort_index+1;
-                $configs = $this->getTierConfig();
-                foreach ($configs as $config) {
-                    if ($config['sort_index'] == $idx) {
-                        $ball += $config['ball_count'];
-                    }
-                }
             }
         }
         Redis::set($cacheKey, $ball);
@@ -684,9 +694,9 @@ class SuperballActivityService
             return [
                 'sort_index' => $r->sort_index,
                 'tier' => $r->tier,
-                'recharge_required' => (int) $r->recharge_required,
-                'turnover_required' => (int) $r->turnover_required,
-                'ball_count' => (int) $r->ball_count,
+                'recharge_required' => (int)$r->recharge_required,
+                'turnover_required' => (int)$r->turnover_required,
+                'ball_count' => (int)$r->ball_count,
             ];
         }, $rows->all());
     }
@@ -711,7 +721,7 @@ class SuperballActivityService
             ->where('UserID', $userId)
             ->where('DateID', $dateId)
             ->first();
-        return $row ? (int) $row->Recharge : 0;
+        return $row ? (int)$row->Recharge : 0;
     }
 
     /** 当日流水:从按日统计表取当天 TotalBet(内部单位) */
@@ -722,7 +732,7 @@ class SuperballActivityService
             ->where('UserID', $userId)
             ->where('DateID', $dateId)
             ->first();
-        return $row && isset($row->TotalBet) ? (int) $row->TotalBet : 0;
+        return $row && isset($row->TotalBet) ? (int)$row->TotalBet : 0;
     }
 
     private function getUserTotalBet(int $userId): int
@@ -730,7 +740,7 @@ class SuperballActivityService
         $row = DB::table(TableName::QPRecordDB() . 'RecordUserTotalStatistics')
             ->where('UserID', $userId)
             ->first();
-        return $row ? (int) $row->TotalBet : 0;
+        return $row ? (int)$row->TotalBet : 0;
     }
 
     private function getUserBalls(int $userId, string $date): array
@@ -743,7 +753,7 @@ class SuperballActivityService
             ->all();
     }
 
-    private function    getUserMultiplier(int $userId): ?\stdClass
+    private function getUserMultiplier(int $userId): ?\stdClass
     {
         return DB::table(TableName::agent() . 'superball_user_multiplier')->where('user_id', $userId)->first();
     }
@@ -768,7 +778,7 @@ class SuperballActivityService
             ->get();
         $map = [];
         foreach ($rows as $r) {
-            $map[$r->pool_date] = (int) $r->lucky_number;
+            $map[$r->pool_date] = (int)$r->lucky_number;
         }
         return array_map(function ($d) use ($map) {
             return ['date' => $d, 'lucky_number' => $map[$d] ?? null];
@@ -786,19 +796,19 @@ class SuperballActivityService
         $consecutive = 1;
 
         if ($row) {
-            $last = $row->last_task_date ? (string) $row->last_task_date : null;
+            $last = $row->last_task_date ? (string)$row->last_task_date : null;
             $prev = Carbon::parse($taskDate)->subDay()->format('Y-m-d');
             if ($last === $prev) {
-                $consecutive = (int) $row->consecutive_days + 1;
-                $multiplier = min(self::MULTIPLIER_MAX, (float) $row->multiplier + self::MULTIPLIER_STEP);
+                $consecutive = (int)$row->consecutive_days + 1;
+                $multiplier = min(self::MULTIPLIER_MAX, (float)$row->multiplier + self::MULTIPLIER_STEP);
             } elseif ($last !== null && $last !== $taskDate) {
-                $daysDiff = (int) Carbon::parse($taskDate)->diffInDays(Carbon::parse($last));
+                $daysDiff = (int)Carbon::parse($taskDate)->diffInDays(Carbon::parse($last));
                 if ($daysDiff > 1) {
-                    $multiplier = max(self::MULTIPLIER_MIN, (float) $row->multiplier - self::MULTIPLIER_STEP);
+                    $multiplier = max(self::MULTIPLIER_MIN, (float)$row->multiplier - self::MULTIPLIER_STEP);
                     $consecutive = 1;
                 } else {
-                    $multiplier = (float) $row->multiplier;
-                    $consecutive = (int) $row->consecutive_days + 1;
+                    $multiplier = (float)$row->multiplier;
+                    $consecutive = (int)$row->consecutive_days + 1;
                 }
             }
         }
@@ -813,4 +823,21 @@ class SuperballActivityService
             ]
         );
     }
+
+    private function sendOpenRewardMailOnce($userId, $ballCount, $luckyNumber, $multiplier, $hitCount, $basePrize): void
+    {
+        $existsKey = sprintf('superball_open_%s_%s', $userId, date('Ymd'));
+        if (Redis::exists($existsKey)) {
+            return;
+        }
+        PrivateMail::sendMail(
+            2,
+            $userId,
+            'Your Lucky Ball Results!',
+            "Yesterday you earned {$ballCount} balls and your lucky number was {$luckyNumber}. 
+            You matched {$hitCount} balls, winning a base prize of {$basePrize} with a {$multiplier}x reward multiplier.",
+            '',
+            '');
+        Redis::setex($existsKey, 86400 * 2, 1);
+    }
 }