package service import ( "encoding/json" "strconv" "strings" "time" "affiliate_dash/internal/pkg/timeutil" ) // buildShipNotifyResultData 把推送结果与游戏字段合并进 ResultData JSON。 func buildShipNotifyResultData(existing string, in ShipNotifyInput, shippedAt *time.Time) string { m := map[string]interface{}{} if existing != "" && json.Valid([]byte(existing)) { _ = json.Unmarshal([]byte(existing), &m) } m["ship_status"] = in.ShipStatus if in.ProviderOrderNo != "" { m["provider_order_no"] = in.ProviderOrderNo } if in.ShipStatus == "success" { delete(m, "fail_reason") } else if in.FailReason != "" { m["fail_reason"] = in.FailReason } if shippedAt != nil { m["shipped_at"] = timeutil.FormatAPITime(*shippedAt) } if in.GameChannel != nil { m["game_channel"] = *in.GameChannel } if in.GameUID != nil { m["game_uid"] = *in.GameUID } if in.RoleName != nil { m["role_name"] = *in.RoleName } if in.PayScore != nil { m["pay_score"] = *in.PayScore } raw, err := json.Marshal(m) if err != nil { return existing } return string(raw) } // mergeResultData 保留已有 JSON 字段,仅覆盖或新增 patch 中的字段,避免各发货阶段互相清空上下文。 func mergeResultData(existing string, patch map[string]interface{}) string { if len(patch) == 0 { return existing } m := map[string]interface{}{} if existing != "" && json.Valid([]byte(existing)) { _ = json.Unmarshal([]byte(existing), &m) } for k, v := range patch { m[k] = v } raw, err := json.Marshal(m) if err != nil { return existing } return string(raw) } func resultDataMap(raw string) map[string]interface{} { m := map[string]interface{}{} if raw != "" && json.Valid([]byte(raw)) { _ = json.Unmarshal([]byte(raw), &m) } return m } // requestDataMap 解析下单透传的 data(RequestData),非法或为空时返回空 map。 func requestDataMap(raw string) map[string]interface{} { m := map[string]interface{}{} if raw != "" && json.Valid([]byte(raw)) { _ = json.Unmarshal([]byte(raw), &m) } return m } // requestDataString 读取下单透传 data 中的字符串字段。 func requestDataString(raw, key string) string { if v, ok := requestDataMap(raw)[key].(string); ok { return strings.TrimSpace(v) } return "" } func resultDataString(raw, key string) string { if v, ok := resultDataMap(raw)[key].(string); ok { return v } return "" } func resultDataNumber(raw, key string) int64 { switch v := resultDataMap(raw)[key].(type) { case float64: return int64(v) case int64: return v case int: return int64(v) case string: if n, err := strconv.ParseInt(v, 10, 64); err == nil { return n } } return 0 } // extractGameFields 从 JSON 文本中还原游戏相关字段(仅填充当前为空的字段)。 func extractGameFields(raw string, out *OpenOrderQuery) { if raw == "" || !json.Valid([]byte(raw)) { return } var m map[string]interface{} if err := json.Unmarshal([]byte(raw), &m); err != nil { return } if out.GameChannel == "" { if v, ok := m["game_channel"].(string); ok { out.GameChannel = v } } if out.GameUID == "" { if v, ok := m["game_uid"].(string); ok { out.GameUID = v } } if out.RoleName == "" { if v, ok := m["role_name"].(string); ok { out.RoleName = v } } if out.PayScore == 0 { if v, ok := toInt(m["pay_score"]); ok { out.PayScore = v } } } func toInt(v interface{}) (int, bool) { switch n := v.(type) { case float64: return int(n), true case int: return n, true case int64: return int(n), true } return 0, false }