591 lines
16 KiB
Markdown
591 lines
16 KiB
Markdown
# 速创生图接口调用示例
|
||
|
||
## 一、数据库配置说明
|
||
|
||
### 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 推送任务完成通知
|
||
|