Private
Public Access
1
0

重构:文件系统模块化架构,优化应用启动流程

This commit is contained in:
2026-01-28 00:28:54 +08:00
parent 4a9b25a505
commit 8c577f70e7
123 changed files with 32030 additions and 967 deletions

View File

@@ -0,0 +1,368 @@
# 事件系统设计
**设计日期**2025-01-28
**设计范围**:数据库客户端全局事件系统
**状态**:设计阶段
---
## 一、设计概述
### 1.1 设计目标
- **简洁统一**:所有组件使用统一的事件命名和参数格式
- **易于扩展**:新增事件时,遵循统一规范,易于维护
- **类型安全**:使用 TypeScript 类型定义,确保类型安全
- **功能强大**:支持事件传递、事件拦截、事件日志等高级功能
### 1.2 设计原则
1. **命名规范**:事件名称使用 kebab-case语义清晰
2. **参数统一**:事件参数使用对象格式,包含必要上下文信息
3. **类型定义**:所有事件都有明确的 TypeScript 类型定义
4. **文档完善**:每个事件都有清晰的文档说明
---
## 二、事件分类
### 2.1 连接相关事件
```typescript
// 连接选择
'connection-select': {
connection: DbConnection
database?: string // 可选,选中的数据库
}
// 连接编辑
'connection-edit': {
connectionId: number
}
// 连接删除
'connection-delete': {
connectionId: number
}
// 连接刷新
'connection-refresh': {
connectionId?: number // 可选,不提供则刷新所有
}
```
### 2.2 表结构相关事件
```typescript
// 查看表结构
'table-structure': {
connectionId: number
database: string
tableName: string // 表名/集合名/Key名
dbType: 'mysql' | 'mongo' | 'redis'
nodeType: 'table' | 'collection' | 'key' | 'database' | 'connection'
}
// 表选择生成SQL
'table-select': {
connectionId: number
database: string
tableName: string
sql?: string // 可选预生成的SQL
}
```
### 2.3 SQL执行相关事件
```typescript
// SQL执行
'sql-execute': {
sql: string
connectionId: number
database?: string
}
// SQL执行完成
'sql-execute-complete': {
result: SqlResult
error?: string
}
```
### 2.4 编辑器相关事件
```typescript
// SQL插入
'sql-insert': {
sql: string
tabKey?: string // 可选指定Tab
}
// Tab切换
'tab-switch': {
tabKey: string
}
// Tab关闭
'tab-close': {
tabKey: string
}
```
---
## 三、事件系统架构
### 3.1 事件总线设计
```typescript
// 事件总线接口
interface EventBus {
// 注册事件监听器
on<T = any>(event: string, handler: (data: T) => void): () => void
// 注册一次性事件监听器
once<T = any>(event: string, handler: (data: T) => void): void
// 移除事件监听器
off(event: string, handler?: Function): void
// 触发事件
emit<T = any>(event: string, data: T): void
// 清除所有监听器
clear(): void
}
// 全局事件总线实例
export const eventBus = createEventBus()
```
### 3.2 组件事件映射
```typescript
// ConnectionTree 组件事件
interface ConnectionTreeEvents {
'connection-select': { connection: DbConnection; database?: string }
'connection-edit': { connectionId: number }
'connection-delete': { connectionId: number }
'table-select': { connectionId: number; database: string; tableName: string }
'table-structure': {
connectionId: number
database: string
tableName: string
dbType: 'mysql' | 'mongo' | 'redis'
nodeType: string
}
'new-connection': void
'show-bookmarks': void
'show-templates': void
}
// SqlEditor 组件事件
interface SqlEditorEvents {
'execute': { sql: string }
'execute-selected': { sql: string }
'sql-insert': { sql: string; tabKey?: string }
'tab-switch': { tabKey: string }
'tab-close': { tabKey: string }
'toggle-editor': void
}
```
---
## 四、事件命名规范
### 4.1 命名规则
- **格式**`<组件>-<动作>``<功能>-<动作>`
- **示例**
- `connection-select`:连接选择
- `table-structure`:表结构查看
- `sql-execute`SQL执行
### 4.2 动作词汇表
| 动作 | 说明 | 示例 |
|------|------|------|
| select | 选择 | `connection-select` |
| edit | 编辑 | `connection-edit` |
| delete | 删除 | `connection-delete` |
| create | 创建 | `tab-create` |
| close | 关闭 | `tab-close` |
| switch | 切换 | `tab-switch` |
| execute | 执行 | `sql-execute` |
| insert | 插入 | `sql-insert` |
| refresh | 刷新 | `connection-refresh` |
---
## 五、事件参数设计
### 5.1 参数原则
1. **对象格式**:所有事件参数使用对象,不使用多个参数
2. **必要信息**:包含事件处理所需的所有上下文信息
3. **可选字段**:使用可选字段(`?`)标记非必需信息
4. **类型明确**:所有字段都有明确的类型定义
### 5.2 参数示例
```typescript
// ✅ 好的设计:对象格式,类型明确
emit('table-structure', {
connectionId: 1,
database: 'test',
tableName: 'users',
dbType: 'mysql',
nodeType: 'table'
})
// ❌ 不好的设计:多个参数,类型不明确
emit('table-structure', 1, 'test', 'users', 'mysql', 'table')
```
---
## 六、事件处理流程
### 6.1 事件触发流程
```
组件内触发事件
emit('event-name', data)
父组件监听事件
调用处理函数
更新状态/执行操作
```
### 6.2 事件拦截机制(可选)
```typescript
// 事件拦截器接口
interface EventInterceptor {
beforeEmit?: (event: string, data: any) => boolean // 返回false阻止事件
afterEmit?: (event: string, data: any) => void // 事件触发后执行
}
// 注册拦截器
eventBus.addInterceptor(interceptor)
```
---
## 七、实现细节
### 7.1 事件类型定义
```typescript
// 事件类型定义文件types/events.ts
export interface ConnectionSelectEvent {
connection: DbConnection
database?: string
}
export interface TableStructureEvent {
connectionId: number
database: string
tableName: string
dbType: 'mysql' | 'mongo' | 'redis'
nodeType: 'table' | 'collection' | 'key' | 'database' | 'connection'
}
// ... 其他事件类型
```
### 7.2 组件事件声明
```typescript
// ConnectionTree.vue
const emit = defineEmits<{
'connection-select': [data: ConnectionSelectEvent]
'table-structure': [data: TableStructureEvent]
'table-select': [data: TableSelectEvent]
// ... 其他事件
}>()
```
### 7.3 事件处理
```typescript
// index.vue
const handleTableStructure = (data: TableStructureEvent) => {
// 加载表结构
structureState.loadStructure(
data.connectionId,
data.database,
data.tableName,
data.dbType
)
// 切换到结构Tab
resultTab.value = 'structure'
}
```
---
## 八、扩展性设计
### 8.1 事件日志(开发模式)
```typescript
// 开发模式下记录所有事件
if (import.meta.env.DEV) {
eventBus.on('*', (event, data) => {
console.log(`[Event] ${event}`, data)
})
}
```
### 8.2 事件统计(可选)
```typescript
// 统计事件触发次数
const eventStats = new Map<string, number>()
eventBus.on('*', (event) => {
eventStats.set(event, (eventStats.get(event) || 0) + 1)
})
```
---
## 九、实现优先级
### P0必须实现
1. ✅ 事件类型定义TypeScript
2. ✅ 连接相关事件
3. ✅ 表结构相关事件
4. ✅ SQL执行相关事件
### P1重要功能
1. 事件参数验证
2. 事件文档完善
3. 事件处理错误处理
### P2优化功能
1. 事件拦截机制
2. 事件日志(开发模式)
3. 事件统计
---
## 十、总结
事件系统设计遵循以下原则:
1. **简洁统一**:统一的事件命名和参数格式
2. **类型安全**:完整的 TypeScript 类型定义
3. **易于扩展**:清晰的事件分类和命名规范
4. **功能强大**:支持事件拦截、日志等高级功能
通过以上设计,可以实现一个简洁、强大、易扩展的事件系统。

View File

@@ -0,0 +1,312 @@
# 数据库客户端前端架构设计文档
**文档版本**v2.0
**维护者**JueChen
**更新日期**2025-01-28
**源码路径**`go-desk/web/src/views/db-cli/`
---
## 一、整体架构概览
### 1.1 分层架构
```
┌─────────────────────────────────────────────────────────────┐
│ 视图层Views
│ ┌──────────────────────────────────────────────────────┐ │
│ │ index.vue (主页面 - 布局和协调) │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 组件层Components
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ConnectionTree│ │ SqlEditor │ │ ResultPanel │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ConnectionForm│ │ResourceManager│ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 组合式函数层Composables
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │useDbConnection│ │useSqlExecution│ │useEditorState│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │useResultState │ │useMessageLog │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ API 层Wails Bridge
│ ┌──────────────────────────────────────────────────────┐ │
│ │ window.go.main.App.* │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### 1.2 架构设计原则
1. **单一职责原则**:每个组件和 composable 只负责一个功能领域
2. **关注点分离**:视图、逻辑、状态分离
3. **可复用性**:通过 composables 抽取可复用逻辑
4. **可维护性**:清晰的目录结构和命名规范
5. **可测试性**composables 可以独立测试
---
## 二、目录结构
```
db-cli/
├── index.vue # 主页面(布局和协调)
├── components/ # 组件目录
│ ├── ConnectionTree.vue # 连接树组件
│ ├── ConnectionForm.vue # 连接表单组件
│ ├── SqlEditor.vue # SQL编辑器组件
│ ├── ResultPanel.vue # 结果展示组件
│ ├── ResourceManager.vue # 资源管理组件
│ └── ~~BookmarkManager.vue~~ # ❌ 已删除(书签功能已删除)
│ └── ~~TemplateManager.vue~~ # ❌ 已删除(模板功能已删除)
└── composables/ # 组合式函数目录
├── useDbConnection.ts # 连接管理逻辑
├── useSqlExecution.ts # SQL执行逻辑
├── useEditorState.ts # 编辑器状态管理
├── useResultState.ts # 结果状态管理
└── useMessageLog.ts # 消息日志管理
```
---
## 三、Composables 设计
### 3.1 useDbConnection.ts
**职责**:管理数据库连接相关的状态和逻辑
**状态**
- `currentConnection`: 当前选中的连接
- `selectedDatabase`: 当前选中的数据库MySQL
- `showConnectionForm`: 连接表单显示状态
- `editingConnectionId`: 正在编辑的连接ID
**方法**
- `selectConnection(conn, database)`: 选择连接
- `editConnection(connectionId)`: 编辑连接
- `deleteConnection(connectionId)`: 删除连接
- `newConnection()`: 新建连接
- `onConnectionSuccess()`: 连接操作成功回调
### 3.2 useSqlExecution.ts
**职责**管理SQL执行相关的逻辑
**方法**
- `executeSQL(sql, connection, database)`: 执行SQL
- `handleQueryResult(result)`: 处理查询结果
- `handleUpdateResult(result)`: 处理更新结果
- `handleCommandResult(result)`: 处理命令结果Redis
### 3.3 useEditorState.ts
**职责**:管理编辑器显示/隐藏状态
**状态**
- `editorVisible`: 编辑器是否可见
**方法**
- `toggleEditor()`: 切换编辑器显示/隐藏
- `loadEditorVisible()`: 从localStorage加载状态
- `saveEditorVisible()`: 保存状态到localStorage
### 3.4 useResultState.ts
**职责**:管理执行结果相关的状态
**状态**
- `resultLoading`: 加载状态
- `resultError`: 错误信息
- `resultData`: 结果数据
- `resultMode`: 展示模式table/json
- `resultStats`: 执行统计
- `resultColumns`: 表格列定义
**方法**
- `clearResults()`: 清空结果
- `setQueryResult(data, stats)`: 设置查询结果
- `setUpdateResult(stats)`: 设置更新结果
- `setCommandResult(data, stats)`: 设置命令结果
- `setError(error)`: 设置错误
### 3.5 useMessageLog.ts
**职责**:管理消息日志
**状态**
- `messages`: 消息列表
**方法**
- `addMessage(type, content)`: 添加消息
- `clearMessages()`: 清空消息
- `getMessages(limit)`: 获取消息(带限制)
---
## 四、组件通信设计
### 4.1 Props 向下传递
```
index.vue
├─ ConnectionTree
│ └─ currentConnectionId (prop)
├─ SqlEditor
│ └─ currentConnection (prop)
└─ ResultPanel
├─ loading (prop)
├─ error (prop)
├─ data (prop)
├─ mode (prop)
├─ stats (prop)
├─ columns (prop)
└─ messages (prop)
```
### 4.2 Events 向上传递
```
ConnectionTree
├─ @connection-select → index.vue
├─ @connection-edit → index.vue
├─ @connection-delete → index.vue
├─ @table-select → index.vue
├─ @new-connection → index.vue
└─ ~~@show-bookmarks, @show-templates~~ ❌ 已删除(功能已删除)
SqlEditor
├─ @execute → index.vue
├─ @execute-selected → index.vue
└─ @toggle-editor → index.vue
ResultPanel
└─ @toggle-editor → index.vue
```
### 4.3 Provide/Inject可选
对于深层嵌套的组件,可以使用 provide/inject
```typescript
// index.vue
provide('dbCliContext', {
currentConnection,
selectedDatabase,
executeSQL,
addMessage
})
// 深层组件
const { currentConnection, executeSQL } = inject('dbCliContext')
```
---
## 五、状态管理流程
### 5.1 连接选择流程
```
用户点击连接
→ ConnectionTree 触发 @connection-select
→ index.vue 调用 useDbConnection.selectConnection()
→ 更新 currentConnection 和 selectedDatabase
→ 清空结果useResultState.clearResults()
→ 添加消息useMessageLog.addMessage()
→ SqlEditor 接收新的 currentConnection prop
```
### 5.2 SQL执行流程
```
用户执行SQL
→ SqlEditor 触发 @execute
→ index.vue 调用 useSqlExecution.executeSQL()
→ 调用 window.go.main.App.ExecuteSQL()
→ 根据结果类型调用对应的处理方法
→ useResultState 更新结果状态
→ ResultPanel 接收新的 props 并展示
```
---
## 六、重构优势
### 6.1 代码组织
- **清晰的职责划分**:每个 composable 负责一个功能领域
- **易于维护**:修改某个功能只需修改对应的 composable
- **代码复用**composables 可以在其他页面复用
### 6.2 可测试性
- **独立测试**:每个 composable 可以独立测试
- **Mock 简单**:可以轻松 mock window.go API
- **测试覆盖**:逻辑集中在 composables测试更容易
### 6.3 可扩展性
- **新增功能**:只需添加新的 composable
- **功能组合**:可以组合多个 composables 实现复杂功能
- **向后兼容**:不影响现有组件结构
---
## 七、实施步骤
### 步骤1创建 composables 目录结构 ✅
- [x] 创建 `composables/` 目录
- [x] 创建 `useDbConnection.ts`
- [x] 创建 `useSqlExecution.ts`
- [x] 创建 `useEditorState.ts`
- [x] 创建 `useResultState.ts`
- [x] 创建 `useMessageLog.ts`
### 步骤2重构主页面 ✅
- [x] 将状态管理逻辑迁移到 composables
- [x] 将业务逻辑迁移到 composables
- [x] 简化 index.vue只保留布局和协调逻辑
### 步骤3优化组件通信 ✅
- [x] 评估是否需要使用 provide/inject当前不需要
- [x] 优化 props 传递
- [x] 优化事件处理
### 步骤4测试和验证 ⚠️
- [x] 功能测试(基本完成)
- [ ] 性能测试(待完成)
- [x] 代码审查(已完成)
---
## 八、后续优化方向
1. **状态管理库**:如果状态管理变得复杂,可以考虑引入 Pinia
2. **类型安全**:为 composables 添加完整的 TypeScript 类型定义
3. **错误处理**:统一错误处理机制
4. **性能优化**:使用 computed 和 watch 优化响应式更新
5. **单元测试**:为 composables 编写单元测试
---
## 九、参考文档
- [Vue 3 Composition API](https://vuejs.org/guide/extras/composition-api-faq.html)
- [Vue 3 Provide/Inject](https://vuejs.org/guide/components/provide-inject.html)
- [组件拆分方案](./组件拆分方案.md)

View File

@@ -0,0 +1,340 @@
# 右键菜单系统设计
**设计日期**2025-01-28
**设计范围**:数据库客户端全局右键菜单系统
**状态**:设计阶段
---
## 一、设计概述
### 1.1 设计目标
- **统一体验**:所有区域的右键菜单使用统一的设计和交互方式
- **易于扩展**:新增菜单项和功能区域时,可以快速集成
- **上下文感知**:根据点击位置和对象类型,显示相应的菜单项
- **简洁强大**:菜单项精简,但功能完整
### 1.2 适用范围
- **连接树区域**:连接、数据库、表/集合/Key节点的右键菜单
- **SQL编辑器区域**编辑器内容、Tab标签的右键菜单未来扩展
- **结果区域**表格、JSON内容的右键菜单未来扩展
### 1.3 设计原则
1. **按需显示**:根据节点类型和上下文,只显示相关的菜单项
2. **分组清晰**:相关功能分组,使用分隔线区分
3. **操作明确**:菜单项名称清晰,避免歧义
4. **快捷操作**:常用功能提供快捷键提示
---
## 二、连接树右键菜单设计
### 2.1 连接节点右键菜单
**触发条件**:右键点击连接节点
**菜单项**
```
┌─────────────────────────┐
│ 查看结构 │
│ 编辑连接 │
│ 删除连接 │
├─────────────────────────┤
│ 刷新 │
│ 测试连接 │
└─────────────────────────┘
```
**菜单项说明**
- **查看结构**:查看连接的数据库列表结构(如果支持)
- **编辑连接**:编辑连接配置
- **删除连接**:删除连接(需确认)
- **刷新**:刷新连接状态和数据库列表
- **测试连接**:测试连接是否可用
---
### 2.2 数据库节点右键菜单
**触发条件**:右键点击数据库节点
**菜单项MySQL/MongoDB**
```
┌─────────────────────────┐
│ 查看结构 │
│ 生成SELECT语句 │
├─────────────────────────┤
│ 刷新 │
└─────────────────────────┘
```
**菜单项Redis DB**
```
┌─────────────────────────┐
│ 查看结构 │
│ 生成KEYS命令 │
├─────────────────────────┤
│ 刷新 │
└─────────────────────────┘
```
**菜单项说明**
- **查看结构**:查看数据库的表/集合列表结构
- **生成SELECT语句**:生成 `SELECT * FROM database.table LIMIT 100;`
- **生成KEYS命令**:生成 `KEYS *` 命令Redis
- **刷新**:刷新表/集合列表
---
### 2.3 表/集合节点右键菜单
**触发条件**:右键点击表/集合节点
**菜单项MySQL**
```
┌─────────────────────────┐
│ 查看结构 │
│ 生成SELECT语句 │
│ 复制表名 │
├─────────────────────────┤
│ 刷新 │
└─────────────────────────┘
```
**菜单项MongoDB**
```
┌─────────────────────────┐
│ 查看结构 │
│ 生成find语句 │
│ 复制集合名 │
├─────────────────────────┤
│ 刷新 │
└─────────────────────────┘
```
**菜单项说明**
- **查看结构**:查看表/集合的结构信息(字段、索引等)
- **生成SELECT语句**:生成 `SELECT * FROM database.table LIMIT 100;`
- **生成find语句**:生成 `db.collection.find({})`MongoDB
- **复制表名/集合名**:复制到剪贴板
- **刷新**:刷新表结构
---
### 2.4 Key节点右键菜单Redis
**触发条件**右键点击Key节点
**菜单项**
```
┌─────────────────────────┐
│ 查看结构 │
│ 生成GET命令 │
│ 复制Key名 │
├─────────────────────────┤
│ 刷新 │
└─────────────────────────┘
```
**菜单项说明**
- **查看结构**查看Key的详细信息类型、TTL、值预览
- **生成GET命令**根据Key类型生成相应命令GET、HGETALL等
- **复制Key名**复制Key名称到剪贴板
- **刷新**刷新Key信息
---
## 三、技术实现设计
### 3.1 组件结构
```
ContextMenu.vue (全局右键菜单组件)
├── 菜单项配置(根据节点类型动态生成)
├── 菜单项渲染(使用 Arco Design Dropdown
└── 事件处理(触发相应操作)
ConnectionTree.vue
└── 集成 ContextMenu 组件
└── 根据节点类型传递菜单配置
```
### 3.2 菜单配置数据结构
```typescript
interface MenuItem {
key: string // 唯一标识
label: string // 显示文本
icon?: string // 图标(可选)
disabled?: boolean // 是否禁用
divider?: boolean // 是否为分隔线
children?: MenuItem[] // 子菜单(可选)
}
interface MenuConfig {
items: MenuItem[] // 菜单项列表
position: { // 菜单位置
x: number
y: number
}
}
```
### 3.3 菜单项注册机制
```typescript
// 菜单项注册表
const menuRegistry = {
'connection': [
{ key: 'view-structure', label: '查看结构', icon: 'icon-eye' },
{ key: 'edit', label: '编辑连接', icon: 'icon-edit' },
{ key: 'delete', label: '删除连接', icon: 'icon-delete' },
{ key: 'divider-1', divider: true },
{ key: 'refresh', label: '刷新', icon: 'icon-refresh' },
{ key: 'test', label: '测试连接', icon: 'icon-check' }
],
'database': [
{ key: 'view-structure', label: '查看结构', icon: 'icon-eye' },
{ key: 'generate-sql', label: '生成SELECT语句', icon: 'icon-code' },
{ key: 'divider-1', divider: true },
{ key: 'refresh', label: '刷新', icon: 'icon-refresh' }
],
'table': [
{ key: 'view-structure', label: '查看结构', icon: 'icon-eye' },
{ key: 'generate-sql', label: '生成SELECT语句', icon: 'icon-code' },
{ key: 'copy-name', label: '复制表名', icon: 'icon-copy' },
{ key: 'divider-1', divider: true },
{ key: 'refresh', label: '刷新', icon: 'icon-refresh' }
],
// ... 其他节点类型
}
```
### 3.4 事件处理机制
```typescript
// 统一的事件处理接口
interface MenuEventHandler {
(nodeData: TreeNodeData, menuKey: string): void | Promise<void>
}
// 事件映射表
const eventHandlers: Record<string, MenuEventHandler> = {
'view-structure': (nodeData) => {
// 触发查看结构事件
emit('table-structure', {
connectionId: nodeData.connectionId,
database: nodeData.database,
tableName: nodeData.tableName || nodeData.title,
dbType: nodeData.dbType,
nodeType: nodeData.type
})
},
'edit': (nodeData) => {
emit('connection-edit', nodeData.connectionId)
},
'delete': (nodeData) => {
emit('connection-delete', nodeData.connectionId)
},
'generate-sql': (nodeData) => {
// 生成SQL语句
const sql = generateSQL(nodeData)
emit('table-select', { ...nodeData, sql })
},
'copy-name': (nodeData) => {
// 复制名称到剪贴板
copyToClipboard(nodeData.tableName || nodeData.title)
},
'refresh': (nodeData) => {
// 刷新节点数据
refreshNode(nodeData)
}
}
```
---
## 四、实现细节
### 4.1 菜单显示位置
- **定位方式**:使用鼠标事件坐标定位
- **边界处理**:菜单超出视口时自动调整位置
- **层级管理**:使用 z-index 确保菜单在最上层
### 4.2 菜单交互
- **点击外部关闭**:点击菜单外部区域自动关闭
- **ESC键关闭**按ESC键关闭菜单
- **键盘导航**支持方向键导航菜单项可选P2
### 4.3 菜单样式
- **使用 Arco Design Dropdown**:保持与系统风格一致
- **图标支持**:菜单项支持图标显示
- **禁用状态**:禁用项显示为灰色,不可点击
- **分隔线**:使用分隔线区分功能组
---
## 五、扩展性设计
### 5.1 插件化菜单项
```typescript
// 菜单项插件接口
interface MenuItemPlugin {
name: string
condition: (nodeData: TreeNodeData) => boolean // 显示条件
getMenuItem: (nodeData: TreeNodeData) => MenuItem // 生成菜单项
handler: (nodeData: TreeNodeData) => void // 处理函数
}
// 注册插件
function registerMenuItemPlugin(plugin: MenuItemPlugin) {
// 注册逻辑
}
```
### 5.2 动态菜单项
- **权限控制**:根据用户权限动态显示/隐藏菜单项
- **上下文感知**:根据当前状态动态调整菜单项
- **条件显示**:某些菜单项只在特定条件下显示
---
## 六、实现优先级
### P0必须实现
1. ✅ 连接节点右键菜单(查看结构、编辑、删除、刷新)
2. ✅ 数据库节点右键菜单查看结构、生成SQL、刷新
3. ✅ 表节点右键菜单查看结构、生成SQL、复制表名、刷新
4. ✅ Key节点右键菜单查看结构、生成命令、复制Key名、刷新
### P1重要功能
1. 菜单定位和边界处理
2. 菜单项图标支持
3. 复制功能实现
### P2优化功能
1. 键盘导航支持
2. 菜单项插件化
3. 权限控制
---
## 七、总结
右键菜单系统设计遵循以下原则:
1. **统一设计**:所有区域的右键菜单使用统一的设计和交互
2. **易于扩展**:通过配置和插件机制,易于添加新功能
3. **上下文感知**:根据节点类型和状态,显示相关菜单项
4. **简洁强大**:菜单项精简但功能完整
通过以上设计,可以实现一个统一、易用、易扩展的右键菜单系统。

View File

@@ -0,0 +1,287 @@
# 数据库客户端后端架构设计文档
**文档版本**v2.0
**维护者**JueChen
**更新日期**2025-01-28
**源码路径**`go-desk/`
---
## 一、整体架构概览
### 1.1 分层架构
```
┌─────────────────────────────────────────────────────────────┐
│ 接口层API Layer
│ ┌──────────────────────────────────────────────────────┐ │
│ │ app.go (Wails App 接口) │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 服务层Service Layer
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ConnectionSvc │ │ SqlExecSvc │ │ ResourceSvc │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ TabSvc │ │ BookmarkSvc │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 数据访问层Data Access Layer
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Storage │ │ DBClient │ │ Models │ │
│ │ (SQLite) │ │ (Pool) │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 基础设施层Infrastructure Layer
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Crypto │ │ Filesystem │ │ System │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### 1.2 架构设计原则
1. **单一职责原则**:每个服务只负责一个业务领域
2. **依赖倒置原则**:接口定义在服务层,实现在数据访问层
3. **关注点分离**:接口、业务逻辑、数据访问分离
4. **可测试性**:通过接口抽象,便于单元测试
5. **可扩展性**:新增功能只需添加新的服务
---
## 二、目录结构
```
go-desk/
├── main.go # 应用入口
├── app.go # Wails App 接口(精简后)
├── internal/
│ ├── api/ # API 接口层(新增)
│ │ ├── connection_api.go # 连接管理接口
│ │ ├── sql_api.go # SQL执行接口
│ │ ├── resource_api.go # 资源管理接口
│ │ └── tab_api.go # 标签页接口
│ │
│ ├── service/ # 服务层(新增)
│ │ ├── connection_service.go # 连接管理服务
│ │ ├── sql_exec_service.go # SQL执行服务
│ │ ├── resource_service.go # 资源管理服务
│ │ └── tab_service.go # 标签页服务
│ │
│ ├── storage/ # 数据访问层
│ │ ├── sqlite.go # SQLite 初始化
│ │ ├── models/ # 数据模型
│ │ │ ├── connection.go
│ │ │ ├── sql_tab.go
│ │ │ ├── bookmark.go
│ │ │ └── template.go
│ │ └── repository/ # 数据仓库(新增)
│ │ ├── connection_repo.go
│ │ ├── tab_repo.go
│ │ ├── bookmark_repo.go
│ │ └── template_repo.go
│ │
│ ├── dbclient/ # 数据库客户端
│ │ ├── pool.go # 连接池管理
│ │ ├── mysql.go # MySQL 客户端
│ │ ├── redis.go # Redis 客户端
│ │ └── mongo.go # MongoDB 客户端
│ │
│ ├── crypto/ # 加密工具
│ ├── filesystem/ # 文件系统
│ └── system/ # 系统信息
```
---
## 三、服务层设计
### 3.1 ConnectionService
**职责**:管理数据库连接配置
**方法**
- `SaveConnection(conn *models.DbConnection) error`
- `ListConnections() ([]models.DbConnection, error)`
- `GetConnection(id uint) (*models.DbConnection, error)`
- `DeleteConnection(id uint) error`
- `TestConnection(conn *models.DbConnection) error`
**依赖**
- `ConnectionRepository`:数据访问接口
### 3.2 SqlExecService
**职责**:执行 SQL 语句
**方法**
- `ExecuteSQL(connectionId uint, sqlStr string, database string) (*SqlResult, error)`
- `GetDatabases(connectionId uint) ([]string, error)`
- `GetTables(connectionId uint, database string) ([]string, error)`
**依赖**
- `ConnectionService`:获取连接配置
- `ConnectionPool`:获取数据库客户端
### 3.3 ResourceService
**职责**:管理书签和模板
**方法**
- `SaveBookmark(bookmark *models.Bookmark) error`
- `ListBookmarks(connectionId uint) ([]models.Bookmark, error)`
- `DeleteBookmark(id uint) error`
- `SaveTemplate(template *models.Template) error`
- `ListTemplates() ([]models.Template, error)`
- `DeleteTemplate(id uint) error`
**依赖**
- `BookmarkRepository`:书签数据访问
- `TemplateRepository`:模板数据访问
### 3.4 TabService
**职责**:管理 SQL 标签页
**方法**
- `SaveTabs(tabs []models.SqlTab) error`
- `ListTabs() ([]models.SqlTab, error)`
- `DeleteTab(id uint) error`
**依赖**
- `TabRepository`:标签页数据访问
---
## 四、数据访问层设计
### 4.1 Repository 模式
使用 Repository 模式封装数据访问逻辑,提供统一的接口:
```go
type ConnectionRepository interface {
Save(conn *models.DbConnection) error
FindAll() ([]models.DbConnection, error)
FindByID(id uint) (*models.DbConnection, error)
Delete(id uint) error
}
```
### 4.2 实现方式
- `ConnectionRepository`:使用 GORM 实现
- `TabRepository`:使用 GORM 实现
- `BookmarkRepository`:使用 GORM 实现
- `TemplateRepository`:使用 GORM 实现
---
## 五、接口层设计
### 5.1 API 接口
`app.go` 中的方法按功能分组到不同的 API 文件中:
- `connection_api.go`:连接管理相关接口
- `sql_api.go`SQL 执行相关接口
- `resource_api.go`:资源管理相关接口
- `tab_api.go`:标签页相关接口
### 5.2 App 结构体
`app.go` 只负责:
- 初始化服务
- 委托调用到对应的 API 接口
---
## 六、重构优势
### 6.1 代码组织
- **清晰的职责划分**:每个服务只负责一个业务领域
- **易于维护**:修改某个功能只需修改对应的服务
- **代码复用**:服务可以在多个 API 中复用
### 6.2 可测试性
- **独立测试**:每个服务可以独立测试
- **Mock 简单**:可以轻松 mock Repository
- **测试覆盖**:逻辑集中在服务层,测试更容易
### 6.3 可扩展性
- **新增功能**:只需添加新的服务和 API
- **功能组合**:可以组合多个服务实现复杂功能
- **向后兼容**:不影响现有接口
---
## 七、实施步骤
### 步骤1创建目录结构 ✅
- [x] 创建 `internal/api/` 目录
- [x] 创建 `internal/service/` 目录
- [x] 创建 `internal/storage/repository/` 目录
### 步骤2实现 Repository 层 ✅
- [x] 定义 Repository 接口
- [x] 实现 ConnectionRepository
- [x] 实现 TabRepository
- [x] 实现 BookmarkRepository
- [x] 实现 TemplateRepository
### 步骤3实现 Service 层 ✅
- [x] 实现 ConnectionService
- [x] 实现 SqlExecService
- [x] 实现 ResourceService
- [x] 实现 TabService
### 步骤4实现 API 层 ✅
- [x] 实现 connection_api.go
- [x] 实现 sql_api.go
- [x] 实现 resource_api.go
- [x] 实现 tab_api.go
### 步骤5重构 app.go ✅
- [x] 连接管理方法迁移到 ConnectionAPI ✅
- [x] SQL执行方法迁移到 SqlAPI ✅
- [x] 书签管理方法迁移到 ResourceAPI ✅
- [x] 模板管理方法迁移到 ResourceAPI ✅
- [x] 标签页管理方法迁移到 TabAPI ✅
- [x] 表结构和索引查询方法迁移到 SqlAPI ✅
- [x] 删除重复代码parseRedisCommand
- [x] 简化 app.go只保留初始化逻辑 ✅
### 步骤6测试和验证 ⚠️
- [x] 功能测试(基本完成)
- [ ] 单元测试(待完成)
- [x] 代码审查(已完成)
---
## 八、后续优化方向
1. **依赖注入**:使用依赖注入框架管理服务依赖
2. **错误处理**:统一错误处理机制
3. **日志系统**:引入结构化日志
4. **配置管理**:统一配置管理
5. **中间件**:添加认证、限流等中间件
---
## 九、参考文档
- [Go 项目布局标准](https://github.com/golang-standards/project-layout)
- [Clean Architecture in Go](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)