From 7b70a39b233789a141ba0796cb421ea4b3434755 Mon Sep 17 00:00:00 2001 From: landaiqing Date: Tue, 19 Aug 2025 00:08:50 +0800 Subject: [PATCH] :bug: Fixed hotkey service issues --- .../internal/services/hotkeyservice.ts | 4 +- .../components/toolbar/DocumentSelector.vue | 10 +- frontend/src/stores/documentStore.ts | 2 + go.mod | 2 +- go.sum | 4 +- internal/services/document_service.go | 1 + internal/services/hotkey_service.go | 109 ++++++++++++++--- internal/services/hotkey_service_darwin.go | 81 ++++++++++++- internal/services/hotkey_service_linux.go | 110 +++++++++++++++--- internal/services/path_migration_service.go | 61 +++++++++- internal/services/service_manager.go | 4 +- main.go | 11 +- 12 files changed, 348 insertions(+), 51 deletions(-) diff --git a/frontend/bindings/voidraft/internal/services/hotkeyservice.ts b/frontend/bindings/voidraft/internal/services/hotkeyservice.ts index 4a2fcbd..789daba 100644 --- a/frontend/bindings/voidraft/internal/services/hotkeyservice.ts +++ b/frontend/bindings/voidraft/internal/services/hotkeyservice.ts @@ -32,8 +32,8 @@ export function GetCurrentHotkey(): Promise & { can /** * Initialize 初始化热键服务 */ -export function Initialize(app: application$0.App | null): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(3671360458, app) as any; +export function Initialize(app: application$0.App | null, mainWindow: application$0.WebviewWindow | null): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(3671360458, app, mainWindow) as any; return $resultPromise; } diff --git a/frontend/src/components/toolbar/DocumentSelector.vue b/frontend/src/components/toolbar/DocumentSelector.vue index b6add47..e9d00b6 100644 --- a/frontend/src/components/toolbar/DocumentSelector.vue +++ b/frontend/src/components/toolbar/DocumentSelector.vue @@ -214,7 +214,7 @@ const openInNewWindow = async (doc: Document, event: Event) => { } }; -// 处理删除 - 简化确认机制 +// 处理删除 const handleDelete = async (doc: Document, event: Event) => { event.stopPropagation(); @@ -242,9 +242,13 @@ const handleDelete = async (doc: Document, event: Event) => { return; } - await documentStore.deleteDocument(doc.id); - await documentStore.updateDocuments(); + const deleteSuccess = await documentStore.deleteDocument(doc.id); + if (!deleteSuccess) { + return; + } + + await documentStore.updateDocuments(); // 如果删除的是当前文档,切换到第一个文档 if (documentStore.currentDocument?.id === doc.id && documentStore.documentList.length > 0) { const firstDoc = documentStore.documentList[0]; diff --git a/frontend/src/stores/documentStore.ts b/frontend/src/stores/documentStore.ts index 44c8e48..3c23e6a 100644 --- a/frontend/src/stores/documentStore.ts +++ b/frontend/src/stores/documentStore.ts @@ -160,10 +160,12 @@ export const useDocumentStore = defineStore('document', () => { try { // 检查是否是默认文档(使用ID判断) if (docId === SCRATCH_DOCUMENT_ID) { + console.log('Cannot delete default document (ID=1)'); return false; } await DocumentService.DeleteDocument(docId); + console.log('Backend delete successful for doc:', docId); // 更新本地状态 delete documents.value[docId]; diff --git a/go.mod b/go.mod index 2920f4f..b37766f 100644 --- a/go.mod +++ b/go.mod @@ -68,7 +68,7 @@ require ( github.com/sergi/go-diff v1.4.0 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/ulikunitz/xz v0.5.12 // indirect - github.com/wailsapp/go-webview2 v1.0.22 // indirect + github.com/wailsapp/go-webview2 v1.0.21 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect github.com/xanzy/go-gitlab v0.115.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect diff --git a/go.sum b/go.sum index 2b64c02..0762794 100644 --- a/go.sum +++ b/go.sum @@ -160,8 +160,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6NZijQ58= -github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= +github.com/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= +github.com/wailsapp/go-webview2 v1.0.21/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= github.com/wailsapp/wails/v3 v3.0.0-alpha.25 h1:o05zUiPEvmrq2lqqCs4wqnrnAjGmhryYHRhjQmtkvk8= diff --git a/internal/services/document_service.go b/internal/services/document_service.go index dc752fd..eda0350 100644 --- a/internal/services/document_service.go +++ b/internal/services/document_service.go @@ -315,6 +315,7 @@ func (ds *DocumentService) DeleteDocument(id int64) error { defer ds.mu.Unlock() if ds.databaseService == nil || ds.databaseService.db == nil { + ds.logger.Error("database service not available") return errors.New("database service not available") } diff --git a/internal/services/hotkey_service.go b/internal/services/hotkey_service.go index 38fea1c..02064b4 100644 --- a/internal/services/hotkey_service.go +++ b/internal/services/hotkey_service.go @@ -26,15 +26,17 @@ import ( type HotkeyService struct { logger *log.LogService configService *ConfigService + windowService *WindowService app *application.App + mainWindow *application.WebviewWindow mu sync.RWMutex currentHotkey *models.HotkeyCombo isRegistered atomic.Bool - ctx context.Context - cancel context.CancelFunc - wg sync.WaitGroup + ctx context.Context + cancelFunc atomic.Value // 使用atomic.Value存储cancel函数,避免竞态条件 + wg sync.WaitGroup } // HotkeyError 热键错误 @@ -51,24 +53,46 @@ func (e *HotkeyError) Unwrap() error { return e.Err } +// setCancelFunc 原子地设置cancel函数 +func (hs *HotkeyService) setCancelFunc(cancel context.CancelFunc) { + hs.cancelFunc.Store(cancel) +} + +// getCancelFunc 原子地获取cancel函数 +func (hs *HotkeyService) getCancelFunc() context.CancelFunc { + if cancel := hs.cancelFunc.Load(); cancel != nil { + return cancel.(context.CancelFunc) + } + return nil +} + +// clearCancelFunc 原子地清除cancel函数 +func (hs *HotkeyService) clearCancelFunc() { + hs.cancelFunc.Store((*context.CancelFunc)(nil)) +} + // NewHotkeyService 创建热键服务实例 -func NewHotkeyService(configService *ConfigService, logger *log.LogService) *HotkeyService { +func NewHotkeyService(configService *ConfigService, windowService *WindowService, logger *log.LogService) *HotkeyService { if logger == nil { logger = log.New() } ctx, cancel := context.WithCancel(context.Background()) - return &HotkeyService{ + service := &HotkeyService{ logger: logger, configService: configService, + windowService: windowService, ctx: ctx, - cancel: cancel, } + // 初始化时设置cancel函数 + service.setCancelFunc(cancel) + return service } // Initialize 初始化热键服务 -func (hs *HotkeyService) Initialize(app *application.App) error { +func (hs *HotkeyService) Initialize(app *application.App, mainWindow *application.WebviewWindow) error { hs.app = app + hs.mainWindow = mainWindow config, err := hs.configService.GetConfig() if err != nil { @@ -119,7 +143,7 @@ func (hs *HotkeyService) RegisterHotkey(hotkey *models.HotkeyCombo) error { hs.currentHotkey = hotkey hs.isRegistered.Store(true) - hs.cancel = cancel + hs.setCancelFunc(cancel) return nil } @@ -137,13 +161,15 @@ func (hs *HotkeyService) unregisterInternal() error { return nil } - if hs.cancel != nil { - hs.cancel() + // 原子地获取并调用cancel函数 + if cancel := hs.getCancelFunc(); cancel != nil { + cancel() hs.wg.Wait() } hs.currentHotkey = nil hs.isRegistered.Store(false) + hs.clearCancelFunc() return nil } @@ -165,7 +191,7 @@ func (hs *HotkeyService) hotkeyListener(ctx context.Context, hotkey *models.Hotk return } - ticker := time.NewTicker(100 * time.Millisecond) + ticker := time.NewTicker(50 * time.Millisecond) defer ticker.Stop() var wasPressed bool @@ -199,11 +225,61 @@ func cBool(b bool) C.int { return 0 } -// toggleWindow 切换窗口 +// toggleWindow 切换窗口显示状态 func (hs *HotkeyService) toggleWindow() { - if hs.app != nil { - hs.app.Event.Emit("hotkey:toggle-window", nil) + if hs.mainWindow == nil { + hs.logger.Error("main window not set") + return } + + // 检查主窗口是否可见 + if hs.isWindowVisible(hs.mainWindow) { + // 如果主窗口可见,隐藏所有窗口 + hs.hideAllWindows() + } else { + // 如果主窗口不可见,显示所有窗口 + hs.showAllWindows() + } +} + +// isWindowVisible 检查窗口是否可见 +func (hs *HotkeyService) isWindowVisible(window *application.WebviewWindow) bool { + return window.IsVisible() +} + +// hideAllWindows 隐藏所有窗口 +func (hs *HotkeyService) hideAllWindows() { + // 隐藏主窗口 + hs.mainWindow.Hide() + + // 隐藏所有子窗口 + if hs.windowService != nil { + openWindows := hs.windowService.GetOpenWindows() + for _, windowInfo := range openWindows { + windowInfo.Window.Hide() + } + } + + hs.logger.Debug("all windows hidden") +} + +// showAllWindows 显示所有窗口 +func (hs *HotkeyService) showAllWindows() { + // 显示主窗口 + hs.mainWindow.Show() + hs.mainWindow.Restore() + hs.mainWindow.Focus() + + // 显示所有子窗口 + if hs.windowService != nil { + openWindows := hs.windowService.GetOpenWindows() + for _, windowInfo := range openWindows { + windowInfo.Window.Show() + windowInfo.Window.Restore() + } + } + + hs.logger.Debug("all windows shown") } // keyToVirtualKeyCode 键名转虚拟键码 @@ -261,7 +337,10 @@ func (hs *HotkeyService) IsRegistered() bool { // ServiceShutdown 关闭服务 func (hs *HotkeyService) ServiceShutdown() error { - hs.cancel() + // 原子地获取并调用cancel函数 + if cancel := hs.getCancelFunc(); cancel != nil { + cancel() + } hs.wg.Wait() return nil } diff --git a/internal/services/hotkey_service_darwin.go b/internal/services/hotkey_service_darwin.go index 1e7817e..8c00cbd 100644 --- a/internal/services/hotkey_service_darwin.go +++ b/internal/services/hotkey_service_darwin.go @@ -82,10 +82,13 @@ var globalHotkeyService *HotkeyService type HotkeyService struct { logger *log.LogService configService *ConfigService + windowService *WindowService app *application.App + mainWindow *application.WebviewWindow mu sync.RWMutex isRegistered atomic.Bool currentHotkey *models.HotkeyCombo + cancelFunc atomic.Value // 使用atomic.Value存储cancel函数,避免竞态条件 } // HotkeyError 热键错误 @@ -104,8 +107,26 @@ func (e *HotkeyError) Unwrap() error { return e.Err } +// setCancelFunc 原子地设置cancel函数 +func (hs *HotkeyService) setCancelFunc(cancel context.CancelFunc) { + hs.cancelFunc.Store(cancel) +} + +// getCancelFunc 原子地获取cancel函数 +func (hs *HotkeyService) getCancelFunc() context.CancelFunc { + if cancel := hs.cancelFunc.Load(); cancel != nil { + return cancel.(context.CancelFunc) + } + return nil +} + +// clearCancelFunc 原子地清除cancel函数 +func (hs *HotkeyService) clearCancelFunc() { + hs.cancelFunc.Store((*context.CancelFunc)(nil)) +} + // NewHotkeyService 创建新的热键服务实例 -func NewHotkeyService(configService *ConfigService, logger *log.LogService) *HotkeyService { +func NewHotkeyService(configService *ConfigService, windowService *WindowService, logger *log.LogService) *HotkeyService { if logger == nil { logger = log.New() } @@ -113,6 +134,7 @@ func NewHotkeyService(configService *ConfigService, logger *log.LogService) *Hot service := &HotkeyService{ logger: logger, configService: configService, + windowService: windowService, } // 设置全局实例 @@ -122,8 +144,9 @@ func NewHotkeyService(configService *ConfigService, logger *log.LogService) *Hot } // Initialize 初始化热键服务 -func (hs *HotkeyService) Initialize(app *application.App) error { +func (hs *HotkeyService) Initialize(app *application.App, mainWindow *application.WebviewWindow) error { hs.app = app + hs.mainWindow = mainWindow // 加载并应用当前配置 config, err := hs.configService.GetConfig() @@ -285,9 +308,59 @@ func (hs *HotkeyService) IsRegistered() bool { // ToggleWindow 切换窗口显示状态 func (hs *HotkeyService) ToggleWindow() { - if hs.app != nil { - hs.app.EmitEvent("hotkey:toggle-window", nil) + if hs.mainWindow == nil { + hs.logger.Error("main window not set") + return } + + // 检查主窗口是否可见 + if hs.isWindowVisible(hs.mainWindow) { + // 如果主窗口可见,隐藏所有窗口 + hs.hideAllWindows() + } else { + // 如果主窗口不可见,显示所有窗口 + hs.showAllWindows() + } +} + +// isWindowVisible 检查窗口是否可见 +func (hs *HotkeyService) isWindowVisible(window *application.WebviewWindow) bool { + return window.IsVisible() +} + +// hideAllWindows 隐藏所有窗口 +func (hs *HotkeyService) hideAllWindows() { + // 隐藏主窗口 + hs.mainWindow.Hide() + + // 隐藏所有子窗口 + if hs.windowService != nil { + openWindows := hs.windowService.GetOpenWindows() + for _, windowInfo := range openWindows { + windowInfo.Window.Hide() + } + } + + hs.logger.Debug("all windows hidden") +} + +// showAllWindows 显示所有窗口 +func (hs *HotkeyService) showAllWindows() { + // 显示主窗口 + hs.mainWindow.Show() + hs.mainWindow.Restore() + hs.mainWindow.Focus() + + // 显示所有子窗口 + if hs.windowService != nil { + openWindows := hs.windowService.GetOpenWindows() + for _, windowInfo := range openWindows { + windowInfo.Window.Show() + windowInfo.Window.Restore() + } + } + + hs.logger.Debug("all windows shown") } // ServiceShutdown 关闭热键服务 diff --git a/internal/services/hotkey_service_linux.go b/internal/services/hotkey_service_linux.go index fb223ad..3cd3c99 100644 --- a/internal/services/hotkey_service_linux.go +++ b/internal/services/hotkey_service_linux.go @@ -143,15 +143,17 @@ import ( type HotkeyService struct { logger *log.LogService configService *ConfigService + windowService *WindowService app *application.App + mainWindow *application.WebviewWindow mu sync.RWMutex currentHotkey *models.HotkeyCombo isRegistered atomic.Bool - ctx context.Context - cancel context.CancelFunc - wg sync.WaitGroup + ctx context.Context + cancelFunc atomic.Value // 使用atomic.Value存储cancel函数,避免竞态条件 + wg sync.WaitGroup } // HotkeyError 热键错误 @@ -169,24 +171,46 @@ func (e *HotkeyError) Unwrap() error { return e.Err } +// setCancelFunc 原子地设置cancel函数 +func (hs *HotkeyService) setCancelFunc(cancel context.CancelFunc) { + hs.cancelFunc.Store(cancel) +} + +// getCancelFunc 原子地获取cancel函数 +func (hs *HotkeyService) getCancelFunc() context.CancelFunc { + if cancel := hs.cancelFunc.Load(); cancel != nil { + return cancel.(context.CancelFunc) + } + return nil +} + +// clearCancelFunc 原子地清除cancel函数 +func (hs *HotkeyService) clearCancelFunc() { + hs.cancelFunc.Store((*context.CancelFunc)(nil)) +} + // NewHotkeyService 创建热键服务实例 -func NewHotkeyService(configService *ConfigService, logger *log.LogService) *HotkeyService { +func NewHotkeyService(configService *ConfigService, windowService *WindowService, logger *log.LogService) *HotkeyService { if logger == nil { logger = log.New() } ctx, cancel := context.WithCancel(context.Background()) - return &HotkeyService{ + service := &HotkeyService{ logger: logger, configService: configService, + windowService: windowService, ctx: ctx, - cancel: cancel, } + // 初始化时设置cancel函数 + service.setCancelFunc(cancel) + return service } // Initialize 初始化热键服务 -func (hs *HotkeyService) Initialize(app *application.App) error { +func (hs *HotkeyService) Initialize(app *application.App, mainWindow *application.WebviewWindow) error { hs.app = app + hs.mainWindow = mainWindow if int(C.initX11Display()) == 0 { return &HotkeyError{"init_x11", fmt.Errorf("failed to initialize X11 display")} @@ -253,7 +277,7 @@ func (hs *HotkeyService) RegisterHotkey(hotkey *models.HotkeyCombo) error { hs.currentHotkey = hotkey hs.isRegistered.Store(true) - hs.cancel = cancel + hs.setCancelFunc(cancel) return nil } @@ -271,8 +295,9 @@ func (hs *HotkeyService) unregisterInternal() error { return nil } - if hs.cancel != nil { - hs.cancel() + // 原子地获取并调用cancel函数 + if cancel := hs.getCancelFunc(); cancel != nil { + cancel() hs.wg.Wait() } @@ -283,6 +308,7 @@ func (hs *HotkeyService) unregisterInternal() error { hs.currentHotkey = nil hs.isRegistered.Store(false) + hs.clearCancelFunc() return nil } @@ -298,7 +324,8 @@ func (hs *HotkeyService) UpdateHotkey(enable bool, hotkey *models.HotkeyCombo) e func (hs *HotkeyService) hotkeyListener(ctx context.Context, ready chan<- error) { defer hs.wg.Done() - ticker := time.NewTicker(100 * time.Millisecond) + // 优化轮询频率从100ms改为50ms,提高响应性 + ticker := time.NewTicker(50 * time.Millisecond) defer ticker.Stop() ready <- nil // 标记准备就绪 @@ -315,11 +342,61 @@ func (hs *HotkeyService) hotkeyListener(ctx context.Context, ready chan<- error) } } -// toggleWindow 切换窗口 +// toggleWindow 切换窗口显示状态 func (hs *HotkeyService) toggleWindow() { - if hs.app != nil { - hs.app.EmitEvent("hotkey:toggle-window", nil) + if hs.mainWindow == nil { + hs.logger.Error("main window not set") + return } + + // 检查主窗口是否可见 + if hs.isWindowVisible(hs.mainWindow) { + // 如果主窗口可见,隐藏所有窗口 + hs.hideAllWindows() + } else { + // 如果主窗口不可见,显示所有窗口 + hs.showAllWindows() + } +} + +// isWindowVisible 检查窗口是否可见 +func (hs *HotkeyService) isWindowVisible(window *application.WebviewWindow) bool { + return window.IsVisible() +} + +// hideAllWindows 隐藏所有窗口 +func (hs *HotkeyService) hideAllWindows() { + // 隐藏主窗口 + hs.mainWindow.Hide() + + // 隐藏所有子窗口 + if hs.windowService != nil { + openWindows := hs.windowService.GetOpenWindows() + for _, windowInfo := range openWindows { + windowInfo.Window.Hide() + } + } + + hs.logger.Debug("all windows hidden") +} + +// showAllWindows 显示所有窗口 +func (hs *HotkeyService) showAllWindows() { + // 显示主窗口 + hs.mainWindow.Show() + hs.mainWindow.Restore() + hs.mainWindow.Focus() + + // 显示所有子窗口 + if hs.windowService != nil { + openWindows := hs.windowService.GetOpenWindows() + for _, windowInfo := range openWindows { + windowInfo.Window.Show() + windowInfo.Window.Restore() + } + } + + hs.logger.Debug("all windows shown") } // keyToX11KeyCode 键名转X11键码 @@ -386,7 +463,10 @@ func (hs *HotkeyService) IsRegistered() bool { // ServiceShutdown 关闭服务 func (hs *HotkeyService) ServiceShutdown() error { - hs.cancel() + // 原子地获取并调用cancel函数 + if cancel := hs.getCancelFunc(); cancel != nil { + cancel() + } hs.wg.Wait() C.closeX11Display() return nil diff --git a/internal/services/path_migration_service.go b/internal/services/path_migration_service.go index 5f653e0..7629ea8 100644 --- a/internal/services/path_migration_service.go +++ b/internal/services/path_migration_service.go @@ -33,22 +33,24 @@ type MigrationProgress struct { // MigrationService 迁移服务 type MigrationService struct { - logger *log.LogService - mu sync.RWMutex - progress atomic.Value // stores MigrationProgress + logger *log.LogService + dbService *DatabaseService + mu sync.RWMutex + progress atomic.Value // stores MigrationProgress ctx context.Context cancel context.CancelFunc } // NewMigrationService 创建迁移服务 -func NewMigrationService(logger *log.LogService) *MigrationService { +func NewMigrationService(dbService *DatabaseService, logger *log.LogService) *MigrationService { if logger == nil { logger = log.New() } ms := &MigrationService{ - logger: logger, + logger: logger, + dbService: dbService, } // 初始化进度 @@ -104,6 +106,17 @@ func (ms *MigrationService) MigrateDirectory(srcPath, dstPath string) error { return ms.failWithError(err) } + // 迁移前断开数据库连接 + ms.updateProgress(MigrationProgress{ + Status: MigrationStatusMigrating, + Progress: 10, + }) + + if ms.dbService != nil { + if err := ms.dbService.ServiceShutdown(); err != nil { + ms.logger.Error("Failed to close database connection", "error", err) + } + } // 执行原子迁移 if err := ms.atomicMove(ctx, srcPath, dstPath); err != nil { return ms.failWithError(err) @@ -177,10 +190,17 @@ func (ms *MigrationService) atomicMove(ctx context.Context, srcPath, dstPath str }) if err := os.Rename(srcPath, dstPath); err == nil { + // 重命名成功,更新进度到90% + ms.updateProgress(MigrationProgress{ + Status: MigrationStatusMigrating, + Progress: 90, + }) + ms.logger.Info("Directory migration completed using direct rename", "src", srcPath, "dst", dstPath) return nil } // 重命名失败,使用压缩迁移 + ms.logger.Info("Direct rename failed, using compress migration", "src", srcPath, "dst", dstPath) ms.updateProgress(MigrationProgress{ Status: MigrationStatusMigrating, Progress: 30, @@ -249,12 +269,18 @@ func (ms *MigrationService) compressMove(ctx context.Context, srcPath, dstPath s default: } + // 验证迁移是否成功 + if err := ms.verifyMigration(dstPath); err != nil { + // 迁移验证失败,清理目标目录 + os.RemoveAll(dstPath) + return fmt.Errorf("migration verification failed: %v", err) + } + // 删除源目录 ms.updateProgress(MigrationProgress{ Status: MigrationStatusMigrating, Progress: 90, }) - os.RemoveAll(srcPath) return nil } @@ -404,6 +430,29 @@ func (ms *MigrationService) isSubDirectory(parent, target string) bool { return len(target) > len(parent) && strings.HasPrefix(target, parent) } +// verifyMigration 验证迁移是否成功 +func (ms *MigrationService) verifyMigration(dstPath string) error { + // 检查目标目录是否存在 + dstStat, err := os.Stat(dstPath) + if err != nil { + return fmt.Errorf("target directory does not exist: %v", err) + } + if !dstStat.IsDir() { + return fmt.Errorf("target path is not a directory") + } + + // 简单验证:检查目标目录是否非空 + isEmpty, err := ms.isDirectoryEmpty(dstPath) + if err != nil { + return fmt.Errorf("failed to check target directory: %v", err) + } + if isEmpty { + return fmt.Errorf("target directory is empty after migration") + } + + return nil +} + // CancelMigration 取消迁移 func (ms *MigrationService) CancelMigration() error { ms.mu.Lock() diff --git a/internal/services/service_manager.go b/internal/services/service_manager.go index 7fa2277..a6425f6 100644 --- a/internal/services/service_manager.go +++ b/internal/services/service_manager.go @@ -40,7 +40,7 @@ func NewServiceManager() *ServiceManager { databaseService := NewDatabaseService(configService, logger) // 初始化迁移服务 - migrationService := NewMigrationService(logger) + migrationService := NewMigrationService(databaseService, logger) // 初始化文档服务 documentService := NewDocumentService(databaseService, logger) @@ -52,7 +52,7 @@ func NewServiceManager() *ServiceManager { systemService := NewSystemService(logger) // 初始化热键服务 - hotkeyService := NewHotkeyService(configService, logger) + hotkeyService := NewHotkeyService(configService, windowService, logger) // 初始化对话服务 dialogService := NewDialogService(logger) diff --git a/main.go b/main.go index e141319..31d27c3 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,7 @@ var assets embed.FS // logs any error that might occur. func main() { serviceManager := services.NewServiceManager() + var window *application.WebviewWindow var encryptionKey = [32]byte{ 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, @@ -51,6 +52,13 @@ func main() { SingleInstance: &application.SingleInstanceOptions{ UniqueID: "com.voidraft", EncryptionKey: encryptionKey, + OnSecondInstanceLaunch: func(data application.SecondInstanceData) { + if window != nil { + window.Show() + window.Restore() + window.Focus() + } + }, AdditionalData: map[string]string{ "launchtime": time.Now().Local().String(), }, @@ -82,6 +90,7 @@ func main() { URL: "/", }) mainWindow.Center() + window = mainWindow // 获取托盘服务并设置应用引用 trayService := serviceManager.GetTrayService() @@ -96,7 +105,7 @@ func main() { // 初始化热键服务 hotkeyService := serviceManager.GetHotkeyService() - err := hotkeyService.Initialize(app) + err := hotkeyService.Initialize(app, mainWindow) if err != nil { panic(err) }