package service import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "os" "time" dto "github.com/topfans/backend/gateway/dto" ) // ImageGenerationResponse 图生图响应 type ImageGenerationResponse struct { Images []string `json:"images"` } // MinimaxService MiniMax API 转发服务 type MinimaxService interface { GenerateImage(ctx context.Context, req *dto.ImageGenerationRequest) (*ImageGenerationResponse, error) } type minimaxService struct { client *http.Client } // NewMinimaxService 创建 MiniMax 服务 func NewMinimaxService() MinimaxService { return &minimaxService{ client: &http.Client{Timeout: 320 * time.Second}, } } // GenerateImage 调用 MiniMax 图生图 API(同步调用) func (s *minimaxService) GenerateImage(ctx context.Context, req *dto.ImageGenerationRequest) (*ImageGenerationResponse, error) { apiURL := os.Getenv("MINIMAX_API_URL") apiKey := os.Getenv("MINIMAX_API_KEY") payload := map[string]interface{}{ "model": req.Model, "prompt": req.Prompt, "aspect_ratio": req.AspectRatio, "subject_reference": req.SubjectReference, "n": req.N, "response_format": "base64", } jsonData, err := json.Marshal(payload) if err != nil { return nil, fmt.Errorf("failed to marshal request: %w", err) } httpReq, err := http.NewRequestWithContext(ctx, "POST", apiURL, bytes.NewBuffer(jsonData)) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } httpReq.Header.Set("Authorization", "Bearer "+apiKey) httpReq.Header.Set("Content-Type", "application/json") resp, err := s.client.Do(httpReq) if err != nil { return nil, fmt.Errorf("failed to call MiniMax API: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("MiniMax API returned status %d: %s", resp.StatusCode, string(body)) } var rawResp map[string]interface{} if err := json.Unmarshal(body, &rawResp); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } var images []string // MiniMax API 响应格式: // {"id": "...", "data": {"image_base64": ["..."]}, "base_resp": {"status_code": 0, "status_msg": "success"}} data, ok := rawResp["data"].(map[string]interface{}) if !ok { return nil, fmt.Errorf("invalid MiniMax response format: missing data field: %s", string(body)) } // 检查 base_resp 状态码 if baseResp, ok := rawResp["base_resp"].(map[string]interface{}); ok { if statusCode, ok := baseResp["status_code"].(float64); ok && statusCode != 0 { statusMsg, _ := baseResp["status_msg"].(string) return nil, fmt.Errorf("MiniMax API error: code=%d, msg=%s", int(statusCode), statusMsg) } } // 优先使用 base64 格式(当 response_format=base64 时) if base64Imgs, ok := data["image_base64"].([]interface{}); ok { for _, img := range base64Imgs { if base64Str, ok := img.(string); ok && base64Str != "" { images = append(images, "data:image/jpeg;base64,"+base64Str) } } } // 回退使用 image_urls 格式 if len(images) == 0 { if urlImgs, ok := data["image_urls"].([]interface{}); ok { for _, img := range urlImgs { if urlStr, ok := img.(string); ok && urlStr != "" { images = append(images, urlStr) } } } } if len(images) == 0 { return nil, fmt.Errorf("no images found in MiniMax response: %s", string(body)) } return &ImageGenerationResponse{Images: images}, nil }