# 速创生图接口调用示例

## 一、数据库配置说明

### 1. 执行SQL脚本
```bash
# 在MySQL中执行V9脚本
mysql -u root -p 1818ai < V9__add_suchuang_image_models.sql
```

### 2. 配置结果
执行后会在 `points_config` 表中插入以下模型：

#### 文生图模型（4个）
| model_name | description | points_cost | task_type | aspectRatio |
|------------|-------------|-------------|-----------|-------------|
| sc_soraimg_text_auto | 速创生图 文生图-自动比例 | 30 | text_to_image | auto |
| sc_soraimg_text_1x1 | 速创生图 文生图-正方形(1:1) | 30 | text_to_image | 1:1 |
| sc_soraimg_text_2x3 | 速创生图 文生图-竖图(2:3) | 30 | text_to_image | 2:3 |
| sc_soraimg_text_3x2 | 速创生图 文生图-横图(3:2) | 30 | text_to_image | 3:2 |

#### 图生图模型（4个）
| model_name | description | points_cost | task_type | aspectRatio |
|------------|-------------|-------------|-----------|-------------|
| sc_soraimg_img2img_auto | 速创生图 图生图-自动比例 | 35 | image_to_image | auto |
| sc_soraimg_img2img_1x1 | 速创生图 图生图-正方形(1:1) | 35 | image_to_image | 1:1 |
| sc_soraimg_img2img_2x3 | 速创生图 图生图-竖图(2:3) | 35 | image_to_image | 2:3 |
| sc_soraimg_img2img_3x2 | 速创生图 图生图-横图(3:2) | 35 | image_to_image | 3:2 |

---

## 二、接口调用示例

### 前提条件
- 用户已登录，获得 JWT Token
- 或使用 API Key 认证
- 用户积分充足

### 基础URL
```
http://localhost:8081
```

---

## 三、文生图调用示例

### 1. cURL 示例

#### 1.1 正方形图片（1:1）
```bash
curl -X POST "http://localhost:8081/user/ai/tasks/submit" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{
    "modelName": "sc_soraimg_text_1x1",
    "prompt": "一个可爱的橘猫在窗台上晒太阳，温馨的室内场景，柔和的光线"
  }'
```

#### 1.2 横图（3:2）
```bash
curl -X POST "http://localhost:8081/user/ai/tasks/submit" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{
    "modelName": "sc_soraimg_text_3x2",
    "prompt": "壮丽的山脉日出，金色阳光洒满山谷，风景摄影风格"
  }'
```

#### 1.3 竖图（2:3）
```bash
curl -X POST "http://localhost:8081/user/ai/tasks/submit" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{
    "modelName": "sc_soraimg_text_2x3",
    "prompt": "高耸的瀑布从山崖飞流而下，竖构图，气势磅礴"
  }'
```

### 2. JavaScript Fetch 示例

```javascript
// 文生图 - 正方形
const response = await fetch("http://localhost:8081/user/ai/tasks/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${YOUR_JWT_TOKEN}`
  },
  body: JSON.stringify({
    modelName: "sc_soraimg_text_1x1",
    prompt: "科幻风格的未来城市，霓虹灯光，赛博朋克风格"
  })
});

const result = await response.json();
console.log("任务编号:", result.data.taskNo);
console.log("任务状态:", result.data.status);
```

### 3. Python Requests 示例

```python
import requests

url = "http://localhost:8081/user/ai/tasks/submit"
headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {YOUR_JWT_TOKEN}"
}
data = {
    "modelName": "sc_soraimg_text_1x1",
    "prompt": "梦幻的樱花树下，粉色花瓣飘落，动漫风格"
}

response = requests.post(url, json=data, headers=headers)
result = response.json()
print(f"任务编号: {result['data']['taskNo']}")
print(f"任务状态: {result['data']['status']}")
```

---

## 四、图生图调用示例

### 1. cURL 示例

#### 1.1 单张参考图
```bash
curl -X POST "http://localhost:8081/user/ai/tasks/submit" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{
    "modelName": "sc_soraimg_img2img_1x1",
    "prompt": "把这张照片转换成可爱的卡通Q版潮玩形象，保留原有特征，增加萌趣感",
    "imageUrl": "https://oss-1818ai-user-img.oss-cn-hangzhou.aliyuncs.com/user123/photo.jpg"
  }'
```

#### 1.2 多张参考图（JSON数组）
```bash
curl -X POST "http://localhost:8081/user/ai/tasks/submit" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{
    "modelName": "sc_soraimg_img2img_3x2",
    "prompt": "结合这些参考图的风格，创作一幅新的艺术作品",
    "imageUrl": "[\"https://example.com/ref1.jpg\",\"https://example.com/ref2.jpg\"]"
  }'
```

### 2. JavaScript Fetch 示例

```javascript
// 图生图 - 单张参考图
const response = await fetch("http://localhost:8081/user/ai/tasks/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${YOUR_JWT_TOKEN}`
  },
  body: JSON.stringify({
    modelName: "sc_soraimg_img2img_1x1",
    prompt: "将人物转换为油画风格，色彩浓郁，笔触明显",
    imageUrl: "https://oss-1818ai-user-img.oss-cn-hangzhou.aliyuncs.com/portrait.jpg"
  })
});

const result = await response.json();
console.log("任务编号:", result.data.taskNo);
```

```javascript
// 图生图 - 多张参考图
const response = await fetch("http://localhost:8081/user/ai/tasks/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${YOUR_JWT_TOKEN}`
  },
  body: JSON.stringify({
    modelName: "sc_soraimg_img2img_1x1",
    prompt: "融合多张参考图的元素，创作新设计",
    imageUrl: JSON.stringify([
      "https://example.com/ref1.jpg",
      "https://example.com/ref2.jpg",
      "https://example.com/ref3.jpg"
    ])
  })
});
```

### 3. Python Requests 示例

```python
import requests
import json

# 图生图 - 单张参考图
url = "http://localhost:8081/user/ai/tasks/submit"
headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {YOUR_JWT_TOKEN}"
}
data = {
    "modelName": "sc_soraimg_img2img_1x1",
    "prompt": "将这张风景照转换为水彩画风格",
    "imageUrl": "https://oss-1818ai-user-img.oss-cn-hangzhou.aliyuncs.com/landscape.jpg"
}

response = requests.post(url, json=data, headers=headers)
result = response.json()

# 图生图 - 多张参考图
data_multi = {
    "modelName": "sc_soraimg_img2img_3x2",
    "prompt": "结合参考图风格创作新作品",
    "imageUrl": json.dumps([
        "https://example.com/ref1.jpg",
        "https://example.com/ref2.jpg"
    ])
}

response = requests.post(url, json=data_multi, headers=headers)
```

---

## 五、查询任务状态

### 1. 查询单个任务详情
```bash
curl -X GET "http://localhost:8081/user/ai/tasks/TASK-20251026153045123-4567" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

**响应示例：**
```json
{
  "code": 200,
  "message": "success",
  "data": {
    "taskNo": "TASK-20251026153045123-4567",
    "modelName": "sc_soraimg_text_1x1",
    "status": "completed",
    "taskType": "text_to_image",
    "progress": 100,
    "promptSnippet": "一个可爱的橘猫在窗台上晒太阳...",
    "imageUrl": null,
    "resultUrl": "https://oss-1818ai-user-img.oss-cn-hangzhou.aliyuncs.com/results/image_xxx.png",
    "createTime": "2025-10-26T15:30:45",
    "completeTime": "2025-10-26T15:31:12",
    "errorMessage": null
  }
}
```

### 2. 查询任务列表（按类型筛选）

#### 查询所有文生图任务
```bash
curl -X GET "http://localhost:8081/user/ai/tasks/list?page=1&size=10&taskType=text_to_image" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

#### 查询所有图生图任务
```bash
curl -X GET "http://localhost:8081/user/ai/tasks/list?page=1&size=10&taskType=image_to_image" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

#### 查询所有生图任务（不区分类型）
```bash
curl -X GET "http://localhost:8081/user/ai/tasks/list?page=1&size=10&status=completed" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

### 3. JavaScript 轮询示例

```javascript
// 提交任务后轮询状态
async function submitAndPoll(modelName, prompt, imageUrl = null) {
  // 1. 提交任务
  const submitResponse = await fetch("http://localhost:8081/user/ai/tasks/submit", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${YOUR_JWT_TOKEN}`
    },
    body: JSON.stringify({
      modelName,
      prompt,
      imageUrl
    })
  });
  
  const submitResult = await submitResponse.json();
  const taskNo = submitResult.data.taskNo;
  console.log(`任务已提交: ${taskNo}`);
  
  // 2. 轮询任务状态（每5秒查询一次）
  const pollInterval = setInterval(async () => {
    const statusResponse = await fetch(
      `http://localhost:8081/user/ai/tasks/${taskNo}`,
      {
        headers: {
          "Authorization": `Bearer ${YOUR_JWT_TOKEN}`
        }
      }
    );
    
    const statusResult = await statusResponse.json();
    const status = statusResult.data.status;
    const progress = statusResult.data.progress;
    
    console.log(`任务状态: ${status}, 进度: ${progress}%`);
    
    if (status === 'completed') {
      clearInterval(pollInterval);
      console.log(`任务完成！结果: ${statusResult.data.resultUrl}`);
      return statusResult.data.resultUrl;
    }
    
    if (status === 'failed') {
      clearInterval(pollInterval);
      console.error(`任务失败: ${statusResult.data.errorMessage}`);
      return null;
    }
  }, 5000);
}

// 使用示例
submitAndPoll("sc_soraimg_text_1x1", "一只可爱的柴犬");
```

---

## 六、查询积分余额

```bash
curl -X GET "http://localhost:8081/user/points/consumption/balance" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

**响应示例：**
```json
{
  "code": 200,
  "message": "success",
  "data": {
    "userId": 17563793187762127,
    "balance": 850,
    "expireTime": null
  }
}
```

---

## 七、完整流程示例（前端实现）

```javascript
class SuChuangImageGenerator {
  constructor(token, baseUrl = "http://localhost:8081") {
    this.token = token;
    this.baseUrl = baseUrl;
  }
  
  // 提交文生图任务
  async generateImage(prompt, aspectRatio = "1:1") {
    const modelMap = {
      "auto": "sc_soraimg_text_auto",
      "1:1": "sc_soraimg_text_1x1",
      "2:3": "sc_soraimg_text_2x3",
      "3:2": "sc_soraimg_text_3x2"
    };
    
    const response = await fetch(`${this.baseUrl}/user/ai/tasks/submit`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${this.token}`
      },
      body: JSON.stringify({
        modelName: modelMap[aspectRatio],
        prompt: prompt
      })
    });
    
    const result = await response.json();
    if (result.code === 200) {
      return result.data.taskNo;
    }
    throw new Error(result.message);
  }
  
  // 提交图生图任务
  async transformImage(prompt, imageUrl, aspectRatio = "1:1") {
    const modelMap = {
      "auto": "sc_soraimg_img2img_auto",
      "1:1": "sc_soraimg_img2img_1x1",
      "2:3": "sc_soraimg_img2img_2x3",
      "3:2": "sc_soraimg_img2img_3x2"
    };
    
    const response = await fetch(`${this.baseUrl}/user/ai/tasks/submit`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${this.token}`
      },
      body: JSON.stringify({
        modelName: modelMap[aspectRatio],
        prompt: prompt,
        imageUrl: imageUrl
      })
    });
    
    const result = await response.json();
    if (result.code === 200) {
      return result.data.taskNo;
    }
    throw new Error(result.message);
  }
  
  // 查询任务状态
  async getTaskStatus(taskNo) {
    const response = await fetch(`${this.baseUrl}/user/ai/tasks/${taskNo}`, {
      headers: {
        "Authorization": `Bearer ${this.token}`
      }
    });
    
    const result = await response.json();
    if (result.code === 200) {
      return result.data;
    }
    throw new Error(result.message);
  }
  
  // 等待任务完成（带超时）
  async waitForCompletion(taskNo, timeout = 180000) {
    const startTime = Date.now();
    
    return new Promise((resolve, reject) => {
      const interval = setInterval(async () => {
        try {
          const task = await this.getTaskStatus(taskNo);
          
          if (task.status === 'completed') {
            clearInterval(interval);
            resolve(task.resultUrl);
          } else if (task.status === 'failed') {
            clearInterval(interval);
            reject(new Error(task.errorMessage || '任务失败'));
          } else if (Date.now() - startTime > timeout) {
            clearInterval(interval);
            reject(new Error('任务超时'));
          }
        } catch (error) {
          clearInterval(interval);
          reject(error);
        }
      }, 5000);
    });
  }
  
  // 查询积分余额
  async getBalance() {
    const response = await fetch(`${this.baseUrl}/user/points/consumption/balance`, {
      headers: {
        "Authorization": `Bearer ${this.token}`
      }
    });
    
    const result = await response.json();
    if (result.code === 200) {
      return result.data.balance;
    }
    throw new Error(result.message);
  }
}

// 使用示例
const generator = new SuChuangImageGenerator(YOUR_JWT_TOKEN);

// 文生图
generator.generateImage("一只可爱的柴犬", "1:1")
  .then(taskNo => {
    console.log(`任务已提交: ${taskNo}`);
    return generator.waitForCompletion(taskNo);
  })
  .then(imageUrl => {
    console.log(`图片生成完成: ${imageUrl}`);
  })
  .catch(error => {
    console.error(`生成失败: ${error.message}`);
  });

// 图生图
generator.transformImage(
  "转换为卡通Q版风格",
  "https://example.com/photo.jpg",
  "1:1"
).then(taskNo => {
  console.log(`任务已提交: ${taskNo}`);
  return generator.waitForCompletion(taskNo);
}).then(imageUrl => {
  console.log(`图片转换完成: ${imageUrl}`);
});
```

---

## 八、错误处理

### 常见错误码

| 错误码 | 说明 | 解决方案 |
|--------|------|----------|
| 401 | 未登录或Token过期 | 重新登录获取Token |
| 402 | 积分不足 | 充值积分 |
| 400 | 参数错误 | 检查请求参数 |
| 500 | 服务器错误 | 联系管理员 |

### 错误处理示例

```javascript
try {
  const taskNo = await generator.generateImage("测试prompt", "1:1");
  const imageUrl = await generator.waitForCompletion(taskNo);
  console.log("成功:", imageUrl);
} catch (error) {
  if (error.message.includes("积分不足")) {
    console.error("请先充值积分");
    // 跳转到充值页面
  } else if (error.message.includes("未登录")) {
    console.error("请先登录");
    // 跳转到登录页面
  } else {
    console.error("生成失败:", error.message);
  }
}
```

---

## 九、注意事项

1. **积分消耗**
   - 文生图：30积分/张
   - 图生图：35积分/张
   - 任务提交时立即扣除积分
   - 任务失败会自动退还积分

2. **图片URL要求**
   - 必须是外网可访问的URL
   - 支持 https 和 http 协议
   - 建议使用自有OSS存储的URL
   - 图生图支持单张或多张参考图

3. **结果存储**
   - 生成的图片会自动转存到您的OSS
   - resultUrl 为永久有效的OSS链接
   - 第三方临时URL会被自动替换

4. **任务状态**
   - created: 已创建
   - queued: 排队中
   - processing: 生成中
   - completed: 已完成
   - failed: 失败

5. **超时时间**
   - 生图任务通常在 30-120 秒内完成
   - 建议轮询间隔: 5秒
   - 建议超时时间: 180秒

---

## 十、技术支持

如有问题，请联系技术支持或查看服务器日志：
- 日志会记录完整的提交负载和API响应
- 错误信息统一为中文，不会乱码
- 支持 WebSocket 推送任务完成通知

