289 lines
8.7 KiB
Markdown
289 lines
8.7 KiB
Markdown
|
|
# 🏗️ 前端重构说明
|
|||
|
|
|
|||
|
|
## 重构目标
|
|||
|
|
|
|||
|
|
将原来的单体 `frontend.py`(280+ 行)拆分为模块化、可维护的架构,参考后端的分层设计模式。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📁 新架构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
frontend/
|
|||
|
|
├── __init__.py # 包初始化
|
|||
|
|
├── frontend.py # 主入口(50 行,仅负责组装)
|
|||
|
|
├── config.py # 配置管理(数据类 + 环境变量)
|
|||
|
|
├── state.py # 状态管理(统一 Session State 操作)
|
|||
|
|
├── api_client.py # API 客户端(封装所有后端通信)
|
|||
|
|
├── utils.py # 工具函数(通用辅助函数)
|
|||
|
|
└── components/ # UI 组件
|
|||
|
|
├── __init__.py
|
|||
|
|
├── sidebar.py # 左侧栏:用户登录 + 历史列表
|
|||
|
|
├── chat_area.py # 中间栏:聊天区域 + 流式响应
|
|||
|
|
└── info_panel.py # 右侧栏:信息面板
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 核心模块说明
|
|||
|
|
|
|||
|
|
### 1. **配置管理** (`config.py`)
|
|||
|
|
|
|||
|
|
**设计理念**:使用 Python `dataclass` 集中管理所有配置,支持环境变量覆盖。
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
@dataclass
|
|||
|
|
class FrontendConfig:
|
|||
|
|
api_base: str = ""
|
|||
|
|
page_title: str = "AI 个人助手"
|
|||
|
|
default_model: str = "zhipu"
|
|||
|
|
history_limit: int = 50
|
|||
|
|
# ... 其他配置
|
|||
|
|
|
|||
|
|
# 全局配置实例
|
|||
|
|
config = FrontendConfig()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优势**:
|
|||
|
|
- ✅ 类型安全(dataclass 自动类型检查)
|
|||
|
|
- ✅ 集中管理(所有配置在一处)
|
|||
|
|
- ✅ 易于测试(可轻松 mock 配置)
|
|||
|
|
- ✅ 环境变量支持(`__post_init__` 中加载)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. **状态管理** (`state.py`)
|
|||
|
|
|
|||
|
|
**设计理念**:封装所有 `st.session_state` 操作,提供统一的 API。
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class AppState:
|
|||
|
|
@staticmethod
|
|||
|
|
def init():
|
|||
|
|
"""初始化所有状态"""
|
|||
|
|
if "user_id" not in st.session_state:
|
|||
|
|
st.session_state.user_id = config.default_user_id
|
|||
|
|
# ...
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def login(username: str):
|
|||
|
|
"""用户登录"""
|
|||
|
|
st.session_state.user_id = username.strip()
|
|||
|
|
st.session_state.logged_in = True
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def get_messages() -> List[Dict[str, str]]:
|
|||
|
|
"""获取消息列表"""
|
|||
|
|
return st.session_state.messages
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优势**:
|
|||
|
|
- ✅ 统一接口(所有状态操作通过 AppState)
|
|||
|
|
- ✅ 类型提示(IDE 自动补全)
|
|||
|
|
- ✅ 易于维护(状态逻辑集中)
|
|||
|
|
- ✅ 避免魔法字符串(不再直接使用 `st.session_state["xxx"]`)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. **API 客户端** (`api_client.py`)
|
|||
|
|
|
|||
|
|
**设计理念**:封装所有与后端的通信,支持流式响应。
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class APIClient:
|
|||
|
|
def get_user_threads(self, user_id: str, limit: int) -> List[Dict]:
|
|||
|
|
"""获取用户历史列表"""
|
|||
|
|
resp = requests.get(f"{self.base_url}/threads", ...)
|
|||
|
|
return resp.json().get("threads", [])
|
|||
|
|
|
|||
|
|
def chat_stream(self, message: str, ...) -> AsyncGenerator[Dict, None]:
|
|||
|
|
"""流式对话"""
|
|||
|
|
with requests.post(..., stream=True) as response:
|
|||
|
|
for line in response.iter_lines():
|
|||
|
|
yield json.loads(line)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优势**:
|
|||
|
|
- ✅ 职责单一(仅负责 API 通信)
|
|||
|
|
- ✅ 错误处理集中(统一的异常捕获)
|
|||
|
|
- ✅ 易于测试(可 mock APIClient)
|
|||
|
|
- ✅ 流式支持(Generator 逐行 yield)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4. **UI 组件** (`components/`)
|
|||
|
|
|
|||
|
|
**设计理念**:每个组件独立渲染,通过 State 和 API Client 交互。
|
|||
|
|
|
|||
|
|
#### `sidebar.py` - 左侧栏
|
|||
|
|
```python
|
|||
|
|
def render_sidebar():
|
|||
|
|
"""渲染左侧栏"""
|
|||
|
|
with st.sidebar:
|
|||
|
|
_render_user_section() # 用户登录
|
|||
|
|
_render_history_section() # 历史列表
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `chat_area.py` - 中间聊天区
|
|||
|
|
```python
|
|||
|
|
def render_chat_area():
|
|||
|
|
"""渲染中间聊天区域"""
|
|||
|
|
_render_model_selector() # 模型选择
|
|||
|
|
_render_chat_container() # 消息显示
|
|||
|
|
_render_input_box() # 输入框 + 流式响应
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `info_panel.py` - 右侧信息面板
|
|||
|
|
```python
|
|||
|
|
def render_info_panel():
|
|||
|
|
"""渲染右侧信息面板"""
|
|||
|
|
_render_thread_info() # 当前线程
|
|||
|
|
_render_message_stats() # 消息统计
|
|||
|
|
_render_tips() # 使用提示
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优势**:
|
|||
|
|
- ✅ 组件独立(每个文件 < 150 行)
|
|||
|
|
- ✅ 职责清晰(一个组件一个文件)
|
|||
|
|
- ✅ 易于复用(可在其他页面复用组件)
|
|||
|
|
- ✅ 易于测试(可独立测试每个组件)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 5. **主入口** (`frontend.py`)
|
|||
|
|
|
|||
|
|
**设计理念**:仅负责组装各模块,代码量 < 50 行。
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from .config import config
|
|||
|
|
from .state import AppState
|
|||
|
|
from .components.sidebar import render_sidebar
|
|||
|
|
from .components.chat_area import render_chat_area
|
|||
|
|
from .components.info_panel import render_info_panel
|
|||
|
|
|
|||
|
|
st.set_page_config(...)
|
|||
|
|
AppState.init()
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
st.title("🤖 个人生活与数据分析助手")
|
|||
|
|
|
|||
|
|
col_sidebar, col_chat, col_info = st.columns([1, 3, 1])
|
|||
|
|
|
|||
|
|
with col_sidebar:
|
|||
|
|
render_sidebar()
|
|||
|
|
with col_chat:
|
|||
|
|
render_chat_area()
|
|||
|
|
with col_info:
|
|||
|
|
render_info_panel()
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优势**:
|
|||
|
|
- ✅ 极简主义(< 50 行)
|
|||
|
|
- ✅ 清晰结构(一眼看懂整体架构)
|
|||
|
|
- ✅ 易于维护(修改功能只需改对应组件)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 重构对比
|
|||
|
|
|
|||
|
|
| 指标 | 重构前 | 重构后 | 改进 |
|
|||
|
|
|------|--------|--------|------|
|
|||
|
|
| **主文件行数** | 280+ 行 | 48 行 | ✅ -83% |
|
|||
|
|
| **代码结构** | 单体文件 | 模块化架构 | ✅ 分层清晰 |
|
|||
|
|
| **组件独立性** | 耦合严重 | 独立组件 | ✅ 可复用 |
|
|||
|
|
| **测试友好性** | 难以测试 | 易于 Mock | ✅ 可测试 |
|
|||
|
|
| **维护成本** | 高(改一处影响全局) | 低(改组件不影响其他) | ✅ 易维护 |
|
|||
|
|
| **代码可读性** | 差(滚动查找) | 优(模块化) | ✅ 易读 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎨 架构设计模式
|
|||
|
|
|
|||
|
|
### 1. **分层架构**
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────┐
|
|||
|
|
│ 表现层 (Components) │
|
|||
|
|
│ sidebar.py, chat_area.py, ... │
|
|||
|
|
├─────────────────────────────────────┤
|
|||
|
|
│ 业务层 (State) │
|
|||
|
|
│ state.py - 状态管理 │
|
|||
|
|
├─────────────────────────────────────┤
|
|||
|
|
│ 数据层 (API Client) │
|
|||
|
|
│ api_client.py - 后端通信 │
|
|||
|
|
├─────────────────────────────────────┤
|
|||
|
|
│ 配置层 (Config) │
|
|||
|
|
│ config.py - 配置管理 │
|
|||
|
|
└─────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. **依赖方向**
|
|||
|
|
```
|
|||
|
|
Components → State → API Client → Config
|
|||
|
|
↑ ↓
|
|||
|
|
└────────────────────────┘
|
|||
|
|
(全局单例实例)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**规则**:
|
|||
|
|
- ✅ 上层依赖下层
|
|||
|
|
- ✅ 禁止循环依赖
|
|||
|
|
- ✅ 配置和客户端为全局单例
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🚀 使用示例
|
|||
|
|
|
|||
|
|
### 扩展新功能:添加对话导出按钮
|
|||
|
|
|
|||
|
|
只需修改 `components/sidebar.py`:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def _render_history_actions():
|
|||
|
|
"""渲染历史操作按钮"""
|
|||
|
|
if st.button("🔄 刷新列表", use_container_width=True):
|
|||
|
|
_refresh_threads()
|
|||
|
|
|
|||
|
|
if st.button("➕ 新对话", type="primary", use_container_width=True):
|
|||
|
|
AppState.start_new_thread()
|
|||
|
|
st.rerun()
|
|||
|
|
|
|||
|
|
# 新增:导出对话按钮
|
|||
|
|
if st.button("📤 导出对话", use_container_width=True):
|
|||
|
|
_export_current_thread()
|
|||
|
|
|
|||
|
|
def _export_current_thread():
|
|||
|
|
"""导出当前对话为 Markdown"""
|
|||
|
|
messages = AppState.get_messages()
|
|||
|
|
content = "\n\n".join([f"**{m['role']}**: {m['content']}" for m in messages])
|
|||
|
|
st.download_button("下载", content, "conversation.md")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优势**:修改仅影响 `sidebar.py`,不影响其他模块!
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 重构优势总结
|
|||
|
|
|
|||
|
|
1. **模块化**:每个文件职责单一,易于理解和维护
|
|||
|
|
2. **可扩展**:添加新功能只需修改对应模块
|
|||
|
|
3. **可测试**:各模块独立,便于编写单元测试
|
|||
|
|
4. **可复用**:组件可在其他项目中复用
|
|||
|
|
5. **类型安全**:使用 dataclass 和类型提示
|
|||
|
|
6. **代码质量**:遵循 SOLID 原则和 Clean Architecture
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📝 后续优化建议
|
|||
|
|
|
|||
|
|
1. **添加单元测试**:为 `state.py` 和 `api_client.py` 编写测试
|
|||
|
|
2. **错误边界**:在组件中添加 try-except,避免单个组件崩溃影响全局
|
|||
|
|
3. **性能优化**:使用 `st.cache_data` 缓存 API 响应
|
|||
|
|
4. **国际化**:提取所有文本到 `i18n.py`,支持多语言
|
|||
|
|
5. **主题支持**:添加暗色/亮色主题切换
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**🎉 前端重构完成!代码结构更清晰,维护成本大幅降低!**
|