- Changed super admin detection from role-based to `is_super_admin` flag in AdminUser model
- Added `requireDefaultAdminSiteId()` method to throw validation error when no integration site exists
- Enhanced site deletion to migrate platform role bindings to fallback site and auto-delete site-specific admin accounts
- Made agent line code optional with auto-generation fallback using `{site_code}-agent-{counter}` format
240 lines
9.2 KiB
PHP
240 lines
9.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Integration;
|
|
|
|
use App\Models\AdminSite;
|
|
use App\Models\AdminRole;
|
|
use App\Models\AdminUser;
|
|
use App\Models\Player;
|
|
use App\Support\SitePlatformRole;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
final class IntegrationSiteService
|
|
{
|
|
public function __construct(
|
|
private readonly PartnerSiteConfigResolver $configResolver,
|
|
) {}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
* @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}, admin_user: AdminUser}
|
|
*/
|
|
public function create(array $data): array
|
|
{
|
|
$secrets = $this->generateSecrets();
|
|
|
|
['site' => $site, 'admin_user' => $adminUser] = DB::transaction(function () use ($data, $secrets): array {
|
|
/** @var array{username: string, nickname: string, password: string, email?: string|null} $adminAccount */
|
|
$adminAccount = $data['admin_account'];
|
|
|
|
$site = AdminSite::query()->create([
|
|
'code' => (string) $data['code'],
|
|
'name' => (string) $data['name'],
|
|
'currency_code' => (string) ($data['currency_code'] ?? 'NPR'),
|
|
'status' => (int) ($data['status'] ?? 1),
|
|
'is_default' => false,
|
|
'wallet_api_url' => $this->nullableTrim($data['wallet_api_url'] ?? null),
|
|
'wallet_debit_path' => (string) ($data['wallet_debit_path'] ?? '/wallet/debit-for-lottery'),
|
|
'wallet_credit_path' => (string) ($data['wallet_credit_path'] ?? '/wallet/credit-from-lottery'),
|
|
'wallet_balance_path' => (string) ($data['wallet_balance_path'] ?? '/wallet/balance'),
|
|
'wallet_timeout_seconds' => max(1, (int) ($data['wallet_timeout_seconds'] ?? 10)),
|
|
'iframe_allowed_origins' => $data['iframe_allowed_origins'] ?? null,
|
|
'lottery_h5_base_url' => $this->nullableTrim($data['lottery_h5_base_url'] ?? null),
|
|
'notes' => $this->nullableTrim($data['notes'] ?? null),
|
|
'sso_jwt_secret_encrypted' => encrypt($secrets['sso_jwt_secret']),
|
|
'wallet_api_key_encrypted' => encrypt($secrets['wallet_api_key']),
|
|
]);
|
|
|
|
$role = SitePlatformRole::resolve();
|
|
$adminUser = $this->createSiteAdminUser($site, $role, $adminAccount);
|
|
|
|
return [
|
|
'site' => $site,
|
|
'admin_user' => $adminUser,
|
|
];
|
|
});
|
|
|
|
$this->configResolver->forgetCache((string) $site->code);
|
|
|
|
return [
|
|
'site' => $site->fresh(),
|
|
'secrets' => $secrets,
|
|
'admin_user' => $adminUser->fresh(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
public function update(AdminSite $site, array $data): AdminSite
|
|
{
|
|
$site->fill([
|
|
'name' => (string) $data['name'],
|
|
'currency_code' => (string) ($data['currency_code'] ?? $site->currency_code),
|
|
'status' => (int) ($data['status'] ?? $site->status),
|
|
'wallet_api_url' => array_key_exists('wallet_api_url', $data)
|
|
? $this->nullableTrim($data['wallet_api_url'])
|
|
: $site->wallet_api_url,
|
|
'wallet_debit_path' => (string) ($data['wallet_debit_path'] ?? $site->wallet_debit_path),
|
|
'wallet_credit_path' => (string) ($data['wallet_credit_path'] ?? $site->wallet_credit_path),
|
|
'wallet_balance_path' => (string) ($data['wallet_balance_path'] ?? $site->wallet_balance_path),
|
|
'wallet_timeout_seconds' => max(1, (int) ($data['wallet_timeout_seconds'] ?? $site->wallet_timeout_seconds)),
|
|
'iframe_allowed_origins' => $data['iframe_allowed_origins'] ?? $site->iframe_allowed_origins,
|
|
'lottery_h5_base_url' => array_key_exists('lottery_h5_base_url', $data)
|
|
? $this->nullableTrim($data['lottery_h5_base_url'])
|
|
: $site->lottery_h5_base_url,
|
|
'notes' => array_key_exists('notes', $data)
|
|
? $this->nullableTrim($data['notes'])
|
|
: $site->notes,
|
|
]);
|
|
$site->save();
|
|
|
|
$this->configResolver->forgetCache((string) $site->code);
|
|
|
|
return $site->fresh();
|
|
}
|
|
|
|
/**
|
|
* @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}}
|
|
*/
|
|
public function destroy(AdminSite $site): void
|
|
{
|
|
$siteCode = (string) $site->code;
|
|
$siteId = (int) $site->id;
|
|
$siteAdminRoleId = SitePlatformRole::resolve()->id;
|
|
|
|
if (AdminSite::query()->count() <= 1) {
|
|
$fallbackSiteId = null;
|
|
} else {
|
|
$fallbackSiteId = (int) AdminSite::query()
|
|
->where('id', '!=', $siteId)
|
|
->orderBy('id')
|
|
->value('id');
|
|
}
|
|
|
|
DB::transaction(function () use ($site, $siteCode, $siteId, $siteAdminRoleId, $fallbackSiteId): void {
|
|
$superRoleId = AdminRole::query()
|
|
->where('slug', AdminUser::ROLE_SUPER_ADMIN)
|
|
->value('id');
|
|
|
|
$platformBindings = DB::table('admin_user_site_roles')
|
|
->where('site_id', $siteId)
|
|
->when($siteAdminRoleId !== null, static fn ($query) => $query->where('role_id', '!=', $siteAdminRoleId))
|
|
->when($superRoleId !== null, static fn ($query) => $query->where('role_id', '!=', $superRoleId))
|
|
->get(['admin_user_id', 'role_id', 'granted_at']);
|
|
|
|
foreach ($platformBindings as $binding) {
|
|
if ($fallbackSiteId === null) {
|
|
continue;
|
|
}
|
|
|
|
DB::table('admin_user_site_roles')->updateOrInsert(
|
|
[
|
|
'admin_user_id' => (int) $binding->admin_user_id,
|
|
'site_id' => $fallbackSiteId,
|
|
'role_id' => (int) $binding->role_id,
|
|
],
|
|
['granted_at' => $binding->granted_at ?? now()],
|
|
);
|
|
}
|
|
|
|
Player::query()->where('site_code', $siteCode)->delete();
|
|
|
|
if ($siteAdminRoleId !== null) {
|
|
$siteAdminUserIds = DB::table('admin_user_site_roles')
|
|
->where('site_id', $siteId)
|
|
->where('role_id', $siteAdminRoleId)
|
|
->pluck('admin_user_id');
|
|
|
|
foreach ($siteAdminUserIds as $userId) {
|
|
$bindings = DB::table('admin_user_site_roles')
|
|
->where('admin_user_id', $userId)
|
|
->get(['site_id', 'role_id']);
|
|
|
|
$onlyAutoSiteAdmin = $bindings->count() === 1
|
|
&& (int) $bindings[0]->site_id === $siteId
|
|
&& (int) $bindings[0]->role_id === (int) $siteAdminRoleId;
|
|
|
|
if ($onlyAutoSiteAdmin) {
|
|
AdminUser::query()->where('id', $userId)->delete();
|
|
}
|
|
}
|
|
}
|
|
|
|
$site->delete();
|
|
});
|
|
|
|
$this->configResolver->forgetCache($siteCode);
|
|
}
|
|
|
|
/**
|
|
* @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}}
|
|
*/
|
|
public function rotateSecrets(AdminSite $site): array
|
|
{
|
|
$secrets = $this->generateSecrets();
|
|
|
|
$site->forceFill([
|
|
'sso_jwt_secret_encrypted' => encrypt($secrets['sso_jwt_secret']),
|
|
'wallet_api_key_encrypted' => encrypt($secrets['wallet_api_key']),
|
|
])->save();
|
|
|
|
$this->configResolver->forgetCache((string) $site->code);
|
|
|
|
return ['site' => $site->fresh(), 'secrets' => $secrets];
|
|
}
|
|
|
|
/**
|
|
* @return array{sso_jwt_secret: string, wallet_api_key: string}
|
|
*/
|
|
private function generateSecrets(): array
|
|
{
|
|
return [
|
|
'sso_jwt_secret' => Str::random(48),
|
|
'wallet_api_key' => Str::random(40),
|
|
];
|
|
}
|
|
|
|
private function nullableTrim(mixed $value): ?string
|
|
{
|
|
if (! is_string($value)) {
|
|
return null;
|
|
}
|
|
|
|
$trimmed = trim($value);
|
|
|
|
return $trimmed === '' ? null : $trimmed;
|
|
}
|
|
|
|
/**
|
|
* @param array{username: string, nickname: string, password: string, email?: string|null} $adminAccount
|
|
*/
|
|
private function createSiteAdminUser(AdminSite $site, AdminRole $role, array $adminAccount): AdminUser
|
|
{
|
|
$username = trim((string) ($adminAccount['username'] ?? ''));
|
|
$nickname = trim((string) ($adminAccount['nickname'] ?? ''));
|
|
$password = (string) ($adminAccount['password'] ?? '');
|
|
$email = $this->nullableTrim($adminAccount['email'] ?? null);
|
|
|
|
if ($username === '' || $nickname === '' || $password === '') {
|
|
throw ValidationException::withMessages([
|
|
'admin_account' => ['站点后台管理账号信息不完整。'],
|
|
]);
|
|
}
|
|
|
|
$user = AdminUser::query()->create([
|
|
'username' => $username,
|
|
'name' => $nickname,
|
|
'email' => $email,
|
|
'password' => $password,
|
|
'status' => 0,
|
|
]);
|
|
|
|
$user->syncSystemRoleSlugsForSite((int) $site->id, [SitePlatformRole::SLUG]);
|
|
|
|
return $user;
|
|
}
|
|
}
|