package api import ( "net/http" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" ) // Router API路由注册 type Router struct { handler Handler } // NewRouter 创建路由 func NewRouter(handler Handler) *Router { return &Router{handler: handler} } // Register 注册所有路由 func (r *Router) Register(engine *gin.Engine) { // 公开接口(无需认证) public := engine.Group("/v1") { public.GET("/admin/health", r.healthCheck) } // 需要认证的接口 api := engine.Group("/v1") api.Use(r.authMiddleware()) { // 股票接口 stock := api.Group("/stock") { stock.GET("/klines/:symbol", r.queryStockKLines) stock.GET("/symbols", r.listStockSymbols) stock.POST("/klines/batch", r.batchQueryStockKLines) stock.GET("/trading-dates", r.getStockTradingDates) } // 期货接口 futures := api.Group("/futures") { futures.GET("/klines/:symbol", r.queryFuturesKLines) futures.GET("/symbols", r.listFuturesSymbols) futures.POST("/klines/batch", r.batchQueryFuturesKLines) futures.GET("/continuous/:underlying", r.queryContinuousKLines) futures.GET("/trading-dates", r.getFuturesTradingDates) futures.GET("/contracts", r.getFuturesContracts) } // 管理接口 admin := api.Group("/admin") { admin.GET("/source/status", r.getDataSourceStatus) admin.POST("/source/switch", r.switchDataSource) admin.POST("/backfill", r.backfillData) } } } // authMiddleware API认证中间件 func (r *Router) authMiddleware() gin.HandlerFunc { return func(c *gin.Context) { apiKey := c.GetHeader("X-API-Key") if apiKey == "" { c.JSON(http.StatusUnauthorized, ErrorResponse{ Code: 401, Message: "缺少API Key", }) c.Abort() return } // TODO: 验证API Key有效性 // 可以将用户信息存入context c.Set("api_key", apiKey) c.Next() } } // ============================================ // 股票接口实现 // ============================================ func (r *Router) queryStockKLines(c *gin.Context) { var req KLineQueryRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } req.Symbol = c.Param("symbol") resp, err := r.handler.QueryKLines(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } func (r *Router) listStockSymbols(c *gin.Context) { var req SymbolListRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } resp, err := r.handler.ListSymbols(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } func (r *Router) batchQueryStockKLines(c *gin.Context) { var req BatchKLineRequest if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } resp, err := r.handler.BatchQueryKLines(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } // ============================================ // 期货接口实现 // ============================================ func (r *Router) queryFuturesKLines(c *gin.Context) { var req KLineQueryRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } req.Symbol = c.Param("symbol") resp, err := r.handler.QueryKLines(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } func (r *Router) listFuturesSymbols(c *gin.Context) { var req SymbolListRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } resp, err := r.handler.ListSymbols(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } func (r *Router) batchQueryFuturesKLines(c *gin.Context) { var req BatchKLineRequest if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } resp, err := r.handler.BatchQueryKLines(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } func (r *Router) queryContinuousKLines(c *gin.Context) { underlying := c.Param("underlying") var req KLineQueryRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } resp, err := r.handler.QueryContinuousKLines(c.Request.Context(), underlying, &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } // ============================================ // 管理接口实现 // ============================================ func (r *Router) getDataSourceStatus(c *gin.Context) { resp, err := r.handler.GetDataSourceStatus(c.Request.Context()) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } func (r *Router) switchDataSource(c *gin.Context) { var req SourceSwitchRequest if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } resp, err := r.handler.SwitchDataSource(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusUnprocessableEntity, ErrorResponse{ Code: 422, Message: "数据源切换失败", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } func (r *Router) backfillData(c *gin.Context) { var req BackfillRequest if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } resp, err := r.handler.BackfillData(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "补录任务失败", Detail: err.Error(), }) return } c.JSON(http.StatusAccepted, resp) } func (r *Router) healthCheck(c *gin.Context) { resp, err := r.handler.HealthCheck(c.Request.Context()) if err != nil { c.JSON(http.StatusServiceUnavailable, ErrorResponse{ Code: 503, Message: "服务不可用", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } // ============================================ // 新增接口:交易日历和期货合约 // ============================================ func (r *Router) getStockTradingDates(c *gin.Context) { var req TradingDatesRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } resp, err := r.handler.GetTradingDates(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } func (r *Router) getFuturesTradingDates(c *gin.Context) { var req TradingDatesRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } resp, err := r.handler.GetTradingDates(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) } func (r *Router) getFuturesContracts(c *gin.Context) { var req FuturesContractsRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{ Code: 400, Message: "参数错误", Detail: err.Error(), }) return } resp, err := r.handler.GetContractsByUnderlying(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse{ Code: 500, Message: "服务器内部错误", Detail: err.Error(), }) return } c.JSON(http.StatusOK, resp) }