# MiniMax 图生图 API 集成方案 ## 一、需求概述 前端传递参数 → 后端调用 MiniMax 图生图 API → 后端返回结果给前端 ## 二、现有项目架构 | 层级 | 技术栈 | 说明 | |------|--------|------| | 前端 | uni-app (Vue 3) | 使用 `uni.request` 发起请求 | | 后端 | Go + Gin | API Gateway 模式,端口 8080 | | 配置 | `.env` 文件 | API keys 等敏感配置 | ## 三、MiniMax API 信息 **接口地址**: `POST https://api.minimaxi.com/v1/image_generation` **请求头**: ```json { "Authorization": "Bearer ", "Content-Type": "application/json" } ``` **请求体**: ```json { "model": "image-01", "prompt": "描述文本", "aspect_ratio": "16:9", "subject_reference": [ { "type": "character", "image_file": "https://..." } ], "n": 2 } ``` ## 四、后端设计方案 ### 4.1 配置文件新增 (.env) ```bash # MiniMax API 配置 MINIMAX_API_KEY=your_api_key_here MINIMAX_API_URL=https://api.minimaxi.com/v1/image_generation ``` ### 4.2 新增 DTO (gateway/dto/image_dto.go) ```go // MiniMax 图生图请求 type ImageGenerationRequest struct { Model string `json:"model" binding:"required"` Prompt string `json:"prompt" binding:"required"` AspectRatio string `json:"aspect_ratio"` SubjectReference []SubjectReference `json:"subject_reference"` N int `json:"n"` } type SubjectReference struct { Type string `json:"type"` ImageFile string `json:"image_file"` } // MiniMax 图生图响应 type ImageGenerationResponse struct { Model string `json:"model"` Prompt string `json:"prompt"` AspectRatio string `json:"aspect_ratio"` Images []Image `json:"images"` } type Image struct { URL string `json:"url"` } ``` ### 4.3 新增 Controller (gateway/controller/image_controller.go) ```go // ImageGeneration 图生图 - 调用 MiniMax API // @Summary 图生图 // @Description 前端传递参数,后端调用 MiniMax 图生图 API // @Tags assets // @Accept json // @Produce json // @Security BearerAuth // @Param request body dto.ImageGenerationRequest true "图生图请求" // @Success 200 {object} response.Response // @Router /api/v1/assets/mints/image/generation [post] func (ctrl *AssetController) ImageGeneration(c *gin.Context) { var req dto.ImageGenerationRequest if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "Invalid request parameters: "+err.Error()) return } // 从 context 获取 userID 和 starID(由认证中间件设置) userID := c.GetInt64("userID") starID := c.GetInt64("starID") // 调用资产服务的 MiniMax 转发服务 result, err := ctrl.assetService.CallMiniMaxImageAPI(c.Request.Context(), userID, starID, &req) if err != nil { response.Error(c, 500, "Image generation failed: "+err.Error()) return } response.Success(c, result) } ``` ### 4.4 新增 Service (assetService/service/minimax_service.go) ```go package service import ( "bytes" "context" "encoding/base64" "encoding/json" "fmt" "image" "image/gif" "image/jpeg" "image/png" "net/http" "time" "github.com/nfnt/resize" "github.com/topfans/backend/services/assetService/config" dto "github.com/topfans/backend/gateway/dto" "go.uber.org/zap" ) // MinimaxService MiniMax API 转发服务接口 type MinimaxService interface { // CallMiniMaxImageAPI 调用 MiniMax 图生图 API CallMiniMaxImageAPI(ctx context.Context, userID, starID int64, req *dto.ImageGenerationRequest) (*dto.ImageGenerationResponse, error) } // minimaxService MiniMax API 转发服务实现 type minimaxService struct { config *config.AssetConfig } // NewMinimaxService 创建 MiniMax 服务实例 func NewMinimaxService(cfg *config.AssetConfig) MinimaxService { return &minimaxService{config: cfg} } // CallMiniMaxImageAPI 调用 MiniMax 图生图 API func (s *minimaxService) CallMiniMaxImageAPI(ctx context.Context, userID, starID int64, req *dto.ImageGenerationRequest) (*dto.ImageGenerationResponse, error) { // 1. 压缩 subject_reference 中的图片 processedRefs := make([]dto.SubjectReference, len(req.SubjectReference)) for i, ref := range req.SubjectReference { compressedURL, err := s.compressImageIfNeeded(ref.ImageFile) if err != nil { // 压缩失败时使用原图 compressedURL = ref.ImageFile zap.S().Warnf("Image compression failed, using original: %v", err) } processedRefs[i] = dto.SubjectReference{ Type: ref.Type, ImageFile: compressedURL, } } // 2. 构建请求体 payload := map[string]interface{}{ "model": req.Model, "prompt": req.Prompt, "aspect_ratio": req.AspectRatio, "subject_reference": processedRefs, "n": req.N, } // 3. 发送 HTTP POST 请求到 MiniMax apiURL := s.config.GetMiniMaxAPIURL() apiKey := s.config.GetMiniMaxAPIKey() client := &http.Client{Timeout: 120 * time.Second} jsonData, _ := json.Marshal(payload) 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 := client.Do(httpReq) if err != nil { return nil, fmt.Errorf("failed to call MiniMax API: %w", err) } defer resp.Body.Close() // 4. 解析响应 var result dto.ImageGenerationResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } return &result, nil } // compressImageIfNeeded 下载图片、压缩后返回 base64 编码 func (s *minimaxService) compressImageIfNeeded(imageURL string) (string, error) { // 下载图片 resp, err := http.Get(imageURL) if err != nil { return "", err } defer resp.Body.Close() // 读取图片数据 imgData, err := io.ReadAll(resp.Body) if err != nil { return "", err } // 解码图片(自动识别格式) img, format, err := image.Decode(bytes.NewReader(imgData)) if err != nil { return "", err } // 计算压缩后的尺寸(最大边 1024px,保持宽高比) bounds := img.Bounds() maxDim := uint(1024) newWidth := uint(bounds.Dx()) newHeight := uint(bounds.Dy()) if newWidth > maxDim || newHeight > maxDim { if newWidth > newHeight { ratio := float64(maxDim) / float64(newWidth) newWidth = maxDim newHeight = uint(float64(newHeight) * ratio) } else { ratio := float64(maxDim) / float64(newHeight) newHeight = maxDim newWidth = uint(float64(newWidth) * ratio) } } // 如果图片尺寸没变,不压缩直接返回原图 if newWidth == uint(bounds.Dx()) && newHeight == uint(bounds.Dy()) { return "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(imgData), nil } // 缩放图片(Lanczos 算法,质量好) resized := resize.Thumbnail(newWidth, newHeight, img, resize.Lanczos) // 重新编码为 JPEG(质量 85%,体积小) var buf bytes.Buffer switch format { case "png": err = png.Encode(&buf, resized) case "gif": err = gif.Encode(&buf, resized, nil) default: err = jpeg.Encode(&buf, resized, &jpeg.Options{Quality: 85}) } if err != nil { return "", err } // 返回 base64 编码(data URI 格式) encoded := base64.StdEncoding.EncodeToString(buf.Bytes()) mimeType := "image/jpeg" if format == "png" { mimeType = "image/png" } else if format == "gif" { mimeType = "image/gif" } return "data:" + mimeType + ";base64," + encoded, nil } ``` ### 4.5 路由注册 (gateway/router/router.go) 在 assets 路由组内添加: ```go // 资产相关路由(需要认证) assets := v1.Group("/assets") assets.Use(middleware.AuthMiddleware()) { // ... 现有路由 ... assets.POST("/mints/image/generation", assetCtrl.ImageGeneration) // 图生图(MiniMax API) } ``` ## 五、前端调用方案 **架构说明**:前端只与后端 API 通信,不直接调用 MiniMax。后端作为代理调用 MiniMax。 ### 5.1 前端 API (frontend/utils/api.js) - 已存在 ```javascript // 图生图 API - 前端传递必要参数,后端调用 MiniMax export function imageGenerationApi(params) { return request({ url: '/api/v1/assets/mints/image/generation', method: 'POST', data: params }) } ``` ### 5.2 前端调用示例 ```javascript // 前端只需要传递这些参数,具体的 MiniMax API 调用由后端完成 imageGenerationApi({ prompt: '描述文字', aspect_ratio: '16:9', subject_reference: [ { type: 'character', image_file: '用户选择的图片URL' } ], n: 2 }).then(res => { // res.data.images 是 MiniMax 返回的图片 URL 列表 console.log('生成的图片:', res.data.images) }) ``` ### 5.3 数据流向 ``` 前端参数 {prompt, aspect_ratio, subject_reference, n} ↓ POST /api/v1/assets/mints/image/generation 后端接收参数 → 调用 assetService 的 MiniMax 转发服务 → 调用 MiniMax API ↓ 后端获取响应 → 直接透传 images 数组 → 返回给前端 ↓ 前端收到 {code: 200, data: {images: [...]}} ``` ## 六、文件修改清单 | 操作 | 文件路径 | 说明 | |------|----------|------| | 修改 | `backend/.env` | 添加 MiniMax 配置 | | 新增 | `backend/gateway/dto/image_dto.go` | 请求/响应 DTO | | 新增 | `backend/services/assetService/service/minimax_service.go` | MiniMax API 转发 + 图片压缩 | | 新增 | `backend/services/assetService/config/minimax_config.go` | MiniMax 配置读取 | | 修改 | `backend/gateway/controller/asset_controller.go` | 新增 ImageGeneration 处理器 | | 修改 | `backend/gateway/router/router.go` | 注册 `/mints/image/generation` 路由 | | 修改 | `frontend/utils/api.js` | 已存在,无需修改 | ### 依赖安装 ```bash cd backend/services/assetService && go get github.com/nfnt/resize ``` ## 七、验证方案 1. **后端启动**: 启动 `backend/gateway/main.go` 和 `backend/services/assetService/main.go` 2. **接口测试**: 使用 curl 或 Postman 测试接口 ```bash curl -X POST http://localhost:8080/api/v1/assets/mints/image/generation \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"image-01","prompt":"test","aspect_ratio":"16:9","n":1,"subject_reference":[]}' ``` 3. **前端测试**: 在页面中调用 `imageGenerationApi()` 并展示返回的图片 ## 八、已确认事项 - 图片直接返回 MiniMax 的 URL,不经过 OSS - subject_reference.image_file 由前端自定义传递给后端(用户选择的自定义图片) - 转发服务位于 `assetService` 中,不在 Gateway