681 lines
27 KiB
Python
681 lines
27 KiB
Python
from dataclasses import dataclass
|
||
from typing import Any
|
||
|
||
from cli.tui.adapters.base import BaseDetailAdapter
|
||
from cli.tui.adapters.models import (
|
||
TUI_ADAPTER_MODEL_RENDERER,
|
||
DetailPageSnapshot,
|
||
DetailSectionSnapshot,
|
||
)
|
||
from cli.tui.copy import TUI_COPY
|
||
from cli.tui.diagnostics import TUI_DIAGNOSTIC_SERVICE
|
||
from cli.utils import NESTED_CLI_SUPPORT, SHELL_TEXT_FORMATTER
|
||
|
||
COMPLETION_PREVIEW_LINE_LIMIT = 12
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class AppDetailSourcePayloads:
|
||
"""
|
||
应用详情页原始数据源快照。
|
||
|
||
:param env_payload: `app env` 结果
|
||
:param config_payload: `app config` 结果
|
||
:param doctor_payload: `app doctor` 结果
|
||
:param routes_payload: `app routes` 结果
|
||
:param completion_payload: `completion doctor` 结果
|
||
:param completion_preview_result: `completion show` 命令结果
|
||
:param preview_shell: 当前补全脚本预览 shell
|
||
"""
|
||
|
||
env_payload: dict[str, Any] | None
|
||
config_payload: dict[str, Any] | None
|
||
doctor_payload: dict[str, Any] | None
|
||
routes_payload: dict[str, Any] | None
|
||
completion_payload: dict[str, Any] | None
|
||
completion_preview_result: Any
|
||
preview_shell: str
|
||
|
||
|
||
class AppDetailSnapshotCollector:
|
||
"""
|
||
应用详情页数据采集器。
|
||
|
||
该对象负责拉取应用详情页所需的多路 CLI 原始结果,
|
||
让 `AppDetailAdapter` 保持详情页编排职责。
|
||
|
||
:param section_builder: 应用详情分区构建器
|
||
"""
|
||
|
||
def __init__(self, section_builder: 'AppSectionBuilder') -> None:
|
||
"""
|
||
初始化应用详情页数据采集器。
|
||
|
||
:param section_builder: 应用详情分区构建器
|
||
:return: None
|
||
"""
|
||
self.section_builder = section_builder
|
||
|
||
def collect(self, env: str) -> AppDetailSourcePayloads:
|
||
"""
|
||
采集应用详情页所需原始结果。
|
||
|
||
:param env: 当前运行环境
|
||
:return: 应用详情页原始数据源快照
|
||
"""
|
||
env_payload = NESTED_CLI_SUPPORT.run(
|
||
'app',
|
||
'env',
|
||
f'--env={env}',
|
||
'--output=json',
|
||
parse_json=True,
|
||
).payload
|
||
config_payload = NESTED_CLI_SUPPORT.run(
|
||
'app',
|
||
'config',
|
||
f'--env={env}',
|
||
'--output=json',
|
||
parse_json=True,
|
||
).payload
|
||
doctor_payload = NESTED_CLI_SUPPORT.run(
|
||
'app',
|
||
'doctor',
|
||
f'--env={env}',
|
||
'--output=json',
|
||
parse_json=True,
|
||
).payload
|
||
routes_payload = NESTED_CLI_SUPPORT.run(
|
||
'app',
|
||
'routes',
|
||
f'--env={env}',
|
||
'--group-by=tag',
|
||
'--output=json',
|
||
parse_json=True,
|
||
).payload
|
||
completion_payload = NESTED_CLI_SUPPORT.run(
|
||
'completion',
|
||
'doctor',
|
||
'--output=json',
|
||
parse_json=True,
|
||
).payload
|
||
preview_shell = self.section_builder.resolve_completion_preview_shell(completion_payload)
|
||
completion_preview_result = NESTED_CLI_SUPPORT.run('completion', 'show', preview_shell)
|
||
return AppDetailSourcePayloads(
|
||
env_payload=env_payload,
|
||
config_payload=config_payload,
|
||
doctor_payload=doctor_payload,
|
||
routes_payload=routes_payload,
|
||
completion_payload=completion_payload,
|
||
completion_preview_result=completion_preview_result,
|
||
preview_shell=preview_shell,
|
||
)
|
||
|
||
|
||
class AppSectionBuilder:
|
||
"""
|
||
应用详情分区构建器。
|
||
|
||
该构建器负责将应用、补全和路由相关 CLI 结果转换为 TUI 详情页分区,
|
||
使详情页适配器本体只保留采集与编排职责。
|
||
"""
|
||
|
||
@staticmethod
|
||
def resolve_completion_preview_shell(payload: dict[str, Any] | None) -> str:
|
||
"""
|
||
解析补全脚本预览应使用的 shell。
|
||
|
||
:param payload: `completion doctor` JSON 负载
|
||
:return: shell 名称
|
||
"""
|
||
if not isinstance(payload, dict):
|
||
return 'bash'
|
||
active_shell = str(payload.get('activeShell') or '').strip().lower()
|
||
shells = payload.get('shells') if isinstance(payload.get('shells'), dict) else {}
|
||
if active_shell and isinstance(shells.get(active_shell), dict) and shells[active_shell].get('supported', False):
|
||
return active_shell
|
||
for shell_name in ('bash', 'zsh', 'fish', 'powershell'):
|
||
shell_payload = shells.get(shell_name)
|
||
if isinstance(shell_payload, dict) and shell_payload.get('supported', False):
|
||
return shell_name
|
||
return 'bash'
|
||
|
||
@staticmethod
|
||
def build_env_section(payload: dict[str, Any] | None) -> DetailSectionSnapshot:
|
||
"""
|
||
构建应用环境解析分区。
|
||
|
||
:param payload: `app env` JSON 负载
|
||
:return: 分区快照
|
||
"""
|
||
runtime = payload.get('runtime') if isinstance(payload, dict) else None
|
||
if not isinstance(runtime, dict):
|
||
return DetailSectionSnapshot(
|
||
title='环境解析',
|
||
status='fail',
|
||
lines=TUI_ADAPTER_MODEL_RENDERER.build_failure_lines(
|
||
payload, empty_label='环境信息', empty_value='不可用'
|
||
),
|
||
)
|
||
return DetailSectionSnapshot(
|
||
title='环境解析',
|
||
status='ok' if payload.get('ok', False) else 'fail',
|
||
lines=[
|
||
'## 环境映射',
|
||
f'CLI 目标环境: {runtime.get("cliEnv", "-")}',
|
||
f'配置模块环境: {runtime.get("configEnv", "-")}',
|
||
f'进程环境变量: {runtime.get("appEnv", "-") or "-"}',
|
||
'',
|
||
'## 文件与进程',
|
||
f'环境文件: {runtime.get("envFile", "-")}',
|
||
f'环境文件存在: {"是" if runtime.get("envFileExists", False) else "否"}',
|
||
f'后端目录: {SHELL_TEXT_FORMATTER.truncate_text(runtime.get("backendDir", "-"), 64)}',
|
||
f'Python 可执行文件: {SHELL_TEXT_FORMATTER.truncate_text(runtime.get("pythonExecutable", "-"), 64)}',
|
||
],
|
||
)
|
||
|
||
@staticmethod
|
||
def build_config_section(payload: dict[str, Any] | None) -> DetailSectionSnapshot:
|
||
"""
|
||
构建应用基础配置分区。
|
||
|
||
:param payload: `app config` JSON 负载
|
||
:return: 分区快照
|
||
"""
|
||
config = payload.get('config') if isinstance(payload, dict) else None
|
||
if not isinstance(config, dict):
|
||
return DetailSectionSnapshot(
|
||
title='应用配置',
|
||
status='fail',
|
||
lines=TUI_ADAPTER_MODEL_RENDERER.build_failure_lines(
|
||
payload, empty_label='应用配置', empty_value='不可用'
|
||
),
|
||
)
|
||
return DetailSectionSnapshot(
|
||
title='应用配置',
|
||
status='ok' if payload.get('ok', False) else 'fail',
|
||
lines=[
|
||
'## 应用入口',
|
||
f'应用名称: {SHELL_TEXT_FORMATTER.truncate_text(config.get("name", "-"), 40)}',
|
||
f'监听地址: {config.get("host", "-")}:{config.get("port", "-")}',
|
||
f'根路径: {config.get("rootPath", "-") or "/"}',
|
||
f'工作进程: {config.get("workers", "-")}',
|
||
f'热重载: {"开启" if config.get("reload", False) else "关闭"}',
|
||
'',
|
||
'## 文档与日志',
|
||
f'Swagger: {"关闭" if config.get("disableSwagger", False) else "开启"}',
|
||
f'ReDoc: {"关闭" if config.get("disableRedoc", False) else "开启"}',
|
||
f'日志级别: {config.get("logLevel", "-")}',
|
||
],
|
||
)
|
||
|
||
@staticmethod
|
||
def build_dependency_section(payload: dict[str, Any] | None) -> DetailSectionSnapshot:
|
||
"""
|
||
构建应用依赖配置分区。
|
||
|
||
:param payload: `app config` JSON 负载
|
||
:return: 分区快照
|
||
"""
|
||
config = payload.get('config') if isinstance(payload, dict) else None
|
||
if not isinstance(config, dict):
|
||
return DetailSectionSnapshot(
|
||
title='依赖配置',
|
||
status='fail',
|
||
lines=TUI_ADAPTER_MODEL_RENDERER.build_failure_lines(
|
||
payload, empty_label='依赖配置', empty_value='不可用'
|
||
),
|
||
)
|
||
return DetailSectionSnapshot(
|
||
title='依赖配置',
|
||
status='ok' if payload.get('ok', False) else 'fail',
|
||
lines=[
|
||
'## 数据库',
|
||
f'类型: {config.get("dbType", "-")}',
|
||
f'地址: {config.get("dbHost", "-")}:{config.get("dbPort", "-")}',
|
||
f'数据库名: {config.get("dbDatabase", "-")}',
|
||
'',
|
||
'## Redis 与加密',
|
||
f'Redis 地址: {config.get("redisHost", "-")}:{config.get("redisPort", "-")}',
|
||
f'传输加密: {"开启" if config.get("transportCryptoEnabled", False) else "关闭"}',
|
||
f'加密模式: {config.get("transportCryptoMode", "-")}',
|
||
],
|
||
)
|
||
|
||
def build_routes_section(self, payload: dict[str, Any] | None) -> DetailSectionSnapshot:
|
||
"""
|
||
构建应用路由摘要分区。
|
||
|
||
:param payload: `app routes` JSON 负载
|
||
:return: 分区快照
|
||
"""
|
||
if not isinstance(payload, dict) or not payload.get('ok', False):
|
||
return DetailSectionSnapshot(
|
||
title='路由摘要',
|
||
status='fail',
|
||
lines=TUI_ADAPTER_MODEL_RENDERER.build_failure_lines(payload, empty_label='路由', empty_value='不可用'),
|
||
)
|
||
|
||
count = payload.get('count', 0)
|
||
routes = payload.get('routes') if isinstance(payload.get('routes'), list) else []
|
||
grouped_routes = payload.get('groupedRoutes') if isinstance(payload.get('groupedRoutes'), dict) else {}
|
||
group_items = sorted(
|
||
((str(tag), len(items) if isinstance(items, list) else 0) for tag, items in grouped_routes.items()),
|
||
key=lambda item: (-item[1], item[0]),
|
||
)
|
||
|
||
lines = [
|
||
'## 路由规模',
|
||
f'总路由数: {count}',
|
||
f'标签分组数: {len(group_items)}',
|
||
'',
|
||
'## 主要标签',
|
||
]
|
||
if group_items:
|
||
lines.extend(f'> {tag} · {size} 条' for tag, size in group_items[:6])
|
||
else:
|
||
lines.append('> 暂无标签分组数据')
|
||
|
||
if routes:
|
||
lines.extend(['', '## 路由样本'])
|
||
for route in routes[:6]:
|
||
if not isinstance(route, dict):
|
||
continue
|
||
methods = '/'.join(route.get('methods', [])) if isinstance(route.get('methods'), list) else '-'
|
||
lines.extend(
|
||
[
|
||
f'> [{methods or "-"}] {SHELL_TEXT_FORMATTER.truncate_text(route.get("path", "-"), 48)}',
|
||
f'> {SHELL_TEXT_FORMATTER.truncate_text(route.get("summary", "-") or "-", 48)}',
|
||
]
|
||
)
|
||
else:
|
||
lines.extend(
|
||
[
|
||
'',
|
||
*TUI_ADAPTER_MODEL_RENDERER.build_empty_lines(
|
||
empty_label='路由样本',
|
||
empty_value='0 条',
|
||
detail='当前环境未返回可展示的路由样本',
|
||
suggestion=TUI_COPY.build_refresh_page_suggestion('应用', '确认当前环境是否已完成路由注册'),
|
||
),
|
||
]
|
||
)
|
||
|
||
return DetailSectionSnapshot(
|
||
title='路由摘要',
|
||
status='ok',
|
||
lines=lines,
|
||
)
|
||
|
||
@staticmethod
|
||
def build_completion_section(payload: dict[str, Any] | None) -> DetailSectionSnapshot:
|
||
"""
|
||
构建 shell completion 诊断分区。
|
||
|
||
:param payload: `completion doctor` JSON 负载
|
||
:return: 分区快照
|
||
"""
|
||
if not isinstance(payload, dict) or not payload.get('ok', False):
|
||
return DetailSectionSnapshot(
|
||
title='补全诊断',
|
||
status='fail',
|
||
lines=TUI_ADAPTER_MODEL_RENDERER.build_failure_lines(
|
||
payload, empty_label='补全诊断', empty_value='不可用'
|
||
),
|
||
)
|
||
|
||
shells = payload.get('shells') if isinstance(payload.get('shells'), dict) else {}
|
||
active_shell = str(payload.get('activeShell') or '-')
|
||
env_choices = payload.get('envChoices') if isinstance(payload.get('envChoices'), list) else []
|
||
active_shell_payload = shells.get(active_shell) if isinstance(shells.get(active_shell), dict) else None
|
||
target_file = active_shell_payload.get('targetFile', '-') if isinstance(active_shell_payload, dict) else '-'
|
||
source_command = (
|
||
active_shell_payload.get('sourceCommand', '-') if isinstance(active_shell_payload, dict) else '-'
|
||
)
|
||
install_command = (
|
||
active_shell_payload.get('recommendedInstallCommand', '-')
|
||
if isinstance(active_shell_payload, dict)
|
||
else payload.get('recommendedInstallCommand', '-')
|
||
)
|
||
|
||
lines = [
|
||
'## 当前环境',
|
||
f'活动 Shell: {active_shell}',
|
||
f'项目目录: {SHELL_TEXT_FORMATTER.truncate_text(payload.get("projectDir", "-"), 64)}',
|
||
f'补全环境变量: {payload.get("completeEnvVar", "-")}',
|
||
f'环境候选数: {len(env_choices)}',
|
||
'',
|
||
'## 激活建议',
|
||
f'目标脚本: {SHELL_TEXT_FORMATTER.truncate_text(target_file, 64)}',
|
||
f'加载命令: {SHELL_TEXT_FORMATTER.truncate_text(source_command, 72)}',
|
||
f'推荐安装命令: {SHELL_TEXT_FORMATTER.truncate_text(install_command, 72)}',
|
||
]
|
||
|
||
shell_lines = []
|
||
for shell_name in ('bash', 'zsh', 'fish', 'powershell'):
|
||
shell_payload = shells.get(shell_name)
|
||
if not isinstance(shell_payload, dict):
|
||
continue
|
||
supported = '支持' if shell_payload.get('supported', False) else '不支持'
|
||
detected = '当前 Shell' if shell_payload.get('detected', False) else '候选 Shell'
|
||
shell_lines.append(f'> {shell_name} · {supported} · {detected}')
|
||
if shell_lines:
|
||
lines.extend(['', '## Shell 支持'])
|
||
lines.extend(shell_lines)
|
||
|
||
return DetailSectionSnapshot(
|
||
title='补全诊断',
|
||
status='ok',
|
||
lines=lines,
|
||
)
|
||
|
||
def build_completion_preview_section(
|
||
self,
|
||
shell: str,
|
||
preview_result: Any,
|
||
) -> DetailSectionSnapshot:
|
||
"""
|
||
构建补全脚本预览分区。
|
||
|
||
:param shell: 当前预览 shell
|
||
:param preview_result: `completion show` 调用结果
|
||
:return: 分区快照
|
||
"""
|
||
stdout = str(getattr(preview_result, 'stdout', '') or '').strip()
|
||
stderr = str(getattr(preview_result, 'stderr', '') or '').strip()
|
||
returncode = int(getattr(preview_result, 'returncode', 1) or 0)
|
||
if returncode != 0:
|
||
return DetailSectionSnapshot(
|
||
title='补全脚本预览',
|
||
status='fail',
|
||
lines=TUI_ADAPTER_MODEL_RENDERER.build_failure_lines(
|
||
{'ok': False, 'message': stderr or stdout or '补全脚本生成失败'},
|
||
empty_label='补全脚本',
|
||
empty_value='不可用',
|
||
),
|
||
)
|
||
preview_lines = [
|
||
SHELL_TEXT_FORMATTER.truncate_text(line, 88) for line in stdout.splitlines()[:COMPLETION_PREVIEW_LINE_LIMIT]
|
||
]
|
||
if not preview_lines:
|
||
return DetailSectionSnapshot(
|
||
title='补全脚本预览',
|
||
status='info',
|
||
lines=TUI_ADAPTER_MODEL_RENDERER.build_empty_lines(
|
||
empty_label='补全脚本',
|
||
empty_value='0 行',
|
||
detail='当前 shell 的补全脚本未返回可预览内容',
|
||
suggestion=TUI_COPY.build_refresh_page_suggestion('应用', '重新生成补全脚本预览'),
|
||
),
|
||
)
|
||
return DetailSectionSnapshot(
|
||
title='补全脚本预览',
|
||
status='ok',
|
||
lines=[
|
||
'## 预览目标',
|
||
f'Shell: {shell}',
|
||
f'预览行数: {len(preview_lines)}',
|
||
'',
|
||
'## 脚本片段',
|
||
*preview_lines,
|
||
],
|
||
)
|
||
|
||
@staticmethod
|
||
def build_doctor_section(payload: dict[str, Any] | None) -> DetailSectionSnapshot:
|
||
"""
|
||
构建应用启动前检查分区。
|
||
|
||
:param payload: `app doctor` JSON 负载
|
||
:return: 分区快照
|
||
"""
|
||
if not isinstance(payload, dict):
|
||
return DetailSectionSnapshot(
|
||
title='启动前检查',
|
||
status='fail',
|
||
lines=TUI_ADAPTER_MODEL_RENDERER.build_failure_lines(
|
||
payload, empty_label='启动前检查', empty_value='不可用'
|
||
),
|
||
)
|
||
|
||
database = payload.get('database') if isinstance(payload.get('database'), dict) else {}
|
||
redis = payload.get('redis') if isinstance(payload.get('redis'), dict) else {}
|
||
crypto = payload.get('crypto') if isinstance(payload.get('crypto'), dict) else {}
|
||
return DetailSectionSnapshot(
|
||
title='启动前检查',
|
||
status='ok' if payload.get('ok', False) else 'fail',
|
||
lines=[
|
||
'## 检查结果',
|
||
f'环境: {payload.get("env", "-")}',
|
||
f'数据库: {"正常" if database.get("ok", False) else "异常"}',
|
||
f'> {SHELL_TEXT_FORMATTER.truncate_text(database.get("message", "-"), 64)}',
|
||
f'Redis: {"正常" if redis.get("ok", False) else "异常"}',
|
||
f'> {SHELL_TEXT_FORMATTER.truncate_text(redis.get("message", "-"), 64)}',
|
||
f'加密组件: {"正常" if crypto.get("ok", False) else "异常"}',
|
||
f'> {SHELL_TEXT_FORMATTER.truncate_text(crypto.get("message", "-"), 64)}',
|
||
],
|
||
)
|
||
|
||
@staticmethod
|
||
def build_run_entry_section() -> DetailSectionSnapshot:
|
||
"""
|
||
构建应用启动向导入口分区。
|
||
|
||
:return: 分区快照
|
||
"""
|
||
return DetailSectionSnapshot(
|
||
title='启动入口',
|
||
status='info',
|
||
lines=TUI_COPY.build_command_hint_lines(
|
||
scenario='准备启动当前环境应用时,可直接启动应用,或先通过向导确认目标环境与启动前检查选项。',
|
||
command=TUI_COPY.build_cli_command_hint('app', 'run', '--env=dev'),
|
||
guide='若需要先做启动前检查或切换环境,建议改用 `wizard app-run` 进入交互式启动流程。',
|
||
),
|
||
)
|
||
|
||
@staticmethod
|
||
def build_completion_install_entry_section() -> DetailSectionSnapshot:
|
||
"""
|
||
构建 shell 补全安装入口分区。
|
||
|
||
:return: 分区快照
|
||
"""
|
||
return DetailSectionSnapshot(
|
||
title='补全安装入口',
|
||
status='info',
|
||
lines=TUI_COPY.build_command_hint_lines(
|
||
scenario='准备为当前 Shell 安装或修复补全脚本时,可直接执行补全安装命令,并按需把激活命令写入 rc 文件。',
|
||
command=TUI_COPY.build_cli_command_hint('completion', 'install', '--activate', '--output=text'),
|
||
guide='当前页面提供补全诊断与安装入口;执行安装后可返回补全诊断分区确认 target file 和 source command 是否更新。',
|
||
),
|
||
)
|
||
|
||
@staticmethod
|
||
def build_overview_section(
|
||
env_payload: dict[str, Any] | None,
|
||
config_payload: dict[str, Any] | None,
|
||
doctor_payload: dict[str, Any] | None,
|
||
routes_payload: dict[str, Any] | None,
|
||
completion_payload: dict[str, Any] | None,
|
||
) -> DetailSectionSnapshot:
|
||
"""
|
||
构建应用页总览判断分区。
|
||
|
||
:param env_payload: `app env` JSON 负载
|
||
:param config_payload: `app config` JSON 负载
|
||
:param doctor_payload: `app doctor` JSON 负载
|
||
:param routes_payload: `app routes` JSON 负载
|
||
:param completion_payload: `completion doctor` JSON 负载
|
||
:return: 分区快照
|
||
"""
|
||
env_ok = bool(isinstance(env_payload, dict) and env_payload.get('ok', False))
|
||
config_ok = bool(isinstance(config_payload, dict) and config_payload.get('ok', False))
|
||
doctor_ok = bool(isinstance(doctor_payload, dict) and doctor_payload.get('ok', False))
|
||
routes_ok = bool(isinstance(routes_payload, dict) and routes_payload.get('ok', False))
|
||
completion_ok = bool(isinstance(completion_payload, dict) and completion_payload.get('ok', False))
|
||
route_count = routes_payload.get('count', 0) if isinstance(routes_payload, dict) else 0
|
||
runtime = (
|
||
env_payload.get('runtime')
|
||
if isinstance(env_payload, dict) and isinstance(env_payload.get('runtime'), dict)
|
||
else {}
|
||
)
|
||
config = (
|
||
config_payload.get('config')
|
||
if isinstance(config_payload, dict) and isinstance(config_payload.get('config'), dict)
|
||
else {}
|
||
)
|
||
|
||
status = 'ok'
|
||
conclusion = '应用基线正常,可继续查看环境映射、配置摘要与路由状态'
|
||
if not env_ok or not config_ok:
|
||
status = 'fail'
|
||
conclusion = '应用基础信息读取异常,优先确认环境映射与应用配置是否可用'
|
||
elif not doctor_ok:
|
||
status = 'fail'
|
||
conclusion = '启动前检查异常,优先确认数据库、Redis 和加密组件状态'
|
||
elif not completion_ok:
|
||
status = 'warn'
|
||
conclusion = '补全诊断读取异常,建议优先确认项目目录、Shell 类型和补全脚本状态'
|
||
elif not routes_ok:
|
||
status = 'warn'
|
||
conclusion = '路由摘要读取异常,建议先确认当前环境是否完成应用初始化'
|
||
elif int(route_count or 0) <= 0:
|
||
status = 'info'
|
||
conclusion = '当前未返回注册路由,建议检查路由注册流程与运行上下文'
|
||
|
||
return DetailSectionSnapshot(
|
||
title='总览判断',
|
||
status=status,
|
||
lines=[
|
||
'## 当前结论',
|
||
conclusion,
|
||
'',
|
||
'## 核心指标',
|
||
f'CLI 环境: {runtime.get("cliEnv", "-")}',
|
||
f'配置环境: {runtime.get("configEnv", "-")}',
|
||
f'监听地址: {config.get("host", "-")}:{config.get("port", "-")}',
|
||
f'注册路由: {route_count} 条',
|
||
f'启动前检查: {"正常" if doctor_ok else "异常"}',
|
||
f'传输加密: {"开启" if config.get("transportCryptoEnabled", False) else "关闭"}',
|
||
f'补全诊断: {"正常" if completion_ok else "异常"}',
|
||
'',
|
||
'## 建议入口',
|
||
'优先关注:环境解析 / 应用配置 / 启动前检查 / 补全诊断 / 路由摘要 / 启动入口',
|
||
],
|
||
)
|
||
|
||
def build_sections(
|
||
self,
|
||
*,
|
||
env_payload: dict[str, Any] | None,
|
||
config_payload: dict[str, Any] | None,
|
||
doctor_payload: dict[str, Any] | None,
|
||
routes_payload: dict[str, Any] | None,
|
||
completion_payload: dict[str, Any] | None,
|
||
completion_preview_result: Any,
|
||
preview_shell: str,
|
||
) -> list[DetailSectionSnapshot]:
|
||
"""
|
||
构建应用页全部详情分区。
|
||
|
||
:param env_payload: `app env` 结果负载
|
||
:param config_payload: `app config` 结果负载
|
||
:param doctor_payload: `app doctor` 结果负载
|
||
:param routes_payload: `app routes` 结果负载
|
||
:param completion_payload: `completion doctor` 结果负载
|
||
:param completion_preview_result: `completion show` 命令结果
|
||
:param preview_shell: 当前预览 shell
|
||
:return: 详情分区列表
|
||
"""
|
||
return [
|
||
self.build_overview_section(
|
||
env_payload,
|
||
config_payload,
|
||
doctor_payload,
|
||
routes_payload,
|
||
completion_payload,
|
||
),
|
||
self.build_env_section(env_payload),
|
||
self.build_config_section(config_payload),
|
||
self.build_dependency_section(config_payload),
|
||
self.build_doctor_section(doctor_payload),
|
||
self.build_completion_section(completion_payload),
|
||
self.build_completion_preview_section(preview_shell, completion_preview_result),
|
||
self.build_completion_install_entry_section(),
|
||
self.build_routes_section(routes_payload),
|
||
self.build_run_entry_section(),
|
||
]
|
||
|
||
|
||
class AppDetailAdapter(BaseDetailAdapter):
|
||
"""
|
||
应用详情页适配器。
|
||
|
||
该适配器负责采集应用相关 CLI 结果,并委托分区构建器组装为
|
||
TUI 详情页快照。
|
||
"""
|
||
|
||
def __init__(
|
||
self,
|
||
section_builder: AppSectionBuilder | None = None,
|
||
snapshot_collector: AppDetailSnapshotCollector | None = None,
|
||
) -> None:
|
||
"""
|
||
初始化应用详情页适配器。
|
||
|
||
:param section_builder: 应用详情分区构建器
|
||
:param snapshot_collector: 应用详情页数据采集器
|
||
:return: None
|
||
"""
|
||
super().__init__(
|
||
page_title='应用',
|
||
search_view_key='app',
|
||
default_suggestions=[
|
||
'总览判断',
|
||
'环境解析',
|
||
'应用配置',
|
||
'依赖配置',
|
||
'启动前检查',
|
||
'补全诊断',
|
||
'补全脚本预览',
|
||
'补全安装入口',
|
||
'路由摘要',
|
||
'启动入口',
|
||
],
|
||
)
|
||
self.section_builder = section_builder or AppSectionBuilder()
|
||
self.snapshot_collector = snapshot_collector or AppDetailSnapshotCollector(self.section_builder)
|
||
|
||
def collect_snapshot(self, env: str, query: str = '') -> DetailPageSnapshot:
|
||
"""
|
||
采集应用状态页只读快照。
|
||
|
||
:param env: 当前运行环境
|
||
:param query: 当前搜索词
|
||
:return: 页面快照
|
||
"""
|
||
source_payloads = self.snapshot_collector.collect(env)
|
||
sections = self.section_builder.build_sections(
|
||
env_payload=source_payloads.env_payload,
|
||
config_payload=source_payloads.config_payload,
|
||
doctor_payload=source_payloads.doctor_payload,
|
||
routes_payload=source_payloads.routes_payload,
|
||
completion_payload=source_payloads.completion_payload,
|
||
completion_preview_result=source_payloads.completion_preview_result,
|
||
preview_shell=source_payloads.preview_shell,
|
||
)
|
||
return DetailPageSnapshot(
|
||
title='应用',
|
||
subtitle=TUI_DIAGNOSTIC_SERVICE.build_app_diagnostic_subtitle(
|
||
source_payloads.env_payload,
|
||
source_payloads.config_payload,
|
||
source_payloads.doctor_payload,
|
||
source_payloads.routes_payload,
|
||
source_payloads.completion_payload,
|
||
),
|
||
sections=self.filter_sections(sections, query),
|
||
search=self.resolve_search_context(query),
|
||
)
|
||
|
||
|
||
APP_DETAIL_ADAPTER = AppDetailAdapter()
|