package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/gin-gonic/gin" "market-data-service/adapter" "market-data-service/adapter/tushare" "market-data-service/api" "market-data-service/internal/handler" "market-data-service/internal/monitor" "market-data-service/internal/repository" "market-data-service/internal/service" "market-data-service/internal/websocket" ) func main() { // 配置 port := getEnv("PORT", "8080") dbURL := getEnv("DATABASE_URL", "postgres://user:password@localhost:5432/marketdata?sslmode=disable") configPath := getEnv("CONFIG_PATH", "./config.json") tushareToken := getEnv("TUSHARE_TOKEN", "") // 设置运行模式 ginMode := getEnv("GIN_MODE", "debug") gin.SetMode(ginMode) // 连接数据库 db, err := repository.NewDB(dbURL) if err != nil { log.Fatalf("Failed to connect to database: %v", err) } defer db.Close() // 初始化配置服务 configService, err := service.NewConfigService(configPath) if err != nil { log.Printf("Warning: Failed to load config from %s: %v", configPath, err) configService, _ = service.NewConfigService("") } // 初始化适配器服务 adapterService := service.NewAdapterService() // 初始化测试服务 testService := service.NewTestService() // 初始化Repository stockRepo := repository.NewStockRepository(db) futuresRepo := repository.NewFuturesRepository(db) // 初始化数据源适配器(如果配置了token) var dataSourceAdapter adapter.DataSourceAdapter if tushareToken != "" { tushareAdapter := tushare.NewAdapter() if err := tushareAdapter.Connect(map[string]string{ "token": tushareToken, }); err != nil { log.Printf("Warning: Failed to connect to Tushare: %v", err) } else { dataSourceAdapter = tushareAdapter log.Println("Tushare adapter initialized successfully") } } else { log.Println("Warning: TUSHARE_TOKEN not set, fallback to source is disabled") } // 初始化Service(注入adapter实现回源功能) stockService := service.NewStockService(stockRepo, dataSourceAdapter) futuresService := service.NewFuturesService(futuresRepo, dataSourceAdapter) adminService := service.NewAdminService(db) // 初始化Handler h := handler.NewHandler(stockService, futuresService, adminService) // 初始化管理后台Handler adminHandler := handler.NewAdminHandlerImpl(configService, adapterService, testService) // 初始化WebSocket Hub hub := websocket.NewHub() go hub.Run() // 初始化WebSocket Server wsServer := websocket.NewServer(hub) // 初始化数据质量监控 alertSender := &monitor.LogAlertSender{} dataMonitor := monitor.NewMonitor(db, stockRepo, futuresRepo, alertSender) // 启动每日检查定时任务 ctx := context.Background() dataMonitor.StartDailyCheckCron(ctx) // 创建Gin引擎 router := gin.New() router.Use(gin.Recovery()) router.Use(loggerMiddleware()) // 注册API路由 apiRouter := api.NewRouter(h) apiRouter.Register(router) // 注册WebSocket路由 router.GET("/v1/stream", wsServer.HandleWebSocket) // 注册管理后台路由 adminRouter := api.NewAdminRouter(h, adminHandler, adminHandler, adminHandler) adminRouter.Register(router) // 创建HTTP服务器 srv := &http.Server{ Addr: ":" + port, Handler: router, } // 启动服务器(异步) go func() { log.Printf("Server starting on port %s", port) log.Printf("Admin dashboard: http://localhost:%s/admin", port) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Server failed to start: %v", err) } }() // 等待中断信号 quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) <-quit log.Println("Shutting down server...") // 优雅关闭 shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(shutdownCtx); err != nil { log.Printf("Server forced to shutdown: %v", err) } log.Println("Server exited") } // loggerMiddleware 日志中间件 func loggerMiddleware() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path raw := c.Request.URL.RawQuery c.Next() latency := time.Since(start) clientIP := c.ClientIP() method := c.Request.Method statusCode := c.Writer.Status() if raw != "" { path = path + "?" + raw } log.Printf("[GIN] %v | %3d | %13v | %15s | %-7s %s", start.Format("2006/01/02 - 15:04:05"), statusCode, latency, clientIP, method, path, ) } } func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue }