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.

419 lines
9.4 KiB

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)
}