- 修复字幕切换模板后冻结的 bug:ASS.js 新实例在视频播放中创建时收不到 play/playing 事件,RAF 循环不会启动。创建实例后手动触发 play 事件。 - VideoGeneration 页面 overhaul:卡片点击预览、左右箭头导航、换一个素材、 动态按钮文案和占位提示。 - 修复私有音色素材预览播放 trialUrl 的问题,改为播放 sourceUrl。 - 放宽空镜素材匹配逻辑:优先满足时长,fallback 到最近时长并随机选择。 - 隐藏脚本生成页面的时长滑块。 - 修复登录页和侧边栏标题渐变 WebKit 兼容问题。 - 清理旧计划文档、测试文件和临时脚本。 - 更新 Makefile、prompts、materials.json 等配置。
35 KiB
美家卡智剪 (Meijiaka Smart Cut) - AI 视频剪辑桌面应用
项目概述
美家卡智剪是一个 AI 驱动的短视频剪辑桌面应用,采用 Tauri + React + FastAPI 混合架构。本项目由「美家卡智影」Fork 而来,从"数字人生成"转向"AI 辅助长视频剪辑"方向:用户导入长视频素材或提供口播文案,AI 自动分镜并生成配音,最终合成带字幕的成品短视频。
核心功能流程(6 步)
- 脚本生成 (Step 1) - AI 生成或粘贴口播文案,自动拆分为带预估时长的分镜
- 音频合成 (Step 2) - AI 声音克隆(KlingAI / MiniMax / Vidu)+ TTS 合成,为每段生成配音音频
- 视频生成 (Step 3) - 导入长视频素材并按脚本时长自动切割为片段,或为空镜匹配素材视频
- 字幕压制 (Step 4) - 基于音频自动对齐时间轴,ASS 字幕渲染并压制到视频
- 封面制作 (Step 5) - 使用 Fabric.js 设计封面,提取视频首帧并叠加标题样式
- 视频合成 (Step 6) - FFmpeg 拼接视频片段,替换原声为 TTS 音频,导出成品
技术架构
- 前端桌面壳: Tauri 2.x + React 19 + TypeScript 5.8 + Vite 7
- 后端服务: FastAPI (Python 3.13+) + PostgreSQL 15 + Redis 7
- 任务调度: 自定义 Async Engine(基于 Redis 槽位管理),非 Celery
- 本地处理: 嵌入式 FFmpeg + ffprobe,Rust 层封装视频/音频处理命令
- AI 服务: 火山方舟(LLM/字幕)、可灵 AI(TTS/声音克隆/视频生成)、MiniMax(TTS)、Vidu(TTS/对口型)、OpenAI 兼容接口
项目结构
meijiaka-zj/
├── python-api/ # FastAPI 后端服务(AI 代理 + 认证 + 任务调度)
│ ├── app/
│ │ ├── api/v1/ # API 路由: auth, system, tasks, script, caption, voice, upload, vidu, materials
│ │ ├── ai/ # AI 模型路由、Provider、提示词模板
│ │ ├── core/ # 安全、配置加载、Token管理器、Redis客户端、异常处理
│ │ ├── crud/ # 数据访问层(users)
│ │ ├── db/ # 数据库配置(PostgreSQL + asyncpg + SQLAlchemy 2.0)
│ │ ├── models/ # SQLAlchemy 模型(当前仅 users)
│ │ ├── schemas/ # Pydantic 校验模型
│ │ ├── services/ # AI 服务代理、字幕/视频/TTS/声音克隆/ASS生成服务
│ │ ├── scheduler/ # Async Engine 异步任务调度
│ │ ├── config.py # Pydantic Settings 配置管理
│ │ └── main.py # FastAPI 入口(含生命周期管理)
│ ├── config/ # AI 模型配置文件(ai_models.yaml),支持热重载
│ ├── alembic/ # 数据库迁移
│ ├── pyproject.toml # Python 依赖和工具配置
│ ├── requirements.lock # uv 锁定依赖版本
│ ├── Makefile # 常用命令封装
│ ├── docker-compose.yml
│ ├── Dockerfile
│ └── .env.example # 环境变量模板
│
├── tauri-app/ # Tauri 桌面应用(业务数据本地存储)
│ ├── src/ # React 前端源码
│ │ ├── api/
│ │ │ ├── adapters/ # 数据转换层(前后端字段映射)
│ │ │ ├── generated/ # OpenAPI 自动生成类型(只读)
│ │ │ ├── modules/ # API 模块封装(HTTP + IPC)
│ │ │ ├── client.ts # HTTP 客户端(自动 camelCase↔snake_case)
│ │ │ └── types.ts # 手写核心类型
│ │ ├── components/ # 可复用组件
│ │ ├── pages/ # 页面组件
│ │ │ ├── VideoCreation/ # 6 步视频创作流程
│ │ │ ├── ContentManagement/
│ │ │ ├── Settings/
│ │ │ ├── Profile/
│ │ │ └── Login/
│ │ ├── store/ # Zustand 状态管理(+ Immer + persist)
│ │ ├── hooks/ # 自定义 React Hooks
│ │ ├── styles/ # 全局 CSS 变量、主题
│ │ └── utils/ # 工具函数
│ ├── src-tauri/ # Rust 后端源码
│ │ ├── src/
│ │ │ ├── lib.rs # Tauri 应用入口,命令注册
│ │ │ ├── main.rs # 二进制入口
│ │ │ ├── ffmpeg_cmd.rs # FFmpeg 命令封装
│ │ │ ├── video_processing.rs # 视频合成业务逻辑
│ │ │ ├── api_proxy.rs # Python API 代理转发
│ │ │ ├── auth.rs # 认证命令
│ │ │ ├── avatar_cache.rs # 头像视频缓存管理
│ │ │ ├── utils.rs # 通用工具函数
│ │ │ ├── commands/ # IPC 命令按领域拆分
│ │ │ │ ├── mod.rs
│ │ │ │ ├── asset.rs
│ │ │ │ ├── auth_state.rs
│ │ │ │ ├── avatar.rs
│ │ │ │ ├── product.rs
│ │ │ │ ├── project.rs
│ │ │ │ ├── video_compose.rs
│ │ │ │ └── voice.rs
│ │ │ └── storage/ # 本地存储引擎
│ │ │ ├── mod.rs
│ │ │ ├── engine.rs # 原子写入、文件锁、路径净化
│ │ │ ├── paths.rs # 集中化路径计算
│ │ │ ├── project.rs
│ │ │ ├── auth.rs
│ │ │ ├── avatar.rs
│ │ │ ├── voice.rs
│ │ │ └── cache.rs
│ │ ├── Cargo.toml
│ │ ├── tauri.conf.json
│ │ ├── binaries/ # 嵌入式 FFmpeg / ffprobe
│ │ └── fonts/ # 封面字体(DouyinSansBold.ttf)
│ ├── package.json
│ ├── vite.config.ts
│ ├── tsconfig.json
│ ├── vitest.config.ts
│ ├── eslint.config.js
│ ├── .prettierrc
│ └── .stylelintrc.json
│
├── docs/ # 项目文档(设计文档、API 参考、实施计划)
├── package.json # 根级 package.json(monorepo 占位)
├── .python-version # 3.13
├── CLAUDE.md # 开发者速查手册
└── AGENTS.md # 本文件
基础设施位置:共享 PostgreSQL + Redis 在
/Users/0fun/work/docker-infra/(与项目平级,不在本项目内),必须先启动。
技术栈详解
后端 (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 / 字幕调用 |
| TTS/语音 | KlingAI / MiniMax / Vidu | - | 语音合成、声音克隆 |
| 认证 | python-jose + passlib | 3.4+ / 1.7+ | JWT 认证 |
| HTTP 客户端 | httpx + aiohttp | 0.28+ / 3.13+ | 异步 HTTP |
| 对象存储 | qiniu | 7.13+ | 七牛云 Kodo |
| 包管理/构建 | uv | - | 虚拟环境、依赖锁定、Docker 构建 |
后端架构说明:
- 后端为"轻量云账号 + 全本地业务数据"模式
- 云端仅存储:用户账户信息(
users表) - 业务数据(项目/脚本/媒体/成品)全部本地存储
- 任务调度使用自定义 Async Engine(基于 Redis 的槽位管理),非 Celery
- 当前活跃 API 模块:9 个:
auth,system,tasks,script,caption,voice,upload,vidu,materials - 调度器已注册 7 个 Handler:
Video,Avatar,Image,Subtitle,Copy,Script,TTS
前端 (tauri-app)
| 组件 | 技术 | 版本 | 用途 |
|---|---|---|---|
| 桌面框架 | Tauri | 2.x | 桌面应用壳 |
| UI 框架 | React | 19.1+ | 用户界面 |
| 路由 | 自定义 NavigationContext | - | 页面切换(react-router-dom 已安装但主流程未使用) |
| 状态管理 | 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 类型 |
| 字幕渲染 | assjs | 0.1.x | ASS 字幕前端渲染 |
| 封面设计 | fabric | 6.x | Canvas 封面编辑 |
Rust 后端 (src-tauri/src)
| 模块 | 用途 |
|---|---|
lib.rs |
Tauri 应用入口,命令注册,全局错误处理 |
main.rs |
二进制入口 |
ffmpeg_cmd.rs |
FFmpeg 命令封装(首帧提取、字幕压制、封面合成、音频替换/混音/标准化) |
video_processing.rs |
视频合成业务逻辑 |
api_proxy.rs |
Python API HTTP 代理转发 |
auth.rs |
认证相关 IPC 命令 |
avatar_cache.rs |
头像视频缓存管理、海报生成 |
utils.rs |
项目目录辅助函数 |
storage/engine.rs |
本地存储引擎(原子写入、文件锁、路径净化) |
storage/paths.rs |
集中化路径计算(~/Documents/Meijiaka-zj/ 为根目录) |
commands/project.rs |
项目本地存储 IPC 命令 |
commands/asset.rs |
资源文件保存 IPC 命令 |
commands/auth_state.rs |
认证状态文件持久化 |
commands/voice.rs |
音频文件保存/列表/删除 IPC 命令 |
commands/product.rs |
成品视频保存/列表/删除/重命名 IPC 命令 |
commands/avatar.rs |
头像列表本地存储 |
commands/video_compose.rs |
视频拼接、文件上传/下载 |
开发环境搭建
1. 启动 Python 后端
必须分两步启动:先启动共享基础设施(db/redis),再启动本项目 API + Scheduler。
第 1 步:启动共享基础设施(仅需一次,保持运行)
cd /Users/0fun/work/docker-infra
docker-compose up -d
这会创建:
meijiaka-db(PostgreSQL 15,端口 5432)meijiaka-redis(Redis 7,端口 6379)- 共享 Docker 网络
meijiaka-network - 自动初始化数据库
meijiaka_zj
第 2 步:启动智剪后端(API + Scheduler)
cd /Users/0fun/work/meijiaka-zj/python-api
# 首次需准备环境变量
cp .env.example .env
# 编辑 .env 填入实际的 AI API Keys
# 启动 API + Scheduler(必须加 -p 参数,避免与其他项目冲突)
docker-compose -p meijiaka-zj up -d
⚠️ 必须加
-p meijiaka-zj,否则容器名会和「智影」项目冲突。
启动后会得到 2 个容器:
meijiaka-zj-api→ API 服务,端口 8081→8000meijiaka-zj-scheduler→ Async Engine 调度器
后端服务地址:
Docker Compose 服务组成:
| 容器 | 用途 | 映射端口 |
|---|---|---|
meijiaka-zj-api |
FastAPI 开发服务器 | 8081→8000 |
meijiaka-zj-scheduler |
Async Engine 统一调度器 | 无 |
关键配置(以 docker-compose.yml 为准):
- 数据库:
meijiaka_zj(通过meijiaka-db连接) - Redis DB:
1 - 依赖外部网络
meijiaka-network
验证启动:
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
预期看到 4 个容器:meijiaka-db、meijiaka-redis、meijiaka-zj-api、meijiaka-zj-scheduler。
停止服务:
# 停止智剪(保留基础设施)
cd /Users/0fun/work/meijiaka-zj/python-api
docker-compose -p meijiaka-zj down
# 停止基础设施(通常保持运行)
cd /Users/0fun/work/docker-infra
docker-compose down
2. 启动 Tauri 前端
cd tauri-app
# 安装依赖
npm install
# 开发模式(自动启动 Vite + Tauri)
npm run tauri dev
前端窗口:
- Vite 开发服务器: http://localhost:1420
- 应用窗口: 1440×960(最小 960×640,可调整大小)
构建命令
Python 后端
cd python-api
# 使用 Makefile(推荐)
make dev # 安装开发依赖 + pre-commit 钩子
make lint # ruff + mypy
make format # black + ruff --fix
make format-check # 检查格式但不修改
make test # pytest(当前无测试文件)
make test-cov # 覆盖率报告(当前无测试文件)
make security # bandit + pip-audit
make lint-semantic # 语义层禁词检查
make ci # 运行所有 CI 检查(format-check + lint + lint-semantic + security)
make docker-run # Docker Compose 启动 api + scheduler(需先启动 docker-infra)
make scheduler # 本地启动 Async Engine Scheduler(不推荐,优先用 Docker)
make clean # 清理缓存文件
# 手动命令
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 构建
make docker
Tauri 前端
cd tauri-app
# 开发
npm run dev # 纯 Vite 开发(不启动 Tauri)
npm run tauri dev # 完整 Tauri 开发模式
# 构建
npm run build # 前端生产构建(tsc + vite 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 调用采用 智能路由 策略:
- HTTP 直连 Python: 纯数据 API(脚本生成、模型管理、任务轮询等)
- 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// 封面生成replace_audio_track// 音频替换mix_audio_tracks// 音频混音standardize_audio// 音频标准化compose_video// 视频拼接save_project_meta*/load_project_meta*// 本地文件系统save_project_segments*/load_project_segments*save_project_asset/get_video_save_path/get_image_save_pathsave_final_product/list_local_products/delete_local_product/rename_local_productsave_audio/list_project_audios/delete_audioload_voice_materials/save_voice_material/delete_voice_material_cmdload_avatars_list/save_avatars_listquery_avatar_cache/cache_avatar_video/save_avatar_poster/delete_avatar_cache/get_cache_stats- 认证状态相关:
load_auth_state/save_auth_state/clear_auth_state
添加新 API 流程:
- Python 端实现端点
- 前端直接调用(默认 HTTP)
- 仅当需要本地能力时,在 Rust 中添加命令并在
lib.rs注册
AI Provider 架构
后端 AI 模块采用 多 Provider 路由 设计:
app/ai/
├── model_router.py # 模型路由器(自动降级、YAML 配置热重载)
├── providers/
│ ├── base.py # Provider 抽象基类
│ ├── generic_llm_provider.py # 通用 OpenAI 兼容 Provider(含 Mock)
│ ├── volcengine_provider.py # 火山方舟官方 SDK
│ ├── klingai_provider.py # KlingAI(可灵 AI)
│ ├── minimax_provider.py # MiniMax
│ └── vidu_provider.py # Vidu
└── prompts/ # 提示词模板(禁止硬编码)
├── loader.py
├── system/ # 系统提示词(按 category/subcategory 组织)
├── user/ # 用户提示词模板
├── polish/ # 润色提示词
└── cover/ # 封面生成提示词
支持的 AI 平台:
- 火山方舟 (字节跳动) - LLM、字幕服务
- OpenAI - GPT 系列(可选)
- 可灵 AI (快手) - TTS 语音合成、声音克隆、视频/图片生成
- MiniMax - TTS 语音合成、声音克隆、视频生成
- Vidu - TTS 语音合成、声音克隆、对口型(lip-sync)
- 文心一言 (百度) - 可选
- 通义千问 (阿里云) - 可选
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_tasksSETSlotManager(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 视频文案提取 |
| TTSHandler | 10 | kling:tts_slots |
Kling TTS 语音合成 |
| AvatarHandler | 2 | kling:avatar_slots |
Kling 形象克隆 |
TokenManager(API 认证 Token 管理)
app/core/token_manager.py 提供通用的 API 认证 Token 缓存与自动刷新:
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-zj/projects/{id}/(meta.json, segments.json, assets/, videos/, images/)~/Documents/Meijiaka-zj/products/~/Documents/Meijiaka-zj/avatars.json~/Documents/Meijiaka-zj/voices.json{app_config_dir}/auth.json{app_data_dir}/avatars/(缓存)
所有本地 JSON 读写必须经过 StorageEngine,禁止在命令处理器中直接调用 fs::write。
数据库模型
后端当前仅保留 1 个活跃模型:
users -- 用户账户信息(mobile, nickname, avatar_url)
BaseModel 提供字段:
id: UUID 主键(字符串格式)created_at: 创建时间(UTC)updated_at: 更新时间(UTC)
注意:当前 BaseModel 不包含软删除字段 (deleted_at)。
业务数据本地存储:
- 项目/脚本/分镜 → 前端本地 JSON 文件(
~/Documents/Meijiaka-zj/projects/) - 音频/视频/图片文件 → 本地磁盘
- 成品视频 →
~/Documents/Meijiaka-zj/products/ - 用户配置 → localStorage(少量 UI 状态)
数据流规范
用户输入主题 ──→ 后端 AI 生成脚本 ──→ 后端返回分镜列表 ──→ 前端保存到本地
│ │
└────────────────── 后端不存储脚本数据 ────────────────────────┘
用户导入长视频 ──→ Rust 本地切割 ──→ 生成分段视频 ──→ 前端保存到本地
本地存储结构
~/Documents/Meijiaka-zj/ # 用户文档目录
├── config.json # 全局配置
├── projects/ # 项目数据
│ └── {project_id}/
│ ├── meta.json # 项目元数据
│ ├── segments.json # 分镜数据
│ ├── assets/ # 资源文件(封面、成品等)
│ ├── videos/ # 导入的原始素材 / 切割后的片段
│ └── images/ # 图片素材
├── products/ # 成品视频目录
├── avatars.json # 形象列表(本地)
└── voices.json # 私有音色素材库
项目元数据 meta.json 关键字段:
id,title,topic,status(draft | published)currentStep: 1=脚本生成, 2=音频合成, 3=视频生成, 4=字幕压制, 5=封面制作, 6=视频合成createdAt,updatedAt,exportedAtcoverPath,finalVideoPathcoverConfig,scriptDuration,scriptType
分镜数据 segments.json 字段:
id,type(segment | empty_shot),scene,voiceover,durationvideoPath,videoUrl,humanId,voiceIdalignmentResult,burnedVideoPath,burnedAt
前端导航
主应用壳使用 自定义 NavigationContext(React Context)实现页面切换,映射 Record<PageType, ComponentType>。react-router-dom 已安装但主要用于未来扩展或特定路由场景,当前主流程不使用 BrowserRouter 进行导航。
页面结构:
video-creation- 6 步视频创作流程voice-material- 音色素材库avatar-clone- 形象/声音克隆管理my-works- 我的作品profile- 个人中心usage-detail- 用量详情settings-*- 设置相关页面
状态管理
八个专门的 Zustand store:
| Store | 职责 | 持久化 |
|---|---|---|
authStore |
JWT、UserInfo、登录/登出 | Tauri auth.json(或 localStorage fallback) |
projectStore |
分镜、currentStep、选题、封面配置 | 仅 UI 标志通过 persist;业务数据显式写入本地 JSON |
taskStore |
异步任务状态/进度/消息 | 无(内存 only,真相源在后端 Redis) |
uiStore |
Toast 通知队列 | 无 |
progressStore |
全局进度模态框 | 无 |
settingsStore |
主题模式、用户偏好 | localStorage |
voiceStore |
语音/形象选择状态、音色素材、头像素材 | 无 |
projectStore 不自动保存。数据在显式过渡点持久化到磁盘(如离开 Step 1 时触发 saveMetaToLocalFile)。saveMetaToLocalFile() 通过 Promise 链串行化写入,避免并发覆盖。
开发规范
核心原则
- 后端环境优先使用 Docker Compose: 先启动共享基础设施
docker-infra,再启动本项目docker-compose -p meijiaka-zj up -d。前端默认连接http://127.0.0.1:8081/api/v1。 - 接口契约优先: 后端承诺无论使用什么 AI 模型,输出永远符合同一个 Schema
- 类型单一来源: 后端 Schema 是权威,前端通过 OpenAPI 生成类型
- Adapter 层隔离: 前后端字段差异只允许在 Adapter 层处理
- 数据库分层: API → Service → CRUD → Model,禁止跨层调用
- 提示词文件化: 除前端输入外,后端不允许硬编码任何 Prompt
- 配置统一管理: 所有配置通过
get_settings()读取,禁止直接使用os.getenv() - 本地存储必须经过 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中
添加新配置流程:
- 在
app/config.py的Settings类中添加字段定义 - 在
.env中添加实际值(敏感信息)或使用默认值 - 在服务层通过
get_settings()读取 - 更新
.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-commit(Black、Ruff、uv lock 同步检查)
- 忽略规则: Ruff 忽略
E501, E402, N802, N803, N806, N815, B008, B904
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, trailingComma=es5
- Stylelint:
stylelint-config-standard,禁止 magic px 用于border-radius和font-size - ESLint 规则:
no-console为 warn(允许 warn/error/info),eqeqeq为 warn,no-var为 error
Rust
- 格式化: rustfmt
- 检查: cargo clippy
- 注释: 中文文档注释
提交规范
feat: 新功能
fix: 修复
docs: 文档
refactor: 重构
test: 测试
chore: 构建/工具
测试策略
后端测试
cd python-api
# 运行所有测试
pytest -v
# 覆盖率报告
pytest --cov=app --cov-report=html --cov-report=term
测试配置 (pyproject.toml):
- asyncio_mode = "auto"
- 测试文件命名:
test_*.py - 测试路径:
tests/
注:当前后端暂无测试文件。
pyproject.toml已配置测试环境,但tests/目录尚未创建。
前端测试
cd tauri-app
# 运行 Vitest
npm run test
# UI 模式
npm run test:ui
# 覆盖率报告
npm run test:coverage
测试配置:
- 测试框架: Vitest 4.x + @testing-library/react 16.3.x + jsdom
- 测试文件:
src/**/*.test.ts(x) - Mock 配置:
src/__tests__/setup.ts - 自动 Mock: localStorage, Tauri API (
@tauri-apps/api/core) - 现有测试:
src/store/__tests__/authStore.test.tsxsrc/store/__tests__/settingsStore.test.tsx
安全注意事项
- SECRET_KEY: 生产环境必须修改为强随机密钥(
get_settings()会在生产环境校验) - CORS: 生产环境限制为实际前端域名,开发环境
DEBUG=true时允许所有来源 - API Keys: 不要提交到 Git,使用
.env文件注入 - FFmpeg: 嵌入的二进制文件需验证来源
- 文件上传: 限制文件类型和大小,防止攻击
- 路径遍历: Rust StorageEngine 的
sanitize_id()和sanitize_filename()防御路径遍历攻击 - 原子写入: 所有本地 JSON 使用
atomic_write_json(先写.tmp再rename) - 文件锁: 并发 RMW 操作使用
with_file_lock防止竞态 - 日志: 后端日志写入
~/Documents/Meijiaka-zj/logs/api_YYYYMMDD.log
配置说明
Python 后端 (.env)
关键环境变量:
# 数据库 (PostgreSQL)
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/meijiaka_zj
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=1
# 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
MINIMAX_API_KEY=sk-api-your-minimax-key
VIDU_API_KEY=your-vidu-api-key
# 七牛云存储
QINIU_ACCESS_KEY=your-qiniu-ak
QINIU_SECRET_KEY=your-qiniu-sk
# AnyToCopy 文案提取
ANYTOCOPY_API_KEY=your-anytocopy-key
ANYTOCOPY_API_SECRET=your-anytocopy-secret
完整环境变量模板见 python-api/.env.example。
Tauri 前端配置
- 产品标识:
cn.meijiaka.ai-jian - 版本:
0.1.0 - 窗口: 1440×960,最小 960×640,可调整大小
- Vite 端口: 1420(
strictPort: true) - Python API 地址:
http://127.0.0.1:8081/api/v1 - Asset Protocol: 已启用,允许访问
$APPLOCALDATA/**,$APPDATA/**,$APPCONFIG/**,/**
关键文件速查
| 文件 | 用途 |
|---|---|
python-api/app/main.py |
FastAPI 应用入口 |
python-api/app/config.py |
Pydantic Settings 配置管理 |
python-api/app/api/v1/router.py |
API 路由聚合 |
python-api/app/scheduler/main.py |
Async Engine 调度器入口 |
python-api/app/core/token_manager.py |
API Token 缓存与自动刷新 |
python-api/config/ai_models.yaml |
AI 模型配置(热重载) |
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/storage/engine.rs |
核心存储引擎 |
tauri-app/src-tauri/src/ffmpeg_cmd.rs |
FFmpeg 命令封装 |
附加文档
项目 docs/ 目录包含详细的深度开发文档:
| 文档 | 主题 |
|---|---|
docs/unified-async-scheduler.md |
统一异步调度器设计 |
docs/volcengine-video-caption-api.md |
火山引擎字幕 API 对接 |
docs/kling-api-dev.md |
Kling AI API 开发文档 |
docs/vidu-tts-api.md |
Vidu TTS API 集成 |
docs/minimax-api-dev.md |
MiniMax API 开发文档 |
docs/anytocopy-api.md |
AnyToCopy API 集成 |
docs/anytocopy-integration.md |
AnyToCopy 服务集成说明 |
docs/qiniu-kodo-python-sdk-guide.md |
七牛云 Kodo Python SDK 指南 |
docs/app-update-system.md |
应用更新系统设计 |
docs/database-design.md |
数据库设计文档 |