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-15 | v0.8.0: 音频引用支持 + 视频 TOS 持久化 + 移除硬编码密钥 + 渐进式轮询 | Full stack |
|
||||
| 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)
|
||||
|
||||
|
||||
@ -273,7 +273,11 @@ def video_generate_view(request):
|
||||
except Exception as e:
|
||||
logger.exception('Seedance API create task 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'])
|
||||
# Refund: API call failed, Seedance didn't charge
|
||||
_refund_quota(record, duration)
|
||||
|
||||
@ -4,6 +4,28 @@ import requests
|
||||
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 = {
|
||||
'seedance_2.0': 'doubao-seedance-2-0-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.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()
|
||||
|
||||
|
||||
|
||||
@ -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 视频持久化
|
||||
|
||||
**状态**: ✅ 已完成 | **验收**: ✅ 通过(本地端到端测试)
|
||||
|
||||
@ -168,123 +168,7 @@ export const useGenerationStore = create<GenerationState>((set, get) => ({
|
||||
const { data } = await videoApi.getTasks();
|
||||
tasks = data.results.map(backendToFrontend).reverse();
|
||||
} catch {
|
||||
// API unavailable — tasks stays empty, mocks will fill in below
|
||||
}
|
||||
|
||||
// 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,
|
||||
},
|
||||
);
|
||||
// API unavailable — tasks stays empty
|
||||
}
|
||||
|
||||
set({ tasks, isLoading: false });
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user