feat(admin): 增加玩家密码管理与角色边界

This commit is contained in:
wchino
2026-07-21 21:11:07 +08:00
parent cec2fd7c4f
commit ba54444979
32 changed files with 845 additions and 34 deletions

View File

@@ -2,11 +2,10 @@
namespace App\Services\Player;
use App\Lottery\ErrorCode;
use App\Models\Player;
use App\Support\PlayerAuthSource;
use Firebase\JWT\JWT;
use Illuminate\Support\Facades\DB;
use App\Models\Player;
use App\Lottery\ErrorCode;
use App\Support\PlayerAuthSource;
use Illuminate\Support\Facades\Hash;
use App\Exceptions\PlayerAuthenticationException;
@@ -103,6 +102,7 @@ final class PlayerNativeAuthService
$payload = [
$playerIdKey => (int) $player->id,
$authKey => PlayerAuthSource::LOTTERY_NATIVE,
'token_version' => (int) ($player->native_token_version ?? 0),
'site_code' => (string) $player->site_code,
'iat' => $now,
'exp' => $now + $ttl,

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Services\Player;
use App\Models\Player;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
final class PlayerPasswordService
{
public function change(Player $player, string $currentPassword, string $newPassword): Player
{
return DB::transaction(function () use ($player, $currentPassword, $newPassword): Player {
$locked = Player::query()->lockForUpdate()->findOrFail($player->id);
$this->assertPasswordManagedLocally($locked);
if (! is_string($locked->password_hash) || ! Hash::check($currentPassword, $locked->password_hash)) {
throw ValidationException::withMessages([
'current_password' => ['current_password_invalid'],
]);
}
if (Hash::check($newPassword, $locked->password_hash)) {
throw ValidationException::withMessages([
'password' => ['new_password_must_differ'],
]);
}
return $this->persist($locked, $newPassword);
});
}
public function reset(Player $player, string $newPassword): Player
{
return DB::transaction(function () use ($player, $newPassword): Player {
$locked = Player::query()->lockForUpdate()->findOrFail($player->id);
$this->assertPasswordManagedLocally($locked);
return $this->persist($locked, $newPassword);
});
}
private function assertPasswordManagedLocally(Player $player): void
{
if (! $player->isLotteryNative()) {
throw ValidationException::withMessages([
'password' => ['native_password_unavailable'],
]);
}
}
private function persist(Player $player, string $newPassword): Player
{
$player->forceFill([
'password_hash' => Hash::make($newPassword),
'native_token_version' => (int) $player->native_token_version + 1,
'login_failed_count' => 0,
'login_locked_until' => null,
])->save();
return $player->refresh();
}
}