|
|
// Package handler HTTP Handler实现
|
|
|
package handler
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
"fmt"
|
|
|
|
|
|
"market-data-service/api"
|
|
|
"market-data-service/internal/service"
|
|
|
)
|
|
|
|
|
|
// Handler HTTP处理器实现
|
|
|
type Handler struct {
|
|
|
stockService service.StockService
|
|
|
futuresService service.FuturesService
|
|
|
adminService service.AdminService
|
|
|
}
|
|
|
|
|
|
// NewHandler 创建Handler
|
|
|
func NewHandler(
|
|
|
stockService service.StockService,
|
|
|
futuresService service.FuturesService,
|
|
|
adminService service.AdminService,
|
|
|
) *Handler {
|
|
|
return &Handler{
|
|
|
stockService: stockService,
|
|
|
futuresService: futuresService,
|
|
|
adminService: adminService,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 确保Handler实现了api.Handler接口
|
|
|
var _ api.Handler = (*Handler)(nil)
|
|
|
|
|
|
// ============================================
|
|
|
// 股票接口实现
|
|
|
// ============================================
|
|
|
|
|
|
// QueryKLines 查询股票K线
|
|
|
func (h *Handler) QueryKLines(ctx context.Context, req *api.KLineQueryRequest) (*api.Response, error) {
|
|
|
// 参数校验
|
|
|
if err := validateKLineRequest(req); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
// 调用Service层
|
|
|
data, err := h.stockService.QueryKLines(ctx, req)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "success",
|
|
|
Data: data,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// ListSymbols 查询股票标的列表
|
|
|
func (h *Handler) ListSymbols(ctx context.Context, req *api.SymbolListRequest) (*api.Response, error) {
|
|
|
// 设置默认值
|
|
|
if req.Page <= 0 {
|
|
|
req.Page = 1
|
|
|
}
|
|
|
if req.Size <= 0 {
|
|
|
req.Size = 20
|
|
|
}
|
|
|
if req.Size > 100 {
|
|
|
req.Size = 100
|
|
|
}
|
|
|
|
|
|
data, err := h.stockService.ListSymbols(ctx, req)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "success",
|
|
|
Data: data,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// BatchQueryKLines 批量查询股票K线
|
|
|
func (h *Handler) BatchQueryKLines(ctx context.Context, req *api.BatchKLineRequest) (*api.Response, error) {
|
|
|
if err := validateBatchKLineRequest(req); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
data, err := h.stockService.BatchQueryKLines(ctx, req)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "success",
|
|
|
Data: data,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// ============================================
|
|
|
// 期货接口实现
|
|
|
// ============================================
|
|
|
|
|
|
// QueryKLines 查询期货K线(期货Handler)
|
|
|
func (h *Handler) QueryKLines(ctx context.Context, req *api.KLineQueryRequest) (*api.Response, error) {
|
|
|
if err := validateKLineRequest(req); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
data, err := h.futuresService.QueryKLines(ctx, req)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "success",
|
|
|
Data: data,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// ListSymbols 查询期货标的列表
|
|
|
func (h *Handler) ListSymbols(ctx context.Context, req *api.SymbolListRequest) (*api.Response, error) {
|
|
|
if req.Page <= 0 {
|
|
|
req.Page = 1
|
|
|
}
|
|
|
if req.Size <= 0 {
|
|
|
req.Size = 20
|
|
|
}
|
|
|
if req.Size > 100 {
|
|
|
req.Size = 100
|
|
|
}
|
|
|
|
|
|
data, err := h.futuresService.ListSymbols(ctx, req)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "success",
|
|
|
Data: data,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// BatchQueryKLines 批量查询期货K线
|
|
|
func (h *Handler) BatchQueryKLines(ctx context.Context, req *api.BatchKLineRequest) (*api.Response, error) {
|
|
|
if err := validateBatchKLineRequest(req); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
data, err := h.futuresService.BatchQueryKLines(ctx, req)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "success",
|
|
|
Data: data,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// QueryContinuousKLines 查询主力连续合约K线(预留)
|
|
|
func (h *Handler) QueryContinuousKLines(ctx context.Context, underlying string, req *api.KLineQueryRequest) (*api.Response, error) {
|
|
|
// TODO: 首期返回空数据或错误提示
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "success",
|
|
|
Data: &api.KLineData{
|
|
|
Symbol: underlying + ".MAIN",
|
|
|
Name: underlying + "主力连续",
|
|
|
Freq: req.Freq,
|
|
|
Count: 0,
|
|
|
Items: []api.KLineItem{},
|
|
|
},
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// ============================================
|
|
|
// 管理接口实现
|
|
|
// ============================================
|
|
|
|
|
|
// GetDataSourceStatus 获取数据源状态
|
|
|
func (h *Handler) GetDataSourceStatus(ctx context.Context) (*api.Response, error) {
|
|
|
data, err := h.adminService.GetDataSourceStatus(ctx)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "success",
|
|
|
Data: data,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// SwitchDataSource 切换数据源
|
|
|
func (h *Handler) SwitchDataSource(ctx context.Context, req *api.SourceSwitchRequest) (*api.Response, error) {
|
|
|
if err := h.adminService.SwitchDataSource(ctx, req); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "数据源切换成功",
|
|
|
Data: nil,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// BackfillData 历史数据补录
|
|
|
func (h *Handler) BackfillData(ctx context.Context, req *api.BackfillRequest) (*api.Response, error) {
|
|
|
// 异步启动补录任务
|
|
|
taskID, err := h.adminService.BackfillData(ctx, req)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "补录任务已启动",
|
|
|
Data: map[string]string{
|
|
|
"task_id": taskID,
|
|
|
},
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// HealthCheck 健康检查
|
|
|
func (h *Handler) HealthCheck(ctx context.Context) (*api.HealthResponse, error) {
|
|
|
// TODO: 检查数据库连接、数据源连通性等
|
|
|
return h.adminService.HealthCheck(ctx)
|
|
|
}
|
|
|
|
|
|
// ============================================
|
|
|
// 新增接口:交易日历和期货合约
|
|
|
// ============================================
|
|
|
|
|
|
// GetTradingDates 获取股票交易日历
|
|
|
func (h *Handler) GetTradingDates(ctx context.Context, req *api.TradingDatesRequest) (*api.Response, error) {
|
|
|
if err := validateTradingDatesRequest(req); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
// 通过请求路径判断是股票还是期货,这里简化处理
|
|
|
// 实际可以通过gin的context或其他方式判断
|
|
|
data, err := h.stockService.GetTradingDates(ctx, req)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "success",
|
|
|
Data: data,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// GetContractsByUnderlying 根据品种获取可交易合约
|
|
|
func (h *Handler) GetContractsByUnderlying(ctx context.Context, req *api.FuturesContractsRequest) (*api.Response, error) {
|
|
|
if req.Underlying == "" {
|
|
|
return nil, fmt.Errorf("underlying不能为空")
|
|
|
}
|
|
|
|
|
|
data, err := h.futuresService.GetContractsByUnderlying(ctx, req)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
return &api.Response{
|
|
|
Code: 0,
|
|
|
Message: "success",
|
|
|
Data: data,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// ============================================
|
|
|
// 私有辅助函数
|
|
|
// ============================================
|
|
|
|
|
|
func validateKLineRequest(req *api.KLineQueryRequest) error {
|
|
|
if req.Symbol == "" {
|
|
|
return fmt.Errorf("symbol不能为空")
|
|
|
}
|
|
|
if req.Start == "" || len(req.Start) != 8 {
|
|
|
return fmt.Errorf("start日期格式错误,应为YYYYMMDD")
|
|
|
}
|
|
|
if req.End == "" || len(req.End) != 8 {
|
|
|
return fmt.Errorf("end日期格式错误,应为YYYYMMDD")
|
|
|
}
|
|
|
if req.Freq == "" {
|
|
|
req.Freq = api.Freq1D
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func validateBatchKLineRequest(req *api.BatchKLineRequest) error {
|
|
|
if len(req.Symbols) == 0 {
|
|
|
return fmt.Errorf("symbols不能为空")
|
|
|
}
|
|
|
if len(req.Symbols) > 100 {
|
|
|
return fmt.Errorf("单次查询标的数不能超过100")
|
|
|
}
|
|
|
if req.Start == "" || len(req.Start) != 8 {
|
|
|
return fmt.Errorf("start日期格式错误,应为YYYYMMDD")
|
|
|
}
|
|
|
if req.End == "" || len(req.End) != 8 {
|
|
|
return fmt.Errorf("end日期格式错误,应为YYYYMMDD")
|
|
|
}
|
|
|
if req.Freq == "" {
|
|
|
req.Freq = api.Freq1D
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func validateTradingDatesRequest(req *api.TradingDatesRequest) error {
|
|
|
if req.Start == "" || len(req.Start) != 8 {
|
|
|
return fmt.Errorf("start日期格式错误,应为YYYYMMDD")
|
|
|
}
|
|
|
if req.End == "" || len(req.End) != 8 {
|
|
|
return fmt.Errorf("end日期格式错误,应为YYYYMMDD")
|
|
|
}
|
|
|
return nil
|
|
|
}
|