Files
meijiaka-zy/CLAUDE.md
T

519 lines
22 KiB
Markdown
Raw 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.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述
美家卡智影 (Meijiaka AI Video) - AI 视频创作平台。一个 AI 驱动的桌面应用,采用 **Tauri + React + FastAPI** 混合架构,用户可以通过 AI 生成脚本、创建数字人视频,自动生成字幕,最终本地合成完整的营销视频。
### 环境要求
| 组件 | 版本要求 |
|------|----------|
| Python | **3.13+** (代码使用 `|` 类型注解语法) |
| Node.js | 20+ |
| Rust | 1.70+ |
| Docker | 20+ (可选,用于数据库) |
核心设计理念:**轻量云账号 + 全本地业务数据** - 云端只存储用户认证和使用日志,所有项目/脚本/媒体都存在用户本地。
## 架构
### 混合架构
- **FastAPI 后端**: 处理 AI 模型调用、用户认证、API 服务
- **Tauri + React 前端**: 桌面 UIReact 负责渲染,Tauri 提供系统能力
- **Rust 后端**: 通过 Tauri IPC 处理本地操作(FFmpeg 视频处理、文件系统访问)
### 存储策略
核心设计理念:**轻量云账号 + 全本地业务数据** - 云端只存储用户认证和使用日志,所有项目/脚本/媒体都存在用户本地。
- **云端**: PostgreSQL 只存储 2 张表:`users` (用户账户)、`model_usage_logs` (用量统计)
- `avatars` 表已废弃:数字人名片元数据现在纯本地存储 `avatars.json`
- **本地**: JSON 文件存储项目/脚本/分镜数据、数字人元数据,用户磁盘存储媒体文件,FFmpeg 处理视频合成
- **缓存/队列**: Redis + Async Engine Scheduler 处理异步任务
### 混合通信模式
| 通信模式 | 使用场景 | 前端调用方式 |
|---------|---------|------------|
| HTTP → FastAPI | AI 生成、认证、配置管理 | `client.get/post/put/delete()` |
| Tauri IPC → Rust | FFmpeg 视频处理、本地文件系统 | `ipc.request()` 或直接 `invoke()` |
**通信模块**:
- `tauri-app/src/api/client.ts` - HTTP 客户端,自动处理 camelCase/snake_case 转换
- `tauri-app/src/api/ipc.ts` - IPC 客户端
- `tauri-app/src/api/modules/localStorage.ts` - 本地项目存储(走 IPC
- `tauri-app/src/api/modules/videoComposite.ts` - 视频合成(走 IPC
### AI Provider 架构
后端 AI 模块采用多 Provider 设计:
- `app/ai/model_router.py` - 模型路由器,支持自动降级
- `app/ai/providers/base.py` - 抽象基类
- `app/ai/providers/*` - 具体实现(OpenAI、火山引擎、KlingAI 等)
- `app/ai/prompts/` - 提示词模板文件
支持的 AI 平台:火山方舟(推荐)、OpenAI、百度文心一言、阿里云通义千问、KlingAI(数字人视频生成)。
模型配置文件:`python-api/config/ai_models.yaml`(支持热重载)
### Token 管理
外部 API 认证 Token 使用 `app/core/token_manager.py` 统一管理:
- Token 缓存(避免重复生成)
- 自动刷新(Token 即将过期时自动刷新)
- 并发安全(双重检查锁定)
- 支持 JWT、OAuth2 等多种策略
### 数据流
1. **脚本生成**: 用户输入 → FastAPI AI 代理 → 标准化输出 → 前端保存到本地 JSON
2. **数字人视频**: 后端调用 KlingAI API → 返回视频 URL → 前端下载并本地存储
3. **视频合成**: 前端 → Tauri IPC → Rust 后端 → FFmpeg → 渲染最终视频文件
### 本地存储结构(用户机器)
```
~/Documents/Meijiaka/
├── config.json # 全局应用配置
├── projects/
│ └── {project_id}/
│ ├── meta.json # 项目元数据
│ ├── segments.json # 脚本/分镜数据
│ └── assets/ # 媒体文件
├── avatars/
│ └── {avatar_id}/
│ ├── meta.json # 数字人名片配置
│ └── source.mp4 # 源视频
└── cache/ # 临时文件
```
## 目录结构
```
ai-meijiaka/
├── python-api/ # FastAPI 后端服务
│ ├── app/
│ │ ├── api/v1/ # REST API 端点
│ │ ├── ai/ # AI 模型路由和 Provider
│ │ ├── ai/prompts/ # 提示词模板文件
│ │ ├── core/ # 安全、配置、异常处理
│ │ ├── db/ # 数据库配置
│ │ ├── models/ # SQLAlchemy 数据模型
│ │ ├── schemas/ # Pydantic 验证模型
│ │ ├── services/ # 业务逻辑和 AI 服务代理
│ │ ├── scheduler/ # Async Engine 统一异步调度器
│ │ ├── config.py # 配置管理
│ │ └── main.py # 应用入口
│ ├── config/ # AI 模型配置(YAML
│ ├── tests/ # pytest 测试套件
│ ├── scripts/ # 管理和测试脚本
│ └── docker-compose.yml # Docker 服务编排
├── tauri-app/ # Tauri 桌面应用
│ ├── src/ # React 前端源码
│ │ ├── api/ # API 客户端和类型
│ │ │ ├── adapters/ # 前后端字段差异适配
│ │ │ ├── generated/ # OpenAPI 自动生成类型
│ │ │ └── modules/ # API 模块封装
│ │ ├── components/ # 可复用 React 组件
│ │ ├── pages/ # 页面组件(路由)
│ │ ├── store/ # Zustand 全局状态管理
│ │ ├── hooks/ # 自定义 React Hooks
│ │ └── utils/ # 前端工具函数
│ ├── src-tauri/ # Rust 后端
│ │ ├── src/
│ │ │ ├── lib.rs # Tauri 应用入口,命令注册
│ │ │ ├── commands/ # 按领域拆分的命令模块
│ │ │ │ ├── asset.rs # 资源文件操作
│ │ │ │ ├── auth_state.rs # 认证状态管理
│ │ │ │ ├── avatar.rs # 数字人头像管理
│ │ │ │ ├── product.rs # 产品相关
│ │ │ │ └── project.rs # 项目存储操作
│ │ │ ├── storage/ # 存储引擎分层
│ │ │ │ ├── mod.rs # 模块导出
│ │ │ │ ├── paths.rs # 路径计算
│ │ │ │ ├── engine.rs # 核心存储引擎(原子写+文件锁)
│ │ │ │ ├── auth.rs # 认证存储
│ │ │ │ ├── project.rs # 项目存储
│ │ │ │ ├── avatar.rs # 头像存储
│ │ │ │ └── cache.rs # 缓存存储
│ │ │ ├── ffmpeg_cmd.rs # FFmpeg 命令封装
│ │ │ ├── video_processing.rs # 视频合成逻辑
│ │ │ ├── api_proxy.rs # Python API 代理
│ │ │ ├── avatar_cache.rs # 头像视频缓存管理
│ │ │ └── utils.rs # 通用工具函数
│ │ ├── binaries/ # 嵌入的 FFmpeg 可执行文件
│ │ └── Cargo.toml # Rust 依赖配置
│ └── package.json # NPM 依赖和脚本
└── docs/ # 开发文档
```
## 常用命令
### 后端 (python-api)
项目使用 `uv` 进行依赖管理,并提供了 `Makefile` 封装常用命令:
```bash
cd python-api
# 使用 uv 和 Makefile(推荐)
make dev # 安装开发依赖并配置 pre-commit
make docker-run # 使用 Docker Compose 启动所有服务(db, redis, api, scheduler
make run # 启动 FastAPI 开发服务器
make scheduler # 启动 Async Engine Scheduler
make lint # 运行代码检查 (ruff + mypy)
make format # 格式化代码
make test # 运行所有测试
make security # 运行安全扫描 (bandit + pip-audit)
# 手动方式
# 安装依赖
python -m venv venv && source venv/bin/activate
pip install -e ".[dev]"
# 启动 PostgreSQL + Redis(必需)
docker-compose up -d db redis
# 启动 FastAPI 开发服务器
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# 启动 Async Engine Scheduler(另开终端)
python -m app.scheduler.main
# 代码质量
black app/ # 格式化代码(行宽 100
ruff check app/ # 代码检查
mypy app/ # 严格类型检查
bandit -c pyproject.toml -r app/ # 安全扫描
pip-audit # 依赖漏洞检测
python scripts/check_config_architecture.py # 检查配置架构一致性
# 导出 OpenAPI 文档到前端
python3 -c "
import logging
logging.disable(logging.WARNING)
from app.main import app
import json
print(json.dumps(app.openapi(), indent=2, ensure_ascii=False))
" > ../tauri-app/src/api/generated/openapi.json
# 测试
pytest # 运行所有测试
pytest tests/test_script.py -v # 运行单个测试文件
pytest --cov=app # 覆盖率报告
# Docker
docker-compose up -d # 启动所有服务(db, redis, api, scheduler
# 端口占用检查
lsof -i :8080 # 检查 8080 端口占用
```
**可用 Makefile 命令:**
| 命令 | 用途 |
|------|------|
| `make help` | 显示帮助信息 |
| `make install` | 安装生产依赖(使用 lock 文件)|
| `make dev` | 安装开发依赖并配置 pre-commit |
| `make update-lock` | 更新 requirements.lock |
| `make lint` | 运行代码检查 (ruff + mypy) |
| `make format` | 格式化代码 (black + ruff) |
| `make format-check` | 检查代码格式(不修改)|
| `make test` | 运行测试 |
| `make test-cov` | 运行测试并生成覆盖率报告 |
| `make security` | 运行安全扫描 |
| `make run` | 启动开发服务器 |
| `make scheduler` | 启动 Async Engine Scheduler |
| `make docker-run` | Docker Compose 启动全部服务 |
| `make docker-down` | 停止 Docker 服务 |
| `make clean` | 清理缓存文件 |
| `make ci` | 运行所有 CI 检查 |
### 前端 (tauri-app)
```bash
cd tauri-app
# 安装依赖
npm install
# 开发
npm run dev # 仅启动 Vite(不打开 Tauri 窗口)
npm run tauri dev # 完整 Tauri 桌面开发模式
# 构建
npm run build # 前端生产构建
npm run tauri build # 打包桌面应用(.dmg/.exe/.AppImage
# 代码质量
npm run lint # ESLint 检查 JS/TS
npm run lint:fix # ESLint 自动修复
npm run format # Prettier 格式化代码
npm run stylelint # CSS 检查
# 测试
npm run test # 运行 Vitest
npm run test:coverage # 覆盖率报告
npm run test:ui # 打开 Vitest UI
# 类型生成
npm run gen:api # 从 OpenAPI schema 生成 TypeScript 类型
```
### 数据库迁移
项目使用 Alembic 进行数据库迁移:
```bash
cd python-api
# 生成新迁移(修改模型后)
alembic revision --autogenerate -m "description"
# 应用迁移
alembic upgrade head
# 回滚迁移
alembic downgrade -1
```
### 开发提示
- **Tauri 调试**: 使用 `npm run tauri dev` 时,Rust 后端日志在终端输出,前端日志在浏览器控制台
- **本地项目路径**: 项目数据保存在 `~/Documents/Meijiaka/projects/{project_id}/`
- **配置修改**: AI 模型配置 `python-api/config/ai_models.yaml` 支持热重载,无需重启服务
- **类型同步**: 修改后端 API 后,记得重新导出 OpenAPI 并运行 `npm run gen:api`
- **Async Engine Scheduler**: 系统使用 Slot-Based Scheduler 统一调度所有第三方异步任务:
- `video` - 数字人视频生成(18 slots)
- `avatar_clone` - 形象克隆(2 slots
- `image` - 图片生成(9 slots
- `subtitle` - 字幕生成(5 slots
- `copy` - 文案提取(5 slots
- **任务状态**: 任务状态唯一真相源为后端 Redis,`taskStore` 不持久化,启动时从后端 `GET /tasks` 查询
- **项目数据**: 项目元数据和分镜数据通过 IPC 显式写入本地文件,不通过 Zustand persist 持久化
- **字幕渲染**: 使用 `assjs` 库进行 ASS/SSA 字幕预览渲染,WASM 和 Worker 文件通过 Vite 插件复制到 `public/` 目录,修改资源路径后需要检查插件配置
## 开发规范
### 后端 (Python)
- **格式化**: Black (行宽: 100)
- **检查**: Ruff
- **类型**: MyPy (strict 模式)
- **架构**: API → Service → CRUD → Model,禁止跨层调用
- **数据库**: 始终使用异步 SQLAlchemy,事务在 API 层控制
- **AI 集成**: 无论使用什么提供者,输出 Schema 必须保持一致,在 Service 层标准化
- **提示词**: 所有提示词放在 `app/ai/prompts/` 单独文件,不硬编码
- **配置管理**: 所有配置通过 `from app.config import get_settings` 读取,禁止直接使用 `os.getenv()`,所有配置项必须在 `Settings` 类中定义
### 配置管理强制规范
**架构层级:**
```
.env (Layer 1) ──→ Settings (Layer 2) ──→ 服务层 (Layer 3)
唯一配置出口
```
**强制规则:**
- **所有服务**必须使用 `from app.config import get_settings` 读取配置
- **禁止**在服务层、API 层直接使用 `os.getenv()``os.environ.get()`
- **所有配置项**必须在 `app/config.py``Settings` 类中定义
- **敏感信息**API Keys、Secrets)必须通过环境变量注入
- **业务默认值**可以硬编码在 `Settings`
**添加新配置流程:**
1.`app/config.py``Settings` 类中添加字段定义
2. 使用 `Field(default=..., description="...")` 提供默认值和说明
3. 敏感信息使用 `str | None = None` 类型
4. 更新 `.env.example` 文档
### Rust (Tauri 后端)
- **格式化**: `rustfmt`(默认配置)
- **检查**: `cargo clippy`(零警告)
- **模块组织**: 命令按领域拆分到 `src/commands/{domain}.rs`,在 `lib.rs` 中注册
- **存储分层**: 存储逻辑按领域拆分到 `src/storage/{domain}.rs`
- **命令参数**: Tauri IPC 命令必须使用 Args 结构体接收参数:
```rust
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SaveProjectMetaArgs {
pub project_id: String,
pub data: serde_json::Value,
}
```
- **禁止**: 命令函数直接使用 camelCase 参数名(会产生 `non_snake_case` 警告)
### 本地数据存储规范(Tauri/Rust
**分层架构:**
```
Layer 1: 页面组件(Pages/Components — 只操作 Store,禁止直接调用 IPC save
Layer 2: Zustand Store(内存状态) — Immer 不可变更新
Layer 3: PersistManager(持久化协调) — debounce 批量、flush 强制、错误上报
Layer 4: API 模块(localStorageApi 等) — 类型安全的 IPC 调用封装
Layer 5: Rust StorageEngine(文件系统) — sanitize + atomic_write + file_lock
```
**强制规范:**
1. **禁止页面组件直接调用 `localProjectApi.saveXxx()`** — 必须通过 Store → PersistManager
2. **禁止 Rust 命令函数直接 `fs::write`** — 必须通过 `StorageEngine::atomic_write_json`
3. **所有 ID 参数必须 `sanitize_id`** — 路径参数白名单校验(`[a-zA-Z0-9_-]+`
4. **所有 JSON 写操作必须原子化** — 临时文件 + `fs::rename`
5. **RMW 操作必须加锁** — `with_file_lock` 或 Mutex
**StorageEngine 核心能力:**
- `sanitize_id(id)` — ID 白名单校验,防御路径遍历
- `sanitize_filename(name)` — 提取纯文件名,拒绝目录组件
- `atomic_write_json(path, value)` — 先写 `.tmp` 再 rename,防崩溃截断
- `with_file_lock(path, f)` — 文件锁保护 RMW 操作
- `read_json<T>(path)` — 安全读取,文件不存在返回 `None`,损坏返回 `Err`
### 前端 (TypeScript/React)
- **类型**: 严格 TypeScript 模式
- **组件**: 函数组件 + Hooks
- **状态管理**: Zustand 管理全局状态,Immer 处理不可变更新
- **数据获取**: SWR 缓存,自动 localStorage 降级
- **API 客户端**: 从后端 OpenAPI schema 自动生成类型
- **命名风格**: camelCase(自动与后端 snake_case 转换)
- **本地存储**: 项目数据通过 Tauri IPC 保存到 `~/Documents/Meijiaka/projects/`
### 提交规范
```
feat: 新功能
fix: 修复
docs: 文档
refactor: 重构
test: 测试
chore: 构建/工具
```
## 环境配置
### 后端 (.env)
```bash
# 数据库
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/meijiaka
REDIS_URL=redis://localhost:6379/0
# JWT 认证
SECRET_KEY=your-secret-key-here
ACCESS_TOKEN_EXPIRE_MINUTES=10080
# AI 服务凭证
VOLCENGINE_API_KEY=your-volcengine-key
VOLCENGINE_CAPTION_APPID=your-caption-appid
VOLCENGINE_CAPTION_TOKEN=your-caption-token
OPENAI_API_KEY=sk-your-openai-key
KLINGAI_ACCESS_KEY=your-kling-access-key
KLINGAI_SECRET_KEY=your-kling-secret-key
# 七牛云存储(数字人视频持久化)
QINIU_ACCESS_KEY=your-qiniu-access-key
QINIU_SECRET_KEY=your-qiniu-secret-key
QINIU_VIDEO_BUCKET=media-bucket
QINIU_IMAGE_BUCKET=image-bucket
# CORS 配置
CORS_ORIGINS=http://localhost:1420,http://127.0.0.1:1420,http://localhost:8080
```
## 服务地址
- API: http://localhost:8080/api/v1
- 文档: http://localhost:8080/docs
- Vite 开发服务器: http://localhost:1420
## 关键开发文件
| 文件 | 用途 |
|------|------|
| `python-api/app/main.py` | FastAPI 应用入口 |
| `python-api/app/api/v1/*.py` | API 端点定义 |
| `python-api/app/ai/model_router.py` | AI 模型路由和降级 |
| `python-api/app/services/*.py` | 业务逻辑和 AI 响应标准化 |
| `python-api/config/ai_models.yaml` | AI 模型配置 |
| `tauri-app/src/App.tsx` | 主 React 组件 |
| `tauri-app/src/api/client.ts` | 智能路由的 API 客户端 |
| `tauri-app/src/store/projectStore.ts` | 项目状态管理 |
| `tauri-app/src-tauri/src/lib.rs` | Rust 命令注册 |
| `tauri-app/src-tauri/src/commands/project.rs` | 项目存储 IPC 命令 |
| `tauri-app/src-tauri/src/storage/engine.rs` | 核心存储引擎(原子写+校验)|
| `tauri-app/src-tauri/src/video_processing.rs` | FFmpeg 视频合成 |
| `tauri-app/src-tauri/src/avatar_cache.rs` | 头像视频缓存管理 |
| `python-api/app/core/token_manager.py` | API Token 缓存与自动刷新 |
| `python-api/app/config.py` | Pydantic Settings 配置管理 |
| `tauri-app/src/pages/VideoCreation/SubtitleBurning.tsx` | 字幕压制页面(ASS 字幕渲染) |
| `tauri-app/src/hooks/useAssJsRenderer.ts` | assjs 字幕渲染 Hook |
| `tauri-app/src/utils/assGenerator.ts` | ASS 字幕文件生成工具 |
## 额外开发文档
项目 `docs/` 目录包含详细的深度开发文档:
| 文档 | 主题 |
|------|------|
| `docs/video-generation-flow.md` | 完整视频生成流程说明 |
| `docs/kling-api-dev.md` | KlingAI 数字人视频 API 对接开发文档 |
| `docs/app-update-system.md` | 应用自动更新系统设计 |
| `docs/anytocopy-integration.md` | 版权素材集成说明 |
| `docs/anytocopy-api.md` | 版权素材 API 文档 |
| `docs/volcengine-video-caption-api.md` | 火山引擎字幕 API 对接 |
| `docs/qiniu-kodo-python-sdk-guide.md` | 七牛云存储 SDK 集成指南 |
| `docs/database-design.md` | 数据库设计文档 |
| `docs/unified-async-scheduler.md` | 统一异步调度器设计 |
| `docs/semantic-refactoring-plan.md` | 后端语义重构计划 |
| `docs/migrate-avatars-to-local.md` | 头像数据迁移到本地说明 |
## 统一术语表(语义治理)
后端代码已完成语义治理重构,所有开发必须遵守统一术语表,禁止使用废弃别名。
整个后端划分为 6 个语义层级,每一层只使用属于该层的术语:
```
Layer 6: Presentation (API Schema / 前端适配层) → Segment, Human, Job, Script
Layer 5: Application (API 路由) → Segment, Human, Job, Project
Layer 4: Orchestration (Scheduler / SlotManager) → Job, JobRecord, Slot, Handler
Layer 3: Domain (Service / 业务逻辑) → Segment, Human, VideoComposition, Caption
Layer 2: Adapter (Provider Client) → KlingJob, KlingElement, VolcJob, ProviderTaskId
Layer 1: Infrastructure (DB / Redis / HTTP) → 底层技术术语
```
### 术语对照表
| 业务概念 | 官方术语 | 使用层级 | 禁止使用的别名 |
|---------|---------|---------|--------------|
| 视频分镜 | `Segment` | Layer 3-6 | `shot`, `scene_desc` |
| 数字人形象 | `Human` / `Avatar` | Layer 3-6DB 用 `avatar`API 用 `human_id` | `element`, `character` |
| 调度器工作单元 | `Job` | Layer 4 | `task` |
| 供应商侧任务 | `ProviderJob` | Layer 2 | `kling_task`, `volc_task` |
| 供应商任务 ID | `provider_task_id` | Layer 2-4 | `kling_task_id`, `video_task_id`, `image_task_id` |
| 分镜状态 | `SegmentStatus` | Layer 3-4 | 裸字符串 |
| 调度器状态 | `JobStatus` | Layer 4 | 裸字符串 |
| 形象克隆状态 | `AvatarCloneStatus` | Layer 3 | 裸字符串 |
| Kling 原始状态 | `KlingTaskStatus` | **Layer 2 仅限** | 泄漏到 Layer 3+ |
### 分层禁令
1. **API 层 (`app/api/v1/`)**:禁止出现 `element_id`, `kling_task_id`, `shot_type`, `omni`
2. **Scheduler 层 (`app/scheduler/`)**:禁止出现 `task_id`(应为 `job_id`),禁止构造供应商 prompt 语法
3. **Service 层 (`app/services/`)**:禁止出现 `<<<element_1>>>` 等供应商专用语法
4. **Provider 层 (`app/ai/providers/`)**:允许使用 `element_id`, `kling_task_id`, `KlingTaskStatus`
### 类型禁令
- 跨层传递的接口禁止裸用 `dict[str, Any]`。`params`、`result`、`changes` 等字段必须使用 Pydantic 模型或 TypedDict
- 状态字段禁止使用裸字符串,必须使用对应的 `StrEnum`
- CRUD 层 `obj_in` 禁止裸字典,必须使用 `CreateSchema` / `UpdateSchema`