fix: map decrypted CoinGlass liq heatmap payload
Some checks failed
ci / build + vet + guard + race (push) Has been cancelled

This commit is contained in:
dela
2026-05-25 17:12:41 +08:00
parent 1c990cd3bc
commit 7e31e9cbaf
3 changed files with 54 additions and 23 deletions

View File

@@ -11,46 +11,65 @@ import (
"cryptoHermes/internal/entity"
)
// CGEnvelope 是所有 CoinGlass V2 endpoint 的统一外壳。
// 解密后的 plaintext JSON 形如:{"success":true,"code":"0","msg":"","data":{...}}
// CGEnvelope 是 CoinGlass V2 endpoint 的兼容外壳。
// HTTP body 解密前有 success/code/data解密后的 plaintext 通常已经是 data 本体。
// 测试和旧 dump 可能仍喂 envelopemapper 保留兼容。
type CGEnvelope struct {
Success bool `json:"success"`
Code json.Number `json:"code"`
Success *bool `json:"success"`
Code json.RawMessage `json:"code"`
Msg string `json:"msg"`
Data json.RawMessage `json:"data"`
}
// MapLiqHeatMap 把解密后的 plaintext envelope 映射成 entity.CGLiqHeatMap。
// MapLiqHeatMap 把解密后的 plaintext 映射成 entity.CGLiqHeatMap。
//
// PR-1 阶段未拿到真实 schema dump先做最薄映射data 整体进 RawGrid
// 真实响应的 plaintext 是 data 本体(例如 {"instrument":...,"liq":[...]})。
// 为兼容早期 fixture如 plaintext 仍带 success/code/data envelope则先拆 data。
// PR-1 阶段做最薄映射data 整体进 RawGrid
// 若顶层有 minPrice/maxPrice/priceMin/priceMax 字段则抽出(容忍命名漂移)。
// 真实 schema 经 spike dump 后回填到 spec §5mapper 同步加显式字段)。
//
// 数值边界ADR-0004 D3解 JSON number 默认 float64 是 transient
// 通过 formatFloat 转 string 进 entityentity 字段全 stringG12.4 兜底)。
func MapLiqHeatMap(symbol string, interval string, ts int64, plaintext []byte) (*entity.CGLiqHeatMap, error) {
var env CGEnvelope
if err := json.Unmarshal(plaintext, &env); err != nil {
return nil, fmt.Errorf("liq_heatmap envelope: %w", err)
}
if !env.Success {
return nil, fmt.Errorf("liq_heatmap business error code=%s msg=%s", env.Code, env.Msg)
raw, err := liqHeatMapDataPayload(plaintext)
if err != nil {
return nil, err
}
out := &entity.CGLiqHeatMap{
Symbol: symbol,
Interval: interval,
Timestamp: ts,
RawGrid: append(json.RawMessage(nil), env.Data...),
RawGrid: append(json.RawMessage(nil), raw...),
}
var probe map[string]json.RawMessage
if err := json.Unmarshal(env.Data, &probe); err == nil {
if err := json.Unmarshal(raw, &probe); err == nil {
out.PriceMin = pickFloatField(probe, "priceMin", "minPrice", "min")
out.PriceMax = pickFloatField(probe, "priceMax", "maxPrice", "max")
}
return out, nil
}
func liqHeatMapDataPayload(plaintext []byte) (json.RawMessage, error) {
if !json.Valid(plaintext) {
return nil, fmt.Errorf("liq_heatmap plaintext: invalid json")
}
var env CGEnvelope
if err := json.Unmarshal(plaintext, &env); err == nil && env.Success != nil {
if !*env.Success {
return nil, fmt.Errorf("%w: code=%s msg=%q", ErrAPIBusiness, envelopeCode(env.Code), env.Msg)
}
if len(env.Data) == 0 || string(env.Data) == "null" {
return nil, fmt.Errorf("%w: empty data code=%s msg=%q", ErrFakeSuccess, envelopeCode(env.Code), env.Msg)
}
return append(json.RawMessage(nil), env.Data...), nil
}
return append(json.RawMessage(nil), plaintext...), nil
}
// pickFloatField 在 probe map 里按候选 key 顺序找第一个能解成 float64 的字段,
// 用 formatFloat 转回 string。找不到返回 ""。
func pickFloatField(probe map[string]json.RawMessage, keys ...string) string {