修复聊天室字号偏好和游戏通知显示
This commit is contained in:
@@ -19,6 +19,7 @@ namespace App\Http\Controllers;
|
||||
use App\Enums\CurrencySource;
|
||||
use App\Models\GameConfig;
|
||||
use App\Models\Sysparam;
|
||||
use App\Models\User;
|
||||
use App\Services\ChatStateService;
|
||||
use App\Services\FishingService;
|
||||
use App\Services\GameRoomScopeService;
|
||||
@@ -36,6 +37,9 @@ use Illuminate\Support\Str;
|
||||
*/
|
||||
class FishingController extends Controller
|
||||
{
|
||||
/**
|
||||
* 注入钓鱼流程需要的状态、会员、金币、商店和房间范围服务。
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly ChatStateService $chatState,
|
||||
private readonly VipService $vipService,
|
||||
@@ -84,6 +88,14 @@ class FishingController extends Controller
|
||||
], 429);
|
||||
}
|
||||
|
||||
$tokenKey = "fishing:token:{$user->id}";
|
||||
if (Redis::exists($tokenKey)) {
|
||||
$activeSessionResponse = $this->restoreActiveFishingSessionResponse($user, $tokenKey);
|
||||
if ($activeSessionResponse) {
|
||||
return $activeSessionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 检查金币是否足够
|
||||
$cost = (int) (GameConfig::param('fishing', 'fishing_cost') ?? Sysparam::getValue('fishing_cost', '5'));
|
||||
if (($user->jjb ?? 0) < $cost) {
|
||||
@@ -93,34 +105,54 @@ class FishingController extends Controller
|
||||
], 422);
|
||||
}
|
||||
|
||||
// 3. 扣除金币
|
||||
$this->currencyService->change(
|
||||
$user, 'gold', -$cost,
|
||||
CurrencySource::FISHING_COST,
|
||||
"钓鱼抛竿消耗 {$cost} 金币",
|
||||
$id,
|
||||
);
|
||||
$user->refresh();
|
||||
|
||||
// 4. 生成一次性 token,存入 Redis(TTL = 等待时间 + 收竿窗口 + 缓冲)
|
||||
// 3. 生成一次性 token,存入 Redis(TTL = 等待时间 + 收竿窗口 + 缓冲)
|
||||
$waitMin = (int) (GameConfig::param('fishing', 'fishing_wait_min') ?? Sysparam::getValue('fishing_wait_min', '8'));
|
||||
$waitMax = (int) (GameConfig::param('fishing', 'fishing_wait_max') ?? Sysparam::getValue('fishing_wait_max', '15'));
|
||||
$waitTime = rand($waitMin, $waitMax);
|
||||
$token = Str::random(32);
|
||||
$tokenKey = "fishing:token:{$user->id}";
|
||||
// token 有效期 = 等待时间 + 8秒点击窗口 + 5秒缓冲
|
||||
// 同时把 cast 时间戳和 wait_time 一起存入,供 reel 做服务端时间校验
|
||||
Redis::setex($tokenKey, $waitTime + 13, json_encode([
|
||||
$tokenTtl = $waitTime + 13;
|
||||
$tokenPayload = json_encode([
|
||||
'token' => $token,
|
||||
'cast_at' => time(),
|
||||
'wait_time' => $waitTime,
|
||||
]));
|
||||
]);
|
||||
|
||||
// 5. 生成随机浮漂坐标(百分比,避开边缘)
|
||||
// 原子占用本次抛竿 token,避免多标签页自动钓鱼互相覆盖令牌。
|
||||
$reserved = Redis::command('set', [$tokenKey, $tokenPayload, 'EX', $tokenTtl, 'NX']);
|
||||
if (! $reserved) {
|
||||
$activeSessionResponse = $this->restoreActiveFishingSessionResponse($user, $tokenKey);
|
||||
if ($activeSessionResponse) {
|
||||
return $activeSessionResponse;
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => '钓鱼状态同步中,请稍后重试。',
|
||||
'retry_after' => 3,
|
||||
], 409);
|
||||
}
|
||||
|
||||
try {
|
||||
// token 占用成功后才扣金币,确保重复抛竿不会多扣费用。
|
||||
$this->currencyService->change(
|
||||
$user, 'gold', -$cost,
|
||||
CurrencySource::FISHING_COST,
|
||||
"钓鱼抛竿消耗 {$cost} 金币",
|
||||
$id,
|
||||
);
|
||||
$user->refresh();
|
||||
} catch (\Throwable $exception) {
|
||||
// 金币扣除失败时释放 token,避免用户被短时间卡在未收竿状态。
|
||||
Redis::del($tokenKey);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
// 4. 生成随机浮漂坐标(百分比,避开边缘)
|
||||
$bobberX = rand(15, 85); // 左右 15%~85%
|
||||
$bobberY = rand(20, 65); // 上下 20%~65%
|
||||
|
||||
// 6. 检查是否持有有效自动钓鱼卡
|
||||
// 5. 检查是否持有有效自动钓鱼卡
|
||||
$autoFishingMinutes = $this->shopService->getActiveAutoFishingMinutesLeft($user);
|
||||
|
||||
return response()->json([
|
||||
@@ -137,6 +169,37 @@ class FishingController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复已有钓鱼会话,避免刷新页面后丢失前端内存里的收竿令牌。
|
||||
*/
|
||||
private function restoreActiveFishingSessionResponse(User $user, string $tokenKey): ?JsonResponse
|
||||
{
|
||||
$stored = json_decode((string) Redis::get($tokenKey), true);
|
||||
if (! is_array($stored) || empty($stored['token'])) {
|
||||
Redis::del($tokenKey);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$elapsed = time() - (int) ($stored['cast_at'] ?? 0);
|
||||
$waitTime = max(0, (int) ($stored['wait_time'] ?? 0) - $elapsed);
|
||||
$autoFishingMinutes = $this->shopService->getActiveAutoFishingMinutesLeft($user);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => '已恢复正在进行的钓鱼,请等待本次收竿。',
|
||||
'wait_time' => $waitTime,
|
||||
'bobber_x' => rand(15, 85),
|
||||
'bobber_y' => rand(20, 65),
|
||||
'token' => (string) $stored['token'],
|
||||
'auto_fishing' => $autoFishingMinutes > 0,
|
||||
'auto_fishing_minutes_left' => $autoFishingMinutes,
|
||||
'cost' => 0,
|
||||
'jjb' => $user->jjb,
|
||||
'restored' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收竿 — 验证浮漂 token,随机计算钓鱼结果,更新经验/金币,广播到聊天室。
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user