You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
4.6 KiB

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
}