- 在多个控制器中引入 SettlementPartyEnrichment 服务,以优化代理结算和账单的处理逻辑。 - 更新 AgentSettlementBillIndexController 和 AgentSettlementBillShowController,支持根据账单 ID 和关键字进行查询。 - 在 AgentSettlementPeriodCloseController 中添加对站点管理权限的验证,确保只有具备相应权限的管理员能够关闭账期。 - 在 AgentSettlementPeriodIndexController 中更新账期数据的返回格式,提升数据的完整性和可用性。 - 引入对相对占成比例的支持,增强代理资料的管理能力,确保数据一致性。
136 lines
4.6 KiB
PHP
136 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Player;
|
|
|
|
use App\Lottery\ErrorCode;
|
|
use App\Models\Player;
|
|
use App\Support\PlayerAuthSource;
|
|
use Firebase\JWT\JWT;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use App\Exceptions\PlayerAuthenticationException;
|
|
|
|
final class PlayerNativeAuthService
|
|
{
|
|
/**
|
|
* @return array{access_token: string, expires_in: int, token_type: string, player: array<string, mixed>}
|
|
*/
|
|
public function login(string $siteCode, string $username, string $password): array
|
|
{
|
|
$username = trim($username);
|
|
$siteCode = trim($siteCode);
|
|
if ($username === '' || $password === '') {
|
|
throw new PlayerAuthenticationException(
|
|
'账号或密码错误',
|
|
ErrorCode::PlayerCredentialsInvalid->value,
|
|
);
|
|
}
|
|
|
|
if ($siteCode === '') {
|
|
$siteCode = trim((string) config('lottery.integration.default_site_code', ''));
|
|
}
|
|
|
|
$player = Player::query()
|
|
->where('site_code', $siteCode)
|
|
->where('username', $username)
|
|
->where('auth_source', PlayerAuthSource::LOTTERY_NATIVE)
|
|
->first();
|
|
|
|
if ($player === null || ! is_string($player->password_hash) || $player->password_hash === '') {
|
|
throw new PlayerAuthenticationException(
|
|
'账号或密码错误',
|
|
ErrorCode::PlayerCredentialsInvalid->value,
|
|
);
|
|
}
|
|
|
|
if ($player->login_locked_until !== null && $player->login_locked_until->isFuture()) {
|
|
throw new PlayerAuthenticationException(
|
|
'登录已锁定',
|
|
ErrorCode::PlayerLoginLocked->value,
|
|
403,
|
|
);
|
|
}
|
|
|
|
if ((int) $player->status !== 0) {
|
|
throw new PlayerAuthenticationException(
|
|
'账号已冻结',
|
|
ErrorCode::PlayerAccountSuspended->value,
|
|
403,
|
|
);
|
|
}
|
|
|
|
if (! Hash::check($password, $player->password_hash)) {
|
|
$this->recordFailedLogin($player);
|
|
|
|
throw new PlayerAuthenticationException(
|
|
'账号或密码错误',
|
|
ErrorCode::PlayerCredentialsInvalid->value,
|
|
);
|
|
}
|
|
|
|
$player->forceFill([
|
|
'login_failed_count' => 0,
|
|
'login_locked_until' => null,
|
|
'last_login_at' => now(),
|
|
])->save();
|
|
|
|
$ttl = (int) config('lottery.player_auth.native.ttl_seconds', 28800);
|
|
$token = $this->issueToken($player, $ttl);
|
|
|
|
return [
|
|
'access_token' => $token,
|
|
'expires_in' => $ttl,
|
|
'token_type' => 'Bearer',
|
|
'player' => [
|
|
'id' => (int) $player->id,
|
|
'site_code' => $player->site_code,
|
|
'username' => $player->username,
|
|
'nickname' => $player->nickname,
|
|
'funding_mode' => $player->funding_mode,
|
|
'auth_source' => $player->auth_source,
|
|
],
|
|
];
|
|
}
|
|
|
|
public function issueToken(Player $player, ?int $ttlSeconds = null): string
|
|
{
|
|
$secret = (string) config('lottery.player_auth.native.secret', '');
|
|
if ($secret === '') {
|
|
throw new PlayerAuthenticationException(
|
|
'原生登录未配置',
|
|
ErrorCode::PlayerSsoSecretNotConfigured->value,
|
|
503,
|
|
);
|
|
}
|
|
|
|
$ttl = $ttlSeconds ?? (int) config('lottery.player_auth.native.ttl_seconds', 28800);
|
|
$now = time();
|
|
$playerIdKey = (string) config('lottery.player_auth.native.claim_player_id', 'player_id');
|
|
$authKey = (string) config('lottery.player_auth.native.claim_auth_source', 'auth_source');
|
|
|
|
$payload = [
|
|
$playerIdKey => (int) $player->id,
|
|
$authKey => PlayerAuthSource::LOTTERY_NATIVE,
|
|
'site_code' => (string) $player->site_code,
|
|
'iat' => $now,
|
|
'exp' => $now + $ttl,
|
|
];
|
|
|
|
return JWT::encode($payload, $secret, (string) config('lottery.player_auth.jwt.algorithm', 'HS256'));
|
|
}
|
|
|
|
private function recordFailedLogin(Player $player): void
|
|
{
|
|
$max = (int) config('lottery.player_auth.native.max_login_attempts', 8);
|
|
$lockMinutes = (int) config('lottery.player_auth.native.lock_minutes', 15);
|
|
$count = (int) $player->login_failed_count + 1;
|
|
|
|
$updates = ['login_failed_count' => $count];
|
|
if ($count >= $max) {
|
|
$updates['login_locked_until'] = now()->addMinutes($lockMinutes);
|
|
$updates['login_failed_count'] = 0;
|
|
}
|
|
|
|
$player->forceFill($updates)->save();
|
|
}
|
|
}
|