package main import ( "context" "fmt" "u-desk/internal/api" "u-desk/internal/database" "u-desk/internal/filesystem" "u-desk/internal/storage" "u-desk/internal/system" "net/http" "os" "path/filepath" "strings" "github.com/wailsapp/wails/v2/pkg/runtime" ) // App 应用结构体 type App struct { ctx context.Context db *database.DB connectionAPI *api.ConnectionAPI sqlAPI *api.SqlAPI tabAPI *api.TabAPI updateAPI *api.UpdateAPI fileServer *http.Server } // NewApp 创建新的应用实例 func NewApp() *App { return &App{} } // Startup 应用启动时调用 func (a *App) Startup(ctx context.Context) { a.ctx = ctx // 1. 核心初始化:SQLite(必须同步,很快) sqliteDB, err := storage.InitFast() if err != nil { panic(fmt.Sprintf("SQLite 初始化失败,应用无法启动: %v", err)) } // 2. 快速初始化核心 API(都是毫秒级操作,不影响启动速度) if err := a.initCoreAPIs(); err != nil { panic(fmt.Sprintf("核心 API 初始化失败: %v", err)) } // 3. 异步初始化:文件服务器(不等待) go a.startFileServer() // 4. 异步初始化:UpdateAPI(涉及网络请求,完全异步) go func() { if updateAPI, err := api.NewUpdateAPI("https://img.1216.top/u-desk/last-version.json"); err == nil { a.updateAPI = updateAPI a.updateAPI.SetContext(ctx) } }() _ = sqliteDB // 标记已使用 } // startFileServer 启动文件服务器 func (a *App) startFileServer() { // 启动独立的本地文件服务器(使用 filesystem 包中的实现) if _, err := filesystem.StartLocalFileServer(); err != nil { fmt.Printf("[文件服务器] 启动失败: %v\n", err) return } // 创建一个占位服务器用于保持引用(实际服务器由 StartLocalFileServer 管理) a.fileServer = &http.Server{ Addr: "localhost:18765", } fmt.Println("[文件服务器] 启动在 http://localhost:18765") } // Shutdown 应用关闭时调用 func (a *App) Shutdown(ctx context.Context) { // 停止文件服务器 if a.fileServer != nil { fmt.Println("[文件服务器] 正在关闭...") a.fileServer.Shutdown(ctx) } } // QueryUsers 查询用户列表 func (a *App) QueryUsers(keyword string, status int, role int, organid int, page int, pageSize int, sortField string, sortOrder string) (map[string]interface{}, error) { db, err := a.getDB() if err != nil { return nil, err } return db.QueryUsers(keyword, status, role, organid, page, pageSize, sortField, sortOrder) } // getDB 获取数据库连接(延迟加载,按需初始化) func (a *App) getDB() (*database.DB, error) { if a.db != nil { return a.db, nil } // 首次调用时才连接数据库 db, err := database.Init() if err != nil { return nil, fmt.Errorf("数据库连接失败: %v", err) } a.db = db return db, nil } // Greet 测试方法 func (a *App) Greet(name string) string { return "Hello " + name + ", It's show time!" } // GetSystemInfo 获取系统信息 func (a *App) GetSystemInfo() (map[string]interface{}, error) { return system.GetSystemInfo() } // GetCPUInfo 获取 CPU 信息 func (a *App) GetCPUInfo() (map[string]interface{}, error) { return system.GetCPUInfo() } // GetMemoryInfo 获取内存信息 func (a *App) GetMemoryInfo() (map[string]interface{}, error) { return system.GetMemoryInfo() } // GetDiskInfo 获取磁盘信息 func (a *App) GetDiskInfo() ([]map[string]interface{}, error) { return system.GetDiskInfo() } // ReadFile 读取文件 func (a *App) ReadFile(path string) (string, error) { return filesystem.ReadFile(path) } // WriteFileRequest 写入文件请求结构体 type WriteFileRequest struct { Path string `json:"path"` Content string `json:"content"` } // WriteFile 写入文件 func (a *App) WriteFile(req WriteFileRequest) error { return filesystem.WriteFile(req.Path, req.Content) } // ListDir 列出目录 func (a *App) ListDir(path string) ([]map[string]interface{}, error) { return filesystem.ListDir(path) } // CreateDir 创建目录 func (a *App) CreateDir(path string) error { return filesystem.CreateDir(path) } // CreateFile 创建文件 func (a *App) CreateFile(path string) error { return filesystem.CreateFile(path) } // DeletePath 删除文件或目录 func (a *App) DeletePath(path string) error { return filesystem.DeletePath(path) } // GetFileInfo 获取文件信息 func (a *App) GetFileInfo(path string) (map[string]interface{}, error) { return filesystem.GetFileInfo(path) } // GetEnvVars 获取环境变量 func (a *App) GetEnvVars() (map[string]string, error) { envVars := make(map[string]string) for _, env := range os.Environ() { if key, value, found := strings.Cut(env, "="); found { envVars[key] = value } } return envVars, nil } // OpenPath 使用系统默认程序打开文件或目录 func (a *App) OpenPath(path string) error { return filesystem.OpenPath(path) } // ========== Zip 文件操作接口 ========== // ListZipContents 列出 zip 文件内容 func (a *App) ListZipContents(zipPath string) ([]map[string]interface{}, error) { return filesystem.ListZipContents(zipPath) } // ExtractFileFromZip 从 zip 文件中提取单个文件内容 func (a *App) ExtractFileFromZip(zipPath, filePath string) (string, error) { return filesystem.ExtractFileFromZip(zipPath, filePath) } // ExtractFileFromZipToTemp 从 zip 文件中提取单个文件到临时目录 // 返回临时文件的完整路径,适用于图片等二进制文件 func (a *App) ExtractFileFromZipToTemp(zipPath, filePath string) (string, error) { return filesystem.ExtractFileFromZipToTemp(zipPath, filePath) } // GetZipFileInfo 获取 zip 文件中特定文件的信息 func (a *App) GetZipFileInfo(zipPath, filePath string) (map[string]interface{}, error) { return filesystem.GetZipFileInfo(zipPath, filePath) } // GetCommonPaths 获取常用系统路径 func (a *App) GetCommonPaths() (map[string]string, error) { homeDir, err := os.UserHomeDir() if err != nil { return nil, err } // 获取所有可用驱动器(Windows) drives := getSystemDrives() paths := map[string]string{ "home": homeDir, "desktop": filepath.Join(homeDir, "Desktop"), "documents": filepath.Join(homeDir, "Documents"), "downloads": filepath.Join(homeDir, "Downloads"), } // 动态添加所有盘符 for _, drive := range drives { key := fmt.Sprintf("root_%s", drive[:1]) paths[key] = drive } return paths, nil } // getSystemDrives 获取系统所有可用驱动器 func getSystemDrives() []string { var drives []string // Windows: 检查 A-Z 所有盘符 for _, drive := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" { path := string(drive) + ":\\" if _, err := os.Stat(path); err == nil { drives = append(drives, path) } } return drives } // ========== 数据库连接管理接口 ========== // initCoreAPIs 初始化核心 API(快速操作,毫秒级完成) func (a *App) initCoreAPIs() error { var err error // 初始化 ConnectionAPI if a.connectionAPI, err = api.NewConnectionAPI(); err != nil { return err } // 初始化 SqlAPI if a.sqlAPI, err = api.NewSqlAPI(); err != nil { return err } // 初始化 TabAPI if a.tabAPI, err = api.NewTabAPI(); err != nil { return err } return nil } // SaveDbConnection 保存数据库连接配置 func (a *App) SaveDbConnection(req api.SaveConnectionRequest) error { return a.connectionAPI.SaveDbConnection(req) } // ListDbConnections 获取连接列表 func (a *App) ListDbConnections() ([]map[string]interface{}, error) { return a.connectionAPI.ListDbConnections() } // DeleteDbConnection 删除连接配置 func (a *App) DeleteDbConnection(id uint) error { return a.connectionAPI.DeleteDbConnection(id) } // TestDbConnection 测试连接(通过已保存的连接ID) func (a *App) TestDbConnection(id uint) error { return a.connectionAPI.TestDbConnection(id) } // TestDbConnectionWithParams 测试数据库连接(直接传入参数,不保存数据) func (a *App) TestDbConnectionWithParams(req api.TestConnectionRequest) error { return a.connectionAPI.TestDbConnectionWithParams(req) } // ExecuteSQL 执行 SQL 语句 // 注意:SQL 语句应该已经包含分页信息(LIMIT 和 OFFSET),由客户端添加 func (a *App) ExecuteSQL(connectionId uint, sqlStr string, database string) (map[string]interface{}, error) { return a.sqlAPI.ExecuteSQL(connectionId, sqlStr, database) } // GetDatabases 获取数据库列表 func (a *App) GetDatabases(connectionId uint) ([]string, error) { return a.sqlAPI.GetDatabases(connectionId) } // GetTables 获取表列表 func (a *App) GetTables(connectionId uint, database string) ([]string, error) { return a.sqlAPI.GetTables(connectionId, database) } // GetTableStructure 获取表结构 func (a *App) GetTableStructure(connectionId uint, database, tableName string) (map[string]interface{}, error) { return a.sqlAPI.GetTableStructure(connectionId, database, tableName) } // GetIndexes 获取索引列表 func (a *App) GetIndexes(connectionId uint, database, tableName string) ([]map[string]interface{}, error) { return a.sqlAPI.GetIndexes(connectionId, database, tableName) } // PreviewTableStructure 预览表结构变更 func (a *App) PreviewTableStructure(connectionId uint, database, tableName string, structure map[string]interface{}) ([]string, error) { return a.sqlAPI.PreviewTableStructure(connectionId, database, tableName, structure) } // UpdateTableStructure 更新表结构 func (a *App) UpdateTableStructure(connectionId uint, database, tableName string, structure map[string]interface{}) ([]string, error) { return a.sqlAPI.UpdateTableStructure(connectionId, database, tableName, structure) } // SaveResult 手动保存执行结果 func (a *App) SaveResult(connectionId uint, database, sql string, resultType string, data interface{}, columns []string, rowsAffected int, executionTime int64) (map[string]interface{}, error) { return a.sqlAPI.SaveResult(connectionId, database, sql, resultType, data, columns, rowsAffected, executionTime) } // GetResultHistory 获取结果历史 func (a *App) GetResultHistory(connectionId *uint, keyword string, limit, offset int) (map[string]interface{}, error) { return a.sqlAPI.GetResultHistory(connectionId, keyword, limit, offset) } // GetResultHistoryByID 根据ID获取结果历史 func (a *App) GetResultHistoryByID(id uint) (map[string]interface{}, error) { return a.sqlAPI.GetResultHistoryByID(id) } // DeleteResultHistory 删除结果历史 func (a *App) DeleteResultHistory(id uint) error { return a.sqlAPI.DeleteResultHistory(id) } // Reload 重新加载窗口(用于菜单项) func (a *App) Reload() { if a.ctx != nil { runtime.WindowReload(a.ctx) } } // ClearCache 清理本地缓存(用于菜单项) func (a *App) ClearCache() { if a.ctx != nil { // 发送事件到前端,让前端清理 localStorage runtime.EventsEmit(a.ctx, "clear-cache") } } // ========== 窗口控制方法 ========== // WindowMinimize 最小化窗口 func (a *App) WindowMinimize() { if a.ctx != nil { runtime.WindowMinimise(a.ctx) } } // WindowMaximize 最大化/还原窗口 func (a *App) WindowMaximize() { if a.ctx != nil { if runtime.WindowIsMaximised(a.ctx) { runtime.WindowUnmaximise(a.ctx) } else { runtime.WindowMaximise(a.ctx) } } } // WindowClose 关闭窗口 func (a *App) WindowClose() { if a.ctx != nil { runtime.Quit(a.ctx) } } // WindowIsMaximized 检查窗口是否最大化 func (a *App) WindowIsMaximized() bool { if a.ctx != nil { return runtime.WindowIsMaximised(a.ctx) } return false } // ========== SQL 标签页管理接口 ========== // SaveSqlTabs 保存 SQL 标签页列表 func (a *App) SaveSqlTabs(tabs []map[string]interface{}) error { return a.tabAPI.SaveSqlTabs(tabs) } // ListSqlTabs 获取 SQL 标签页列表 func (a *App) ListSqlTabs() ([]map[string]interface{}, error) { return a.tabAPI.ListSqlTabs() } // ========== 版本更新管理接口 ========== // CheckUpdate 检查更新(UpdateAPI 可能尚未初始化完成) func (a *App) CheckUpdate() (map[string]interface{}, error) { if a.updateAPI == nil { return nil, fmt.Errorf("更新功能正在初始化中") } return a.updateAPI.CheckUpdate() } // GetCurrentVersion 获取当前版本号 func (a *App) GetCurrentVersion() (map[string]interface{}, error) { if a.updateAPI == nil { return nil, fmt.Errorf("更新功能正在初始化中") } return a.updateAPI.GetCurrentVersion() } // GetUpdateConfig 获取更新配置 func (a *App) GetUpdateConfig() (map[string]interface{}, error) { if a.updateAPI == nil { return nil, fmt.Errorf("更新功能正在初始化中") } return a.updateAPI.GetUpdateConfig() } // SetUpdateConfig 设置更新配置 func (a *App) SetUpdateConfig(autoCheckEnabled bool, checkIntervalMinutes int, checkURL string) (map[string]interface{}, error) { if a.updateAPI == nil { return nil, fmt.Errorf("更新功能正在初始化中") } return a.updateAPI.SetUpdateConfig(autoCheckEnabled, checkIntervalMinutes, checkURL) } // DownloadUpdate 下载更新包 func (a *App) DownloadUpdate(downloadURL string) (map[string]interface{}, error) { if a.updateAPI == nil { return nil, fmt.Errorf("更新功能正在初始化中") } return a.updateAPI.DownloadUpdate(downloadURL) } // InstallUpdate 安装更新包 func (a *App) InstallUpdate(installerPath string, autoRestart bool) (map[string]interface{}, error) { if a.updateAPI == nil { return nil, fmt.Errorf("更新功能正在初始化中") } return a.updateAPI.InstallUpdate(installerPath, autoRestart) } // InstallUpdateWithHash 安装更新包(带哈希验证) func (a *App) InstallUpdateWithHash(installerPath string, autoRestart bool, expectedHash string, hashType string) (map[string]interface{}, error) { if a.updateAPI == nil { return nil, fmt.Errorf("更新功能正在初始化中") } return a.updateAPI.InstallUpdateWithHash(installerPath, autoRestart, expectedHash, hashType) } // VerifyUpdateFile 验证更新文件哈希值 func (a *App) VerifyUpdateFile(filePath string, expectedHash string, hashType string) (map[string]interface{}, error) { if a.updateAPI == nil { return nil, fmt.Errorf("更新功能正在初始化中") } return a.updateAPI.VerifyUpdateFile(filePath, expectedHash, hashType) } // ========== 应用生命周期管理 ========== // shutdown 应用关闭时调用,清理资源 func (a *App) shutdown(ctx context.Context) { // 关闭审计日志 filesystem.CloseAudit() // 停止文件服务器 if a.fileServer != nil { _ = a.fileServer.Shutdown(ctx) } } // ========== 审计日志接口 ========== // GetAuditLogs 获取审计日志 func (a *App) GetAuditLogs(limit int) ([]map[string]interface{}, error) { userDataDir := getUserDataDir() logDir := filepath.Join(userDataDir, "logs") entries, err := filesystem.GetRecentLogs(logDir, limit) if err != nil { return nil, err } // 转换为map格式 result := make([]map[string]interface{}, len(entries)) for i, entry := range entries { result[i] = map[string]interface{}{ "timestamp": entry.Timestamp.Format("2006-01-02 15:04:05"), "operation": entry.Operation, "path": entry.Path, "size": entry.Size, "is_directory": entry.IsDirectory, "success": entry.Success, "error": entry.Error, } } return result, nil } // ========== 文件服务器接口 ========== // GetFileServerURL 获取本地文件服务器的URL func (a *App) GetFileServerURL() string { return "http://localhost:18765" } // ========== 回收站接口 ========== // GetRecycleBinEntries 获取回收站条目 func (a *App) GetRecycleBinEntries() ([]map[string]interface{}, error) { bin := filesystem.GetRecycleBin() if bin == nil { return []map[string]interface{}{}, nil } entries := bin.ListEntries() result := make([]map[string]interface{}, len(entries)) for i, entry := range entries { result[i] = map[string]interface{}{ "original_path": entry.OriginalPath, "deleted_path": entry.DeletedPath, "deleted_time": entry.DeletedTime.Format("2006-01-02 15:04:05"), "size": entry.Size, "is_directory": entry.IsDirectory, } } return result, nil } // RestoreFromRecycleBin 从回收站恢复文件 func (a *App) RestoreFromRecycleBin(recyclePath string) error { bin := filesystem.GetRecycleBin() if bin == nil { return fmt.Errorf("回收站未初始化") } return bin.RestoreFromRecycleBin(recyclePath) } // DeletePermanently 永久删除回收站中的文件 func (a *App) DeletePermanently(recyclePath string) error { bin := filesystem.GetRecycleBin() if bin == nil { return fmt.Errorf("回收站未初始化") } return bin.DeletePermanently(recyclePath) } // EmptyRecycleBin 清空回收站 func (a *App) EmptyRecycleBin() error { bin := filesystem.GetRecycleBin() if bin == nil { return fmt.Errorf("回收站未初始化") } return bin.Empty() }