Files
2026-06-30 21:44:14 +08:00

180 lines
3.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cloud
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
type APIError struct {
StatusCode int
Payload map[string]any
Body string
}
func (e *APIError) Error() string {
if e == nil {
return ""
}
message := strings.TrimSpace(extractErrorMessage(e.Payload))
if message == "" {
message = strings.TrimSpace(e.Body)
}
if message == "" {
message = http.StatusText(e.StatusCode)
}
return fmt.Sprintf("HTTP %d: %s", e.StatusCode, message)
}
func (c *Client) do(req *http.Request) (map[string]any, error) {
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
payloadBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode >= http.StatusBadRequest {
return nil, newAPIError(resp.StatusCode, payloadBytes)
}
if len(payloadBytes) == 0 {
return map[string]any{}, nil
}
var result map[string]any
if err := json.Unmarshal(payloadBytes, &result); err != nil {
return nil, err
}
return result, nil
}
func newAPIError(statusCode int, payloadBytes []byte) error {
apiErr := &APIError{
StatusCode: statusCode,
Body: string(payloadBytes),
}
if len(payloadBytes) > 0 {
var payload map[string]any
if err := json.Unmarshal(payloadBytes, &payload); err == nil {
apiErr.Payload = payload
}
}
return apiErr
}
func errorResponse(err error, taskID string, requestID string) map[string]any {
output := map[string]any{
"success": false,
}
if apiErr, ok := err.(*APIError); ok {
if apiErr.Payload != nil {
output["error"] = apiErr.Payload
} else {
output["error"] = strings.TrimSpace(apiErr.Body)
}
if requestID == "" {
requestID = extractRequestID(apiErr.Payload)
}
if taskID == "" {
taskID = extractTaskID(apiErr.Payload)
}
} else if err != nil {
output["error"] = err.Error()
}
if output["error"] == nil || output["error"] == "" {
output["error"] = "unknown error"
}
if taskID != "" {
output["task_id"] = taskID
}
if requestID != "" {
output["request_id"] = requestID
}
return output
}
// isBusinessFailure 检测业务级失败:HTTP 2xx 但响应体中 success=false。
func isBusinessFailure(payload map[string]any) bool {
if len(payload) == 0 {
return false
}
value, ok := payload["success"]
if !ok {
return false
}
if success, ok := value.(bool); ok {
return !success
}
return false
}
// businessFailureResponse 把业务级失败的响应体直接透传输出。
func businessFailureResponse(payload map[string]any) map[string]any {
output := map[string]any{
"success": false,
}
if errField, ok := payload["error"]; ok && errField != nil {
output["error"] = errField
} else {
output["error"] = "unknown error"
}
if taskID := extractTaskID(payload); taskID != "" {
output["task_id"] = taskID
}
if requestID := extractRequestID(payload); requestID != "" {
output["request_id"] = requestID
}
return output
}
func extractErrorMessage(payload map[string]any) string {
if len(payload) == 0 {
return ""
}
if nested, ok := payload["error"].(map[string]any); ok {
for _, key := range []string{"message", "error", "msg"} {
if value := strings.TrimSpace(fmt.Sprint(nested[key])); value != "" && value != "<nil>" {
return value
}
}
}
for _, key := range []string{"message", "msg"} {
if value := strings.TrimSpace(fmt.Sprint(payload[key])); value != "" && value != "<nil>" {
return value
}
}
if value, ok := payload["error"].(string); ok {
value = strings.TrimSpace(value)
if value != "" {
return value
}
}
return ""
}
func extractTaskID(payload map[string]any) string {
return extractStringField(payload, "task_id")
}
func extractRequestID(payload map[string]any) string {
return extractStringField(payload, "request_id")
}
func extractStringField(payload map[string]any, key string) string {
if len(payload) == 0 {
return ""
}
value := strings.TrimSpace(fmt.Sprint(payload[key]))
if value == "" || value == "<nil>" {
return ""
}
return value
}