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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
}