package external_api import ( "bytes" "crypto/tls" "encoding/json" "errors" "github.com/tuusuario/go-sync-service/internal/domain/dto" "io" "net/http" "time" ) // Estructura para guardar trazabilidad // Client genérico con reintentos type GenericClient struct { Client *http.Client MaxRetries int RetryDelay time.Duration EnableTrace bool // true = guarda trazabilidad } // Constructor func NewGenericClient(maxRetries int, retryDelay time.Duration) *GenericClient { return &GenericClient{ Client: &http.Client{Timeout: 30 * time.Second, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }}, MaxRetries: maxRetries, RetryDelay: retryDelay, } } // Método genérico func (gc *GenericClient) DoRequest(method, url string, headers map[string]string, body []byte) (map[string]interface{}, *dto.RequestTrace, error) { trace := &dto.RequestTrace{ Method: method, URL: url, Headers: headers, RequestBody: string(body), StartedAt: time.Now(), } var lastErr error var resp *http.Response for attempt := 0; attempt <= gc.MaxRetries; attempt++ { trace.Retries = attempt req, err := http.NewRequest(method, url, bytes.NewReader(body)) if err != nil { lastErr = err break } // Headers for k, v := range headers { req.Header.Set(k, v) } start := time.Now() resp, err = gc.Client.Do(req) trace.Duration = time.Since(start) if err == nil && resp.StatusCode < 500 { break // éxito o error de cliente, no reintentar } lastErr = err time.Sleep(gc.RetryDelay * (1 << attempt)) // backoff exponencial } if lastErr != nil { trace.EndedAt = time.Now() return nil, trace, lastErr } defer resp.Body.Close() trace.StatusCode = resp.StatusCode bodyResp, _ := io.ReadAll(resp.Body) trace.Response = string(bodyResp) trace.EndedAt = time.Now() if resp.StatusCode >= 400 { return nil, trace, errors.New("HTTP error: " + resp.Status) } // Si el código de estado es 204 (No Content), no hay cuerpo de respuesta if resp.StatusCode == http.StatusNoContent { return nil, trace, nil } var result map[string]interface{} if err := json.Unmarshal(bodyResp, &result); err != nil { return nil, trace, err } return result, trace, nil }