// 平台 API 命令行工具。 package main import ( "encoding/json" "fmt" "io" "os" "strings" "git.apinb.com/bsm-sdk/core/database" "git.apinb.com/heqiapp/platforms/backend/api/internal/config" "git.apinb.com/heqiapp/platforms/backend/api/internal/initdb" "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform" "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "git.apinb.com/heqiapp/platforms/backend/api/internal/routers" "git.apinb.com/heqiapp/platforms/backend/api/internal/seed" "github.com/gin-gonic/gin" "gorm.io/gorm" ) const serviceKey = "heqi" func main() { if len(os.Args) < 2 { printUsage() os.Exit(1) } switch os.Args[1] { case "version": fmt.Println("platform-cli 0.1.0") case "resource-contract": if err := writeResourceContract(os.Stdout); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } case "mock-data": if err := writeMockData(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Println("mock data written successfully") case "migrate": if err := migrateDatabase(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Println("database migrated successfully") default: fmt.Fprintf(os.Stderr, "unknown command: %s\n", os.Args[1]) printUsage() os.Exit(1) } } func printUsage() { fmt.Fprintln(os.Stderr, "usage: platform-cli ") } type route struct { Method string `json:"method"` Path string `json:"path"` } type contract struct { Domain string `json:"domain"` Name string `json:"name"` Path string `json:"path"` PageKind string `json:"pageKind"` Mode string `json:"mode"` } type manifest struct { Resources []contract `json:"resources"` Routes []route `json:"routes"` } func writeResourceContract(output io.Writer) error { gin.SetMode(gin.ReleaseMode) engine := gin.New() routers.RegisterPlatform(serviceKey, engine) routes := make([]route, 0, len(engine.Routes())) for _, item := range engine.Routes() { routes = append(routes, route{ Method: item.Method, Path: strings.TrimPrefix(item.Path, "/"+serviceKey+"/platform/v1"), }) } expected := platform.ExpectedResources() contracts := make([]contract, 0, len(expected)) for _, item := range expected { contracts = append(contracts, contract{ Domain: item.Domain, Name: item.Name, Path: item.Path, PageKind: item.PageKind, Mode: string(item.Mode), }) } return json.NewEncoder(output).Encode(manifest{Resources: contracts, Routes: routes}) } func writeMockData() error { config.New("platform") if config.Spec.Databases == nil { return fmt.Errorf("database configuration is required") } databaseService, err := database.NewDatabase( config.Spec.Databases.Driver, config.Spec.Databases.Source, nil, ) if err != nil { return fmt.Errorf("connect database: %w", err) } if err := initdb.New(databaseService); err != nil { return fmt.Errorf("initialize platform data: %w", err) } if err := seed.MockData(databaseService); err != nil { return fmt.Errorf("write mock data: %w", err) } return nil } func migrateDatabase() error { config.New("platform") if config.Spec.Databases == nil { return fmt.Errorf("database configuration is required") } var migrationDatabase interface { Migrator() gorm.Migrator } var err error switch strings.ToLower(config.Spec.Databases.Driver) { case "postgres": migrationDatabase, err = database.NewPostgres(config.Spec.Databases.Source, nil) case "mysql": migrationDatabase, err = database.NewMysql(config.Spec.Databases.Source, nil) default: return fmt.Errorf("unsupported database driver: %s", config.Spec.Databases.Driver) } if err != nil { return fmt.Errorf("connect database before migration: %w", err) } const legacyPhoneIndex = "idx_platform_account_phone" if migrationDatabase.Migrator().HasIndex(&models.PlatformAccount{}, legacyPhoneIndex) { if err := migrationDatabase.Migrator().DropIndex(&models.PlatformAccount{}, legacyPhoneIndex); err != nil { return fmt.Errorf("drop legacy platform account phone index: %w", err) } } databaseService, err := database.NewDatabase( config.Spec.Databases.Driver, config.Spec.Databases.Source, nil, ) if err != nil { return fmt.Errorf("migrate database: %w", err) } return initdb.New(databaseService) }