setName('admin:stack:mode') ->setDescription('管理 Stack 模式:list/use/current/rollback') ->addArgument('action', Argument::OPTIONAL, '操作:list|use|current|rollback', 'list') ->addArgument('value', Argument::OPTIONAL, 'mode 或 backup_id') ->addOption('force', 'f', Option::VALUE_NONE, '跳过确认步骤'); } protected function execute(Input $input, Output $output) { $action = strtolower((string)$input->getArgument('action')); $value = (string)$input->getArgument('value'); $force = (bool)$input->getOption('force'); try { /** @var StackModeService $service */ $service = app(StackModeService::class); switch ($action) { case 'list': $this->handleList($service, $output); break; case 'current': $this->handleCurrent($service, $output); break; case 'use': $this->handleUse($service, $input, $output, $value, $force); break; case 'rollback': $this->handleRollback($service, $input, $output, $value, $force); break; default: throw new RuntimeException('不支持的 action:' . $action); } } catch (\Throwable $e) { $output->error($e->getMessage()); } } protected function handleList(StackModeService $service, Output $output): void { $modes = $service->listModes(); if (empty($modes)) { $output->warning('未发现可用模式'); return; } $output->writeln('可用模式:'); foreach ($modes as $item) { $mode = (string)$item['mode']; $desc = (string)$item['description']; $authorOnly = (bool)$item['author_only'] ? 'yes' : 'no'; $output->writeln("- {$mode} | author_only={$authorOnly} | {$desc}"); } } protected function handleCurrent(StackModeService $service, Output $output): void { $state = $service->getCurrentState(); $output->writeln('当前模式:' . ((string)$state['mode'] === '' ? '未记录' : (string)$state['mode'])); $output->writeln('切换时间:' . ((string)$state['switched_at'] === '' ? '未记录' : (string)$state['switched_at'])); $output->writeln('备份ID:' . ((string)$state['backup_id'] === '' ? '未记录' : (string)$state['backup_id'])); } protected function handleUse( StackModeService $service, Input $input, Output $output, string $mode, bool $force ): void { $mode = trim($mode); if ($mode === '') { throw new RuntimeException('请提供 mode,例如:admin:stack:mode use base-build'); } $planData = $service->getModePlan($mode); $output->writeln("将切换到模式:{$mode}"); foreach ($planData['plan'] as $item) { $file = (string)$item['file']; $sourceMode = (string)$item['source_mode']; $output->writeln("- {$file} <= {$sourceMode}"); } if (!$force && !$output->confirm($input, '确认执行覆盖切换?', true)) { $output->warning('已取消'); return; } $result = $service->applyMode($mode, get_current_user() ?: 'cli'); $output->info('模式切换完成'); $output->writeln('mode=' . $result['mode']); $output->writeln('backup_id=' . $result['backup_id']); } protected function handleRollback( StackModeService $service, Input $input, Output $output, string $backupId, bool $force ): void { $backupId = trim($backupId); if ($backupId === '') { throw new RuntimeException('请提供 backup_id,例如:admin:stack:mode rollback 20260424120000-abcd1234'); } if (!$force && !$output->confirm($input, "确认回滚到备份 {$backupId}?", true)) { $output->warning('已取消'); return; } $result = $service->rollback($backupId, get_current_user() ?: 'cli'); $output->info('回滚完成'); $output->writeln('backup_id=' . $result['backup_id']); $output->writeln('restored_files=' . count($result['restored_files'])); } }