installService === null) { try { $this->installService = new InstallService(); } catch (\Throwable $e) { throw new \RuntimeException( 'Failed to initialize InstallService: ' . $e->getMessage(), 0, $e ); } } return $this->installService; } /** * 配置命令参数和选项 */ protected function configure(): void { $this ->setDescription('安装 MDAdmin 框架') ->setHelp('此命令用于安装 MDAdmin 框架,支持交互式和参数化两种方式') // 数据库参数 ->addOption('db-host', null, InputOption::VALUE_OPTIONAL, '数据库主机') ->addOption('db-port', null, InputOption::VALUE_OPTIONAL, '数据库端口', '3306') ->addOption('db-name', null, InputOption::VALUE_OPTIONAL, '数据库名称') ->addOption('db-user', null, InputOption::VALUE_OPTIONAL, '数据库用户名') ->addOption('db-pass', null, InputOption::VALUE_OPTIONAL, '数据库密码') ->addOption('db-prefix', null, InputOption::VALUE_OPTIONAL, '数据库表前缀', 'ma_') // 管理员参数 ->addOption('admin-username', null, InputOption::VALUE_OPTIONAL, '管理员用户名') ->addOption('admin-password', null, InputOption::VALUE_OPTIONAL, '管理员密码') ->addOption('admin-email', null, InputOption::VALUE_OPTIONAL, '管理员邮箱') // 安装选项 ->addOption('install-database', null, InputOption::VALUE_OPTIONAL, '是否安装数据库表(1=是,0=否)', '1') ->addOption('build-project', null, InputOption::VALUE_OPTIONAL, '是否构建前端项目(1=是,0=否)', '1') ->addOption('non-interactive', 'y', InputOption::VALUE_NONE, '非交互模式(使用所有默认值)') ->addOption('skip-env-check', null, InputOption::VALUE_NONE, '跳过环境检查'); } /** * 执行安装命令 */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); // 显示欢迎信息 $io->title('MDAdmin 框架安装程序'); $io->text([ '', '欢迎使用 MDAdmin 框架安装程序!', '此程序将引导您完成框架的安装配置。', '', '提示: 密码输入时不会显示字符(出于安全考虑),输入完成后会显示确认信息', '', ]); // 检查是否已安装 if ($this->getInstallService()->checkInstalled()) { $io->error('系统已安装,请勿重复安装!'); $io->note('如需重新安装,请先删除 ' . base_path('install.lock') . ' 文件'); return Command::FAILURE; } // 交互式或非交互式 $isInteractive = !$input->getOption('non-interactive'); $skipEnvCheck = $input->getOption('skip-env-check'); // 1. 环境检测 if (!$skipEnvCheck) { $io->section('1. 环境检测'); $this->handleEnvironmentCheck($io); } // 2. 数据库配置 $io->section('2. 数据库配置'); $installParams = $this->collectInstallParams($io, $input, $isInteractive); // 3. 确认安装 $io->section('3. 安装确认'); if (!$this->confirmInstallation($io, $installParams, $isInteractive)) { $io->note('安装已取消'); return Command::SUCCESS; } // 4. 执行安装 $io->section('4. 执行安装'); $io->text('开始安装...'); try { // 调用安装服务(共享在线安装逻辑) $generator = $this->getInstallService()->install($installParams); // 处理 SSE 事件输出 foreach ($generator as $chunk) { $result = $this->handleSseEvent($chunk, $io, null); if ($result !== null) { return $result; } } return Command::SUCCESS; } catch (\Throwable $e) { $this->outputError($io, '安装失败: ' . $e->getMessage(), $e); return Command::FAILURE; } } /** * 处理环境检查 */ private function handleEnvironmentCheck(SymfonyStyle $io): void { $environment = $this->getInstallService()->checkEnvironment(); // 显示系统信息 $io->text('系统信息:'); $io->definitionList( ['PHP 版本' => $environment['php_version']], ['服务器软件' => $environment['server_software']], ['操作系统' => $environment['operating_system']], ['内存限制' => $environment['memory_limit']], ['最大执行时间' => $environment['max_execution_time'] . '秒'], ); $io->newLine(); // 检查系统要求 $allOk = $environment['all_requirements_met']; if (!$allOk) { $io->warning('环境检查未通过,请检查以下项目:'); // 显示失败的检查项 foreach ($environment['check_items'] as $item) { if (!$item['status']) { $io->error($item['message']); } } foreach ($environment['directory_check_items'] as $item) { if (!$item['status'] && $item['required']) { $io->error($item['message']); } } // 确认是否继续 if (!$this->confirm($io, '环境检查未通过,是否继续安装?', false)) { throw new \Exception('环境检查未通过,安装已取消'); } } else { $io->success('环境检查通过!'); } $io->newLine(); } /** * 收集安装参数 */ private function collectInstallParams(SymfonyStyle $io, InputInterface $input, bool $isInteractive): array { $params = []; // 数据库配置 $io->text('数据库配置'); $dbHost = $input->getOption('db-host'); if (!$dbHost && $isInteractive) { $dbHost = $io->ask('请输入数据库主机', '127.0.0.1'); } $params['db_host'] = $dbHost ?? '127.0.0.1'; $dbPort = $input->getOption('db-port'); if (!$dbPort && $isInteractive) { $dbPort = $io->ask('请输入数据库端口', '3306'); } $params['db_port'] = $dbPort ?? '3306'; $dbName = $input->getOption('db-name'); if (!$dbName && $isInteractive) { $dbName = $io->ask('请输入数据库名称', 'md_admin'); } $params['db_database'] = $dbName ?? 'md_admin'; $dbUser = $input->getOption('db-user'); if (!$dbUser && $isInteractive) { $dbUser = $io->ask('请输入数据库用户名', 'root'); } $params['db_username'] = $dbUser ?? 'root'; $dbPass = $input->getOption('db-pass'); if (!$dbPass && $isInteractive) { $io->text('提示: 密码输入时显示掩码字符 *'); $dbPass = $this->askPassword($io, '请输入数据库密码: '); if ($dbPass !== '') { $io->text('✓ 数据库密码已设置'); } } $params['db_password'] = $dbPass ?? ''; $dbPrefix = $input->getOption('db-prefix'); if (!$dbPrefix && $isInteractive) { $dbPrefix = $io->ask('请输入数据库表前缀', 'ma_'); } $params['db_prefix'] = $dbPrefix ?? 'ma_'; // 管理员配置 $io->newLine(); $io->text('管理员配置'); $adminUsername = $input->getOption('admin-username'); if (!$adminUsername && $isInteractive) { $adminUsername = $io->ask('请输入管理员用户名', 'admin'); } $params['admin_username'] = $adminUsername ?? 'admin'; $adminPassword = $input->getOption('admin-password'); if (!$adminPassword && $isInteractive) { $io->text('提示: 密码输入时显示掩码字符 *'); do { $adminPassword = $this->askPassword($io, '请输入管理员密码(至少6位): '); if (strlen($adminPassword) < 6) { $io->error('密码长度不能少于6位!'); } } while (strlen($adminPassword) < 6); $io->text('✓ 管理员密码已设置'); } $params['admin_password'] = $adminPassword ?? '123456'; $adminEmail = $input->getOption('admin-email'); if (!$adminEmail && $isInteractive) { $adminEmail = $io->ask('请输入管理员邮箱', 'admin@example.com'); } $params['admin_email'] = $adminEmail ?? 'admin@example.com'; // 安装选项 $io->newLine(); $io->text('安装选项'); $installDatabase = $input->getOption('install-database'); if ($installDatabase === null && $isInteractive) { $installDatabase = $this->confirm($io, '是否安装数据库表?', true) ? '1' : '0'; } $params['install_database'] = $installDatabase ?? '1'; $buildProject = $input->getOption('build-project'); if ($buildProject === null && $isInteractive) { $buildProject = $this->confirm($io, '是否构建前端项目?', false) ? '1' : '0'; } $params['build_project'] = $buildProject ?? '0'; return $params; } /** * 确认安装信息 */ private function confirmInstallation(SymfonyStyle $io, array $params, bool $isInteractive): bool { // 显示配置摘要 $io->text('安装配置摘要:'); $io->definitionList( ['数据库主机' => $params['db_host']], ['数据库端口' => $params['db_port']], ['数据库名称' => $params['db_database']], ['数据库用户' => $params['db_username']], ['表前缀' => $params['db_prefix']], ['管理员用户名' => $params['admin_username']], ['管理员邮箱' => $params['admin_email']], ['安装数据库' => $params['install_database'] == '1' ? '是' : '否'], ['构建前端' => $params['build_project'] == '1' ? '是' : '否'], ); $io->newLine(); // 确认安装 if ($isInteractive) { return $this->confirm($io, '确认开始安装?安装前请确保数据库配置正确!', false); } return true; } }