feat: 初始化NewLife Studio项目,完成基础框架与数据管理模块
|
# NewLife.Studio — 详细实施计划
## 1. 总体架构
```
┌──────────────────────────────────────────────────────────────â”
│ NewLife.Studio.App │
│ (Avalonia Desktop Shell) │
├────────────┬────────────────────────────────────┬───────────┤
│ NavBar │ Content Area │ AI Panel │
│ ──────── │ ┌───────────────────────────────┠│ (å³ä¾§ │
│ Data │ │ Active Module View │ │ å¯æŠ˜å ) │
│ Studio ◠│ │ │ │ │
│ MQTT │ │ ┌─────────┠┌─────────────┠│ │ Chat │
│ Studio │ │ │ å¯¹è±¡æ ‘ │ │ 编辑/结果区 │ │ │ History │
│ MQ │ │ │ (左侧) │ │ (å³ä¾§) │ │ │ │
│ Studio │ │ │ │ │ │ │ │ Input │
│ Redis │ │ └─────────┘ └─────────────┘ │ │ │
│ Studio │ │ │ │ │
│ ... │ └───────────────────────────────┘ │ │
├────────────┴────────────────────────────────────┴───────────┤
│ StatusBar (当å‰è¿žæŽ¥/Module状æ€/åŽå°ä»»åŠ¡) │
└──────────────────────────────────────────────────────────────┘
```
**分层架构:**
```
src/
├── Framework/ # 框架层 — æ‰€æœ‰æ¨¡å—æ— æ¡ä»¶å¼•用
│ ├── NewLife.Studio.Core/ # IStudioModule, DTOs, StudioServices
│ ├── NewLife.Studio.Store/ # 本地æŒä¹…化 + åŠ å¯†
│ └── NewLife.Studio.AI/ # IAIProvider + Tool Calling
├── Providers/ # åè®®/基础设施æä¾›è€…层 — æ¨¡å—æŒ‰éœ€å¼•用
│ ├── NewLife.Studio.Data/ # IDataProvider, IDbSession, SQLite
│ ├── NewLife.Studio.Mqtt/ # IMqttService (åŽç»)
│ ├── NewLife.Studio.WebSocket/ # IWebSocketService (åŽç»)
│ └── NewLife.Studio.Mq/ # IMqService (åŽç»)
├── App/
│ └── NewLife.Studio.App/ # Shell 壳程åº
├── Modules/ # UI æ¨¡å— â€” 实现 IStudioModule
│ ├── DataStudio/ # MVP 首å‘
│ ├── MqttStudio/ # 预留
│ ├── MqStudio/ # 预留
│ ├── RedisStudio/ # 预留
│ └── ...
```
**ä¾èµ–æ–¹å‘:** App → Modules → Providers → Framework,永远没有åå‘引用。Module ä¹‹é—´ç¦æ¢ç›´æŽ¥å¼•用。
***
## 2. 解决方案项目结构
```
NewLife.Studio/
├── NewLife.Studio.sln
├── src/
│ ├── Framework/
│ │ ├── NewLife.Studio.Core/ # æ ¸å¿ƒæŠ½è±¡
│ │ │ ├── IStudioModule.cs # æ¨¡å—æŽ¥å£
│ │ │ ├── ModuleInfo.cs # 模å—元数æ®
│ │ │ ├── StudioServices.cs # æœåŠ¡å®šä½å™¨
│ │ │ ├── DTOs/ # 共享 DTO
│ │ │ │ ├── ConnectionInfo.cs
│ │ │ │ ├── QueryRequest.cs
│ │ │ │ ├── QueryResult.cs
│ │ │ │ ├── TableInfo.cs
│ │ │ │ ├── ColumnInfo.cs
│ │ │ │ ├── QueryHistoryEntry.cs
│ │ │ │ ├── AiProfile.cs
│ │ │ │ └── AppPreference.cs
│ │ │ └── Exceptions/
│ │ │ └── StudioException.cs
│ │ │
│ │ ├── NewLife.Studio.Store/ # 本地æŒä¹…化层
│ │ │ ├── IStoreService.cs # å˜å‚¨æœåŠ¡æŽ¥å£
│ │ │ ├── StoreService.cs # JSON 文件实现
│ │ │ ├── SecretProtection.cs # æ•æ„Ÿä¿¡æ¯ä¿æŠ¤
│ │ │ └── Models/
│ │ │ ├── StoredConnection.cs
│ │ │ ├── StoredQueryHistory.cs
│ │ │ ├── StoredAiProfile.cs
│ │ │ └── StoredAppPreference.cs
│ │ │
│ │ └── NewLife.Studio.AI/ # AI 助手层
│ │ ├── IAIProvider.cs # AI Provider 接å£
│ │ ├── AIProviderFactory.cs
│ │ ├── Models/
│ │ │ └── ChatModels.cs # 对è¯/工具调用模型
│ │ ├── Providers/
│ │ │ └── OpenAIProvider.cs
│ │ ├── ToolCalling/
│ │ │ ├── ToolRegistry.cs # 工具注册ä¸å¿ƒ
│ │ │ ├── AIService.cs # 对è¯ç®¡ç† + Tool Calling 循环
│ │ │ └── BuiltInTools/
│ │ │ └── BuiltInDatabaseTools.cs
│ │ └── Safety/
│ │ └── QuerySafetyFilter.cs # SQL 安全检查
│ │
│ ├── Providers/
│ │ └── NewLife.Studio.Data/ # æ•°æ®è®¿é—®æŠ½è±¡å±‚
│ │ ├── IDataProvider.cs # æ•°æ®åº“ Provider 接å£
│ │ ├── IDbSession.cs # ä¼šè¯æŠ½è±¡
│ │ └── Providers/
│ │ └── SQLite/
│ │ ├── SQLiteProvider.cs
│ │ ├── SQLiteSession.cs
│ │ └── SQLiteMetadataReader.cs
│ │
│ ├── App/
│ │ └── NewLife.Studio.App/ # Avalonia 主项目 (Shell)
│ │ ├── App.axaml / App.axaml.cs # 应用入å£
│ │ ├── MainWindow.axaml/.cs # ä¸»çª—å£ (Shell 布局)
│ │ ├── Program.cs # Main å…¥å£
│ │ ├── Controls/
│ │ │ ├── NavBar.axaml/.cs # 左侧导航æ
│ │ │ ├── StatusBar.axaml/.cs # åº•éƒ¨çŠ¶æ€æ
│ │ │ ├── ModuleHost.axaml/.cs # 模å—内容容器
│ │ │ └── AIPanel.axaml/.cs # AI åŠ©æ‰‹é¢æ¿
│ │ ├── Services/
│ │ │ └── ModuleLoader.cs # æ¨¡å—æ‰«æä¸ŽåŠ è½½
│ │ └── ViewModels/
│ │ └── ViewModelBase.cs
│ │
│ └── Modules/
│ └── DataStudio/ # Data Studio æ¨¡å— (MVP)
│ ├── DataStudioModule.cs # IStudioModule 实现
│ ├── ViewModels/
│ │ ├── DataStudioViewModel.cs
│ │ ├── ConnectionListViewModel.cs
│ │ ├── ObjectTreeViewModel.cs
│ │ ├── SqlEditorViewModel.cs
│ │ └── ResultGridViewModel.cs
│ └── Views/
│ ├── DataStudioView.axaml/.cs
│ ├── ConnectionListView.axaml/.cs
│ ├── ObjectTreeView.axaml/.cs
│ ├── SqlEditorView.axaml/.cs
│ └── ResultGridView.axaml/.cs
│
└── tests/
├── NewLife.Studio.Core.Tests/
├── NewLife.Studio.Store.Tests/
├── NewLife.Studio.Data.Tests/
├── NewLife.Studio.AI.Tests/
└── NewLife.Studio.Modules.DataStudio.Tests/
```
***
## 3. æ ¸å¿ƒæŽ¥å£è®¾è®¡
### 3.1 IStudioModule — æ¨¡å—æ³¨å†ŒæŽ¥å£
```csharp
namespace NewLife.Studio.Core;
/// <summary>Studio æ¨¡å—æ ‡å‡†æŽ¥å£</summary>
public interface IStudioModule
{
/// <summary>模å—å”¯ä¸€æ ‡è¯†</summary>
string Id { get; }
/// <summary>显示åç§°</summary>
string DisplayName { get; }
/// <summary>导航æ å›¾æ ‡ (Material/Path icon key)</summary>
string Icon { get; }
/// <summary>å¯¼èˆªæŽ’åº (è¶Šå°è¶Šé å‰)</summary>
int Order { get; }
/// <summary>æ¨¡å—æ¿€æ´»æ—¶è°ƒç”¨</summary>
Task OnActivateAsync(CancellationToken ct = default);
/// <summary>模å—åœç”¨æ—¶è°ƒç”¨</summary>
Task OnDeactivateAsync(CancellationToken ct = default);
/// <summary>èŽ·å–æ¨¡å—主视图 (返回 Avalonia UserControl)</summary>
Control GetView();
}
```
### 3.2 IDataProvider — æ•°æ®åº“ Provider 接å£
```csharp
namespace NewLife.Studio.Data;
public interface IDataProvider
{
string ProviderName { get; }
string[] SupportedSchemes { get; } // e.g. ["sqlite", "sqlite3"]
bool CanTestConnection { get; }
Task<bool> TestConnectionAsync(ConnectionInfo conn, CancellationToken ct = default);
Task<IDbSession> OpenSessionAsync(ConnectionInfo conn, CancellationToken ct = default);
}
public interface IDbSession : IDisposable
{
string SessionId { get; }
ConnectionInfo Connection { get; }
bool IsOpen { get; }
Task<TableInfo[]> GetTablesAsync(CancellationToken ct = default);
Task<ColumnInfo[]> GetColumnsAsync(string tableName, CancellationToken ct = default);
Task<QueryResult> ExecuteQueryAsync(QueryRequest request, CancellationToken ct = default);
Task CloseAsync();
}
```
### 3.3 IStoreService — 本地å˜å‚¨æŽ¥å£
```csharp
namespace NewLife.Studio.Store;
public interface IStoreService
{
// 连接管ç†
Task<ConnectionInfo[]> ListConnectionsAsync();
Task SaveConnectionAsync(ConnectionInfo conn);
Task DeleteConnectionAsync(string id);
// 查询历å²
Task AddQueryHistoryAsync(QueryHistoryEntry entry);
Task<QueryHistoryEntry[]> GetRecentQueriesAsync(int count = 50);
// AI é…ç½®
Task<AIProfile> GetAIProfileAsync();
Task SaveAIProfileAsync(AIProfile profile);
// 应用å好
Task<AppPreference> GetPreferencesAsync();
Task SavePreferencesAsync(AppPreference pref);
}
```
### 3.4 IAIProvider — AI Provider 接å£
```csharp
namespace NewLife.Studio.AI;
public interface IAIProvider
{
string ProviderName { get; }
Task<ChatResponse> ChatAsync(
ChatRequest request,
CancellationToken ct = default);
}
public class ChatRequest
{
public List<ChatMessage> Messages { get; set; }
public List<ToolDefinition> Tools { get; set; }
public string Model { get; set; }
public int MaxTokens { get; set; } = 4096;
}
public class ChatResponse
{
public string Content { get; set; }
public List<ToolCall> ToolCalls { get; set; }
public long InputTokens { get; set; }
public long OutputTokens { get; set; }
}
```
***
## 4. æ ¸å¿ƒæ•°æ®æ¨¡åž‹ (DTO)
| DTO | 关键嗿®µ | 说明 |
| ------------------- | --------------------------------------------------------------- | ----- |
| `ConnectionInfo` | Id, Name, ConnectionString, ProviderType, LastUsedAt, Group | 连接é…ç½® |
| `QueryRequest` | Sql, ConnectionId, MaxRows(默认1000), TimeoutSeconds(默认30) | 查询请求 |
| `QueryResult` | Columns\[], Rows\[]\[], RowCount, ElapsedMs, Truncated, Error | 查询结果 |
| `TableInfo` | Name, Schema, RowCount(å¯é€‰) | è¡¨ä¿¡æ¯ |
| `ColumnInfo` | Name, DataType, IsNullable, DefaultValue, IsPrimaryKey, Ordinal | åˆ—ä¿¡æ¯ |
| `QueryHistoryEntry` | Id, Sql, ConnectionName, ExecutedAt, ElapsedMs, RowCount | 历å²è®°å½• |
| `AIProfile` | ProviderType, Endpoint, ApiKey, Model | AI é…ç½® |
| `AppPreference` | MaxRows, DefaultExportPath, Theme, Language | 用户å好 |
***
## 5. UI ç»„ä»¶æ ‘
```
MainWindow
├── NavBar (左侧, 48px 宽)
│ └── ModuleNavItem[] (按 Order 排åº)
│ ├── Icon
│ ├── DisplayName (ToolTip)
│ └── IsActive (高亮指示)
│
├── ModuleHost (ä¸é—´, 填充剩余)
│ └── ActiveView (当剿¿€æ´» Module çš„ GetView() 返回值)
│ │
│ │ ★ DataStudio 模å—内部布局:
│ │ ┌──────────────────────────────────────â”
│ │ │ Toolbar (新建连接/æ–å¼€/导出) │
│ │ ├────────────┬─────────────────────────┤
│ │ │ ObjectTree │ TabControl │
│ │ │ ───────── │ ├── Query1 (SQL编辑器 │
│ │ │ â–¸ conn-1 │ │ + ç»“æžœç½‘æ ¼) │
│ │ │ ▸ tables │ ├── Query2 │
│ │ │ ▸ views │ └── + 新查询 │
│ │ │ ▸ conn-2 │ │
│ │ └────────────┴─────────────────────────┘
│ └──────────────────────────────────────────
│
├── AIPanel (å³ä¾§, å¯æŠ˜å , 300px)
│ ├── ChatMessageList
│ │ └── ChatBubble[]
│ │ ├── Role (User/Assistant/Tool)
│ │ ├── Content (Markdown)
│ │ └── ToolCallInfo (展开查看工具调用)
│ └── ChatInput (多行输入 + å‘逿Œ‰é’®)
│
└── StatusBar (底部, 24px)
├── ActiveConnection
├── BackgroundTaskIndicator
└── ModuleName
```
***
## 6. æ•°æ®æµ
### 6.1 用户执行 SQL 查询æµç¨‹
```
用户输入 SQL → SqlEditorViewModel.ExecuteCommand
→ QueryRequest { Sql, ConnectionId, MaxRows }
→ IDbSession.ExecuteQueryAsync()
→ SQLite 执行 (Microsoft.Data.Sqlite)
→ QueryResult { Columns[], Rows[][], ElapsedMs, Truncated }
→ ResultGridViewModel.Result ↠QueryResult
→ UI 绑定刷新 DataGrid
→ Store.AddQueryHistoryAsync() (异æ¥ã€ä¸é˜»å¡ž UI)
```
### 6.2 AI 工具调用é—环æµç¨‹
```
用户æé—® ──────────────────────────────────────────────────────â”
│ │
▼ │
ChatInput → AIService.ChatAsync() │
│ │
▼ │
IAIProvider.ChatAsync(messages + tools) ──► AI 模型 │
│ │ │
│◄────── ChatResponse { Content / ToolCalls } │
│ │
├── 有 ToolCalls? ──YES──► ToolExecutor.Execute(toolCall) │
│ │ │
│ ▼ │
│ 内置工具执行 (安全过滤) │
│ │ │
│ ▼ │
│ ToolResult { Output, Error } │
│ │ │
│ ▼ │
│ è¿½åŠ Assistant(tool_call) + │
│ Tool(result) 消æ¯åˆ°å¯¹è¯ │
│ │ │
│ └──► 回到 ChatAsync() ç»§ç»æŽ¨ç† â”‚
│ │
└── NO ToolCalls ──► 直接展示 Markdown å›žå¤ â”‚
```
### 6.3 模å—åˆ‡æ¢æµç¨‹
```
用户点击 NavBar 上的 ModuleNavItem
→ ModuleLoader.SwitchTo(moduleId)
→ å½“å‰ Module.OnDeactivateAsync() (ä¿å˜çжæ€, ä¸å…³é—连接)
→ ç›®æ ‡ Module.OnActivateAsync()
→ ModuleHost.Content â† ç›®æ ‡ Module.GetView()
→ NavBar 更新激活指示
→ StatusBar æ›´æ–°å½“å‰æ¨¡å—å
```
***
## 7. 安全ç–ç•¥
### 7.1 AI SQL 安全过滤
所有通过 AI Tool Calling 执行的 SQL 在 `QuerySafetyFilter` 䏿£€æŸ¥ï¼š
| 规则 | 行为 |
| -------------------------------------- | -------------------- |
| ä»…å…许 SELECT / EXPLAIN / PRAGMA (åªè¯») | æ‹’ç»å¹¶è¿”回错误 |
| INSERT/UPDATE/DELETE/DROP/ALTER/CREATE | æ‹’ç»ï¼Œæç¤º"AI 模å¼ä¸æ”¯æŒå†™æ“作" |
| 多è¯å¥ (分å·åˆ†éš”) | æ‹’ç» |
| 结果行数 > MaxRows | è£å‰ªå¹¶æ ‡æ³¨ Truncated=true |
| 查询超时 > TimeoutSeconds | å–æ¶ˆ CancellationToken |
### 7.2 æ•æ„Ÿä¿¡æ¯ä¿æŠ¤
| æ•°æ® | ä¿æŠ¤æ–¹å¼ |
| ---------- | --------------------- |
| æ•°æ®åº“密ç | AES åŠ å¯†å˜å‚¨ / ç³»ç»Ÿå‡æ®åº“ |
| AI API Key | AES åŠ å¯†å˜å‚¨ |
| 连接å—符串 | 脱æ•å˜å‚¨ (密ç 部分替æ¢ä¸º \*\*\*) |
***
## 8. 关键技术选型
| 层 | 技术 | 说明 |
| ----- | ---------------------------------------- | ----------------------------- |
| UI 框架 | Avalonia 11.x | è·¨å¹³å°æ¡Œé¢ (Windows/Linux/macOS) |
| MVVM | CommunityToolkit.Mvvm | 简化 ViewModel 与绑定 |
| æ•°æ®è®¿é—® | Microsoft.Data.Sqlite + NewLife.XCode | SQLite MVP; XCode 为多数æ®åº“扩展 |
| AI | OpenAI-compatible API | æ”¯æŒ OpenAI / Azure / 本地 Ollama |
| å˜å‚¨ | System.Text.Json + 本地文件 | JSON 文件å˜å‚¨ï¼ŒAppData 目录 |
| åŠ å¯† | System.Security.Cryptography (AES) | æ•æ„Ÿä¿¡æ¯åР坆 |
| 日志 | NewLife.Core (XTrace) | 统一日志 |
| ä¾èµ–注入 | Microsoft.Extensions.DependencyInjection | æ¨¡å—æ³¨å†Œä¸ŽæœåŠ¡ç®¡ç† |
***
## 9. 实施阶段 (8 个 Task)
### Phase 1: 骨架æå»º (Task 1-2, 约 40%)
```
Task 1: 解决方案 + Shell + Module 接å£
├── dotnet new avalonia.app → NewLife.Studio.App
├── dotnet new classlib → Core / Store / Data / AI / Shared
├── 定义 IStudioModule 接å£
├── 实现 Shell 布局 (NavBar + ModuleHost + StatusBar)
├── 实现 ModuleLoader (Assembly 扫æ + 注册)
└── 验è¯: 空 Shell 窗å£å¯åŠ¨ï¼Œæ˜¾ç¤ºå¯¼èˆªæ 骨架
Task 2: 本地å˜å‚¨
├── 定义 StoredConnection / QueryHistory / AIProfile / AppPreference
├── 实现 StoreService (JSON 读写)
├── 实现 SecretProtection (AES å ä½)
└── 验è¯: 连接信æ¯å†™å…¥/读å–/é‡å¯æ¢å¤
```
### Phase 2: Data Studio æ ¸å¿ƒ (Task 3-4, 约 25%)
```
Task 3: SQLite Provider
├── 定义 IDataProvider / IDbSession
├── 实现 SQLiteProvider (连接测试 + 打开会è¯)
├── 实现 SQLiteMetadataReader (表/列/主键信æ¯)
└── 验è¯: 打开 SQLite 文件,读å–元数æ®
Task 4: 查询执行
├── 定义 QueryRequest / QueryResult
├── 实现 SELECT 执行 + 结果å°è£…
├── 实现 MaxRows è£å‰ª + 耗时统计
├── 实现历å²è®°å½•写入
└── 验è¯: 执行 SELECTï¼Œè¿”å›žç½‘æ ¼æ•°æ®
```
### Phase 3: Data Studio UI (Task 5-6, 约 20%)
```
Task 5: UI é—环
├── ConnectionListView (连接 CRUD + 测试按钮)
├── ObjectTreeView (é€’å½’æ ‘: 连接→表→列)
├── SqlEditorView (多 Tab 编辑器 + 执行按钮)
├── ResultGridView (DataGrid 绑定 + 耗时显示)
├── DataStudioModule 实现 IStudioModule 并注册
└── 验è¯: 完整 UI 交互é—环
Task 6: 导出
├── CSV 导出 (UTF-8 + 逗å·åˆ†éš”)
├── JSON 导出 (æ•°ç»„æ ¼å¼)
└── 验è¯: 结果集导出为 CSV/JSON 文件
```
### Phase 4: AI 助手 (Task 7, 约 10%)
```
Task 7: AI Tool Calling
├── IAIProvider æŽ¥å£ + OpenAI 实现
├── ToolRegistry (6 个内置工具)
├── QuerySafetyFilter (åªè¯» + 行é™åˆ¶)
├── AI 颿¿ UI (Chat + 工具调用å¯è§†åŒ–)
└── 验è¯: AI åˆ†æžæ•°æ®åº“é—环
```
### Phase 5: æ‰“åŒ…éªŒè¯ (Task 8, 约 5%)
```
Task 8: 打包与验è¯
├── ç«¯åˆ°ç«¯éªŒè¯ (连接→查询→导出→AI 分æž)
├── 跨平å°å‘布é…ç½® (dotnet publish)
└── 验è¯: Windows/Linux/macOS å¯è¿è¡Œ
```
***
## 10. ä¾èµ–关系图
```
Task 1 (Shell + Module接å£)
├──► Task 2 (Store)
│ └──► Task 4 (查询执行) ──► Task 5 (UI) ──► Task 6 (导出)
│ │ │ │
├──► Task 3 (SQLite) │ │
└──► Task 4 ──────────────────┘ │
│
Task 7 (AI) ─── requires Task 1,2,3,4 ──────────┤
│
Task 8 (打包) ─── requires Task 5,6,7 ───────────┘
```
***
## 11. 风险与缓解
| 风险 | å½±å“ | 缓解 |
| ----------------- | ---------- | --------------------------------------- |
| Avalonia è·¨å¹³å°æ¸²æŸ“ä¸ä¸€è‡´ | UI 布局差异 | 早期三平å°éªŒè¯ï¼›ä½¿ç”¨ Grid/StackPanel é¿å…ç»å¯¹å®šä½ |
| SQLite 大文件性能 | å…ƒæ•°æ®æµè§ˆå¡é¡¿ | 异æ¥åŠ è½½ + è™šæ‹ŸåŒ–æ ‘ï¼›å…ƒæ•°æ®ç¼“å˜ |
| AI API ä¸å¯ç”¨/è¶…æ—¶ | AI 助手ä¸å¯ç”¨ | é™çº§ç–略:AI 颿¿æ˜¾ç¤ºè¿žæŽ¥é”™è¯¯ï¼Œä¸å½±å“其他功能 |
| åŽç»æ¨¡å—接å£ä¸è¶³ | 大改 Core æŽ¥å£ | IStudioModule ä¿æŒæœ€å°é›†åˆï¼›Module 间通过 DI èŽ·å–æœåŠ¡ |
| è·¨å¹³å°æ–‡ä»¶è·¯å¾„差异 | å˜å‚¨/导出路径错误 | 统一使用 `Environment.SpecialFolder` |
|