Files
chatroom/app/Http/Controllers/EarnController.php

134 lines
4.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Http\Controllers;
use App\Enums\CurrencySource;
use App\Events\MessageSent;
use App\Models\User;
use App\Services\ChatStateService;
use App\Services\UserCurrencyService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redis;
/**
* 文件功能:用户赚取金币与经验(观看视频广告等任务)控制器
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
class EarnController extends Controller
{
public function __construct(
private readonly ChatStateService $chatState,
private readonly UserCurrencyService $currencyService,
) {}
/**
* @var int 每日观看最大次数限制
*/
private int $maxDailyLimit = 3;
/**
* @var int 每次领奖后至少需要的冷却时间(秒)
*/
private int $cooldownSeconds = 5;
/**
* 申领看视频的奖励
* 成功看完视频后前端发起此请求。
* 为防止刷包,必须加上每日总次数及短时频率限制。
*/
public function claimVideoReward(Request $request): JsonResponse
{
/** @var User $user */
$user = Auth::user();
$userId = $user->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' => "今日视频收益次数已达上限(每天最多{$this->maxDailyLimit}次),请明天再来。",
]);
}
// 3. 增加今日次数计数
$newCount = Redis::incr($dailyCountKey);
if ($newCount === 1) {
Redis::expire($dailyCountKey, 86400 * 2);
}
// 4. 配置:单次 5000 金币500 经验
$rewardCoins = 5000;
$rewardExp = 500;
$roomId = (int) $request->input('room_id', 0);
// 参照钓鱼逻辑:通过 UserCurrencyService 写日志并变更金币/经验
$this->currencyService->change(
$user, 'gold', $rewardCoins, CurrencySource::VIDEO_REWARD,
"看视频赚取金币(第{$newCount}次)", $roomId,
);
$this->currencyService->change(
$user, 'exp', $rewardExp, CurrencySource::VIDEO_REWARD,
"看视频赚取经验(第{$newCount}次)", $roomId,
);
// 刷新模型以获取 service 原子更新后的最新字段值
$user->refresh();
// 5. 设置冷却时间
Redis::setex($cooldownKey, $this->cooldownSeconds, 1);
// 6. 广播全服系统消息
if ($roomId > 0) {
$promoTag = ' <span onclick="window.dispatchEvent(new CustomEvent(\'open-earn-panel\'))" '
.'style="display:inline-block;margin-left:6px;padding:1px 7px;background:#e9e4f5;'
.'color:#6d4fa8;border-radius:10px;font-size:10px;cursor:pointer;font-weight:bold;vertical-align:middle;'
.'border:1px solid #d0c4ec;" title="点击赚金币">💰 看视频赚金币</span>';
$sysMsg = [
'id' => $this->chatState->nextMessageId($roomId),
'room_id' => $roomId,
'from_user' => '系统播报',
'to_user' => '大家',
'content' => "👍 【{$user->username}】刚刚看视频赚取了 {$rewardCoins} 金币 + {$rewardExp} 经验!{$promoTag}",
'is_secret' => false,
'font_color' => '#16a34a',
'action' => '',
'sent_at' => now()->toDateTimeString(),
];
$this->chatState->pushMessage($roomId, $sysMsg);
broadcast(new MessageSent($roomId, $sysMsg));
}
$remainingToday = $this->maxDailyLimit - $newCount;
return response()->json([
'success' => true,
'message' => "观看完毕!获得 {$rewardCoins} 金币 + {$rewardExp} 经验。今日还可观看 {$remainingToday} 次。",
'new_jjb' => $user->jjb,
'level_up' => false,
'new_level_name' => '',
]);
}
}