diff --git a/Tools/quickly/app.go b/Tools/quickly/app.go deleted file mode 100644 index af53038..0000000 --- a/Tools/quickly/app.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "context" - "fmt" -) - -// App struct -type App struct { - ctx context.Context -} - -// NewApp creates a new App application struct -func NewApp() *App { - return &App{} -} - -// startup is called when the app starts. The context is saved -// so we can call the runtime methods -func (a *App) startup(ctx context.Context) { - a.ctx = ctx -} - -// Greet returns a greeting for the given name -func (a *App) Greet(name string) string { - return fmt.Sprintf("Hello %s, It's show time!", name) -} diff --git a/Tools/quickly/backend/app.go b/Tools/quickly/backend/app.go new file mode 100644 index 0000000..d432973 --- /dev/null +++ b/Tools/quickly/backend/app.go @@ -0,0 +1,14 @@ +package backend + +import ( + "context" +) + +func NewApp() *App { + return &App{} +} + +func (a *App) Startup(ctx context.Context) { + a.ctx = ctx + a.loadSettings() +} diff --git a/Tools/quickly/backend/database.go b/Tools/quickly/backend/database.go new file mode 100644 index 0000000..a9d0e45 --- /dev/null +++ b/Tools/quickly/backend/database.go @@ -0,0 +1,56 @@ +package backend + +import ( + "fmt" +) + +func (a *App) AddDatabaseConfig(name string, targetPath string, modelPackagePath string) error { + if name == "" { + return fmt.Errorf("database name is empty") + } + if targetPath == "" { + return fmt.Errorf("target path is empty") + } + + for _, db := range a.settings.Databases { + if db.Name == name { + return fmt.Errorf("database with name '%s' already exists", name) + } + } + + a.settings.Databases = append(a.settings.Databases, DatabaseConfig{ + Name: name, + TargetPath: targetPath, + ModelPackagePath: modelPackagePath, + }) + return a.saveSettings() +} + +func (a *App) RemoveDatabaseConfig(name string) error { + for i, db := range a.settings.Databases { + if db.Name == name { + a.settings.Databases = append(a.settings.Databases[:i], a.settings.Databases[i+1:]...) + return a.saveSettings() + } + } + return fmt.Errorf("database with name '%s' not found", name) +} + +func (a *App) UpdateDatabaseConfig(oldName string, newName string, targetPath string, modelPackagePath string) error { + if newName == "" { + return fmt.Errorf("database name is empty") + } + if targetPath == "" { + return fmt.Errorf("target path is empty") + } + + for i, db := range a.settings.Databases { + if db.Name == oldName { + a.settings.Databases[i].Name = newName + a.settings.Databases[i].TargetPath = targetPath + a.settings.Databases[i].ModelPackagePath = modelPackagePath + return a.saveSettings() + } + } + return fmt.Errorf("database with name '%s' not found", oldName) +} diff --git a/Tools/quickly/backend/greet.go b/Tools/quickly/backend/greet.go new file mode 100644 index 0000000..2c7655a --- /dev/null +++ b/Tools/quickly/backend/greet.go @@ -0,0 +1,9 @@ +package backend + +import ( + "fmt" +) + +func (a *App) Greet(name string) string { + return fmt.Sprintf("Hello %s, It's show time!", name) +} diff --git a/Tools/quickly/backend/helpers.go b/Tools/quickly/backend/helpers.go new file mode 100644 index 0000000..585ff38 --- /dev/null +++ b/Tools/quickly/backend/helpers.go @@ -0,0 +1,56 @@ +package backend + +import ( + "os" + "path/filepath" +) + +func copyDir(src string, dst string) error { + srcInfo, err := os.Stat(src) + if err != nil { + return err + } + + if err := os.MkdirAll(dst, srcInfo.Mode()); err != nil { + return err + } + + entries, err := os.ReadDir(src) + if err != nil { + return err + } + + for _, entry := range entries { + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + if entry.IsDir() { + if err := copyDir(srcPath, dstPath); err != nil { + return err + } + } else { + if err := copyFile(srcPath, dstPath); err != nil { + return err + } + } + } + + return nil +} + +func copyFile(src string, dst string) error { + source, err := os.Open(src) + if err != nil { + return err + } + defer source.Close() + + destination, err := os.Create(dst) + if err != nil { + return err + } + defer destination.Close() + + _, err = destination.ReadFrom(source) + return err +} diff --git a/Tools/quickly/backend/models.go b/Tools/quickly/backend/models.go new file mode 100644 index 0000000..e86f16f --- /dev/null +++ b/Tools/quickly/backend/models.go @@ -0,0 +1,34 @@ +package backend + +import ( + "context" +) + +type DatabaseConfig struct { + Name string `json:"name"` + TargetPath string `json:"targetPath"` + ModelPackagePath string `json:"modelPackagePath"` +} + +type ProjectConfig struct { + Name string `json:"name"` + Path string `json:"path"` +} + +type Settings struct { + Theme string `json:"theme"` + Language string `json:"language"` + Notifications bool `json:"notifications"` + AutoStart bool `json:"autoStart"` + MysqlModelPath string `json:"mysqlModelPath"` + DefaultQueryPackagePath string `json:"defaultQueryPackagePath"` + ModelBasePath string `json:"modelBasePath"` + SwaggerDir string `json:"swaggerDir"` + Databases []DatabaseConfig `json:"databases"` + Projects []ProjectConfig `json:"projects"` +} + +type App struct { + ctx context.Context + settings Settings +} diff --git a/Tools/quickly/backend/mysql_model.go b/Tools/quickly/backend/mysql_model.go new file mode 100644 index 0000000..cbbde13 --- /dev/null +++ b/Tools/quickly/backend/mysql_model.go @@ -0,0 +1,171 @@ +package backend + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/wailsapp/wails/v2/pkg/runtime" +) + +func (a *App) SelectFile(title string, defaultDir string, filter string) (string, error) { + return runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{ + Title: title, + DefaultDirectory: defaultDir, + Filters: []runtime.FileFilter{{Pattern: filter}}, + }) +} + +func (a *App) SelectDirectory(title string, defaultDir string) (string, error) { + return runtime.OpenDirectoryDialog(a.ctx, runtime.OpenDialogOptions{ + Title: title, + DefaultDirectory: defaultDir, + }) +} + +func (a *App) CheckGenPs1Exists(filePath string) (bool, error) { + if filePath == "" { + return false, fmt.Errorf("file path is empty") + } + + _, err := os.Stat(filePath) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + return true, nil +} + +func (a *App) ReadGoModModule(filePath string) (string, error) { + if filePath == "" { + return "", fmt.Errorf("file path is empty") + } + + dirPath := filepath.Dir(filePath) + goModPath := filepath.Join(dirPath, "go.mod") + + content, err := os.ReadFile(goModPath) + if err != nil { + return "", fmt.Errorf("go.mod not found: %w", err) + } + + lines := strings.Split(string(content), "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "module ") { + moduleName := strings.TrimPrefix(line, "module ") + moduleName = strings.TrimSpace(moduleName) + return moduleName, nil + } + } + + return "", fmt.Errorf("module declaration not found in go.mod") +} + +func (a *App) ExecuteGenPs1(genPs1Path string, dbName string, targetPath string, modelPackagePath string) (string, error) { + if genPs1Path == "" { + return "", fmt.Errorf("gen.ps1 path is empty") + } + if dbName == "" { + return "", fmt.Errorf("database name is empty") + } + + if _, err := os.Stat(genPs1Path); err != nil { + if os.IsNotExist(err) { + return "", fmt.Errorf("gen.ps1 not found") + } + return "", fmt.Errorf("error checking gen.ps1: %w", err) + } + + dirPath := filepath.Dir(genPs1Path) + + psPath, err := exec.LookPath("powershell.exe") + if err != nil { + return "", fmt.Errorf("PowerShell not found: %w", err) + } + + args := []string{ + "-NoProfile", + "-ExecutionPolicy", "Bypass", + "-Command", + fmt.Sprintf("Set-Location -Path '%s'; & .\\gen.ps1 -dbName '%s'", dirPath, dbName), + } + + cmd := exec.Command(psPath, args...) + + output, err := cmd.CombinedOutput() + if err != nil { + return "", fmt.Errorf("script execution failed: %w\nOutput: %s", err, string(output)) + } + + if targetPath != "" { + sourceDir := filepath.Join(dirPath, dbName) + if _, err := os.Stat(sourceDir); err == nil { + modelSource := filepath.Join(sourceDir, "model") + querySource := filepath.Join(sourceDir, "query") + + modelTarget := filepath.Join(targetPath, "model") + queryTarget := filepath.Join(targetPath, "query") + + if _, err := os.Stat(modelSource); err == nil { + if err := copyDir(modelSource, modelTarget); err != nil { + return string(output), fmt.Errorf("failed to copy model directory: %w", err) + } + } + + if _, err := os.Stat(querySource); err == nil { + if err := copyDir(querySource, queryTarget); err != nil { + return string(output), fmt.Errorf("failed to copy query directory: %w", err) + } + if modelPackagePath != "" { + if err := replaceImportPaths(queryTarget, dbName, modelPackagePath, a.settings.DefaultQueryPackagePath); err != nil { + return string(output), fmt.Errorf("failed to replace import paths: %w", err) + } + } + } + } + } + + return string(output), nil +} + +func replaceImportPaths(queryDir string, dbName string, newPackagePath string, basePath string) error { + err := filepath.Walk(queryDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() && strings.HasSuffix(path, ".gen.go") { + if err := replaceImportInFile(path, dbName, newPackagePath, basePath); err != nil { + return err + } + } + + return nil + }) + + return err +} + +func replaceImportInFile(filePath string, dbName string, newPackagePath string, basePath string) error { + content, err := os.ReadFile(filePath) + if err != nil { + return err + } + + if basePath == "" { + basePath = "git.hlsq.asia/mmorpg" + } + + oldImport := fmt.Sprintf(`"%s/%s/model"`, basePath, dbName) + newImport := fmt.Sprintf(`"%s"`, newPackagePath) + + newContent := bytes.ReplaceAll(content, []byte(oldImport), []byte(newImport)) + + return os.WriteFile(filePath, newContent, 0644) +} diff --git a/Tools/quickly/backend/project.go b/Tools/quickly/backend/project.go new file mode 100644 index 0000000..46bcb41 --- /dev/null +++ b/Tools/quickly/backend/project.go @@ -0,0 +1,73 @@ +package backend + +import ( + "fmt" + "os" + "path/filepath" +) + +func (a *App) AddProjectConfig(name string, path string) error { + if path == "" { + return fmt.Errorf("project path is empty") + } + + if name == "" { + name = filepath.Base(path) + } + + goModPath := filepath.Join(path, "go.mod") + if _, err := os.Stat(goModPath); err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("go.mod file not found in the specified path") + } + return fmt.Errorf("error checking go.mod file: %w", err) + } + + for _, project := range a.settings.Projects { + if project.Name == name { + return fmt.Errorf("project with name '%s' already exists", name) + } + } + + a.settings.Projects = append(a.settings.Projects, ProjectConfig{ + Name: name, + Path: path, + }) + return a.saveSettings() +} + +func (a *App) RemoveProjectConfig(name string) error { + for i, project := range a.settings.Projects { + if project.Name == name { + a.settings.Projects = append(a.settings.Projects[:i], a.settings.Projects[i+1:]...) + return a.saveSettings() + } + } + return fmt.Errorf("project with name '%s' not found", name) +} + +func (a *App) UpdateProjectConfig(oldName string, newName string, path string) error { + if newName == "" { + return fmt.Errorf("project name is empty") + } + if path == "" { + return fmt.Errorf("project path is empty") + } + + goModPath := filepath.Join(path, "go.mod") + if _, err := os.Stat(goModPath); err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("go.mod file not found in the specified path") + } + return fmt.Errorf("error checking go.mod file: %w", err) + } + + for i, project := range a.settings.Projects { + if project.Name == oldName { + a.settings.Projects[i].Name = newName + a.settings.Projects[i].Path = path + return a.saveSettings() + } + } + return fmt.Errorf("project with name '%s' not found", oldName) +} diff --git a/Tools/quickly/backend/settings.go b/Tools/quickly/backend/settings.go new file mode 100644 index 0000000..c1dd4f1 --- /dev/null +++ b/Tools/quickly/backend/settings.go @@ -0,0 +1,104 @@ +package backend + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" +) + +func (a *App) getSettingsPath() (string, error) { + configDir, err := os.UserConfigDir() + if err != nil { + return "", err + } + appConfigDir := filepath.Join(configDir, "quickly") + if err := os.MkdirAll(appConfigDir, 0755); err != nil { + return "", err + } + return filepath.Join(appConfigDir, "settings.json"), nil +} + +func (a *App) loadSettings() { + settingsPath, err := a.getSettingsPath() + if err != nil { + fmt.Println("Error getting settings path:", err) + a.settings = Settings{ + Theme: "light", + Language: "zh-CN", + Notifications: true, + AutoStart: false, + MysqlModelPath: "", + DefaultQueryPackagePath: "", + ModelBasePath: "", + SwaggerDir: "", + Databases: []DatabaseConfig{}, + Projects: []ProjectConfig{}, + } + return + } + + data, err := os.ReadFile(settingsPath) + if err != nil { + if os.IsNotExist(err) { + a.settings = Settings{ + Theme: "light", + Language: "zh-CN", + Notifications: true, + AutoStart: false, + MysqlModelPath: "", + DefaultQueryPackagePath: "", + ModelBasePath: "", + SwaggerDir: "", + Databases: []DatabaseConfig{}, + Projects: []ProjectConfig{}, + } + return + } + fmt.Println("Error reading settings file:", err) + return + } + + if err := json.Unmarshal(data, &a.settings); err != nil { + fmt.Println("Error parsing settings file:", err) + a.settings = Settings{ + Theme: "light", + Language: "zh-CN", + Notifications: true, + AutoStart: false, + MysqlModelPath: "", + DefaultQueryPackagePath: "", + ModelBasePath: "", + SwaggerDir: "", + Databases: []DatabaseConfig{}, + Projects: []ProjectConfig{}, + } + } +} + +func (a *App) saveSettings() error { + settingsPath, err := a.getSettingsPath() + if err != nil { + return fmt.Errorf("error getting settings path: %w", err) + } + + data, err := json.MarshalIndent(a.settings, "", " ") + if err != nil { + return fmt.Errorf("error marshaling settings: %w", err) + } + + if err := os.WriteFile(settingsPath, data, 0644); err != nil { + return fmt.Errorf("error writing settings file: %w", err) + } + + return nil +} + +func (a *App) GetSettings() Settings { + return a.settings +} + +func (a *App) SaveSettings(settings Settings) error { + a.settings = settings + return a.saveSettings() +} diff --git a/Tools/quickly/backend/swagger.go b/Tools/quickly/backend/swagger.go new file mode 100644 index 0000000..67da8e0 --- /dev/null +++ b/Tools/quickly/backend/swagger.go @@ -0,0 +1,239 @@ +package backend + +import ( + "fmt" + "net" + "net/http" + "os" + "path/filepath" + "strings" + "sync" + "time" +) + +type SwaggerFile struct { + Name string `json:"name"` + Path string `json:"path"` + Size int64 `json:"size"` + ModifiedTime string `json:"modifiedTime"` +} + +type SwaggerServer struct { + server *http.Server + port int + running bool + mu sync.Mutex +} + +var swaggerServer *SwaggerServer +var swaggerServerMu sync.Mutex + +func (a *App) GetSwaggerFiles(dirPath string) ([]SwaggerFile, error) { + if dirPath == "" { + return nil, fmt.Errorf("directory path is empty") + } + + var files []SwaggerFile + + err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + if strings.HasSuffix(strings.ToLower(info.Name()), ".swagger.json") { + files = append(files, SwaggerFile{ + Name: info.Name(), + Path: path, + Size: info.Size(), + ModifiedTime: info.ModTime().Format("2006-01-02 15:04:05"), + }) + } + + return nil + }) + + if err != nil { + return nil, fmt.Errorf("failed to walk directory: %w", err) + } + + return files, nil +} + +func (a *App) ReadSwaggerFile(filePath string) (string, error) { + if filePath == "" { + return "", fmt.Errorf("file path is empty") + } + + content, err := os.ReadFile(filePath) + if err != nil { + return "", fmt.Errorf("failed to read file: %w", err) + } + + return string(content), nil +} + +func (a *App) StartSwaggerServer(dirPath string) (string, error) { + if dirPath == "" { + return "", fmt.Errorf("directory path is empty") + } + + swaggerServerMu.Lock() + defer swaggerServerMu.Unlock() + + if swaggerServer != nil && swaggerServer.running { + return fmt.Sprintf("http://localhost:%d", swaggerServer.port), nil + } + + port := 8080 + for i := 0; i < 100; i++ { + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err == nil { + listener.Close() + break + } + port++ + } + + mux := http.NewServeMux() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + swaggerHTML := ` + +
+ + +
-
+ Quickly
高效开发工具,提升工作效率
+{{ feature.description }}
+{{ fileContent }}
+