argument('identifier'); // 식별자 형식 검증 $validator = Validator::make( ['identifier' => $identifier], ['identifier' => [new ValidExtensionIdentifier]] ); if ($validator->fails()) { $this->error('❌ '.$validator->errors()->first('identifier')); return Command::FAILURE; } $force = (bool) $this->option('force'); try { // 모듈 디렉토리 스캔 및 로드 $this->moduleManager->loadModules(); // 이미 설치된 모듈인지 확인 (force 시 스킵하여 재설치/복구 허용) $existingModule = $this->moduleRepository->findByIdentifier($identifier); if ($existingModule && ! $force) { $this->warn('⚠️ '.__('modules.commands.install.already_installed', ['module' => $identifier])); return Command::FAILURE; } if ($existingModule && $force) { $this->warn('⚠️ '.__('modules.commands.install.force_reinstall', ['module' => $identifier])); } // 모듈 설치 $vendorMode = VendorMode::fromStringOrAuto((string) $this->option('vendor-mode')); $onProgress = $this->createProgressCallback(ModuleManager::INSTALL_STEPS); try { $result = $this->moduleManager->installModule($identifier, $onProgress, $vendorMode, $force); $this->finishProgress(); } catch (\Exception $e) { $this->finishProgress(); throw $e; } if ($result) { // 설치된 모듈 정보 조회 $module = $this->moduleRepository->findByIdentifier($identifier); // 모듈 인스턴스에서 생성된 role 개수 조회 $moduleInstance = $this->moduleManager->getModule($identifier); $rolesCount = 0; if ($moduleInstance && method_exists($moduleInstance, 'getRoles')) { $rolesCount = count($moduleInstance->getRoles()); } // 권한 및 메뉴 개수 조회 $permissionsCount = Permission::byExtension(ExtensionOwnerType::Module, $identifier)->count(); $menusCount = Menu::byExtension(ExtensionOwnerType::Module, $identifier)->count(); // 성공 메시지 $this->info('✅ '.__('modules.commands.install.success', ['module' => $identifier])); $this->info(' - '.__('modules.commands.install.vendor', ['vendor' => $module->vendor])); $this->info(' - '.__('modules.commands.install.version', ['version' => $module->version])); $this->info(' - '.__('modules.commands.install.roles_created', ['count' => $rolesCount])); $this->info(' - '.__('modules.commands.install.permissions_created', ['count' => $permissionsCount])); $this->info(' - '.__('modules.commands.install.menus_created', ['count' => $menusCount])); Log::info(__('modules.commands.install.success', ['module' => $identifier])); // 검색 인덱스 재생성은 운영자가 선택했을 때만 수행한다 (인덱스 잠금·재색인 비용) $this->handleSearchIndexRebuild(); return Command::SUCCESS; } return Command::FAILURE; } catch (\Exception $e) { $this->error('❌ '.$e->getMessage()); Log::error('모듈 설치 실패', [ 'module' => $identifier, 'error' => $e->getMessage(), ]); return Command::FAILURE; } } }