为什么:原 controller handler 在 HTTP 层同时承担了 4 项 usecase 职责 (多源拼装、错误降级策略、limit 硬编码、响应结构组装),违反了 "controller 只做参数解析 + 调用 + 序列化" 的边界。等后续给衍生品加 缓存 / taker volume / 数据质量字段时,HTTP 层会持续变厚。 改动: - 新增 internal/usecase/market_query.go: MarketQueryUsecase.GetDerivatives() 并发拉 current funding/OI + 串行查历史 + 错误聚合为 warnings 返回 - /derivatives handler 退化到 4 行;MarketDeps / restapi.Deps 同步收窄 - /klines 保持直查 KlineRepo(thin query,G11 例外) - 新增 G11 守卫:grep "FundingRepo|OIRepo|LSRepo|TakerRepo" internal/controller 破坏性 API 变更(/derivatives 响应): - 删除 funding.error / openInterest.error - 新增顶层 warnings: []string 顶层 symbol / funding / openInterest / longShortRatio / takerBuySellVolume 保持。 harness 同步: - AGENTS.md §6 加 G11 grep - ai/project-map.md 加 market_query.go + /derivatives 数据流分支 - ai/task-templates.md T6 加"破坏性变体"分支(本次重构暴露了原假设过严) - ai/harness-health.md W1 记一次轻量验证 + 守卫数 10→11 验收: go build / go vet / G6 grep / G11 grep 全绿。
114 lines
2.7 KiB
Go
114 lines
2.7 KiB
Go
package usecase
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
"cryptoHermes/internal/entity"
|
|
)
|
|
|
|
const (
|
|
derivativesFundingHistoryLen = 100
|
|
derivativesOIHistoryLen = 200
|
|
derivativesLongShortLen = 200
|
|
)
|
|
|
|
type MarketQueryUsecase struct {
|
|
derivatives DerivativesProvider
|
|
fundingRepo FundingRepository
|
|
oiRepo OpenInterestRepository
|
|
lsRepo LongShortRatioRepository
|
|
}
|
|
|
|
func NewMarketQueryUsecase(
|
|
derivatives DerivativesProvider,
|
|
fundingRepo FundingRepository,
|
|
oiRepo OpenInterestRepository,
|
|
lsRepo LongShortRatioRepository,
|
|
) *MarketQueryUsecase {
|
|
return &MarketQueryUsecase{
|
|
derivatives: derivatives,
|
|
fundingRepo: fundingRepo,
|
|
oiRepo: oiRepo,
|
|
lsRepo: lsRepo,
|
|
}
|
|
}
|
|
|
|
func (u *MarketQueryUsecase) GetDerivatives(ctx context.Context, symbol, period string) (*entity.DerivativesBundle, []string, error) {
|
|
var (
|
|
currentFund *entity.FundingRate
|
|
currentOI *entity.OpenInterest
|
|
warnMu sync.Mutex
|
|
warnings []string
|
|
)
|
|
addWarn := func(w string) {
|
|
warnMu.Lock()
|
|
warnings = append(warnings, w)
|
|
warnMu.Unlock()
|
|
}
|
|
|
|
g, gctx := errgroup.WithContext(ctx)
|
|
g.Go(func() error {
|
|
f, err := u.derivatives.GetCurrentFunding(gctx, symbol)
|
|
if err != nil {
|
|
addWarn("current funding fetch failed: " + err.Error())
|
|
return nil
|
|
}
|
|
currentFund = f
|
|
return nil
|
|
})
|
|
g.Go(func() error {
|
|
oi, err := u.derivatives.GetCurrentOpenInterest(gctx, symbol)
|
|
if err != nil {
|
|
addWarn("current OI fetch failed: " + err.Error())
|
|
return nil
|
|
}
|
|
currentOI = oi
|
|
return nil
|
|
})
|
|
_ = g.Wait()
|
|
|
|
fundingHist, err := u.fundingRepo.FindRecent(ctx, symbol, derivativesFundingHistoryLen)
|
|
if err != nil {
|
|
addWarn("funding history query failed: " + err.Error())
|
|
}
|
|
|
|
oiHist, err := u.oiRepo.FindRecent(ctx, symbol, period, derivativesOIHistoryLen)
|
|
if err != nil {
|
|
addWarn("OI history query failed: " + err.Error())
|
|
}
|
|
|
|
globalLS, err := u.lsRepo.FindRecent(ctx, symbol, period, entity.RatioTypeGlobalAccount, derivativesLongShortLen)
|
|
if err != nil {
|
|
addWarn("global long/short query failed: " + err.Error())
|
|
}
|
|
|
|
topLS, err := u.lsRepo.FindRecent(ctx, symbol, period, entity.RatioTypeTopTraderPosition, derivativesLongShortLen)
|
|
if err != nil {
|
|
addWarn("top trader position long/short query failed: " + err.Error())
|
|
}
|
|
|
|
bundle := &entity.DerivativesBundle{
|
|
Funding: entity.FundingBundle{
|
|
Current: currentFund,
|
|
History: fundingHist,
|
|
},
|
|
OpenInterest: entity.OpenInterestBundle{
|
|
Current: currentOI,
|
|
History: oiHist,
|
|
},
|
|
LongShortRatio: entity.LongShortBundle{
|
|
Global: globalLS,
|
|
TopTraderPosition: topLS,
|
|
},
|
|
TakerBuySellVolume: []entity.TakerBuySellVolume{},
|
|
}
|
|
|
|
if warnings == nil {
|
|
warnings = []string{}
|
|
}
|
|
return bundle, warnings, nil
|
|
}
|