video-shuoshan/CLAUDE.md
zyc a389495ee7
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m21s
add change host
2026-03-20 16:25:22 +08:00

472 lines
24 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Jimeng Clone — AI Video Generation Platform
> This file is the single source of truth for AI-assisted development on this project.
> AI agents (including Claude Code) should read this file first before making any changes.
## Quick Start
### Development Environment
- **Backend**: Python 3.12 + Django 4.2 + DRF + SimpleJWT
- **Frontend**: React 18 + Vite 6 + TypeScript 5 + Zustand
- **Database**: SQLite (dev) / Aliyun RDS MySQL (prod)
- **UI Library**: Arco Design (`@arco-design/web-react`)
- **Charts**: ECharts 6 + echarts-for-react
### Setup Commands
```bash
# Backend
cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py runserver 8000
# Frontend
cd web
npm install
npm run dev # Dev server at localhost:5173
npm run build # Production build: tsc -b && vite build
```
### Test Commands
```bash
# Frontend unit tests
cd web && npx vitest run
# Backend checks
cd backend && python manage.py check
```
### Admin Credentials (Dev)
- Username: `admin` / Password: `admin123`
## Project Architecture
### Directory Structure
```
jimeng-clone/
├── backend/ # Django REST Framework backend
│ ├── config/ # Django settings, urls, wsgi
│ │ ├── settings.py # Main config (DB, CORS, JWT, apps)
│ │ └── urls.py # Root URL routing
│ ├── apps/
│ │ ├── accounts/ # User auth: models, views, serializers, urls
│ │ └── generation/ # Video generation: models, views, serializers, urls
│ ├── utils/ # Shared utilities (geo_client, anomaly_detector, alert_service, tos_client)
│ ├── data/ # Offline data files (ip2region.xdb)
│ ├── requirements.txt # Python dependencies
│ └── Dockerfile # Python 3.12 + gunicorn
├── web/ # React 18 + Vite frontend
│ ├── src/
│ │ ├── App.tsx # Root router (all routes defined here)
│ │ ├── main.tsx # Entry point
│ │ ├── pages/ # Page-level components (one per route)
│ │ ├── components/ # Reusable UI components
│ │ ├── store/ # Zustand state stores
│ │ ├── lib/ # Utilities (API client, log center)
│ │ └── types/ # TypeScript type definitions
│ ├── nginx.conf # Production nginx config
│ └── Dockerfile # Node 18 + nginx
├── k8s/ # Kubernetes deployment configs
├── docs/ # PRD, design review documents
├── .gitea/workflows/ # CI/CD pipeline
└── .autonomous/ # Autonomous skill task tracking
```
### Key Files (Most Commonly Modified)
- `web/src/App.tsx` — Route definitions. Modify when adding new pages.
- `web/src/store/auth.ts` — Auth state (login/logout/JWT refresh)
- `web/src/store/generation.ts` — Video generation state & API calls
- `web/src/store/inputBar.ts` — Input bar state (mode, files, prompt)
- `web/src/lib/api.ts` — Axios client with JWT interceptor. API base URL configured here.
- `web/src/types/index.ts` — All TypeScript interfaces
- `backend/config/settings.py` — Django settings (DB, CORS, JWT, installed apps)
- `backend/config/urls.py` — Root URL routing
- `backend/apps/accounts/models.py` — User model (extends AbstractUser)
- `backend/apps/generation/models.py` — GenerationRecord + QuotaConfig models
- `backend/apps/generation/views.py` — All API endpoints (video, admin, profile)
- `backend/apps/generation/urls.py` — API URL patterns
## Development Conventions
### Code Style
- **Frontend**: TypeScript strict mode, functional components, named exports
- **Backend**: PEP 8, function-based views with `@api_view` decorator
- **CSS**: Inline styles + Arco Design component props (no separate CSS modules)
### Naming Conventions
- **React Components**: PascalCase files (e.g., `LoginPage.tsx`, `InputBar.tsx`)
- **Stores**: camelCase files (e.g., `auth.ts`, `generation.ts`)
- **Django Apps**: lowercase (e.g., `accounts`, `generation`)
- **API URLs**: `/api/v1/` prefix, no trailing slashes in URL patterns
- **Database**: snake_case (Django default)
### State Management Pattern
- Zustand stores in `web/src/store/`, one store per domain
- Each store exports a `use[Name]Store` hook
- API calls are defined inside stores (not in components)
- Pattern: `const doSomething = useAuthStore((s) => s.doSomething)`
### Component Pattern
- Pages in `web/src/pages/` — full-page components mounted by router
- Reusable components in `web/src/components/`
- Protected routes use `<ProtectedRoute>` wrapper, admin routes add `requireAdmin` prop
### Backend Pattern
- Function-based views with `@api_view(['GET', 'POST'])` decorator
- Auth via `@permission_classes([IsAuthenticated])`
- Admin endpoints check `request.user.is_staff`
- Singleton pattern for QuotaConfig (pk always = 1)
## API Endpoints
### Auth (`/api/v1/auth/`)
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/v1/auth/register` | User registration (disabled) |
| POST | `/api/v1/auth/login` | JWT login (returns access + refresh tokens, creates ActiveSession) |
| POST | `/api/v1/auth/token/refresh` | Refresh JWT access token |
| GET | `/api/v1/auth/me` | Get current user info + quota + team + must_change_password |
| POST | `/api/v1/auth/change-password` | Change own password (clears must_change_password) |
### Video Generation (`/api/v1/`)
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/v1/media/upload` | Upload reference media files |
| POST | `/api/v1/video/generate` | Submit video generation task |
| GET | `/api/v1/video/tasks` | List user's generation tasks |
| GET | `/api/v1/video/tasks/<uuid>` | Get task status & result |
### Admin (`/api/v1/admin/`)
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/v1/admin/stats` | Dashboard statistics & charts data |
| GET | `/api/v1/admin/users` | List all users (search, filter, paginate) |
| POST | `/api/v1/admin/users/create` | Create new user |
| GET/PUT | `/api/v1/admin/users/<id>` | Get/update user details |
| PUT | `/api/v1/admin/users/<id>/quota` | Update user quota limits |
| PUT | `/api/v1/admin/users/<id>/status` | Toggle user active status |
| GET | `/api/v1/admin/records` | List all generation records |
| GET/PUT | `/api/v1/admin/settings` | Get/update global settings (QuotaConfig) |
| GET | `/api/v1/admin/teams` | List all teams |
| POST | `/api/v1/admin/teams/create` | Create new team |
| GET/PUT | `/api/v1/admin/teams/<id>` | Get/update team details |
| POST | `/api/v1/admin/teams/<id>/topup` | Add seconds to team pool |
| PUT | `/api/v1/admin/teams/<id>/set-pool` | Directly set team total seconds pool |
| POST | `/api/v1/admin/teams/<id>/admin` | Create team admin user |
| GET | `/api/v1/admin/anomalies` | Login anomaly records (filter by team/rule/level/date) |
| POST | `/api/v1/admin/test-feishu` | Send test Feishu alert message |
| POST | `/api/v1/admin/teams/<id>/auto-learn` | Auto-learn expected regions from login history |
| POST | `/api/v1/admin/teams/<id>/apply-learned-regions` | Apply auto-learned regions to team |
| GET | `/api/v1/admin/logs` | Audit logs (filter by action/operator/date) |
| GET | `/api/v1/admin/assets/overview` | Content assets: global stats + per-team summary |
| GET | `/api/v1/admin/assets/team/<id>/members` | Content assets: team members with video stats |
| GET | `/api/v1/admin/assets/user/<id>/videos` | Content assets: user's completed videos (paginated) |
### Team Admin Assets (`/api/v1/team/assets/`)
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/v1/team/assets/overview` | Team stats + per-member video summary |
| GET | `/api/v1/team/assets/member/<id>/videos` | Member's completed videos (paginated) |
### Profile (`/api/v1/profile/`)
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/v1/profile/overview` | User consumption summary |
| GET | `/api/v1/profile/records` | User's own generation records |
## Database Models
### User (extends AbstractUser)
- `email` (unique), `daily_seconds_limit` (default: 600), `monthly_seconds_limit` (default: 6000)
- `must_change_password` (default: True) — forces password change on first login
- `team` (FK to Team), `is_team_admin`, `disabled_by` (''|'admin'|'system')
- `created_at`, `updated_at`
### Team
- `name`, `total_seconds_pool`, `total_seconds_used`, `monthly_seconds_limit`, `daily_member_limit_default`
- `expected_regions` (CharField 500, comma-separated cities for anomaly detection R1)
- `disabled_by` (''|'admin'|'system'), `is_active`
### GenerationRecord
- `user` (FK), `task_id` (UUID), `ark_task_id`, `prompt`, `mode` (universal|keyframe)
- `model` (seedance_2.0|seedance_2.0_fast), `aspect_ratio`, `duration`, `seconds_consumed`
- `status` (queued|processing|completed|failed), `result_url`, `error_message`, `reference_urls` (JSON)
- Index: (user, created_at)
### AdminAuditLog
- `operator` (FK User, SET_NULL), `operator_name` (denormalized), `action` (12 choices)
- `target_type`, `target_id`, `target_name`, `before` (JSON), `after` (JSON)
- `ip_address`, `created_at` (indexed)
### ActiveSession
- `user` (FK), `session_id` (UUID, unique), `device_type` (desktop|mobile|unknown)
- `user_agent`, `created_at`
- Used for concurrent session limiting via JWT session_id claim
### LoginRecord
- `user` (FK), `team` (FK, redundant for efficient R4/R5 queries), `ip_address`, `user_agent`
- `geo_country`, `geo_province`, `geo_city`, `geo_source` ('online'|'offline'|'skip'|'failed')
- `created_at` (indexed)
### TeamAnomalyConfig (OneToOne → Team)
- Per-team anomaly detection thresholds (null = use global default)
- `r1_enabled`, `r2_enabled`/`r2_window_seconds`, `r3_enabled`/`r3_window_seconds`/`r3_max_count`
- `r4_enabled`/`r4_window_seconds`/`r4_city_count`, `r5_enabled`/`r5_days`/`r5_country_count`
### LoginAnomaly
- `team` (FK), `user` (FK), `login_record` (FK)
- `level` (warning|critical), `rule` (region_mismatch|impossible_travel|login_frequency|multi_city|overseas_ip_diversity)
- `detail` (JSON), `alerted`, `auto_disabled`, `disabled_target` (user|team|'')
- `created_at` (indexed)
### QuotaConfig (Singleton, pk=1)
- `default_daily_seconds_limit`, `default_monthly_seconds_limit`
- `announcement`, `announcement_enabled`
- `max_desktop_sessions` (default: 1), `max_mobile_sessions` (default: 0)
- Anomaly detection global defaults: `anomaly_detection_enabled`, R1-R5 enabled/thresholds
- `feishu_alert_mobiles`, `sms_alert_mobiles`, `alert_cooldown_seconds`
- `updated_at`
## Frontend Routes
| Path | Component | Auth | Description |
|------|-----------|------|-------------|
| `/login` | LoginPage | No | Login page |
| `/` | VideoGenerationPage | Yes | Main video generation UI |
| `/profile` | ProfilePage | Yes | User consumption overview |
| `/admin/dashboard` | DashboardPage | Admin | Stats & charts |
| `/admin/users` | UsersPage | Admin | User management |
| `/admin/records` | RecordsPage | Admin | Generation records |
| `/admin/settings` | SettingsPage | Admin | Global quota, announcement & anomaly detection config |
| `/admin/security` | AnomalyLogPage | Admin | Login anomaly records (security log) |
| `/admin/logs` | AuditLogsPage | Admin | Admin operation audit logs |
| `/admin/assets` | AdminAssetsPage | Admin | Content assets (team→member→video hierarchy) |
| `/team/assets` | TeamAssetsPage | TeamAdmin | Team content assets (member→video hierarchy) |
## Incremental Development Guide
### How to Add Features to This Project
This project was generated and maintained using **agent-auto**(多 Agent 协作自动开发系统)。
项目路径:`/Users/maidong/Desktop/zyc/研究openclaw/agent-auto/`
增量开发有两种方式:
#### 方式 1Direct AI Development小改动
Bug 修复、UI 微调、单个接口等小改动,直接在 Claude Code 中描述需求即可。
Claude Code 会读取本文件CLAUDE.md理解项目上下文。
示例:
- "Add a logout button to the navbar"
- "Fix the date format in the records table"
- "修改轮询间隔为 3 分钟"
- "Change the default daily quota from 600 to 1200 seconds"
**IMPORTANT — 每次改动后必须更新文档:**
1. **更新 `CLAUDE.md`**(本文件):
- 新 API 端点 → 加到 API Endpoints 表
- 新页面/路由 → 加到 Frontend Routes 表
- 新 model/字段 → 加到 Database Models 区
- 配置变更 → 更新对应区
- 始终在底部 **Change History** 表加一行
2. **更新 Claude Code memory**(如有):
- 更新 `~/.claude/projects/` 下的 memory 文件
- 确保跨会话的 AI 也能了解变更
3. **非架构性改动也要记录**
- Bug fix / UI 微调 / 依赖更新 / 配置值变更 → 都加到 Change History
未更新文档会导致后续 AI 会话使用过期上下文,引发冲突或重复工作。
#### 方式 2agent-auto大功能 / 全栈新模块)
涉及 3+ 文件、新模块、跨模块重构等复杂需求,使用 agent-auto 自动完成全流程:
```
需求 → PRD → HTML 原型 → 设计评审 → 全栈代码 → 测试 → Bug 修复循环 → 完成
```
**运行命令:**
```bash
cd /Users/maidong/Desktop/zyc/研究openclaw/agent-auto
# 增量需求(在已有项目上加功能)
npx tsx src/index.ts --resume /Users/maidong/Desktop/zyc/研究openclaw/视频生成平台/jimeng-clone "你的增量需求描述"
# 崩溃恢复 / 继续执行
npx tsx src/index.ts --resume /Users/maidong/Desktop/zyc/研究openclaw/视频生成平台/jimeng-clone
```
**agent-auto 会自动:**
1. 读取 `.agent-auto/state.json` 恢复项目状态
2. product-agent 修订 PRD`docs/prd.md`
3. design-agent 更新 HTML 原型(`prototype/`
4. product-agent 设计评审(`docs/design-review.md`APPROVED/REJECTED 循环)
5. dev-agent 增量开发(保留已有代码,只新增/修改)
6. test-agent 测试 + Bug 分类(`test-report.md`
7. Bug 修复循环CODE_BUG → dev-agent / DESIGN_BUG → design-agent / REQUIREMENT_BUG → product-agent
8. 全部通过 → COMPLETE
**状态持久化**:进度保存在 `.agent-auto/state.json`,中断后可随时 `--resume` 继续。
**Agent 记忆**:每个 Agent 有独立记忆文件(`.agent-auto/{agent-name}/memory.md`),跨轮次保持上下文。
#### 变更日志维护
每次需求开发完成、测试验收通过后,**必须**在 `docs/changelog.md` 中记录本次变更:
- 按日期倒序添加新条目(最新的在最前面)
- 包含:变更标题、状态/验收、变更内容、变更文件、触发原因
- 文件末尾有模板可直接复制使用
- Direct AI 和 agent-auto 两种方式均需记录
#### 何时用哪种方式
| 场景 | 方式 |
|------|------|
| Bug 修复 / 单行改动 | Direct AI |
| UI 微调(按钮、颜色、文案) | Direct AI |
| 添加单个 API 端点 | Direct AI |
| 修改已有组件行为 | Direct AI |
| 新功能模块3+ 文件) | agent-auto |
| 新增整页 CRUD如管理页面 | agent-auto |
| 跨多文件重构 | agent-auto |
| 新增子系统(通知、支付、权限) | agent-auto |
### How to Add a New Page
1. Create page component in `web/src/pages/NewPage.tsx`
2. Add route in `web/src/App.tsx` inside `<Routes>`:
```tsx
<Route path="/new-page" element={<ProtectedRoute><NewPage /></ProtectedRoute>} />
```
3. If it needs state, create store in `web/src/store/newPage.ts`
4. Add API calls in the store using `api.get()` / `api.post()` from `web/src/lib/api.ts`
### How to Add a New API Endpoint
1. Add view function in `backend/apps/generation/views.py` (or relevant app):
```python
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def new_endpoint_view(request):
return Response({'data': ...})
```
2. Add URL pattern in `backend/apps/generation/urls.py`:
```python
path('new/endpoint', views.new_endpoint_view, name='new_endpoint'),
```
3. Add TypeScript types in `web/src/types/index.ts`
4. Add API call in the relevant Zustand store
### How to Add a New Database Model
1. Define model in `backend/apps/generation/models.py` (or relevant app)
2. Run migrations:
```bash
cd backend
python manage.py makemigrations
python manage.py migrate
```
3. Register in admin: `backend/apps/generation/admin.py`
4. Create serializer if needed: `backend/apps/generation/serializers.py`
## Environment Variables
| Variable | Description | Required |
|----------|-------------|----------|
| `DATABASE_URL` | MySQL connection string (prod) | Prod only |
| `SECRET_KEY` | Django secret key | Yes |
| `TOS_ACCESS_KEY` | Volcano Engine TOS AccessKeyId | Yes (upload) |
| `TOS_SECRET_KEY` | Volcano Engine TOS SecretAccessKey | Yes (upload) |
| `TOS_BUCKET` | Volcano TOS bucket name (default: `airdrama-media`) | Yes (upload) |
| `TOS_ENDPOINT` | TOS endpoint URL (default: `https://tos-cn-beijing.volces.com`) | Yes (upload) |
| `TOS_REGION` | TOS region (default: `cn-beijing`) | Yes (upload) |
| `TOS_CDN_DOMAIN` | TOS CDN domain for permanent URLs (default: `https://airdrama-media.tos-cn-beijing.volces.com`) | Yes (upload) |
| `ARK_API_KEY` | Volcano Engine ARK API key for Seedance | Yes (video gen) |
| `ARK_BASE_URL` | ARK API base URL (default: `https://ark.cn-beijing.volces.com/api/v3`) | No |
| `ALIYUN_IP_GEO_APPCODE` | Aliyun marketplace IP geolocation API AppCode | Yes (anomaly detection) |
| `FEISHU_APP_SECRET` | Feishu bot app secret for alert notifications | Yes (anomaly alerts) |
## Deployment
- **CI/CD**: Gitea Actions (`.gitea/workflows/deploy.yaml`)
- **Registry**: Huawei Cloud SWR
- **Orchestration**: Kubernetes (`k8s/` directory)
- **Backend URL**: `airflow-studio-api.airlabs.art`
- **Frontend URL**: `airflow-studio.airlabs.art`
- **Database**: Aliyun RDS MySQL (`rm-7xv1uaw910558p1788o.mysql.rds.aliyuncs.com:3306`)
## Testing
- **Unit Tests**: 227 tests in `web/` via Vitest — `cd web && npx vitest run`
- **E2E Tests**: 49 tests via Playwright — `cd web && npx playwright test`
- **Backend**: Django system checks — `cd backend && python manage.py check`
- **Status**: 211 passing, 16 pre-existing path-resolution failures in phase2/phase3 tests
- **Test DB Isolation**: E2E tests use `TESTING=true` env var → backend writes to `test_db.sqlite3` (not `db.sqlite3`). Playwright auto-starts a test backend on port 8000 with this env var. Dev data is never polluted by tests.
## Design Guidelines
- **Color Palette**: `#0a0a0f` (bg), `#111118`, `#16161e`, `#2a2a38`, `#00b8e6` (accent)
- **Typography**: Noto Sans SC (Chinese) + Space Grotesk (UI) + JetBrains Mono (code)
- **Style**: Linear/Vercel dark theme aesthetic
- **Responsive**: `grid-cols-4 max-lg:grid-cols-2`
## Change History
> 完整变更日志见 [`docs/changelog.md`](docs/changelog.md),以下为摘要。
| Date | Change | Scope |
|------|--------|-------|
| 2025-01 | Phase 1: Core video generation UI | Frontend |
| 2025-02 | Phase 2: Backend + Auth + Admin panel | Full stack |
| 2025-03 | Phase 3: Seconds-based quota, profile page, ECharts dashboard | Full stack |
| 2026-03 | Added CLAUDE.md for AI-assisted incremental development | Documentation |
| 2026-03-13 | Phase 4: TOS upload + Seedance API integration + DB persistence | Full stack |
| 2026-03-13 | Test DB isolation: TESTING=true → test_db.sqlite3, Playwright auto-starts test backend | Backend + Config |
| 2026-03-13 | Removed mock data fallback from DashboardPage — charts show real data only | Frontend |
| 2026-03-13 | LogCenterMiddleware: Django 中间件兜底异常上报(捕获 DRF 之外的导入/中间件/URL 解析错误) | Backend |
| 2026-03-13 | custom_exception_handler: 未处理异常返回 JSON 500 而非 HTML | Backend |
| 2026-03-13 | 前端轮询间隔从 5 秒改为 3 分钟 | Frontend |
| 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 |
| 2026-03-16 | v0.8.2: 管理后台 UI 修复 — DatePicker/Select 暗色主题、公告跑马灯、Toast 全局化、失败原因 tooltip | Full stack |
| 2026-03-16 | v0.8.3: 团队详情抽屉→弹窗重构(VideoDetailModal 规范) + 修改秒数池功能 + member_count 修复 | Full stack |
| 2026-03-16 | v0.8.4: 管理员操作审计日志 — AdminAuditLog 模型 + 12 处埋点 + 日志查询页面 | Full stack |
| 2026-03-18 | v0.9.0: 首次登录强制改密 — must_change_password 字段 + ForceChangePasswordModal | Full stack |
| 2026-03-18 | v0.9.0: 并发会话限制 — ActiveSession + SessionJWT + 可配置桌面/移动端会话数 | Full stack |
| 2026-03-18 | v0.9.0: 登录记录 — LoginRecord 模型IP + User-Agent为异常检测打基础 | Backend |
| 2026-03-18 | v0.9.0: Token 生命周期缩短 — access 30min, refresh 1天 | Backend |
| 2026-03-18 | v0.9.0: 内容资产页 — 超管/团队管三级折叠式资产浏览(团队→成员→视频) | Full stack |
| 2026-03-18 | v0.9.1: 登录风控第二期 — IP归属地解析 + 5条异常检测规则(R1-R5) + 飞书告警 + 自动封禁 | Full stack |
| 2026-03-18 | v0.9.1: 安全日志页面 — LoginAnomaly 记录列表,按团队/规则/级别/时间筛选 | Frontend |
| 2026-03-18 | v0.9.1: 系统设置页 — 异常检测总开关、R1-R5默认阈值、飞书接收人+测试、告警冷却 | Frontend |
| 2026-03-18 | v0.9.1: 团队管理 — 预期登录城市(必填) + 自动学习 + disabled_by 来源标签 | Full stack |
| 2026-03-18 | v0.9.1: 前端拦截器 — user_disabled/team_disabled 错误码处理,弹窗提示后跳登录 | Frontend |
| 2026-03-19 | fix: LoginRecord 创建时显式传 geo 空字段,修复 MySQL 严格模式 IntegrityError | Backend |
### Phase 4 Details (2026-03-13)
**Backend new files:**
- `backend/utils/tos_client.py` — Volcano Engine TOS upload via official `tos` SDK
- `backend/utils/seedance_client.py` — Seedance/ARK API client (create_task, query_task)
**Backend changes:**
- `backend/config/settings.py` — Added TOS and ARK config
- `backend/apps/generation/models.py` — Added `ark_task_id`, `result_url`, `error_message`, `reference_urls` (JSONField)
- `backend/apps/generation/views.py` — New endpoints: `upload_media_view`, `video_tasks_list_view`, `video_task_detail_view`; Rewritten `video_generate_view` with Seedance integration
- `backend/apps/generation/urls.py` — Added 3 routes: `media/upload`, `video/tasks`, `video/tasks/<uuid>`
- `backend/apps/generation/serializers.py` — Added `references` field to VideoGenerateSerializer
- `backend/requirements.txt` — Added `tos>=2.7`, `requests>=2.31`
- Migration: `0003_generationrecord_ark_task_id_and_more.py`
**Frontend changes:**
- `web/src/types/index.ts` — Added `BackendTask` interface, `tosUrl` to `UploadedFile`, `taskId`/`errorMessage` to `GenerationTask`
- `web/src/lib/api.ts` — Added `mediaApi.upload()`, `videoApi.getTasks()`, `videoApi.getTaskStatus()`; Changed `videoApi.generate()` to JSON
- `web/src/store/generation.ts` — Complete rewrite: async TOS upload + API calls + polling + `loadTasks()` for refresh persistence
- `web/src/components/VideoGenerationPage.tsx` — Added `loadTasks()` on mount
- `web/src/components/GenerationCard.tsx` — Video player for results, error state display
**Flow:** Upload files → TOS → Generate API (Seedance) → DB record → Poll status → Display result