| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- <?php
- namespace App\Services;
- use App\Game\GlobalUserInfo;
- use App\Game\Services\OuroGameService;
- use App\Http\helper\NumConfig;
- use App\Models\RewardCode;
- use App\Models\RewardCodeClaim;
- use Carbon\Carbon;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Str;
- class RewardCodeService
- {
- const STATUS_ACTIVE = 1;
- const STATUS_INACTIVE = 0;
- /**
- * Generate a unique 4-char alphanumeric reward code.
- */
- public static function generateUniqueCode(int $maxRetry = 5): string
- {
- for ($i = 0; $i < $maxRetry; $i++) {
- $code = strtoupper(Str::random(4));
- if (!RewardCode::where('code', $code)->exists()) {
- return $code;
- }
- }
- throw new \RuntimeException('Failed to generate unique reward code');
- }
- /**
- * Redeem reward by code.
- */
- public static function redeem(string $code, string $GlobalUID, string $clientIp = '', int $UserID = 0): array
- {
- $code = strtoupper($code);
- $now = Carbon::now();
- if (!$UserID) {
- $user = GlobalUserInfo::getGameUserInfo('GlobalUID', $GlobalUID);
- if (!$user) {
- throw new \InvalidArgumentException('user_not_exist');
- }
- $UserID = $user->UserID;
- }
- return DB::connection('write')->transaction(function () use ($code, $UserID, $GlobalUID, $clientIp, $now) {
- $rewardCode = RewardCode::where('code', $code)->lockForUpdate()->first();
- if (!$rewardCode || $rewardCode->status != self::STATUS_ACTIVE) {
- throw new \InvalidArgumentException('code_not_found');
- }
- if ($rewardCode->expire_at && $now->gt(Carbon::parse($rewardCode->expire_at))) {
- throw new \InvalidArgumentException('code_expired');
- }
- if ($rewardCode->claimed_count >= $rewardCode->total_count) {
- throw new \InvalidArgumentException('code_limit_reached');
- }
- if ($rewardCode->claimed_amount >= $rewardCode->total_amount) {
- throw new \InvalidArgumentException('code_amount_exhausted');
- }
- if (RewardCodeClaim::where('reward_code_id', $rewardCode->id)->where('UserID', $UserID)->exists()) {
- throw new \InvalidArgumentException('already_claimed');
- }
- $remainingCount = $rewardCode->total_count - $rewardCode->claimed_count;
- $remainingAmount = $rewardCode->total_amount - $rewardCode->claimed_amount;
- if ($remainingAmount < $rewardCode->min_amount) {
- throw new \InvalidArgumentException('code_amount_exhausted');
- }
- // fixed-point to support decimals (<1)
- $scale = 100;
- $min = (int)round($rewardCode->min_amount * $scale);
- $max = (int)round($rewardCode->max_amount * $scale);
- $remainingScaled = (int)floor($remainingAmount * $scale);
- $minReserve = ($remainingCount - 1) * $min;
- $maxPossible = $remainingScaled - $minReserve;
- $maxPossible = max($min, min($max, $maxPossible));
- $reward = mt_rand($min, $maxPossible) / $scale;
- $reward = round($reward, 2);
- $rewardCode->claimed_count += 1;
- $rewardCode->claimed_amount = round($rewardCode->claimed_amount + $reward, 2);
- $rewardCode->save();
- RewardCodeClaim::create([
- 'reward_code_id' => $rewardCode->id,
- 'code' => $rewardCode->code,
- 'UserID' => $UserID,
- 'GlobalUID' => $GlobalUID,
- 'amount' => $reward,
- 'client_ip' => $clientIp,
- ]);
- $scoreAmount = (int)round($reward * NumConfig::NUM_VALUE);
- OuroGameService::AddScore($UserID, $scoreAmount, OuroGameService::REASON_RewardCode,false);
- return [
- 'code' => $rewardCode->code,
- 'amount' => $reward,
- 'claimed_count' => $rewardCode->claimed_count,
- 'claimed_amount' => $rewardCode->claimed_amount,
- 'total_count' => $rewardCode->total_count,
- 'total_amount' => $rewardCode->total_amount,
- 'expire_at' => $rewardCode->expire_at,
- ];
- });
- }
- }
|