feat: v0.8.1 — Seedance API 友好错误提示 + 前端 Mock 数据清理
- 新增 SeedanceAPIError 异常类 + ERROR_MESSAGES 错误码中文映射 - views.py 异常处理区分 SeedanceAPIError,存储友好错误信息 - 移除 DEV 环境 7 个 mock 任务,消除 404 轮询错误 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
32f0ee58f4
commit
f803a1ba71
@ -364,6 +364,7 @@ npx tsx src/index.ts --resume /Users/maidong/Desktop/zyc/研究openclaw/视频
|
|||||||
| 2026-03-13 | CLAUDE.md 增量开发指南更新为 agent-auto(替换 Autonomous Skill) | Documentation |
|
| 2026-03-13 | CLAUDE.md 增量开发指南更新为 agent-auto(替换 Autonomous Skill) | Documentation |
|
||||||
| 2026-03-15 | v0.8.0: 音频引用支持 + 视频 TOS 持久化 + 移除硬编码密钥 + 渐进式轮询 | Full stack |
|
| 2026-03-15 | v0.8.0: 音频引用支持 + 视频 TOS 持久化 + 移除硬编码密钥 + 渐进式轮询 | Full stack |
|
||||||
| 2026-03-15 | TOS 桶切换到 airdrama-media (cn-beijing),K8s Secret 注入 TOS 密钥 | Infra |
|
| 2026-03-15 | TOS 桶切换到 airdrama-media (cn-beijing),K8s Secret 注入 TOS 密钥 | Infra |
|
||||||
|
| 2026-03-15 | v0.8.1: Seedance API 友好错误提示 (SeedanceAPIError) + 前端 Mock 数据清理 | Full stack |
|
||||||
|
|
||||||
### Phase 4 Details (2026-03-13)
|
### Phase 4 Details (2026-03-13)
|
||||||
|
|
||||||
|
|||||||
@ -273,7 +273,11 @@ def video_generate_view(request):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception('Seedance API create task failed')
|
logger.exception('Seedance API create task failed')
|
||||||
record.status = 'failed'
|
record.status = 'failed'
|
||||||
record.error_message = str(e)
|
from utils.seedance_client import SeedanceAPIError
|
||||||
|
if isinstance(e, SeedanceAPIError):
|
||||||
|
record.error_message = e.user_message
|
||||||
|
else:
|
||||||
|
record.error_message = str(e)
|
||||||
record.save(update_fields=['status', 'error_message'])
|
record.save(update_fields=['status', 'error_message'])
|
||||||
# Refund: API call failed, Seedance didn't charge
|
# Refund: API call failed, Seedance didn't charge
|
||||||
_refund_quota(record, duration)
|
_refund_quota(record, duration)
|
||||||
|
|||||||
@ -4,6 +4,28 @@ import requests
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
# Seedance API error code → user-friendly Chinese message
|
||||||
|
ERROR_MESSAGES = {
|
||||||
|
'InputImageSensitiveContentDetected.PrivacyInformation': '参考图片中检测到真实人脸,Seedance 不允许处理包含真人面部的图片',
|
||||||
|
'InputImageSensitiveContentDetected': '参考图片包含敏感内容,请更换图片后重试',
|
||||||
|
'InputVideoSensitiveContentDetected': '参考视频包含敏感内容,请更换视频后重试',
|
||||||
|
'InvalidParameter': '请求参数无效,请检查输入',
|
||||||
|
'RateLimitExceeded': 'API 调用频率超限,请稍后重试',
|
||||||
|
'InsufficientBalance': '账户余额不足,请联系管理员充值',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SeedanceAPIError(Exception):
|
||||||
|
"""Raised when Seedance API returns an error response."""
|
||||||
|
def __init__(self, code, message, status_code=400):
|
||||||
|
self.code = code
|
||||||
|
self.api_message = message
|
||||||
|
self.status_code = status_code
|
||||||
|
# Use friendly message if available, otherwise use API message
|
||||||
|
self.user_message = ERROR_MESSAGES.get(code, message)
|
||||||
|
super().__init__(self.user_message)
|
||||||
|
|
||||||
|
|
||||||
MODEL_MAP = {
|
MODEL_MAP = {
|
||||||
'seedance_2.0': 'doubao-seedance-2-0-260128',
|
'seedance_2.0': 'doubao-seedance-2-0-260128',
|
||||||
'seedance_2.0_fast': 'doubao-seedance-2-0-fast-260128',
|
'seedance_2.0_fast': 'doubao-seedance-2-0-fast-260128',
|
||||||
@ -48,7 +70,15 @@ def create_task(prompt, model, content_items, aspect_ratio, duration, generate_a
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp = requests.post(url, json=payload, headers=_headers(), timeout=60)
|
resp = requests.post(url, json=payload, headers=_headers(), timeout=60)
|
||||||
resp.raise_for_status()
|
if resp.status_code != 200:
|
||||||
|
# Extract human-readable error from Seedance API response
|
||||||
|
try:
|
||||||
|
err = resp.json().get('error', {})
|
||||||
|
code = err.get('code', '')
|
||||||
|
message = err.get('message', resp.text)
|
||||||
|
except Exception:
|
||||||
|
code, message = '', resp.text
|
||||||
|
raise SeedanceAPIError(code, message, resp.status_code)
|
||||||
return resp.json()
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,32 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-03-15 — v0.8.1: Seedance API 友好错误提示 + Mock 数据清理
|
||||||
|
|
||||||
|
**状态**: ✅ 已完成 | **验收**: ✅ 通过(本地端到端测试)
|
||||||
|
|
||||||
|
### 变更内容
|
||||||
|
1. **Seedance API 友好错误提示** — `seedance_client.py` 新增 `SeedanceAPIError` 异常类 + `ERROR_MESSAGES` 错误码映射表,API 报错时返回中文友好提示(如"参考图片中检测到真实人脸")而非原始英文错误
|
||||||
|
2. **views.py 错误传递优化** — `video_generate_view` 异常处理识别 `SeedanceAPIError`,将 `user_message` 存入 `error_message` 字段,前端直接展示具体原因
|
||||||
|
3. **移除前端 Mock 数据** — `generation.ts` 删除 DEV 环境下的 7 个硬编码 mock 任务,消除页面加载时的 404 轮询错误
|
||||||
|
|
||||||
|
### 变更文件
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `backend/utils/seedance_client.py` | 新增 `SeedanceAPIError` 异常类 + `ERROR_MESSAGES` 映射 + `create_task` 错误解析 |
|
||||||
|
| `backend/apps/generation/views.py` | 异常处理区分 `SeedanceAPIError`,存储友好错误信息 |
|
||||||
|
| `web/src/store/generation.ts` | 删除 DEV mock 数据(7 个假任务),消除 404 轮询 |
|
||||||
|
|
||||||
|
### 触发原因
|
||||||
|
- 本地测试上传含真人面部的图片,Seedance 返回 400 但前端只显示"生成失败,请重试",用户无法理解失败原因
|
||||||
|
- DEV 环境 mock 数据的假 taskId 触发持续 404 轮询错误
|
||||||
|
|
||||||
|
### 备注
|
||||||
|
- 已覆盖错误码:隐私人脸、敏感图片/视频、参数无效、频率超限、余额不足
|
||||||
|
- 未匹配的错误码会直接展示 API 原始 message
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-03-15 — v0.8.0: Seedance API 全流程修复 + TOS 视频持久化
|
## 2026-03-15 — v0.8.0: Seedance API 全流程修复 + TOS 视频持久化
|
||||||
|
|
||||||
**状态**: ✅ 已完成 | **验收**: ✅ 通过(本地端到端测试)
|
**状态**: ✅ 已完成 | **验收**: ✅ 通过(本地端到端测试)
|
||||||
|
|||||||
@ -168,123 +168,7 @@ export const useGenerationStore = create<GenerationState>((set, get) => ({
|
|||||||
const { data } = await videoApi.getTasks();
|
const { data } = await videoApi.getTasks();
|
||||||
tasks = data.results.map(backendToFrontend).reverse();
|
tasks = data.results.map(backendToFrontend).reverse();
|
||||||
} catch {
|
} catch {
|
||||||
// API unavailable — tasks stays empty, mocks will fill in below
|
// API unavailable — tasks stays empty
|
||||||
}
|
|
||||||
|
|
||||||
// Dev-only mock tasks for previewing all card states
|
|
||||||
if (import.meta.env.DEV) {
|
|
||||||
tasks.push(
|
|
||||||
// ① 已完成 — 16:9 城市航拍
|
|
||||||
{
|
|
||||||
id: 'mock_completed_169',
|
|
||||||
taskId: 'demo-169',
|
|
||||||
prompt: '航拍镜头从城市上空缓缓下降,金色夕阳照亮整个天际线,镜头缓慢推进穿过云层',
|
|
||||||
editorHtml: '航拍镜头从城市上空缓缓下降,金色夕阳照亮整个天际线,镜头缓慢推进穿过云层',
|
|
||||||
mode: 'universal' as const,
|
|
||||||
model: 'seedance_2.0' as const,
|
|
||||||
aspectRatio: '16:9' as const,
|
|
||||||
duration: 10 as const,
|
|
||||||
references: [],
|
|
||||||
status: 'completed' as const,
|
|
||||||
progress: 100,
|
|
||||||
resultUrl: '/demo/demo-16-9.mp4',
|
|
||||||
createdAt: Date.now() - 3600000,
|
|
||||||
},
|
|
||||||
// ② 已完成 — 21:9 爆炸场景
|
|
||||||
{
|
|
||||||
id: 'mock_completed_219',
|
|
||||||
taskId: 'demo-219',
|
|
||||||
prompt: '0-3s:手持近景镜头 + 轻微晃动 + 缓慢推近,爆炸后的烟尘缓缓落下,环境沉闷压抑',
|
|
||||||
editorHtml: '0-3s:手持近景镜头 + 轻微晃动 + 缓慢推近,爆炸后的烟尘缓缓落下',
|
|
||||||
mode: 'universal' as const,
|
|
||||||
model: 'seedance_2.0' as const,
|
|
||||||
aspectRatio: '21:9' as const,
|
|
||||||
duration: 15 as const,
|
|
||||||
references: [],
|
|
||||||
status: 'completed' as const,
|
|
||||||
progress: 100,
|
|
||||||
resultUrl: '/demo/demo-21-9.mp4',
|
|
||||||
createdAt: Date.now() - 7200000,
|
|
||||||
},
|
|
||||||
// ③ 已完成 — 9:16 人物出场
|
|
||||||
{
|
|
||||||
id: 'mock_completed_916',
|
|
||||||
taskId: 'demo-916',
|
|
||||||
prompt: '出场人物:张磊、队员1-8;紧张的救援场面,烟雾弥漫中队员们有序前进',
|
|
||||||
editorHtml: '出场人物:张磊、队员1-8;紧张的救援场面,烟雾弥漫中队员们有序前进',
|
|
||||||
mode: 'universal' as const,
|
|
||||||
model: 'seedance_2.0_fast' as const,
|
|
||||||
aspectRatio: '9:16' as const,
|
|
||||||
duration: 4 as const,
|
|
||||||
references: [],
|
|
||||||
status: 'completed' as const,
|
|
||||||
progress: 100,
|
|
||||||
resultUrl: '/demo/demo-9-16.mp4',
|
|
||||||
createdAt: Date.now() - 6000000,
|
|
||||||
},
|
|
||||||
// ④ 生成中 — 刚开始 (5%)
|
|
||||||
{
|
|
||||||
id: 'mock_generating_low',
|
|
||||||
taskId: 'demo-gen-low',
|
|
||||||
prompt: '微距镜头拍摄雨滴落在花瓣上的慢动作,水珠在花瓣表面缓缓滑落',
|
|
||||||
editorHtml: '微距镜头拍摄雨滴落在花瓣上的慢动作,水珠在花瓣表面缓缓滑落',
|
|
||||||
mode: 'universal' as const,
|
|
||||||
model: 'seedance_2.0' as const,
|
|
||||||
aspectRatio: '16:9' as const,
|
|
||||||
duration: 10 as const,
|
|
||||||
references: [],
|
|
||||||
status: 'generating' as const,
|
|
||||||
progress: 5,
|
|
||||||
createdAt: Date.now() - 60000,
|
|
||||||
},
|
|
||||||
// ⑤ 生成中 — 进行中 (60%)
|
|
||||||
{
|
|
||||||
id: 'mock_generating_mid',
|
|
||||||
taskId: 'demo-gen-mid',
|
|
||||||
prompt: '水墨风格的山水画卷缓缓展开,远处群山叠嶂,近处溪流潺潺',
|
|
||||||
editorHtml: '水墨风格的山水画卷缓缓展开,远处群山叠嶂,近处溪流潺潺',
|
|
||||||
mode: 'universal' as const,
|
|
||||||
model: 'seedance_2.0_fast' as const,
|
|
||||||
aspectRatio: '1:1' as const,
|
|
||||||
duration: 5 as const,
|
|
||||||
references: [],
|
|
||||||
status: 'generating' as const,
|
|
||||||
progress: 60,
|
|
||||||
createdAt: Date.now() - 120000,
|
|
||||||
},
|
|
||||||
// ⑥ 失败 — 参数错误
|
|
||||||
{
|
|
||||||
id: 'mock_failed_param',
|
|
||||||
taskId: 'demo-fail-param',
|
|
||||||
prompt: '深海探索镜头,潜水艇灯光照亮周围的珊瑚礁和鱼群',
|
|
||||||
editorHtml: '深海探索镜头,潜水艇灯光照亮周围的珊瑚礁和鱼群',
|
|
||||||
mode: 'universal' as const,
|
|
||||||
model: 'seedance_2.0' as const,
|
|
||||||
aspectRatio: '16:9' as const,
|
|
||||||
duration: 10 as const,
|
|
||||||
references: [],
|
|
||||||
status: 'failed' as const,
|
|
||||||
progress: 0,
|
|
||||||
errorMessage: '请求参数有误,请检查输入内容',
|
|
||||||
createdAt: Date.now() - 5000000,
|
|
||||||
},
|
|
||||||
// ⑦ 失败 — 服务器错误
|
|
||||||
{
|
|
||||||
id: 'mock_failed_server',
|
|
||||||
taskId: 'demo-fail-server',
|
|
||||||
prompt: '星空延时摄影,银河缓缓转动,前景是雪山湖泊的倒影',
|
|
||||||
editorHtml: '星空延时摄影,银河缓缓转动,前景是雪山湖泊的倒影',
|
|
||||||
mode: 'universal' as const,
|
|
||||||
model: 'seedance_2.0' as const,
|
|
||||||
aspectRatio: '4:3' as const,
|
|
||||||
duration: 8 as const,
|
|
||||||
references: [],
|
|
||||||
status: 'failed' as const,
|
|
||||||
progress: 0,
|
|
||||||
errorMessage: '服务器繁忙,请稍后重试',
|
|
||||||
createdAt: Date.now() - 4000000,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set({ tasks, isLoading: false });
|
set({ tasks, isLoading: false });
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user