isSuperAdmin()) { $this->agentProfileService->assertActorMayCreateChildAgent($actor); } $parent = AgentNode::query()->findOrFail((int) $payload['parent_id']); $this->agentProfileService->assertChildCapabilityGrantsWithinParent($parent, $payload, $actor); $name = trim((string) $payload['name']); $code = $this->resolveCodeForCreate($parent, $payload['code'] ?? null, (string) ($payload['username'] ?? '')); $username = trim((string) ($payload['username'] ?? '')); if ($username === '') { $username = 'agent_'.$code; } $password = (string) ($payload['password'] ?? ''); if ($password === '') { if (app()->environment('testing')) { $password = 'TestPass1!'; } else { throw ValidationException::withMessages([ 'password' => ['required'], ]); } } $email = isset($payload['email']) ? trim((string) $payload['email']) : null; $status = (int) ($payload['status'] ?? 1); if ($name === '') { throw ValidationException::withMessages([ 'name' => ['required'], ]); } if (AgentNode::query()->where('admin_site_id', $parent->admin_site_id)->where('code', $code)->exists()) { throw ValidationException::withMessages([ 'code' => ['unique'], ]); } if (AdminUser::query()->where('username', $username)->exists()) { throw ValidationException::withMessages([ 'username' => ['unique'], ]); } if ($email !== null && $email !== '' && AdminUser::query()->where('email', $email)->exists()) { throw ValidationException::withMessages([ 'email' => ['unique'], ]); } return DB::transaction(function () use ($actor, $parent, $code, $name, $username, $password, $email, $status, $payload): AgentNode { $node = AgentNode::query()->create([ 'admin_site_id' => $parent->admin_site_id, 'parent_id' => $parent->id, 'path' => '/', 'depth' => (int) $parent->depth + 1, 'code' => $code, 'name' => $name, 'status' => $status === 0 ? 0 : 1, 'created_by' => $actor->id, 'extra_json' => null, ]); $node->path = (string) $parent->path.$node->id.'/'; $node->save(); AgentPlatformRole::resolve(); $user = AdminUser::query()->create([ 'username' => $username, 'name' => $name, 'email' => $email !== '' ? $email : null, 'password' => $password, 'status' => AdminUserStatus::fromAgentNodeStatus($status === 0 ? 0 : 1), ]); DB::table('admin_user_agents')->insert([ 'admin_user_id' => $user->id, 'agent_node_id' => $node->id, 'is_primary' => true, 'granted_at' => now(), ]); AgentPlatformRole::assignPrimaryOperator($user, $node); $profilePayload = [ 'total_share_rate' => (float) ($payload['total_share_rate'] ?? 0), 'credit_limit' => (int) ($payload['credit_limit'] ?? 0), 'rebate_limit' => (float) ($payload['rebate_limit'] ?? 0), 'default_player_rebate' => (float) ($payload['default_player_rebate'] ?? 0), 'settlement_cycle' => (string) ($payload['settlement_cycle'] ?? 'weekly'), 'can_grant_extra_rebate' => (bool) ($payload['can_grant_extra_rebate'] ?? false), 'can_create_child_agent' => (bool) ($payload['can_create_child_agent'] ?? false), 'can_create_player' => (bool) ($payload['can_create_player'] ?? true), ]; if (array_key_exists('relative_share_rate', $payload)) { $profilePayload['relative_share_rate'] = (float) $payload['relative_share_rate']; } $this->agentProfileService->upsertForNode($node, $profilePayload, $parent); return $node->fresh(['adminSite']); }); } /** * @param array{ * name?: string, * username?: string, * email?: ?string, * password?: ?string, * status?: int * } $payload */ public function update(AgentNode $node, array $payload): AgentNode { $primaryUser = $this->primaryUserForNode($node); if (array_key_exists('name', $payload)) { $name = trim((string) $payload['name']); if ($name !== '') { $node->name = $name; if ($primaryUser instanceof AdminUser) { $primaryUser->name = $name; } } } if (array_key_exists('username', $payload)) { $username = trim((string) $payload['username']); if ($username === '') { throw ValidationException::withMessages([ 'username' => ['required'], ]); } if ($primaryUser instanceof AdminUser) { if ($username !== $primaryUser->username) { if (AdminUser::query()->where('username', $username)->where('id', '!=', $primaryUser->id)->exists()) { throw ValidationException::withMessages(['username' => ['unique']]); } $primaryUser->username = $username; } } else { $password = (string) ($payload['password'] ?? ''); if ($password === '') { if (app()->environment('testing')) { $password = 'TestPass1!'; } else { throw ValidationException::withMessages([ 'password' => ['required'], ]); } } $email = array_key_exists('email', $payload) ? ($payload['email'] !== null ? trim((string) $payload['email']) : null) : null; $status = array_key_exists('status', $payload) ? ((int) $payload['status'] === 0 ? 0 : 1) : ((int) $node->status === 0 ? 0 : 1); $primaryUser = $this->provisionPrimaryOwnerAccount( $node, $username, $password, $email, $status, ); if ($node->name !== '') { $primaryUser->name = $node->name; } } } if (array_key_exists('email', $payload) && $primaryUser instanceof AdminUser) { $email = $payload['email'] !== null ? trim((string) $payload['email']) : null; if ($email !== null && $email !== '' && AdminUser::query()->where('email', $email)->where('id', '!=', $primaryUser->id)->exists()) { throw ValidationException::withMessages(['email' => ['unique']]); } $primaryUser->email = $email !== '' ? $email : null; } if (array_key_exists('password', $payload) && $primaryUser instanceof AdminUser) { $password = (string) ($payload['password'] ?? ''); if ($password !== '') { $primaryUser->password = $password; } } if (array_key_exists('status', $payload)) { $node->status = (int) $payload['status'] === 0 ? 0 : 1; if ($primaryUser instanceof AdminUser) { $primaryUser->status = AdminUserStatus::fromAgentNodeStatus($node->status); } AdminRole::query() ->where('owner_agent_id', $node->id) ->update(['status' => $node->status]); } $node->save(); if ($primaryUser instanceof AdminUser) { $primaryUser->save(); } return $node->fresh(['adminSite']); } public function destroy(AgentNode $node): void { DB::transaction(static function () use ($node): void { $userIds = DB::table('admin_user_agents') ->where('agent_node_id', $node->id) ->pluck('admin_user_id') ->map(static fn ($id): int => (int) $id) ->all(); if ($userIds !== []) { DB::table('admin_user_agent_roles') ->where('agent_node_id', $node->id) ->whereIn('admin_user_id', $userIds) ->delete(); DB::table('admin_user_agents') ->where('agent_node_id', $node->id) ->whereIn('admin_user_id', $userIds) ->delete(); DB::table('admin_user_site_roles') ->whereIn('admin_user_id', $userIds) ->where('site_id', $node->admin_site_id) ->delete(); AdminUser::query()->whereIn('id', $userIds)->delete(); } DB::table('admin_role_menu_actions') ->whereIn( 'role_id', AdminRole::query()->where('owner_agent_id', $node->id)->pluck('id')->all(), ) ->delete(); AdminRole::query() ->where('owner_agent_id', $node->id) ->delete(); $node->delete(); }); } public function hasBlockingCustomRoles(AgentNode $node): bool { return AdminRole::query() ->where('owner_agent_id', $node->id) ->whereNull('delegated_from_role_id') ->exists(); } private function primaryUserForNode(AgentNode $node): ?AdminUser { $userId = DB::table('admin_user_agents') ->where('agent_node_id', $node->id) ->orderByDesc('is_primary') ->orderBy('admin_user_id') ->value('admin_user_id'); if ($userId === null) { return null; } return AdminUser::query()->find((int) $userId); } private function provisionPrimaryOwnerAccount( AgentNode $node, string $username, string $password, ?string $email, int $status, ): AdminUser { if (AdminUser::query()->where('username', $username)->exists()) { throw ValidationException::withMessages(['username' => ['unique']]); } if ($email !== null && $email !== '' && AdminUser::query()->where('email', $email)->exists()) { throw ValidationException::withMessages(['email' => ['unique']]); } return DB::transaction(function () use ($node, $username, $password, $email, $status): AdminUser { AgentPlatformRole::resolve(); $user = AdminUser::query()->create([ 'username' => $username, 'name' => $node->name !== '' ? $node->name : $username, 'email' => $email !== null && $email !== '' ? $email : null, 'password' => $password, 'status' => AdminUserStatus::fromAgentNodeStatus($status), ]); DB::table('admin_user_agents')->insert([ 'admin_user_id' => $user->id, 'agent_node_id' => $node->id, 'is_primary' => true, 'granted_at' => now(), ]); AgentPlatformRole::assignPrimaryOperator($user, $node); return $user; }); } private function resolveCodeForCreate(AgentNode $parent, mixed $rawCode, string $username): string { $preferred = trim((string) $rawCode); if ($preferred !== '') { if (AgentNode::query()->where('admin_site_id', $parent->admin_site_id)->where('code', $preferred)->exists()) { throw ValidationException::withMessages([ 'code' => ['unique'], ]); } return $preferred; } $base = preg_replace('/[^a-zA-Z0-9_-]+/', '_', $username) ?? ''; $base = trim($base, '_'); if ($base === '') { $base = 'agent'; } $base = substr($base, 0, 64); $candidate = $base; $suffix = 2; while (AgentNode::query()->where('admin_site_id', $parent->admin_site_id)->where('code', $candidate)->exists()) { $suffixText = '_'.$suffix; $candidate = substr($base, 0, max(1, 64 - strlen($suffixText))).$suffixText; $suffix++; } return $candidate; } }