ShiErFeng/shierfeng-fastapi-backend/cli/tui/adapters/models.py
2026-07-24 09:43:10 +08:00

228 lines
7.1 KiB
Python

from collections.abc import Callable
from dataclasses import dataclass, field
from cli.tui.copy import TUI_COPY
from cli.tui.search import PageFilterOption, PageSearchContext
from cli.utils import SHELL_TEXT_FORMATTER
@dataclass(frozen=True)
class DetailSectionSnapshot:
"""
TUI 详情页单个分区快照。
:param title: 分区标题
:param status: 分区状态
:param lines: 分区正文文本行
"""
title: str
status: str
lines: list[str]
@dataclass(frozen=True)
class DetailPageSnapshot:
"""
TUI 详情页聚合快照。
:param title: 页面标题
:param subtitle: 页面副标题
:param sections: 页面分区列表
:param search: 当前页面搜索上下文
"""
title: str
subtitle: str
sections: list[DetailSectionSnapshot]
search: PageSearchContext | None = None
@dataclass(frozen=True)
class BrowserRecordSnapshot:
"""
TUI 浏览页单条记录快照。
:param key: 记录唯一标识
:param title: 记录标题
:param status: 记录状态
:param summary: 记录摘要
:param metadata_lines: 记录元信息文本行
:param detail_sections: 记录联动详情分区列表
"""
key: str
title: str
status: str
summary: str
metadata_lines: list[str]
detail_sections: list[DetailSectionSnapshot]
detail_loader: Callable[[], list[DetailSectionSnapshot]] | None = None
_cached_detail_sections: tuple[DetailSectionSnapshot, ...] | None = field(
default=None,
init=False,
repr=False,
compare=False,
)
def resolve_detail_sections(self) -> list[DetailSectionSnapshot]:
"""
获取当前记录详情分区,必要时按需加载并缓存。
:return: 详情分区列表
"""
if self.detail_loader is None:
return self.detail_sections
if self._cached_detail_sections is None:
object.__setattr__(self, '_cached_detail_sections', tuple(self.detail_loader()))
return list(self._cached_detail_sections)
@dataclass(frozen=True)
class BrowserPageSnapshot:
"""
TUI 浏览页聚合快照。
:param title: 页面标题
:param subtitle: 页面副标题
:param records: 可浏览记录列表
:param shared_sections: 全局共享分区列表
:param filters: 页面可用筛选项
:param active_filter_key: 当前激活筛选键
:param search: 当前页面搜索上下文
"""
title: str
subtitle: str
records: list[BrowserRecordSnapshot]
shared_sections: list[DetailSectionSnapshot]
filters: list[PageFilterOption] = field(default_factory=list)
active_filter_key: str | None = None
search: PageSearchContext | None = None
class TuiAdapterModelRenderService:
"""
TUI 适配层模型渲染服务。
该对象集中封装结果负载摘要提取,以及空态、加载态、失败态文本行
的统一构建逻辑,避免这些模块级辅助函数继续散落扩张。
"""
@staticmethod
def extract_payload_message(payload: dict[str, object] | None) -> str:
"""
提取结果负载中的可读消息。
:param payload: 标准结果负载
:return: 摘要消息文本
"""
if not isinstance(payload, dict):
return '-'
if payload.get('message'):
return SHELL_TEXT_FORMATTER.truncate_text(payload.get('message', '-'), 120)
if payload.get('error'):
return SHELL_TEXT_FORMATTER.truncate_text(payload.get('error', '-'), 120)
return '-'
@staticmethod
def build_empty_lines(
*,
empty_label: str,
empty_value: str,
detail: str,
suggestion: str = TUI_COPY.build_empty_state_suggestion(),
) -> list[str]:
"""
构建统一的空态文本行。
:param empty_label: 空态主字段名称
:param empty_value: 空态主字段值
:param detail: 空态说明
:param suggestion: 建议操作
:return: 空态文本行列表
"""
return [
TUI_COPY.build_state_section_title('status'),
f'{empty_label}: {empty_value}',
'',
TUI_COPY.build_state_section_title('detail'),
detail,
'',
TUI_COPY.build_state_section_title('suggestion'),
suggestion,
]
@staticmethod
def build_loading_lines(
*,
loading_label: str,
loading_value: str,
detail: str,
suggestion: str = TUI_COPY.build_loading_state_suggestion(),
) -> list[str]:
"""
构建统一的加载中文本行。
:param loading_label: 加载态主字段名称
:param loading_value: 加载态主字段值
:param detail: 加载说明
:param suggestion: 建议操作
:return: 加载态文本行列表
"""
return [
TUI_COPY.build_state_section_title('status'),
f'{loading_label}: {loading_value}',
'',
TUI_COPY.build_state_section_title('detail'),
detail,
'',
TUI_COPY.build_state_section_title('suggestion'),
suggestion,
]
def build_failure_lines(
self,
payload: dict[str, object] | None,
*,
empty_label: str,
empty_value: str,
) -> list[str]:
"""
为失败状态构建统一的可读文本行。
:param payload: 标准结果负载
:param empty_label: 主字段标签
:param empty_value: 主字段兜底值
:return: 失败状态文本行列表
"""
lines = [
TUI_COPY.build_state_section_title('status'),
f'{empty_label}: {empty_value}',
'',
TUI_COPY.build_state_section_title('error'),
]
if not isinstance(payload, dict):
lines.append('结果消息: -')
lines.extend(
['', TUI_COPY.build_state_section_title('suggestion'), TUI_COPY.build_failure_state_suggestion()]
)
return lines
lines.append(f'结果消息: {self.extract_payload_message(payload)}')
detail_lines: list[str] = []
if payload.get('hint'):
detail_lines.append(f'建议提示: {SHELL_TEXT_FORMATTER.truncate_text(payload.get("hint", "-"), 120)}')
if payload.get('error'):
detail_lines.append(f'错误信息: {SHELL_TEXT_FORMATTER.truncate_text(payload.get("error", "-"), 120)}')
if payload.get('stderr'):
detail_lines.append(f'标准错误: {SHELL_TEXT_FORMATTER.truncate_text(payload.get("stderr", "-"), 120)}')
if payload.get('stdout'):
detail_lines.append(f'标准输出: {SHELL_TEXT_FORMATTER.truncate_text(payload.get("stdout", "-"), 120)}')
if detail_lines:
lines.extend(['', TUI_COPY.build_state_section_title('diagnostic'), *detail_lines])
lines.extend(['', TUI_COPY.build_state_section_title('suggestion'), TUI_COPY.build_failure_state_suggestion()])
return lines
TUI_ADAPTER_MODEL_RENDERER = TuiAdapterModelRenderService()