topfans/backend/services/activityService/main.go
2026-06-24 19:51:13 +08:00

255 lines
7.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"context"
"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/activity"
pbUser "github.com/topfans/backend/pkg/proto/user"
activityClient "github.com/topfans/backend/services/activityService/client"
"github.com/topfans/backend/services/activityService/provider"
"github.com/topfans/backend/services/activityService/repository"
"github.com/topfans/backend/services/activityService/service"
)
var (
port = flag.Int("port", getEnvInt("PORT", 20004), "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")
redisHost = flag.String("redis-host", getEnv("REDIS_HOST", "localhost"), "Redis host")
redisPort = flag.Int("redis-port", getEnvInt("REDIS_PORT", 6379), "Redis port")
redisPassword = flag.String("redis-password", getEnv("REDIS_PASSWORD", ""), "Redis password")
redisDB = flag.Int("redis-db", getEnvInt("REDIS_DB", 0), "Redis database")
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: "activity-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 Activity Service...")
// 初始化数据库
if err := initDatabase(); err != nil {
logger.Sugar.Fatalf("Failed to initialize database: %v", err)
}
// 初始化 Redis
if err := database.InitRedis(database.RedisConfig{
Host: *redisHost,
Port: *redisPort,
Password: *redisPassword,
DB: *redisDB,
}); err != nil {
logger.Sugar.Fatalf("Failed to initialize Redis: %v", err)
}
// 自动迁移数据库表
if err := autoMigrate(); err != nil {
logger.Sugar.Fatalf("Failed to migrate database: %v", err)
}
// 初始化 Repository
activityRepo := repository.NewActivityRepository()
mintingActivityRepo := repository.NewMintingActivityRepository()
// 初始化 User RPC Client
userRPCClient, err := createUserRPCClient()
if err != nil {
logger.Sugar.Fatalf("Failed to create user RPC client: %v", err)
}
// 初始化 Service
activityService := service.NewActivityService(activityRepo, mintingActivityRepo, userRPCClient, database.GetRedis())
// 启动 combo Stream worker3 秒连击合并推送)
workerCtx, workerCancel := context.WithCancel(context.Background())
defer workerCancel()
activityService.StartComboStreamWorker(workerCtx)
// 初始化 Provider
activityProvider := provider.NewActivityProvider(activityService)
// 初始化 Dubbo-go 服务器
if err := initDubboService(activityProvider); err != nil {
logger.Sugar.Fatalf("Failed to initialize Dubbo service: %v", err)
}
// 等待信号(优雅关闭)
logger.Sugar.Info("Activity 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.Activity{},
&models.ActivityItem{},
&models.ActivityContribution{},
&models.ActivityUserStats{},
&models.ActivityMessage{},
&models.MintingActivity{},
}
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
}
// createUserRPCClient 创建User Service RPC客户端
func createUserRPCClient() (activityClient.UserRPCClient, error) {
// 创建 Dubbo 客户端(直连模式)
cli, err := client.NewClient(
client.WithClientURL(*userServiceURL),
)
if err != nil {
return nil, fmt.Errorf("failed to create Dubbo client: %w", err)
}
// 创建 UserSocialService 客户端
_, err = pbUser.NewUserSocialService(cli)
if err != nil {
return nil, fmt.Errorf("failed to create UserSocialService: %w", err)
}
// 创建我们的 RPC 客户端封装
userRPCClient, err := activityClient.NewUserRPCClient(cli)
if err != nil {
return nil, fmt.Errorf("failed to create user RPC client: %w", err)
}
logger.Sugar.Infof("User service client created successfully, url: %s", *userServiceURL)
return userRPCClient, nil
}
// initDubboService 初始化 Dubbo 服务
func initDubboService(activityProvider *provider.ActivityProvider) error {
// 启动健康检查 HTTP 服务器
healthPort := *port + 1000 // e.g., 20004 -> 21004
healthHandler = health.NewHandler("activity-service", healthPort)
healthHandler.Start()
// 创建 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)
}
// 注册服务
if err := pb.RegisterActivityServiceHandler(srv, activityProvider); err != nil {
return fmt.Errorf("failed to register ActivityService handler: %w", err)
}
logger.Sugar.Infof("Dubbo-go provider registered successfully, service: topfans.activity.ActivityService, port: %d", *port)
// 在后台启动 Dubbo 服务器
go func() {
if err := srv.Serve(); err != nil {
logger.Sugar.Fatalf("Failed to serve Dubbo: %v", err)
}
}()
return nil
}
// gracefulShutdown 优雅关闭
func gracefulShutdown() {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
logger.Sugar.Info("Shutting down Activity Service...")
// 关闭健康检查服务器
if healthHandler != nil {
healthHandler.Stop()
}
// 关闭数据库连接
if err := database.Close(); err != nil {
logger.Sugar.Errorf("Error closing database: %v", err)
}
logger.Sugar.Info("Activity Service stopped")
}