GetDerivatives 签名里的 error 返回永远是 nil,controller 那段 if err != nil 是死分支,会误导调用方以为存在"全失败"语义。当前产品策略是 best-effort response:局部失败 → warnings,bundle 永远非 nil,HTTP 永远 200。 等 Hermes 上游真要求"全失败 → 5xx"时再重新引入 error 返回。 同时把 docs/dev.md §13.5 示例补上 warnings 字段(上次重构遗漏的文档同步)。
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) {
|
|
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
|
|
}
|