feat: init meijiaka-zj project from ai-meijiaka template

This commit is contained in:
小鱼开发
2026-04-20 16:39:57 +08:00
commit 74983ce5ec
291 changed files with 76164 additions and 0 deletions
+784
View File
@@ -0,0 +1,784 @@
<!-- From: /Users/0fun/work/ai-meijiaka/AGENTS.md -->
# 美家卡智影 (Meijiaka AI Video) - AI 视频创作平台
## 项目概述
美家卡智影是一个 AI 驱动的视频创作桌面应用,采用 **Tauri + React + FastAPI** 混合架构。用户可以通过 AI 生成脚本、创建数字人视频,最终合成完整的营销视频。
### 核心功能
- **AI 脚本生成**: 基于 LLM 自动生成视频脚本和分镜
- **数字人视频**: 基于 KlingAI 创建数字人视频片段
- **字幕生成**: 基于火山引擎豆包语音自动生成字幕并压制到视频
- **封面制作**: 提取视频首帧并叠加字幕样式生成封面
- **视频合成**: 本地 FFmpeg 处理视频拼接、音频混流、导出成品
- **形象克隆**: 基于 KlingAI 的自定义数字人形象管理
- **项目管理**: 项目数据本地 JSON 文件存储,认证状态云端同步
## 项目结构
```
ai-meijiaka/
├── python-api/ # FastAPI 后端服务(AI 代理 + 认证 + 任务调度)
│ ├── app/
│ │ ├── api/v1/ # API 路由 (REST): auth, script, ai_models, klingai,
│ │ │ # qiniu, video, avatar, system,
│ │ │ # caption, tasks
│ │ ├── ai/ # AI 模型路由、Provider、提示词模板
│ │ ├── core/ # 安全、配置加载、Token管理器、Redis客户端、异常处理
│ │ ├── crud/ # 数据访问层(users, model_usage, avatar
│ │ ├── db/ # 数据库配置(PostgreSQL + asyncpg + SQLAlchemy 2.0
│ │ ├── models/ # SQLAlchemy 模型(users, model_usage_logs, avatars
│ │ ├── schemas/ # Pydantic 校验模型
│ │ ├── services/ # AI 服务代理、DTO标准化、七牛/字幕/视频服务
│ │ ├── scheduler/ # Async Engine 异步任务调度(video, image, script,
│ │ │ # subtitle, copy, avatar_clone
│ │ ├── config.py # Pydantic Settings 配置管理
│ │ └── main.py # FastAPI 入口(含生命周期管理)
│ ├── config/ # AI 模型配置文件(ai_models.yaml),支持热重载
│ ├── alembic/ # 数据库迁移
│ ├── scripts/ # 初始化/测试脚本
│ ├── pyproject.toml # Python 依赖和工具配置
│ ├── requirements.lock # uv 锁定依赖版本
│ ├── Makefile # 常用命令封装
│ ├── docker-compose.yml
│ └── Dockerfile
├── tauri-app/ # Tauri 桌面应用(业务数据本地存储)
│ ├── src/ # React 前端源码
│ │ ├── api/
│ │ │ ├── adapters/ # 数据转换层(前后端字段映射)
│ │ │ ├── generated/ # OpenAPI 自动生成类型(只读)
│ │ │ ├── modules/ # API 模块封装(HTTP + IPC
│ │ │ ├── client.ts # HTTP 客户端(自动 camelCase↔snake_case
│ │ │ ├── types.ts # 手写核心类型
│ │ │ └── ipc.ts # Tauri IPC 调用封装
│ │ ├── components/ # 可复用组件
│ │ ├── pages/ # 页面组件
│ │ ├── store/ # Zustand 状态管理(+ Immer + persist
│ │ ├── hooks/ # 自定义 React Hooks
│ │ ├── styles/ # 全局 CSS 变量、主题
│ │ └── utils/ # 工具函数
│ ├── src-tauri/ # Rust 后端源码
│ │ ├── src/
│ │ │ ├── lib.rs # Tauri 应用入口,命令注册
│ │ │ ├── ffmpeg_cmd.rs # FFmpeg 命令封装
│ │ │ ├── video_processing.rs # 视频合成业务逻辑
│ │ │ ├── storage/ # 本地存储引擎(原子写入、文件锁、路径净化)
│ │ │ ├── commands/ # IPC 命令按领域拆分(project/asset/auth/avatar
│ │ │ ├── api_proxy.rs # Python API 代理转发
│ │ │ ├── auth.rs # 认证命令(已迁移至 commands/auth_state.rs
│ │ │ ├── avatar_cache.rs # 头像缓存管理
│ │ │ └── utils.rs # 通用工具函数
│ │ ├── Cargo.toml
│ │ ├── tauri.conf.json
│ │ └── binaries/ # 嵌入式 FFmpeg
│ ├── package.json
│ ├── vite.config.ts
│ ├── tsconfig.json
│ └── eslint.config.js
└── docs/ # 项目文档
├── anytocopy-api.md
├── anytocopy-integration.md
├── app-update-system.md
├── database-design.md
├── kling-api-dev.md
├── migrate-avatars-to-local.md
├── qiniu-kodo-python-sdk-guide.md
├── video-generation-flow.md
└── volcengine-video-caption-api.md
```
## 技术栈
### 后端 (python-api)
**⚠️ Python 版本要求: 3.13+** (项目使用 `|` 类型注解语法)
| 组件 | 技术 | 版本 | 用途 |
|------|------|------|------|
| Python | - | 3.13+ | 运行环境 |
| Web 框架 | FastAPI | 0.116+ | REST API |
| 数据库 | PostgreSQL | 15+ | 用户认证 + 成本统计 + 形象管理 |
| ORM | SQLAlchemy | 2.0 (异步) | 数据模型 |
| 缓存/调度 | Redis + Async Engine | 5.2+ / 自定义 | 异步任务槽位调度 |
| AI SDK | OpenAI / volcengine | 1.58+ / 5.0+ | LLM 调用 |
| 认证 | python-jose + passlib | 3.4+ / 1.7+ | JWT 认证 |
| 对象存储 | qiniu | 7.13+ | 七牛云存储 |
| HTTP 客户端 | httpx + aiohttp | 0.28+ / 3.13+ | 异步 HTTP |
| 包管理/构建 | uv | - | 虚拟环境、依赖锁定、Docker 构建 |
**后端架构说明**
- 后端为"轻量云账号 + 全本地业务数据"模式
- 云端仅存储:用户账户、形象元数据、成本统计
- 业务数据(项目/脚本/媒体)全部本地存储
- 任务调度使用**自定义 Async Engine**(基于 Redis 的槽位管理),**非 Celery**
### 前端 (tauri-app)
| 组件 | 技术 | 版本 | 用途 |
|------|------|------|------|
| 桌面框架 | Tauri | 2.x | 桌面应用壳 |
| UI 框架 | React | 19.1+ | 用户界面 |
| 路由 | React Router DOM | 7.x | 页面路由(主壳使用 NavigationContext |
| 状态管理 | Zustand | 5.x | 全局状态 + Immer 中间件 |
| 数据获取 | SWR | 2.x | 请求缓存 |
| 虚拟列表 | @tanstack/react-virtual | 3.x | 大数据列表渲染 |
| 构建工具 | Vite | 7.x | 构建、开发服务器 |
| 测试 | Vitest + @testing-library | 4.x | 单元测试 |
| 类型生成 | openapi-typescript | 7.x | 从 OpenAPI 生成 TS 类型 |
### Rust 后端 (src-tauri/src)
| 模块 | 用途 |
|------|------|
| lib.rs | Tauri 应用入口,命令注册 |
| ffmpeg_cmd.rs | FFmpeg 命令封装(首帧提取、字幕压制、封面合成) |
| video_processing.rs | 视频合成业务逻辑 |
| storage/engine.rs | 本地存储引擎(原子写入、文件锁、路径净化) |
| storage/paths.rs | 集中化路径计算 |
| commands/project.rs | 项目本地存储 IPC 命令 |
| commands/asset.rs | 资源文件保存 IPC 命令 |
| commands/auth_state.rs | 认证状态文件持久化 |
| api_proxy.rs | Python API 代理转发 |
| avatar_cache.rs | 头像视频缓存管理 |
## 开发环境搭建
### 1. 启动 Python 后端
```bash
cd python-api
# 方式一:Docker Compose(推荐)
cp .env.example .env
docker-compose up -d
# 方式二:本地开发(若 Docker 不可用)
# 启动 PostgreSQL 和 Redis
docker-compose up -d db redis
# 安装依赖(使用 uv
uv pip install -e ".[dev]"
# 启动开发服务器(注意:Docker API 会占用 8080 端口)
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# 另开终端启动 Async Engine Scheduler(必须同时启动,否则任务不会执行)
python -m app.scheduler.main
```
后端服务地址:
- API: http://localhost:8080/api/v1
- 文档: http://localhost:8080/docs
- 健康检查: http://localhost:8080/health
**Docker Compose 服务组成**4 个服务):
- `db`: PostgreSQL 15
- `redis`: Redis 7
- `api`: FastAPI 开发服务器(端口 8080→8000)
- `scheduler`: Async Engine 统一调度器,处理所有第三方异步任务
### 2. 启动 Tauri 前端
```bash
cd tauri-app
# 安装依赖
npm install
# 开发模式(自动启动 Vite + Tauri
npm run tauri dev
```
前端窗口:
- Vite 开发服务器: http://localhost:1420
- 应用窗口: 1440×960(最小 960×640,可调整大小)
## 构建命令
### Python 后端
```bash
cd python-api
# 使用 Makefile(推荐)
make dev # 安装开发依赖 + pre-commit 钩子
make lint # ruff + mypy
make format # black + ruff --fix
make test # pytest
make test-cov # 覆盖率报告
make security # bandit + pip-audit
make lint-semantic # 语义层禁词检查
make ci # 运行所有 CI 检查(format-check + lint + lint-semantic + test + security
make docker-run # Docker Compose 启动全部服务
make scheduler # 启动 Async Engine Scheduler
# 手动命令
black app/
ruff check app/
mypy app/
bandit -c pyproject.toml -r app/
pip-audit
pytest
pytest --cov=app
# 导出 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
# Docker 构建
docker build -t meijiaka-api .
```
### Tauri 前端
```bash
cd tauri-app
# 开发
npm run dev # 纯 Vite 开发(不启动 Tauri
npm run tauri dev # 完整 Tauri 开发模式
# 构建
npm run build # 前端生产构建
npm run tauri build # 打包桌面应用
# 测试
npm run test # 运行 Vitest
npm run test:ui # UI 模式
npm run test:coverage # 覆盖率报告
# 代码质量
npm run lint # ESLint 检查
npm run lint:fix # ESLint 自动修复
npm run format # Prettier 格式化
npm run format:check # Prettier 格式检查
npm run stylelint # CSS 检查
npm run stylelint:fix # CSS 自动修复
# 类型生成
npm run gen:api # 从 OpenAPI 生成 TypeScript 类型
```
## 架构说明
### 混合路由架构
前端 API 调用采用 **智能路由** 策略:
1. **HTTP 直连 Python**: 纯数据 API(脚本生成、模型管理、任务轮询等)
2. **Tauri IPC → Rust**: 需要本地能力的 API(FFmpeg、文件系统)
路由决策在 `tauri-app/src/api/client.ts` 中实现。HTTP 客户端会自动处理 `camelCase``snake_case` 字段名转换。需要走 Rust IPC 的 API 包括:
- `video_composite_synthesis` // FFmpeg 视频合成
- `burn_subtitle` // 字幕压制
- `extract_video_first_frame` // 首帧提取
- `generate_cover_image` // 封面生成
- `save_project_meta*` / `load_project_meta*` // 本地文件系统
- `save_project_segments*` / `load_project_segments*`
- `save_project_asset` / `get_video_save_path` / `get_image_save_path`
- `save_final_product`
- 头像缓存相关 API
**添加新 API 流程**
1. Python 端实现端点
2. 前端直接调用(默认 HTTP
3. 仅当需要本地能力时,在 Rust 中添加命令并在 `lib.rs` 注册
### AI Provider 架构
后端 AI 模块采用 **多 Provider 路由** 设计:
```
app/ai/
├── model_router.py # 模型路由器(自动降级)
├── providers/
│ ├── base.py # Provider 抽象基类
│ ├── generic_llm_provider.py # 通用 OpenAI 兼容 Provider
│ ├── volcengine_provider.py # 火山方舟官方 SDK
│ └── klingai_provider.py # KlingAI 数字人
└── prompts/ # 提示词模板(禁止硬编码)
```
支持的 AI 平台:
- **火山方舟** (字节跳动) - 推荐,性价比高
- **OpenAI** - GPT 系列
- **文心一言** (百度)
- **通义千问** (阿里云)
- **可灵 AI** (快手) - 视频生成、数字人、形象克隆
AI 模型配置位于 `python-api/config/ai_models.yaml`,支持热重载,无需重启服务即可更新模型配置。
### Async Engine(异步任务调度)
**⚠️ 重要:项目不使用 Celery,使用自定义 Async Engine**
架构:
```
API (POST /tasks/{type}) → Redis JobRegistry → AsyncEngine tick loop → Handlers
```
组件:
- **`AsyncEngine`** (`app/scheduler/engine.py`): 每 ~10s 执行 `tick()`,加载运行中任务,按类型分组,并行分发给 Handler,通过 Pipeline 应用 `StateChange`,清理已完成任务
- **`JobRegistry`** (`app/scheduler/registry.py`): Redis-based 任务 CRUD,使用 `job:{id}` hash + `scheduler:running_tasks` SET
- **`SlotManager`** (`app/scheduler/slot_manager.py`): Redis Lua 原子脚本实现并发槽位抢占/释放
- **`JobRecord`** / **`StateChange`** (`app/scheduler/models.py`): 调度器内部类型
已注册的 Handler`app/scheduler/main.py`):
| Handler | 槽位数 | Redis Key | 用途 |
|---------|--------|-----------|------|
| VideoHandler | 18 | `kling:video_slots` | Kling 视频生成(omni + image2video |
| ImageHandler | 9 | `kling:image_slots` | Kling 图片生成 |
| ScriptHandler | 10 | `script:slots` | LLM 脚本生成(含 AnyToCopy 视频文案提取) |
| SubtitleHandler | 5 | `volc:subtitle_slots` | 火山引擎字幕/自动对齐 |
| CopyHandler | 5 | `anytocopy:slots` | AnyToCopy 视频文案提取 |
| AvatarHandler | 2 | `kling:avatar_slots` | Kling 形象克隆(状态机: pending→voice_processing→element_pending→element_processing→succeed |
### TokenManagerAPI 认证 Token 管理)
`app/core/token_manager.py` 提供通用的 API 认证 Token 缓存与自动刷新:
```python
from app.core.token_manager import JWTTokenStrategy, TokenManager
class MyProvider:
def __init__(self, access_key: str, secret_key: str):
self._token_strategy = JWTTokenStrategy(
access_key=access_key,
secret_key=secret_key,
expires_in=1800, # 30分钟
)
async def _get_headers(self) -> dict[str, str]:
token_info = await TokenManager.get_instance().get_token(self._token_strategy)
return {"Authorization": f"Bearer {token_info.token}"}
```
**特性**
- Token 缓存(避免重复生成)
- 自动刷新(Token 即将过期时自动刷新)
- 并发安全(双重检查锁定,确保并发请求只生成一次 Token)
- 后台预热(提前 10 分钟刷新,避免请求时等待)
- 支持 JWT、OAuth2 等多种策略
### 本地存储引擎(Rust
Rust 层实现了 defense-in-depth 的本地存储系统:`src-tauri/src/storage/`
- **`engine.rs`**: 核心原子操作
- `sanitize_id()` — 白名单 `[a-zA-Z0-9_-]+`,防御路径遍历
- `sanitize_filename()` — 提取纯文件名,拒绝目录组件
- `atomic_write_json()` / `atomic_write_bytes()` — 先写 `.tmp``rename` 原子替换
- `with_file_lock()` — 通过 `fs2` 实现独占文件锁
- `read_json<T>()` — 安全读取,文件不存在返回 `None`
- **`paths.rs`**: 集中路径计算
- `~/Documents/Meijiaka/projects/{id}/` (meta.json, segments.json, assets/)
- `~/Documents/Meijiaka/products/`
- `{app_config_dir}/auth.json`
- `{app_data_dir}/avatars/`
**所有本地 JSON 读写必须经过 StorageEngine,禁止在命令处理器中直接调用 `fs::write`**
### 数据库模型
后端仅保留 **3 个表**
```
users -- 用户账户信息(mobile, nickname, avatar_url
model_usage_logs -- 大模型调用记录(token, 成本, 响应时间)
avatars -- 克隆形象元数据(云端备份,前端已迁移至本地 JSON)
```
**业务数据本地存储**
- 项目/脚本/分镜 → 前端本地 JSON 文件(`~/Documents/Meijiaka/projects/`
- 音频/视频/图片文件 → 本地磁盘
- 用户配置 → localStorage(少量 UI 状态)
### 数据流规范
```
用户输入主题 ──→ 后端 AI 生成脚本 ──→ 后端返回分镜列表 ──→ 前端保存到本地
│ │
└────────────────── 后端不存储脚本数据 ────────────────────────┘
```
### 本地存储结构
```
~/Documents/Meijiaka/ # 用户文档目录
├── config.json # 全局配置
├── projects/ # 项目数据
│ └── {project_id}/
│ ├── meta.json # 项目元数据
│ ├── segments.json # 分镜数据
│ └── assets/ # 资源文件(封面、成品等)
├── products/ # 成品视频目录
├── avatars.json # 形象列表(本地)
└── cache/ # 缓存目录
```
项目元数据 `meta.json` 关键字段:
- `id`, `title`, `topic`, `status` (draft | published)
- `currentStep`: 1=脚本生成, 2=形象视频, 3=字幕压制, 4=封面制作, 5=视频合成
- `createdAt`, `updatedAt`, `exportedAt`
- `coverPath`, `finalVideoPath`
- `selectedElementId`, `selectedHumanId`
- `coverConfig`, `scriptDuration`, `scriptType`
分镜数据 `segments.json` 字段:
- `id`, `type` (segment | empty_shot), `scene`, `voiceover`, `duration`
- `videoPath`, `videoUrl`, `elementId`, `voiceId`
- `alignmentResult`, `burnedVideoPath`, `burnedAt`
### 前端导航
主应用壳使用 **自定义 NavigationContext**React Context)实现页面切换,映射 `Record<PageType, ComponentType>``react-router-dom` 已安装但主要用于未来扩展或特定路由场景,当前主流程不使用 BrowserRouter 进行导航。
### 状态管理
六個專門的 Zustand store
| Store | 职责 | 持久化 |
|-------|------|--------|
| `authStore` | JWT、UserInfo、登录/登出 | Tauri `auth.json`(或 localStorage fallback |
| `projectStore` | 分镜、currentStep、选题、封面配置 | **仅 UI 标志**通过 `persist`;业务数据显式写入本地 JSON |
| `taskStore` | 异步任务状态/进度/消息 | **无**(内存 only,真相源在后端 Redis |
| `uiStore` | Toast 通知队列 | 无 |
| `progressStore` | 全局进度模态框 | 无 |
| `settingsStore` | 主题模式、用户偏好 | localStorage |
`projectStore` **不自动保存**。数据在显式过渡点持久化到磁盘(如进入 step 2、调用 `setFinalVideoPath` 时触发 `saveMetaToLocalFile`)。`saveMetaToLocalFile()` 通过 Promise 链串行化写入,避免并发覆盖。
## 开发规范
### 核心原则
1. **后端环境优先使用 Docker Compose**: 开发时通过 `docker-compose up -d` 启动后端。前端默认连接 `http://127.0.0.1:8080/api/v1`
2. **接口契约优先**: 后端承诺无论使用什么 AI 模型,输出永远符合同一个 Schema
3. **类型单一来源**: 后端 Schema 是权威,前端通过 OpenAPI 生成类型
4. **Adapter 层隔离**: 前后端字段差异只允许在 Adapter 层处理
5. **数据库分层**: API → Service → CRUD → Model,禁止跨层调用
6. **提示词文件化**: 除前端输入外,后端不允许硬编码任何 Prompt
7. **配置统一管理**: 所有配置通过 `get_settings()` 读取,禁止直接使用 `os.getenv()`
8. **本地存储必须经过 StorageEngine**: Rust 层所有文件操作使用 `atomic_write_json` + `with_file_lock`
### 配置管理规范
**架构层级:**
```
.env (Layer 1) ──→ Settings (Layer 2) ──→ 服务层 (Layer 3)
唯一配置出口
```
**强制规范:**
- **所有服务**必须使用 `from app.config import get_settings` 读取配置
- **禁止**在服务层使用 `os.getenv()``os.environ.get()`
- **所有配置项**必须在 `app/config.py``Settings` 类中定义
- **敏感信息**API Keys、Secrets)必须通过环境变量注入
- **业务默认值**可以硬编码在 `Settings`
**添加新配置流程:**
1.`app/config.py``Settings` 类中添加字段定义
2.`.env` 中添加实际值(敏感信息)或使用默认值
3. 在服务层通过 `get_settings()` 读取
4. 更新 `.env.example` 文档
### 语义层防护网
项目强制执行语义分层,禁止供应商术语泄漏到业务层:
| 层级 | 职责 | 禁词示例 |
|------|------|----------|
| Layer 6 (Presentation) | API Schema | `element_id`, `kling_task_id` |
| Layer 4 (Orchestration) | Scheduler | `task_id`(应使用 `job_id` |
| Layer 3 (Domain) | Service | 供应商特定术语 |
| Layer 2 (Adapter) | Provider | 允许使用供应商原生术语 |
Makefile 提供 `make lint-semantic` 进行自动化检查:
- API 层(除 `klingai.py`)禁止使用 `element_id`(应使用 `provider_element_id``human_id`
- Scheduler 层禁止使用 `task_id`(应使用 `job_id`
- 全局禁止 `kling_task_id`(应使用 `provider_task_id`
- Scheduler Redis key 必须使用 `job:` 而非 `task:`
### 快速参考
| 场景 | 正确做法 |
|------|---------|
| 后端换 AI 模型 | 修改 `services/ai_response_utils.py` 标准化层,不修改 Schema |
| 后端新增字段 | `Optional[T] = Field(None)`,向后兼容 |
| 后端修改字段 | 保留旧字段,标记 deprecated,逐步迁移 |
| 前端需要新字段 | Store 中 `extends` 基础类型 |
| 数据清洗 | **只在** Adapter 层,禁止在组件层 |
| 新增数据库实体 | 创建 Model → CRUD → API(分层开发)|
| 数据库查询 | 在 CRUD 层封装,API 层调用 |
| 事务管理 | API 层控制,通过 `get_db` 依赖注入 |
| 新增提示词 | 创建 `.txt` 文件,使用 `_load_prompt()` 加载 |
| 新增本地文件操作 | 使用 `storage::engine` 原子写入 + 文件锁 |
### 后端分层架构
```
API Layer (api/v1/*.py)
↓ 调用
Service Layer (services/*.py) - 可选,复杂业务
↓ 调用
CRUD Layer (crud/*.py)
↓ 调用
Model Layer (models/*.py)
↓ 调用
Database Layer (db/*.py)
```
**禁止**
- API 层直接操作 Model
- CRUD 层返回 Schema(应返回 Model
- Service 层直接操作数据库(应通过 CRUD)
- 在业务代码中写 SQL
### 前端类型规范
| 层级 | 类型来源 | 说明 |
|------|----------|------|
| 后端 Schema | `python-api/app/schemas/*.py` | Pydantic 模型,OpenAPI 生成源 |
| 前端基础类型 | `tauri-app/src/api/types.ts` | 手写的核心类型,与后端对齐 |
| 前端完整类型 | `tauri-app/src/api/generated/schema.ts` | OpenAPI 自动生成,只读 |
| Store 扩展 | `tauri-app/src/store/*.ts` | `extends` 基础类型添加前端字段 |
### 间距规范
前端使用基于 4px 的网格系统,定义在 `tauri-app/src/styles/variables.css`
| 变量 | 值 | 使用场景 |
|------|-----|----------|
| `--spacing-2xs` | 2px | 微调控件、边框线 |
| `--spacing-xs` | 4px | 紧凑间隙、图标边距 |
| `--spacing-sm` | 8px | 小间隙、按钮内边距-y |
| `--spacing-md` | 12px | 标准间隙、卡片内边距 |
| `--spacing-lg` | 16px | 大间隙、区块间距 |
| `--spacing-xl` | 24px | 页面区块、内容分隔 |
| `--spacing-2xl` | 32px | 大区块间距、页面边距 |
| `--spacing-3xl` | 48px | 页面级间距、Hero 区域 |
## 代码风格
### Python
- **格式化**: Black (line-length: 100, target-version: py313)
- **检查**: Ruff (E, F, I, N, W, UP, B, C4, SIM)
- **类型**: MyPy(非严格模式全局,但 `app.schemas.*``app.crud.*``app.scheduler.handlers.*` 强制严格模式)
- **文档**: 中文注释,Google Style Docstrings
- **安全**: Bandit + pip-audit
- **Git Hooks**: pre-commitBlack、Ruff、uv lock 同步检查)
### TypeScript/React
- **类型**: 严格 TypeScript 模式(`strict: true`, `noUnusedLocals: true`, `noUnusedParameters: true`
- **组件**: 函数组件 + Hooks
- **状态**: Zustand 管理全局状态(配合 Immer 处理不可变更新)
- **样式**: 普通 CSS + CSS 变量(`tauri-app/src/styles/variables.css`
- **ESLint**: 使用 `eslint.config.js`Flat Config),含 React Hooks 和 React Refresh 规则
- **Prettier**: semi=true, singleQuote=true, tabWidth=2, printWidth=100
- **Stylelint**: `stylelint-config-standard`,禁止 magic px 用于 `border-radius``font-size`
### Rust
- **格式化**: rustfmt
- **检查**: cargo clippy
- **注释**: 中文文档注释
### 提交规范
```
feat: 新功能
fix: 修复
docs: 文档
refactor: 重构
test: 测试
chore: 构建/工具
```
## 测试策略
### 后端测试
```bash
cd python-api
# 运行所有测试
pytest -v
# 覆盖率报告
pytest --cov=app --cov-report=html --cov-report=term
```
**测试配置** (`pyproject.toml`):
- asyncio_mode = "auto"
- 测试文件命名: `test_*.py`
> **注**:当前项目中 `python-api/tests/` 目录尚未创建,后端测试待补充。
### 前端测试
```bash
cd tauri-app
# 运行 Vitest
npm run test
# UI 模式
npm run test:ui
# 覆盖率报告
npm run test:coverage
```
**测试配置**:
- 测试框架: Vitest 4.x + @testing-library/react + jsdom
- 测试文件: `src/**/*.test.ts(x)`
- Mock 配置: `src/__tests__/setup.ts`
- 自动 Mock: localStorage, Tauri API (`@tauri-apps/api/core`)
- 示例测试: `src/store/__tests__/authStore.test.tsx`
## 安全注意事项
1. **SECRET_KEY**: 生产环境必须修改为强随机密钥(`get_settings()` 会在生产环境校验)
2. **CORS**: 生产环境限制为实际前端域名,开发环境 `DEBUG=true` 时允许所有来源
3. **API Keys**: 不要提交到 Git,使用 `.env` 文件注入
4. **FFmpeg**: 嵌入的二进制文件需验证来源
5. **文件上传**: 限制文件类型和大小,防止攻击
6. **路径遍历**: Rust StorageEngine 的 `sanitize_id()``sanitize_filename()` 防御路径遍历攻击
7. **原子写入**: 所有本地 JSON 使用 `atomic_write_json`(先写 `.tmp``rename`
8. **文件锁**: 并发 RMW 操作使用 `with_file_lock` 防止竞态
9. **日志**: 后端日志写入 `~/Documents/Meijiaka/logs/api_YYYYMMDD.log`
## 配置说明
### Python 后端 (.env)
关键环境变量:
```bash
# 数据库 (PostgreSQL)
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/meijiaka
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
# JWT 密钥(生产环境必须修改)
SECRET_KEY=your-secret-key-here-change-in-production
ACCESS_TOKEN_EXPIRE_MINUTES=10080
# AI API Keys
VOLCENGINE_API_KEY=your-volcengine-key
VOLCENGINE_CAPTION_APPID=your-caption-appid
VOLCENGINE_CAPTION_TOKEN=your-caption-token
KLINGAI_ACCESS_KEY=your-kling-access-key
KLINGAI_SECRET_KEY=your-kling-secret-key
OPENAI_API_KEY=sk-your-openai-key
# 七牛云存储
QINIU_ACCESS_KEY=your-qiniu-access-key
QINIU_SECRET_KEY=your-qiniu-secret-key
QINIU_VIDEO_BUCKET=media-liche
QINIU_IMAGE_BUCKET=img-liche
# CORS 允许的前端地址
CORS_ORIGINS=http://localhost:1420,http://127.0.0.1:1420,http://localhost:8080
```
### Tauri 配置 (tauri.conf.json)
```json
{
"productName": "美家卡智影",
"identifier": "cn.meijiaka.ai-video",
"build": {
"devUrl": "http://localhost:1420",
"frontendDist": "../dist"
},
"bundle": {
"externalBin": ["binaries/ffmpeg"],
"resources": {
"fonts/*": "fonts/"
}
}
}
```
### AI 模型配置 (config/ai_models.yaml)
模型配置文件支持热重载,无需重启服务即可更新模型配置。主要配置项:
- **platforms**: AI 平台配置(mock, volcengine, klingai
- **models**: 可用模型列表及其能力标签 [script, polish, chat, image, embedding, vision]
- **task_defaults**: 任务类型到模型的默认映射
## 视频创作流程
1. **脚本生成** (Step 1) - AI 生成视频脚本和分镜
2. **形象视频** (Step 2) - 选择数字人形象,生成视频片段
3. **字幕压制** (Step 3) - 生成字幕并压制到视频中
4. **封面制作** (Step 4) - 生成视频封面
5. **视频合成** (Step 5) - FFmpeg 拼接视频片段,导出最终视频
## 常见问题
### Q: 火山方舟如何配置?
1. 注册火山引擎账号并实名认证
2. 创建 API Key
3. 开通模型并创建推理接入点
4.`.env` 中设置 `VOLCENGINE_API_KEY`
### Q: 可灵 AI 如何配置?
1. 前往可灵 AI 开发者平台 https://klingai.com/document-api
2. 获取 Access Key 和 Secret Key
3.`.env` 中设置 `KLINGAI_ACCESS_KEY``KLINGAI_SECRET_KEY`
### Q: FFmpeg 在哪里?
Tauri 应用已嵌入 FFmpeg 二进制文件:
- 位置: `tauri-app/src-tauri/binaries/ffmpeg-*`
- 使用: Rust 层通过 `ffmpeg_cmd` 模块调用
- 打包时会作为 `externalBin` 资源嵌入
### Q: 后端换了 AI 模型,输出格式变了怎么办?
修改 `services/ai_response_utils.py` 中的标准化函数,增加新的字段映射,**不要**修改 API Schema。
### Q: 如何新增/修改提示词?
1. 创建文件: `app/ai/prompts/my_prompt.txt`
2. 加载使用: `prompt = self._load_prompt("my_prompt")`
3. **禁止**: 在 Python 代码中直接写 `"""你是一位..."""`
### Q: 项目数据是如何持久化的?
- 项目元数据(`meta.json`)和分镜数据(`segments.json`)保存在 `~/Documents/Meijiaka/projects/{project_id}/`
- 不通过 Zustand `persist` 保存项目数据,而是通过 `localProjectApi` 显式调用 Tauri IPC 写入文件
- `projectStore``persist` 中间件仅保存少量 UI 状态
### Q: Async Engine 和 Celery 有什么区别?
本项目使用**自定义 Async Engine** 替代 Celery
- 基于 Redis 的槽位管理(SlotManager),限制各类型任务的并发数
- 独立的 `scheduler` 进程(`python -m app.scheduler.main`
- 每个 Handler 实现 `AsyncHandler` 接口,状态机驱动任务生命周期
- 优势:更细粒度的并发控制、统一状态机、无 Celery 依赖
---
**最后更新**: 2026-04-17
**架构模式**: 单机版(轻量云账号 + 全本地业务数据)