package core import ( "context" "fmt" "github.com/energye/systray" "github.com/wailsapp/wails/v2/pkg/runtime" "go.uber.org/zap/zapcore" "golang.design/x/hotkey" "net" "net/http" "os" "skapp/pkg/config" st "skapp/pkg/systray" "skapp/pkg/config/toml" "skapp/pkg/global" "skapp/pkg/logger" "skapp/pkg/pid" server "skapp/pkg/server" "time" hk "skapp/pkg/hotkey" ) var pidFile = "./.pid" var hotKeys []*hotkey.Hotkey // App struct type App struct { ctx context.Context srv *http.Server Handler http.Handler pidLock *pid.PidLock } // NewApp creates a new App application struct func NewApp() *App { app := &App{} app.load() return app } func (a *App) Ctx() context.Context { return a.ctx } func (a *App) ReloadApp() { runtime.WindowReloadApp(a.ctx) } func (a *App) ForceClose() { a.Shutdown(a.ctx) os.Exit(0) } func (a *App) load() { a.pidLock = pid.NewPidLock(pidFile) conf, err := toml.LoadConfig() if err != nil { logger.Log.Fatalf("%s", err) } global.Config = conf if conf.HasDebug() && conf.Debug.Enable { logger.SetLevel(zapcore.DebugLevel) } else { logger.SetLevel(zapcore.ErrorLevel) } engine := server.Server(conf) a.srv = &http.Server{ Addr: fmt.Sprintf("%s:%d", conf.Server.Host, conf.Server.Port), Handler: engine, } a.Handler = engine.Handler() } func (a *App) Startup(ctx context.Context) { if !global.Config.HasDebug() || !global.Config.Debug.Enable { err := a.pidLock.Lock() if err != nil { os.Exit(-1) } } a.ctx = ctx st.Init(func() { mainMenuItem := systray.AddMenuItem("主页面", "显示主页面") mainMenuItem.Click(func() { runtime.WindowShow(ctx) }) systray.AddSeparator() quitMenuItem := systray.AddMenuItem("退出", "退出程序") quitMenuItem.Click(func() { a.ForceClose() }) }) hotKeys = append(hotKeys, hk.Register([]hotkey.Modifier{hotkey.ModShift, hotkey.ModCtrl, hotkey.ModAlt}, hotkey.KeyX, func() { logger.Log.Info("热键触发: Ctrl+Shift+Alt+X") }, nil)) go func() { logger.Log.Infof("启动本地后台服务: %s", a.srv.Addr) // 服务连接 if err := a.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { switch err.(type) { case *net.OpError: _, _ = runtime.MessageDialog(ctx, runtime.MessageDialogOptions{ Title: "错误", Type: runtime.ErrorDialog, Message: fmt.Sprintf("服务启动失败: 请检查 %s 是否被其他进程占用", a.srv.Addr), }) default: _, _ = runtime.MessageDialog(ctx, runtime.MessageDialogOptions{ Title: "错误", Type: runtime.ErrorDialog, Message: err.Error(), }) } logger.Log.Fatalf("listen: %s\n", err) os.Exit(-1) } }() } // DomReady is called after the front-end dom has been loaded // domReady 在前端Dom加载完毕后调用 func (a *App) DomReady(ctx context.Context) { // Add your action here // 在这里添加你的操作 } func (a *App) BeforeClose(ctx context.Context) bool { for _, k := range hotKeys { k.Unregister() } return false } func (a *App) Shutdown(ctx context.Context) { ctx2, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if !global.Config.HasDebug() || !global.Config.Debug.Enable { a.pidLock.UnLock() } if err := a.srv.Shutdown(ctx2); err != nil { logger.Log.Fatalf("Server Shutdown:", err) } } func (a *App) GetConfig() *config.Config { return global.Config } func (a *App) OpenFileDialog(options runtime.OpenDialogOptions) (string, error) { return runtime.OpenFileDialog(a.ctx, options) } func (a *App) SaveFileDialog(options runtime.SaveDialogOptions) (string, error) { return runtime.SaveFileDialog(a.ctx, options) }