id; $dateKey = now()->format('Y-m-d'); $dailyCountKey = "earn_video:count:{$userId}:{$dateKey}"; $cooldownKey = "earn_video:cooldown:{$userId}"; // 1. 检查冷却时间 if (Redis::exists($cooldownKey)) { return response()->json([ 'success' => false, 'message' => '操作过快,请稍后再试。', ]); } // 2. 检查每日最大次数 $todayCount = (int) Redis::get($dailyCountKey); if ($todayCount >= $this->maxDailyLimit) { return response()->json([ 'success' => false, 'message' => '今日视频收益次数已达上限(每天最多10次),请明天再来。', ]); } // 3. 开始发放奖励并增加次数 // 增量前可能需要锁机制,但简单的 incr 在并发也不容易超量很多,且有限流 $newCount = Redis::incr($dailyCountKey); // 设置每日次数键在同一天结束时过期,留一点余量 if ($newCount === 1) { Redis::expire($dailyCountKey, 86400 * 2); } // 配置:单次 5000 金币,500 经验 $rewardCoins = 5000; $rewardExp = 500; $user->increment('jjb', $rewardCoins); $user->increment('exp_num', $rewardExp); $user->refresh(); // 刷新模型以获取 increment 后的最新字段值 // 设置冷却时间 Redis::setex($cooldownKey, $this->cooldownSeconds, 1); // 4. 检查是否升级 $levelUp = false; $newLevelName = ''; // 我们利用现有的 levelCache 或者根据 exp_num 和当前 user_level 判断 // 因为这是一个常见的游戏房逻辑,通常判断用户当前经验是否大于下一级的经验要求 // 简化起见,如果需要严格触发升级系统,可参考已有的升华逻辑。 // (在此处简化为:只要给了经验,前端自然显示就好。部分框架逻辑可能不需要在此检查升降,而是下一次进入发言时自动算) $roomId = (int) $request->input('room_id', 0); if ($roomId > 0) { $promoTag = ' 💰 看视频赚金币'; $sysMsg = [ 'id' => $this->chatState->nextMessageId($roomId), 'room_id' => $roomId, 'from_user' => '金币任务', 'to_user' => '大家', 'content' => "👍 【{$user->username}】刚刚看视频赚取了 {$rewardCoins} 金币 + {$rewardExp} 经验!{$promoTag}", 'is_secret' => false, 'font_color' => '#d97706', 'action' => '', 'sent_at' => now()->toDateTimeString(), ]; $this->chatState->pushMessage($roomId, $sysMsg); broadcast(new \App\Events\MessageSent($roomId, $sysMsg)); } $remainingToday = $this->maxDailyLimit - $newCount; return response()->json([ 'success' => true, 'message' => "观看完毕!获得 {$rewardCoins} 金币 + {$rewardExp} 经验。今日还可观看 {$remainingToday} 次。", 'new_jjb' => $user->jjb, // refresh 后的真实值 'level_up' => false, 'new_level_name' => '', ]); } }