11 KiB
11 KiB
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
请求头:
{
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
请求体:
{
"model": "image-01",
"prompt": "描述文本",
"aspect_ratio": "16:9",
"subject_reference": [
{
"type": "character",
"image_file": "https://..."
}
],
"n": 2
}
四、后端设计方案
4.1 配置文件新增 (.env)
# 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)
// 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)
// 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)
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 路由组内添加:
// 资产相关路由(需要认证)
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) - 已存在
// 图生图 API - 前端传递必要参数,后端调用 MiniMax
export function imageGenerationApi(params) {
return request({
url: '/api/v1/assets/mints/image/generation',
method: 'POST',
data: params
})
}
5.2 前端调用示例
// 前端只需要传递这些参数,具体的 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 |
已存在,无需修改 |
依赖安装
cd backend/services/assetService && go get github.com/nfnt/resize
七、验证方案
- 后端启动: 启动
backend/gateway/main.go和backend/services/assetService/main.go - 接口测试: 使用 curl 或 Postman 测试接口
curl -X POST http://localhost:8080/api/v1/assets/mints/image/generation \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{"model":"image-01","prompt":"test","aspect_ratio":"16:9","n":1,"subject_reference":[]}' - 前端测试: 在页面中调用
imageGenerationApi()并展示返回的图片
八、已确认事项
- 图片直接返回 MiniMax 的 URL,不经过 OSS
- subject_reference.image_file 由前端自定义传递给后端(用户选择的自定义图片)
- 转发服务位于
assetService中,不在 Gateway