package main import ( "flag" "fmt" "os" "os/signal" "strconv" "syscall" "dubbo.apache.org/dubbo-go/v3/client" _ "dubbo.apache.org/dubbo-go/v3/imports" "dubbo.apache.org/dubbo-go/v3/protocol" "dubbo.apache.org/dubbo-go/v3/server" "github.com/topfans/backend/pkg/database" "github.com/topfans/backend/pkg/health" "github.com/topfans/backend/pkg/logger" "github.com/topfans/backend/pkg/models" pb "github.com/topfans/backend/pkg/proto/social" pbUser "github.com/topfans/backend/pkg/proto/user" socialClient "github.com/topfans/backend/services/socialService/client" "github.com/topfans/backend/services/socialService/provider" "github.com/topfans/backend/services/socialService/repository" "github.com/topfans/backend/services/socialService/service" ) var ( port = flag.Int("port", getEnvInt("PORT", 20002), "Dubbo service port") dbHost = flag.String("db-host", getEnv("DB_HOST", "localhost"), "Database host") dbPort = flag.Int("db-port", getEnvInt("DB_PORT", 5432), "Database port") dbUser = flag.String("db-user", getEnv("DB_USER", "postgres"), "Database user") dbPassword = flag.String("db-password", getEnv("DB_PASSWORD", ""), "Database password") dbName = flag.String("db-name", getEnv("DB_NAME", "top-fans"), "Database name") userServiceURL = flag.String("user-service-url", getEnv("USER_SERVICE_URL", "tri://localhost:20000"), "User service URL") assetServiceURL = flag.String("asset-service-url", getEnv("ASSET_SERVICE_URL", "tri://localhost:20003"), "Asset service URL") healthHandler *health.Handler ) func getEnv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func getEnvInt(key string, fallback int) int { if v := os.Getenv(key); v != "" { if n, err := strconv.Atoi(v); err == nil { return n } } return fallback } func main() { flag.Parse() // 初始化日志(必须在最前面) env := os.Getenv("ENV") if env == "" { env = "development" } if err := logger.Init(logger.Config{ ServiceName: "social-service", Environment: env, LogLevel: os.Getenv("LOG_LEVEL"), }); err != nil { panic(fmt.Sprintf("Failed to initialize logger: %v", err)) } defer logger.Sync() logger.Sugar.Info("Starting Social Service...") // 初始化数据库 if err := initDatabase(); err != nil { logger.Sugar.Fatalf("Failed to initialize database: %v", err) } // 自动迁移数据库表 if err := autoMigrate(); err != nil { logger.Sugar.Fatalf("Failed to migrate database: %v", err) } // 初始化 Dubbo-go 服务器 if err := initDubboService(); err != nil { logger.Sugar.Fatalf("Failed to initialize Dubbo service: %v", err) } // 等待信号(优雅关闭) logger.Sugar.Info("Social service started successfully. Press Ctrl+C to exit.") gracefulShutdown() } // initDatabase 初始化数据库连接 func initDatabase() error { config := database.Config{ Host: *dbHost, Port: *dbPort, User: *dbUser, Password: *dbPassword, DBName: *dbName, SSLMode: "disable", TimeZone: "Asia/Shanghai", } return database.Init(config) } // autoMigrate 自动迁移数据库表 func autoMigrate() error { db := database.GetDB() if db == nil { return fmt.Errorf("database is not initialized") } // 迁移好友相关表 tables := []interface{}{ &models.FriendRequest{}, &models.Friendship{}, } for _, table := range tables { if err := db.AutoMigrate(table); err != nil { return fmt.Errorf("failed to migrate table: %w", err) } } logger.Sugar.Info("Database migration completed successfully") return nil } // gracefulShutdown 优雅关闭 func gracefulShutdown() { quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit logger.Sugar.Info("Shutting down server...") // 关闭健康检查服务器 if healthHandler != nil { healthHandler.Stop() } // 关闭数据库连接 if err := database.Close(); err != nil { logger.Sugar.Errorf("Error closing database: %v", err) } logger.Sugar.Info("Server exited") } // initDubboService 初始化Dubbo-go服务 func initDubboService() error { // 启动健康检查 HTTP 服务器 healthPort := *port + 1000 // e.g., 20002 -> 21002 healthHandler = health.NewHandler("social-service", healthPort) healthHandler.Start() db := database.GetDB() if db == nil { return fmt.Errorf("database is not initialized") } // 创建Repository实例 socialRepo := repository.NewSocialRepository() // 创建UserService RPC客户端 userServiceClient, err := createUserServiceClient() if err != nil { return fmt.Errorf("failed to create user service client: %w", err) } // 创建AssetService RPC客户端 assetClient, err := createAssetServiceClient(*assetServiceURL) if err != nil { return fmt.Errorf("failed to create asset service client: %w", err) } // 创建Service实例 friendService := service.NewFriendService(socialRepo, userServiceClient, db) assetLikeService := service.NewAssetLikeService(assetClient) // 创建Provider实例 socialProvider := provider.NewSocialProvider(friendService, assetLikeService) // 创建 Dubbo Server srv, err := server.NewServer( server.WithServerProtocol( protocol.WithPort(*port), protocol.WithTriple(), ), ) if err != nil { return fmt.Errorf("failed to create Dubbo server: %w", err) } // 使用 Triple 协议生成的 RegisterHandler 函数注册服务 if err := pb.RegisterSocialServiceHandler(srv, socialProvider); err != nil { return fmt.Errorf("failed to register SocialService handler: %w", err) } logger.Sugar.Info("Dubbo-go social provider registered successfully", "service", "topfans.social.SocialService", "port", *port, ) // 在后台启动 Dubbo 服务器 go func() { if err := srv.Serve(); err != nil { logger.Sugar.Fatalf("Failed to serve Dubbo: %v", err) } }() return nil } // createUserServiceClient 创建UserService RPC客户端 func createUserServiceClient() (service.UserServiceClient, error) { // 创建 Dubbo 客户端(直连模式) // URL 格式:tri://host:port cli, err := client.NewClient( client.WithClientURL(*userServiceURL), ) if err != nil { return nil, fmt.Errorf("failed to create Dubbo client: %w", err) } // 创建 UserSocialService 客户端 // NewUserSocialService 会自动处理接口名称和连接 svc, err := pbUser.NewUserSocialService(cli) if err != nil { return nil, fmt.Errorf("failed to create UserSocialService: %w", err) } // 创建我们的 RPC 客户端封装 userServiceClient := service.NewUserServiceClient(svc) logger.Sugar.Info("User service client created successfully", "url", *userServiceURL) return userServiceClient, nil } // createAssetServiceClient 创建AssetService RPC客户端 func createAssetServiceClient(serviceURL string) (*socialClient.AssetClient, error) { assetClient, err := socialClient.NewAssetClient(serviceURL, logger.Logger) if err != nil { return nil, fmt.Errorf("failed to create asset client: %w", err) } logger.Sugar.Info("Asset service client created successfully", "url", serviceURL) return assetClient, nil }