- 新增 GET /api/v1/me/liked-assets 接口 - 新增 GET /api/v1/me/exhibited-assets 接口 - 新增 GetMyLikedAssets 和 GetMyExhibitedAssets RPC 方法 - 新增 ExhibitedAssetItemDTO 和 GetMyExhibitedAssetsResponseDTO - 前端新增 getUserLikedAssetsApi 和 getUserExhibitedAssetsApi(暂不实现) - 更新设计文档,标记他人作品统计接口为暂不实现 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
257 lines
7.0 KiB
Go
257 lines
7.0 KiB
Go
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, socialRepo)
|
||
|
||
// 创建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
|
||
}
|