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

313
app.go
View File

@@ -8,6 +8,7 @@ import (
"go-desk/internal/filesystem"
"go-desk/internal/storage"
"go-desk/internal/system"
"net/http"
"os"
"path/filepath"
"strings"
@@ -23,6 +24,7 @@ type App struct {
sqlAPI *api.SqlAPI
tabAPI *api.TabAPI
updateAPI *api.UpdateAPI
fileServer *http.Server
}
// NewApp 创建新的应用实例
@@ -34,37 +36,79 @@ func NewApp() *App {
func (a *App) Startup(ctx context.Context) {
a.ctx = ctx
// 初始化 SQLite 本地存储(核心依赖,必须成功
// 如果失败,应用无法正常工作,应该 panic
_, err := storage.Init()
// 1. 核心初始化SQLite(必须同步,很快
sqliteDB, err := storage.InitFast()
if err != nil {
panic(fmt.Sprintf("SQLite 初始化失败,应用无法启动: %v", err))
}
// 初始化数据库连接(可选,用于测试功能
// 失败不影响核心功能,只记录日志
appDB, err := database.Init()
if err != nil {
println("数据库连接失败(可选功能):", err.Error())
} else {
a.db = appDB
// 2. 快速初始化核心 API都是毫秒级操作不影响启动速度
if err := a.initCoreAPIs(); err != nil {
panic(fmt.Sprintf("核心 API 初始化失败: %v", err))
}
// 初始化 API 层(依赖 storage
// 如果失败,应用无法正常工作,应该 panic
if err := a.initAPIs(); 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/go-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
}
// 设置 updateAPI 的上下文
if a.updateAPI != nil {
a.updateAPI.SetContext(ctx)
// 创建一个占位服务器用于保持引用(实际服务器由 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) {
return a.db.QueryUsers(keyword, status, role, organid, page, pageSize, sortField, sortOrder)
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 测试方法
@@ -97,9 +141,15 @@ 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(path, content string) error {
return filesystem.WriteFile(path, content)
func (a *App) WriteFile(req WriteFileRequest) error {
return filesystem.WriteFile(req.Path, req.Content)
}
// ListDir 列出目录
@@ -112,6 +162,11 @@ 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)
@@ -133,6 +188,34 @@ func (a *App) GetEnvVars() (map[string]string, error) {
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()
@@ -151,10 +234,9 @@ func (a *App) GetCommonPaths() (map[string]string, error) {
}
// 动态添加所有盘符
for i, drive := range drives {
for _, drive := range drives {
key := fmt.Sprintf("root_%s", drive[:1])
paths[key] = drive
_ = i // 避免未使用变量警告
}
return paths, nil
@@ -177,23 +259,26 @@ func getSystemDrives() []string {
// ========== 数据库连接管理接口 ==========
// initAPIs 初始化所有API在startup中调用
func (a *App) initAPIs() error {
// initCoreAPIs 初始化核心 API快速操作,毫秒级完成
func (a *App) initCoreAPIs() error {
var err error
a.connectionAPI, err = api.NewConnectionAPI()
if err != nil {
// 初始化 ConnectionAPI
if a.connectionAPI, err = api.NewConnectionAPI(); err != nil {
return err
}
a.sqlAPI, err = api.NewSqlAPI()
if err != nil {
// 初始化 SqlAPI
if a.sqlAPI, err = api.NewSqlAPI(); err != nil {
return err
}
a.tabAPI, err = api.NewTabAPI()
if err != nil {
// 初始化 TabAPI
if a.tabAPI, err = api.NewTabAPI(); err != nil {
return err
}
a.updateAPI, err = api.NewUpdateAPI("https://img.1216.top/go-desk/last-version.json")
return err
return nil
}
// SaveDbConnection 保存数据库连接配置
@@ -292,6 +377,41 @@ func (a *App) ClearCache() {
}
}
// ========== 窗口控制方法 ==========
// 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 标签页列表
@@ -306,43 +426,170 @@ func (a *App) ListSqlTabs() ([]map[string]interface{}, error) {
// ========== 版本更新管理接口 ==========
// CheckUpdate 检查更新
// 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()
}