feat: 初始化NewLife Studio项目,完成基础框架与数据管理模块
|
# Tasks
## Phase 1: 骨架搭建 — Task 1 ~ 2
- [ ] Task 1: 初始化解决方案与项目结构(Avalonia Studio Shell + Core + Module 接口)
- [ ] `dotnet new avalonia.app` 创建 `src/App/NewLife.Studio.App`,添加 `<TargetFrameworks>net9.0</TargetFrameworks>`
- [ ] `dotnet new classlib` 创建 4 个工程(按分层存放):
- `src/Framework/NewLife.Studio.Core` — 无 UI 依赖的纯抽象层
- `src/Framework/NewLife.Studio.Store` — 本地持久化(依赖 Core)
- `src/Providers/NewLife.Studio.Data` — 数据访问抽象(依赖 Core)
- `src/Framework/NewLife.Studio.AI` — AI Provider 抽象(依赖 Core)
- [ ] 定义 `src/Framework/NewLife.Studio.Core/IStudioModule.cs`:
- 属性:`Id`、`DisplayName`、`Icon`、`Order`
- 方法:`OnActivateAsync(CancellationToken)`、`OnDeactivateAsync(CancellationToken)`、`GetView()` → 返回 `Avalonia.Controls.Control`
- [ ] 引入 NuGet 依赖:
- App:`Avalonia.Desktop`、`CommunityToolkit.Mvvm`、`Microsoft.Extensions.DependencyInjection`
- Core:`NewLife.Core`(日志 XTrace)
- 所有项目统一使用 `System.Text.Json` 做序列化
- [ ] 实现 Shell 布局 `src/App/NewLife.Studio.App/MainWindow.axaml`:
- `Grid` 三列:`NavBar`(48px) | `ModuleHost`(*) | `AIPanel`(300px, 可折叠)
- `Grid` 一行:`Content`(*) | `StatusBar`(24px, 底部)
- [ ] 实现 `src/App/NewLife.Studio.App/Controls/NavBar.axaml/.cs`:垂直图标列表,点击切换 Module
- [ ] 实现 `src/App/NewLife.Studio.App/Controls/ModuleHost.axaml/.cs`:`ContentControl` 绑定当前激活 Module 视图
- [ ] 实现 `src/App/NewLife.Studio.App/Controls/StatusBar.axaml/.cs`:显示当前连接 / 模块名 / 后台任务
- [ ] 实现 `src/App/NewLife.Studio.App/Services/ModuleLoader.cs`:
- 扫描所有已加载 Assembly 中实现 `IStudioModule` 的类型
- 按 `Order` 排序,注入导航项,激活第一个 Module
- [ ] 验证:`dotnet run` 启动空 Shell 窗口,显示 NavBar/ModuleHost/StatusBar 骨架,无异常退出
- [ ] Task 2: 本地存储(连接 / 历史 / 偏好 / AI 配置)
- [ ] 在 `src/Framework/NewLife.Studio.Core/DTOs/` 定义共享 DTO:
- `ConnectionInfo.cs`:Id, Name, ConnectionString, ProviderType, LastUsedAt, Group
- `QueryRequest.cs`:Sql, ConnectionId, MaxRows(默认1000), TimeoutSeconds(默认30)
- `QueryResult.cs`:Columns[], Rows[][], RowCount, ElapsedMs, Truncated, Error
- `TableInfo.cs`:Name, Schema, RowCount
- `ColumnInfo.cs`:Name, DataType, IsNullable, DefaultValue, IsPrimaryKey, Ordinal
- `QueryHistoryEntry.cs`:Id, Sql, ConnectionName, ExecutedAt, ElapsedMs, RowCount
- [ ] 在 `src/Framework/NewLife.Studio.Store/Models/` 定义存储模型:
- `StoredConnection.cs`:持久化格式(密码字段加密)
- `StoredQueryHistory.cs`:JSON 行存储
- `StoredAiProfile.cs`:AiProviderType, Endpoint, ApiKey(加密), Model
- `StoredAppPreference.cs`:MaxRows, DefaultExportPath, Theme, Language
- [ ] 实现 `src/Framework/NewLife.Studio.Store/IStoreService.cs` 接口:
- 连接 CRUD:`ListConnectionsAsync` / `SaveConnectionAsync` / `DeleteConnectionAsync`
- 历史:`AddQueryHistoryAsync` / `GetRecentQueriesAsync(count)`
- AI:`GetAiProfileAsync` / `SaveAiProfileAsync`
- 偏好:`GetPreferencesAsync` / `SavePreferencesAsync`
- [ ] 实现 `src/Framework/NewLife.Studio.Store/StoreService.cs`:
- 基于 `System.Text.Json` + 本地文件(`Environment.SpecialFolder.LocalApplicationData`)
- 按类型分文件:`connections.json` / `history.json` / `ai_profile.json` / `preferences.json`
- [ ] 实现 `src/Framework/NewLife.Studio.Store/SecretProtection.cs`:
- `Protect(string plain)` / `Unprotect(string cipher)` — AES 加密
- 连接保存时自动加密密码、AI Key 字段
- [ ] 注册:`services.AddSingleton<IStoreService, StoreService>()`
- [ ] 验证:写入连接 → 重启 → 读取仍存在;密码字段在文件中不可明文读取
---
## Phase 2: Data Studio 核心 — Task 3 ~ 4
- [ ] Task 3: Data Studio — SQLite Provider(会话与元数据)
- [ ] 定义 `src/Providers/NewLife.Studio.Data/IDataProvider.cs`:
- `ProviderName`、`SupportedSchemes[]`、`CanTestConnection`
- `TestConnectionAsync(ConnectionInfo, CancellationToken)` → `Task<bool>`
- `OpenSessionAsync(ConnectionInfo, CancellationToken)` → `Task<IDbSession>`
- [ ] 定义 `src/Providers/NewLife.Studio.Data/IDbSession.cs`:
- `SessionId`、`Connection`、`IsOpen`
- `GetTablesAsync(CancellationToken)` → `Task<TableInfo[]>`
- `GetColumnsAsync(string tableName, CancellationToken)` → `Task<ColumnInfo[]>`
- `ExecuteQueryAsync(QueryRequest, CancellationToken)` → `Task<QueryResult>`
- `CloseAsync()`
- 继承 `IDisposable`
- [ ] 实现 `src/Providers/NewLife.Studio.Data/Providers/SQLite/SQLiteProvider.cs`:
- `ProviderName = "SQLite"`,`SupportedSchemes = ["sqlite", "sqlite3"]`
- `TestConnectionAsync`:打开 `Microsoft.Data.Sqlite.SqliteConnection` → 执行 `SELECT 1` → 关闭
- `OpenSessionAsync`:创建并返回 `SQLiteSession` 实例
- [ ] 实现 `src/Providers/NewLife.Studio.Data/Providers/SQLite/SQLiteSession.cs`:
- 持有 `Microsoft.Data.Sqlite.SqliteConnection`
- `CloseAsync` → `connection.Close()` + `Dispose()`
- `GetTablesAsync` → 查询 `sqlite_master WHERE type='table' ORDER BY name`
- `GetColumnsAsync` → 使用 `PRAGMA table_info('{tableName}')`
- `ExecuteQueryAsync` → 委托给 Task 4
- [ ] 实现 `src/Providers/NewLife.Studio.Data/Providers/SQLite/SQLiteMetadataReader.cs`:
- 解析 `PRAGMA table_info` 结果 → `ColumnInfo[]`
- 解析 `PRAGMA index_list` + `PRAGMA index_info` → 索引信息
- 解析 `PRAGMA foreign_key_list` → 外键信息(可为空列表)
- [ ] 注册:`services.AddSingleton<IDataProvider, SQLiteProvider>()`(后续多 Provider 改为工厂)
- [ ] 验证:打开测试 SQLite 文件,列表出所有表,每张表可展开列信息
- [ ] Task 4: Data Studio — 查询执行与结果模型
- [ ] 确认 Core DTO `QueryRequest` / `QueryResult` 字段完整(已在 Task 2 定义)
- [ ] 在 `SQLiteSession.ExecuteQueryAsync` 中实现:
- 创建 `SqliteCommand`,设置 `CommandTimeout = request.TimeoutSeconds`
- 使用 `ExecuteReaderAsync` + `CancellationToken` 实现取消
- 逐行读取,达到 `MaxRows` 时停止并标记 `Truncated = true`
- 记录 `ElapsedMs = Stopwatch` 耗时
- [ ] 结果封装为 `QueryResult`:
- `Columns` 从 `reader.GetSchemaTable()` 提取
- `Rows` 为 `List<object[]>`,每个 cell 使用 `reader.GetValue(i)` 取原始值
- 异常时设置 `Error` 字段并返回空结果
- [ ] 实现历史记录写入:
- 依赖注入 `IStoreService`
- 查询成功或失败均异步写入 `QueryHistoryEntry`(不阻塞 UI)
- [ ] 验证:执行 `SELECT * FROM some_table` → 返回网格数据,耗时可见,超限行数被裁剪
---
## Phase 3: Data Studio UI — Task 5 ~ 6
- [ ] Task 5: Data Studio — UI(MVP 闭环)
- [ ] 创建 Module 项目 `src/Modules/DataStudio/`(作为独立 `classlib`,引用 Avalonia + Core + Store + Data)
- [ ] 实现 `DataStudioModule.cs : IStudioModule`:
- `Id = "data-studio"`,`DisplayName = "数据管理"`,`Order = 0`
- `GetView()` → new `DataStudioView`
- [ ] 实现 `DataStudioViewModel`(主 ViewModel,持有子 ViewModel 引用)
- [ ] 实现 `src/Modules/DataStudio/ViewModels/ConnectionListViewModel.cs`:
- `ObservableCollection<ConnectionInfo> Connections`
- `AddConnectionCommand` → 弹出对话框输入 Name / FilePath → 调用 `IStoreService.SaveConnectionAsync`
- `EditConnectionCommand` / `DeleteConnectionCommand`
- `TestConnectionCommand` → 调用 `IDataProvider.TestConnectionAsync` → 显示成功/失败提示
- `OpenConnectionCommand` → 调用 `IDataProvider.OpenSessionAsync` → 传入 ObjectTreeVM
- [ ] 实现 `src/Modules/DataStudio/Views/ConnectionListView.axaml/.cs`:
- `ListBox` 绑定 `Connections`,每项显示 Name + 最近使用时间
- ToolBar 按钮:`+ 新建` `测试` `编辑` `删除`
- [ ] 实现 `src/Modules/DataStudio/ViewModels/ObjectTreeViewModel.cs`:
- `ObservableCollection<TreeNode> Nodes`(连接→Table→Column 递归树)
- `ExpandTableCommand` → 调用 `IDbSession.GetColumnsAsync` → 填充子节点
- 选中表时触发 `TableSelected` 事件
- [ ] 实现 `src/Modules/DataStudio/Views/ObjectTreeView.axaml/.cs`:
- `TreeView` 绑定 `Nodes`,HierarchicalDataTemplate 渲染三层
- [ ] 实现 `src/Modules/DataStudio/ViewModels/SqlEditorViewModel.cs`:
- `ObservableCollection<QueryTab> Tabs`(每个 Tab 有 Sql 文本框 + Result)
- `ExecuteCommand` → 构建 `QueryRequest` → 调用 `IDbSession.ExecuteQueryAsync` → 更新 `Result`
- `NewTabCommand` / `CloseTabCommand`
- [ ] 实现 `src/Modules/DataStudio/Views/SqlEditorView.axaml/.cs`:
- `TabControl` 绑定 `Tabs`,每个 Tab 内含 `TextBox`(SQL 编辑) + `Button`(执行)
- [ ] 实现 `src/Modules/DataStudio/ViewModels/ResultGridViewModel.cs`:
- `Columns` / `Rows` 绑定到 DataGrid
- `ElapsedMs` / `RowCount` / `TruncatedWarning` 显示
- `ExportCsvCommand` / `ExportJsonCommand`(委托 Task 6)
- [ ] 实现 `src/Modules/DataStudio/Views/ResultGridView.axaml/.cs`:
- `DataGrid` 自动列生成(AutoGenerateColumns=True)
- 底部显示耗时 + 行数 + 裁剪提示
- [ ] 在 App 启动时注册 DataStudioModule:
- `ModuleLoader` 扫描到 `DataStudioModule`,添加 NavBar 图标,默认激活
- [ ] 验证:完整 UI 交互闭环(新建连接 → 打开 → 浏览表 → 执行 SQL → 看结果)
- [ ] Task 6: Data Studio — 导出(CSV / JSON)
- [ ] 在 `ResultGridViewModel` 中实现:
- `ExportCsvAsync(string filePath)`:
- 使用 `StreamWriter` + UTF-8 BOM
- 首行写入列名(逗号分隔),值含逗号/引号时加双引号转义
- 逐行写入数据行
- `ExportJsonAsync(string filePath)`:
- 使用 `System.Text.Json` 序列化
- 格式:`[{ "col1": val1, "col2": val2 }, ...]`
- [ ] UI 层:
- 结果区 ToolBar 添加 `导出 CSV` / `导出 JSON` 按钮
- 点击弹出 `SaveFileDialog`(Avalonia `StorageProvider`)选择保存路径
- [ ] 验证:查询结果导出为 CSV → 用记事本/Excel 打开 → 列正确无乱码;导出为 JSON → 格式合法
---
## Phase 4: AI 助手 — Task 7
- [ ] Task 7: AI 助手(Provider 可插拔 + Tool Calling MVP)
- [ ] 定义 `src/Framework/NewLife.Studio.AI/IAiProvider.cs`:
- `ProviderName`
- `ChatAsync(ChatRequest, CancellationToken)` → `Task<ChatResponse>`
- [ ] 定义 AI 模型 DTO `src/Framework/NewLife.Studio.AI/Models/`:
- `ChatMessage.cs`:Role, Content, ToolCalls[], ToolCallId
- `ChatRequest.cs`:Messages[], Tools[], Model, MaxTokens
- `ChatResponse.cs`:Content, ToolCalls[], InputTokens, OutputTokens
- `ToolCall.cs`:Id, Name, Arguments(JSON)
- `ToolResult.cs`:ToolCallId, Output, Error
- `ToolDefinition.cs`:Name, Description, Parameters(JSON Schema)
- [ ] 实现 `src/Framework/NewLife.Studio.AI/AiProviderFactory.cs`:
- 根据 `AiProfile.ProviderType`("openai"/"azure"/"ollama")创建对应 `IAiProvider`
- [ ] 实现 OpenAI Provider(首个实现):
- HttpClient 调用 `https://api.openai.com/v1/chat/completions`
- 支持 Tool Calling(tools 参数传递)
- 解析响应中的 `tool_calls` 与 `content`
- [ ] 实现 `src/Framework/NewLife.Studio.AI/ToolCalling/ToolRegistry.cs`:
- `Register(string name, Func<string, Task<string>> handler, ToolDefinition definition)`
- `GetAllDefinitions()` → `ToolDefinition[]`
- [ ] 实现 `src/Framework/NewLife.Studio.AI/ToolCalling/ToolExecutor.cs`:
- `ExecuteAsync(ToolCall call)` → `Task<ToolResult>`
- 通过 `ToolRegistry` 查找 handler,执行并返回结果
- [ ] 实现 6 个内置工具 `src/Framework/NewLife.Studio.AI/ToolCalling/BuiltInTools/`:
| 工具名 | 功能 |
|---|---|
| `connections.list` | 列出所有已保存的连接(脱敏) |
| `db.open` | 打开指定数据库连接,返回会话 ID |
| `schema.tables` | 列出当前会话的表 |
| `schema.table` | 查看指定表的列详情 |
| `query.select` | 执行 SELECT(经安全过滤) |
| `query.sample` | 取表的前 N 行样本数据 |
- [ ] 实现 `src/Framework/NewLife.Studio.AI/Safety/QuerySafetyFilter.cs`:
- 仅允许 `SELECT` / `EXPLAIN` / `PRAGMA` 开头的语句
- 拒绝所有写操作(INSERT/UPDATE/DELETE/DROP/ALTER/CREATE)
- 拒绝多语句(分号分隔)
- 超行限制 + 超时取消
- [ ] 实现 Shell 级 AI 面板 `src/App/NewLife.Studio.App/Controls/AIPanel.axaml/.cs`:
- `ChatMessageList`:`ItemsControl` 渲染对话气泡(User 右对齐蓝底,Assistant 左对齐灰底,Tool 灰底斜体)
- Markdown 渲染(简单实现:支持 ``` 代码块和纯文本)
- `ToolCallInfo`:可展开区域,显示工具名 + 参数 + 返回结果
- `ChatInput`:多行 `TextBox` + `Button`("发送")
- [ ] 实现 `AIService`(跨 Module 共享):
- 管理对话历史 `List<ChatMessage>`
- 实现 Tool Calling 循环:发送 → 检测 tool_calls → 执行工具 → 追加结果 → 再发送
- [ ] 在 AI Profile 未配置时,AI Panel 显示引导提示"请先配置 AI 服务"
- [ ] 验证:AI 分析数据库完整闭环(列出表 → 查看结构 → 执行样本查询 → 输出分析汇总)
---
## Phase 5: 打包验证 — Task 8
- [ ] Task 8: 打包与基本验证
- [ ] 端到端集成验证:
1. `dotnet run` → Shell 窗口启动 → NavBar 显示"数据管理"图标
2. 新建 SQLite 连接 → 测试连接成功 → 打开连接
3. 对象树展开 Table 列表 → 点击表查看列信息
4. SQL 编辑器输入 `SELECT * FROM table LIMIT 10` → 执行 → 结果网格显示
5. 导出 CSV / JSON → 文件内容正确
6. 打开 AI 面板 → 输入"分析数据库结构" → AI 调用工具 → 输出分析
7. AI 尝试 `INSERT INTO` → 被拒绝 → 显示错误提示
8. 关闭应用 → 重新启动 → 连接/历史/偏好仍存在
- [ ] 验证 `IStudioModule` 可扩展性:
- 编写一个 Mock Module 实现 `IStudioModule`,编译为独立 dll 放入 App 目录
- 启动后 NavBar 出现该模块图标,点击可切换视图
- [ ] 跨平台发布配置:
- `dotnet publish -c Release -r win-x64 --self-contained` → 生成 Windows 产物
- `dotnet publish -c Release -r linux-x64 --self-contained` → 生成 Linux 产物
- `dotnet publish -c Release -r osx-x64 --self-contained` → 生成 macOS 产物
- 验证:三个平台的发布产物均可启动(解压即用)
---
# Task Dependencies
```
Task 1 (Shell + Module接口)
├──► Task 2 (Store)
│ └──► Task 4 (查询执行)
│ └──► Task 5 (UI)
│ └──► Task 6 (导出)
│
└──► Task 3 (SQLite Provider)
└──► Task 4 ──► Task 5 ──► Task 6
│
Task 7 (AI) ────────────┤
(依赖 Task 1,2,3,4) │
│
Task 8 (打包) ───────────┘
(依赖 Task 5,6,7)
```
- Task 2 depends on Task 1
- Task 3 depends on Task 1
- Task 4 depends on Task 3 and Task 2
- Task 5 depends on Task 1, Task 2, Task 3, and Task 4
- Task 6 depends on Task 4 and Task 5
- Task 7 depends on Task 1, Task 2, Task 3, and Task 4
- Task 8 depends on Task 5, Task 6, and Task 7
---
# Module Expansion Roadmap(后续迭代参考)
- Task 9: MQTT Studio 模块
- `src/Providers/NewLife.Studio.Mqtt/` — MQTT 客户端封装
- `src/Modules/MqttStudio/` — 连接 MQTT Broker(NewLife.MQTT)
- Topic 树浏览、消息发布/订阅
- 客户端连接状态监控
- Task 10: MQ Studio 模块
- `src/Providers/NewLife.Studio.Mq/` — 消息队列抽象
- `src/Modules/MqStudio/` — MQ 队列管理
- MQ 队列列表(NewLife.MQ / NewLife.RocketMQ)
- 消息查看、消费状态、堆积监控
- Task 11: Redis Studio 模块
- `src/Providers/NewLife.Studio.Redis/` — Redis 客户端封装
- `src/Modules/RedisStudio/` — Key 浏览与搜索
- Key 浏览与搜索(NewLife.Redis)
- 命令执行控制台、内存/命中率监控
- Task 12: Stardust Console 模块
- `src/Modules/StardustConsole/` — 星尘平台管理
- 星尘平台节点/应用/配置总览(NewLife.Stardust)
- APM 调用链查看、日志搜索
- Task 13: Modbus Studio 模块
- `src/Providers/NewLife.Studio.Modbus/` — Modbus 协议封装
- `src/Modules/ModbusStudio/` — Modbus 设备管理
- Modbus 设备扫描、寄存器读写(NewLife.Modbus / NewLife.IoT)
|