docs: API文档准确性修复
1. 修正上传端点schema命名:新增UploadAssetRequest模型替代自动生成的Body_upload_asset名称 2. 清理认证重复端点:移除/forgot-password和/reset-password旧路由,保留/password/forgot和/password/reset 3. 清理workspace_id残留:从edit_plan/asset_diagnosis/task_center/project_title等schema中移除,project_management和projects中改为可选 4. 更新README:移除过时的多租户/订阅/refresh token描述,修正仓库地址为git.xiaoxiajianji.com,更新API路径列表
This commit is contained in:
@@ -1,70 +1,68 @@
|
|||||||
# 小虾 SaaS - 自动化剪辑 SaaS 平台
|
# 小虾 SaaS - 自动化视频剪辑平台
|
||||||
|
|
||||||
[](https://opensource.org/licenses/MIT)
|
|
||||||
[](https://www.python.org/downloads/)
|
[](https://www.python.org/downloads/)
|
||||||
[](https://fastapi.tiangolo.com)
|
[](https://fastapi.tiangolo.com)
|
||||||
[](https://www.postgresql.org/)
|
[](https://www.postgresql.org/)
|
||||||
|
|
||||||
一个功能完整、生产就绪的多租户 SaaS 平台,专为自动化视频剪辑服务设计。
|
自动化视频剪辑 SaaS 平台,支持素材上传、AI 分类、智能剪辑计划生成、自动化视频合成与成片管理。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✨ 核心特性
|
## ✨ 核心功能
|
||||||
|
|
||||||
### 🎬 视频剪辑主链路(Phase 7 已完成)
|
### 🎬 视频剪辑主链路
|
||||||
- 素材上传与管理
|
- 素材上传(直传 OSS + 分片上传大文件,最大 2GB)
|
||||||
- AI 智能分类
|
- AI 智能分类与质量评分
|
||||||
- 自动化视频生成
|
- 4 种剪辑模式:one_take / pip(画中画)/ voice_over(口播+B-roll)/ voice_pip
|
||||||
- 成片下载与管理
|
- 剪辑计划模板 + 智能生成
|
||||||
|
- 自动化视频合成任务(Celery 异步)
|
||||||
|
- 成片下载与审核管理
|
||||||
|
- 资产诊断(素材就绪度评估、缺口分析)
|
||||||
|
|
||||||
### 🔐 完整的认证系统
|
### 🔐 认证系统
|
||||||
- JWT 认证(access + refresh token)
|
- JWT Bearer Token 认证
|
||||||
- 邮箱验证和密码重置
|
- 邮箱注册 + 邮箱验证
|
||||||
- Session 管理
|
- 密码重置(邮箱找回)
|
||||||
- bcrypt 密码加密
|
- bcrypt 密码加密
|
||||||
|
|
||||||
### 🏢 多租户架构
|
### 📋 项目管理
|
||||||
- 工作空间隔离
|
- 项目 CRUD + 共享
|
||||||
- 团队成员管理
|
- 任务管理(创建/更新/状态流转/进度追踪)
|
||||||
- 基于角色的权限控制(Owner/Admin/Member/Viewer)
|
- 里程碑管理
|
||||||
- 邀请和审批流程
|
- 任务问题追踪
|
||||||
|
|
||||||
### 💸 订阅管理
|
### 📊 素材库管理
|
||||||
- 3 级订阅计划(Free/Pro/Enterprise)
|
- 素材库创建与管理
|
||||||
- 配额管理(项目数/存储空间)
|
- 素材上传、审核状态流转(pending_review → approved/rejected)
|
||||||
- 升级和取消订阅
|
- 素材诊断(就绪度评分、缺口分析、智能视图)
|
||||||
|
|
||||||
### ⚡ 高性能
|
|
||||||
- 数据库连接池(5-6x 性能提升)
|
|
||||||
- 请求日志和监控
|
|
||||||
- 慢查询检测
|
|
||||||
- 健康检查(Kubernetes 就绪)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 快速开始
|
## 🚀 快速开始
|
||||||
|
|
||||||
### 方式 1: Docker(推荐)
|
### 方式 1: Docker Compose(推荐)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. 克隆仓库
|
# 1. 克隆仓库
|
||||||
git clone https://github.com/your-org/xiaoxia-saas.git
|
git clone https://git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas.git
|
||||||
cd xiaoxia-saas
|
cd xiaoxia-saas
|
||||||
|
|
||||||
# 2. 启动所有服务
|
# 2. 配置环境变量
|
||||||
|
cp .env.example .env
|
||||||
|
# 编辑 .env 填写数据库、Redis、OSS 等配置
|
||||||
|
|
||||||
|
# 3. 启动所有服务
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
|
|
||||||
# 3. 访问 API 文档
|
# 4. 访问 API 文档
|
||||||
open http://localhost:8000/docs
|
open http://localhost:8000/docs
|
||||||
```
|
```
|
||||||
|
|
||||||
就这么简单!🎉
|
|
||||||
|
|
||||||
### 方式 2: 本地开发
|
### 方式 2: 本地开发
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. 克隆仓库
|
# 1. 克隆仓库
|
||||||
git clone https://github.com/your-org/xiaoxia-saas.git
|
git clone https://git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas.git
|
||||||
cd xiaoxia-saas
|
cd xiaoxia-saas
|
||||||
|
|
||||||
# 2. 创建虚拟环境
|
# 2. 创建虚拟环境
|
||||||
@@ -74,36 +72,90 @@ source venv/bin/activate # Windows: venv\Scripts\activate
|
|||||||
# 3. 安装依赖
|
# 3. 安装依赖
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
# 4. 启动开发服务器
|
# 4. 配置环境变量
|
||||||
|
cp .env.example .env
|
||||||
|
|
||||||
|
# 5. 启动 API 服务
|
||||||
uvicorn apps.api.main:app --reload
|
uvicorn apps.api.main:app --reload
|
||||||
|
|
||||||
# 5. 访问 API 文档
|
# 6. 访问 API 文档
|
||||||
open http://localhost:8000/docs
|
open http://localhost:8000/docs
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📚 主要文档
|
## 📚 API 文档
|
||||||
|
|
||||||
### 快速入口
|
### 交互式文档
|
||||||
- **[API 主线清单](docs/API-MAINLINE.md)** - 所有 API endpoints 总览
|
- **Swagger UI**: https://saas-api.xiaoxiajianji.com/docs
|
||||||
- **[代码状态标注](docs/CODE-STATUS.md)** - 代码库导航指南
|
- **OpenAPI Schema**: https://saas-api.xiaoxiajianji.com/openapi.json
|
||||||
- **[API 使用指南](docs/API-GUIDE.md)** - API 详细用法
|
|
||||||
|
|
||||||
### Phase 7 交付
|
### 核心 API 路径
|
||||||
- **[Phase 7 进度](docs/PHASE7-PROGRESS.md)** - 核心视频剪辑业务已完成
|
|
||||||
- **[Phase 7 设计](docs/PHASE7-DESIGN.md)** - 设计文档
|
|
||||||
|
|
||||||
### 基础设施
|
**认证** (`/api/v1/auth`)
|
||||||
- **[Docker 部署指南](docs/DOCKER-DEPLOYMENT.md)**
|
|
||||||
- **[CI/CD 文档](docs/CI-CD.md)**
|
|
||||||
- **[性能监控指南](docs/PERFORMANCE-MONITORING.md)**
|
|
||||||
- **[健康检查指南](docs/HEALTH-CHECKS.md)**
|
|
||||||
|
|
||||||
### 开发规范
|
| 方法 | 路径 | 说明 |
|
||||||
- **[Git 工作流](docs/GIT-WORKFLOW.md)**
|
|------|------|------|
|
||||||
- **[环境配置指南](docs/ENVIRONMENT-CONFIG.md)**
|
| POST | `/register` | 用户注册 |
|
||||||
- **[贡献指南](CONTRIBUTING.md)**
|
| POST | `/login` | 用户登录 |
|
||||||
|
| GET | `/me` | 获取当前用户信息 |
|
||||||
|
| POST | `/password/forgot` | 忘记密码 |
|
||||||
|
| POST | `/password/reset` | 重置密码 |
|
||||||
|
|
||||||
|
**视频剪辑主链路**
|
||||||
|
|
||||||
|
```
|
||||||
|
上传素材 → POST /api/v1/upload(直传)或 /api/v1/upload/chunk/init(分片)
|
||||||
|
↓
|
||||||
|
创建素材 → POST /api/v1/assets
|
||||||
|
↓
|
||||||
|
AI 分类 → POST /api/v1/classification-jobs
|
||||||
|
↓
|
||||||
|
生成剪辑计划 → POST /api/v1/projects/{id}/edit-plans/auto-generate
|
||||||
|
↓
|
||||||
|
创建生成任务 → POST /api/v1/generation/tasks/
|
||||||
|
↓
|
||||||
|
查询结果 → GET /api/v1/generation/tasks/{task_id}/results/
|
||||||
|
↓
|
||||||
|
获取成片 → GET /api/v1/generated-videos/{video_id}/download-url
|
||||||
|
```
|
||||||
|
|
||||||
|
**项目管理** (`/api/v1/project-management`)
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| GET/POST | `/tasks` | 任务列表/创建 |
|
||||||
|
| PATCH | `/tasks/{id}` | 更新任务信息 |
|
||||||
|
| PATCH | `/tasks/{id}/status` | 更新任务状态 |
|
||||||
|
| PATCH | `/tasks/{id}/progress` | 更新任务进度 |
|
||||||
|
| GET/POST | `/milestones` | 里程碑列表/创建 |
|
||||||
|
| GET/POST | `/issues` | 问题列表/创建 |
|
||||||
|
| PATCH | `/issues/{id}/resolve` | 解决问题 |
|
||||||
|
|
||||||
|
**素材与上传**
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| POST | `/api/v1/upload` | 直传素材(multipart/form-data) |
|
||||||
|
| POST | `/api/v1/upload/direct/prepare` | 准备 OSS 直传签名 |
|
||||||
|
| POST | `/api/v1/upload/direct/complete` | 确认直传完成 |
|
||||||
|
| POST | `/api/v1/upload/chunk/init` | 初始化分片上传 |
|
||||||
|
| POST | `/api/v1/upload/chunk/{id}/{index}` | 上传分片 |
|
||||||
|
| POST | `/api/v1/upload/chunk/{id}/complete` | 完成分片上传 |
|
||||||
|
| GET | `/api/v1/assets` | 素材列表 |
|
||||||
|
| PATCH | `/api/v1/assets/{id}/review` | 更新素材审核状态 |
|
||||||
|
| GET | `/api/v1/projects/{id}/asset-diagnosis` | 资产诊断 |
|
||||||
|
|
||||||
|
**成片管理** (`/api/v1/generated-videos`)
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| GET | `/` | 成片列表 |
|
||||||
|
| GET | `/{video_id}` | 成片详情 |
|
||||||
|
| GET | `/{video_id}/download-url` | 下载链接 |
|
||||||
|
| PATCH | `/{video_id}/review` | 审核状态 |
|
||||||
|
|
||||||
|
完整 API 列表请查看 [API 主线清单](docs/API-MAINLINE.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -111,61 +163,49 @@ open http://localhost:8000/docs
|
|||||||
|
|
||||||
```
|
```
|
||||||
小虾 SaaS
|
小虾 SaaS
|
||||||
├── packages/ # 核心业务逻辑
|
├── packages/ # 核心业务逻辑(Clean Architecture)
|
||||||
│ ├── domain/ # 领域模型
|
│ ├── domain/ # 领域模型(dataclass)
|
||||||
│ ├── application/ # 用例
|
│ ├── application/ # 用例(Use Cases)
|
||||||
│ ├── ports/ # 接口定义
|
│ ├── ports/ # 接口定义(抽象端口)
|
||||||
│ └── adapters/ # 适配器实现
|
│ └── adapters/ # 适配器实现(SQLAlchemy、Redis、SMTP 等)
|
||||||
├── apps/ # 应用层
|
├── apps/ # 应用层
|
||||||
│ ├── api/ # FastAPI 应用
|
│ ├── api/ # FastAPI 应用 + 路由 + Pydantic schemas
|
||||||
│ ├── web/ # React 前端
|
│ ├── web/ # React + Vite 前端
|
||||||
│ └── worker/ # Celery worker
|
│ └── worker/ # Celery 异步任务(视频处理、分类等)
|
||||||
├── migrations/ # 数据库迁移
|
├── migrations/ # Alembic 数据库迁移
|
||||||
├── tests/ # 测试
|
├── tests/ # 测试
|
||||||
│ ├── unit/ # 单元测试
|
│ ├── unit/ # 单元测试
|
||||||
│ └── integration/ # 集成测试
|
│ ├── integration/ # 集成测试
|
||||||
|
│ └── e2e/ # 端到端测试
|
||||||
└── docs/ # 文档
|
└── docs/ # 文档
|
||||||
```
|
```
|
||||||
|
|
||||||
**设计模式:**
|
**设计模式:**
|
||||||
- Clean Architecture
|
- Clean Architecture(依赖方向:外层 → 内层)
|
||||||
- 依赖注入
|
- 依赖注入(FastAPI Depends)
|
||||||
- Repository 模式
|
- Repository 模式(通过 ports 抽象)
|
||||||
- Domain-Driven Design
|
- Domain-Driven Design
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔑 核心 API 路径
|
## 🛠️ 技术栈
|
||||||
|
|
||||||
### 视频剪辑主链路(Phase 7)
|
**后端:**
|
||||||
|
- Python 3.12 + FastAPI 0.115.0
|
||||||
|
- PostgreSQL 16(生产)
|
||||||
|
- Redis 7(缓存 + Celery Broker)
|
||||||
|
- Celery(异步任务:视频处理、素材导入、分类)
|
||||||
|
- 阿里云 OSS(文件存储)
|
||||||
|
|
||||||
```
|
**前端:**
|
||||||
上传素材
|
- React 18 + TypeScript
|
||||||
↓
|
- Vite(构建工具)
|
||||||
POST /api/v1/upload
|
- Ant Design(UI 组件)
|
||||||
|
|
||||||
创建 Asset
|
**部署:**
|
||||||
↓
|
- Docker + Docker Compose
|
||||||
POST /api/v1/assets
|
- Gitea + Gitea Actions(CI/CD)
|
||||||
|
- Nginx(反向代理)
|
||||||
发起分类
|
|
||||||
↓
|
|
||||||
POST /api/v1/classification-jobs
|
|
||||||
|
|
||||||
创建生成任务
|
|
||||||
↓
|
|
||||||
POST /api/v1/generation/tasks
|
|
||||||
|
|
||||||
查询生成结果
|
|
||||||
↓
|
|
||||||
GET /api/v1/generation/tasks/{task_id}/results
|
|
||||||
|
|
||||||
获取下载链接
|
|
||||||
↓
|
|
||||||
GET /api/v1/generated-videos/{video_id}/download-url
|
|
||||||
```
|
|
||||||
|
|
||||||
完整 API 列表请查看 [API 主线清单](docs/API-MAINLINE.md)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -185,51 +225,32 @@ pytest tests/integration -v
|
|||||||
pytest --cov=packages --cov-report=html
|
pytest --cov=packages --cov-report=html
|
||||||
```
|
```
|
||||||
|
|
||||||
**测试统计:**
|
|
||||||
- 单元测试: 17+ 个 ✅
|
|
||||||
- 集成测试: 6+ 个 ✅
|
|
||||||
- 测试覆盖率: 持续提升中
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠️ 技术栈
|
|
||||||
|
|
||||||
**后端:**
|
|
||||||
- Python 3.12
|
|
||||||
- FastAPI 0.115.0
|
|
||||||
- PostgreSQL 16
|
|
||||||
- Redis 7
|
|
||||||
- Celery
|
|
||||||
- MinIO
|
|
||||||
|
|
||||||
**前端:**
|
|
||||||
- React 18
|
|
||||||
- TypeScript
|
|
||||||
- Vite
|
|
||||||
- Ant Design
|
|
||||||
|
|
||||||
**测试:**
|
|
||||||
- pytest
|
|
||||||
- pytest-asyncio
|
|
||||||
- pytest-cov
|
|
||||||
|
|
||||||
**部署:**
|
|
||||||
- Docker
|
|
||||||
- Docker Compose
|
|
||||||
- Gitea Actions
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 当前状态
|
## 📊 当前状态
|
||||||
|
|
||||||
| 模块 | 状态 |
|
| 模块 | 状态 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| Phase 7(视频剪辑主链) | ✅ 已完成 |
|
| 视频剪辑主链路(Phase 7) | ✅ 已完成 |
|
||||||
| CI/CD 稳定性 | ✅ 已修复 |
|
| 分片上传(最大 2GB) | ✅ 已完成 |
|
||||||
| 认证系统 | ✅ 完整 |
|
| 4 种剪辑模式 | ✅ 已完成 |
|
||||||
| 多租户架构 | ✅ 完整 |
|
| 项目管理 + 任务追踪 | ✅ 已完成 |
|
||||||
| 订阅系统 | ✅ 完整 |
|
| 资产诊断 | ✅ 已完成 |
|
||||||
| 前端联调 | ✅ 完成 |
|
| 认证系统(JWT) | ✅ 已完成 |
|
||||||
|
| CI/CD 流水线 | ✅ 运行中 |
|
||||||
|
| 前端界面(Vite) | ✅ 已完成 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 更多文档
|
||||||
|
|
||||||
|
- [API 主线清单](docs/API-MAINLINE.md) - 全部端点总览
|
||||||
|
- [API 使用指南](docs/API-GUIDE.md) - 详细用法
|
||||||
|
- [代码状态标注](docs/CODE-STATUS.md) - 代码库导航
|
||||||
|
- [Docker 部署指南](docs/DOCKER-DEPLOYMENT.md)
|
||||||
|
- [CI/CD 文档](docs/CI-CD.md)
|
||||||
|
- [Git 工作流](docs/GIT-WORKFLOW.md)
|
||||||
|
- [环境配置指南](docs/ENVIRONMENT-CONFIG.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -237,43 +258,8 @@ pytest --cov=packages --cov-report=html
|
|||||||
|
|
||||||
欢迎贡献!请查看 [贡献指南](CONTRIBUTING.md)
|
欢迎贡献!请查看 [贡献指南](CONTRIBUTING.md)
|
||||||
|
|
||||||
1. Fork 项目
|
**仓库地址**: https://git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas
|
||||||
2. 创建分支 (`git checkout -b feature/AmazingFeature`)
|
|
||||||
3. 提交更改 (`git commit -m 'feat: Add some AmazingFeature'`)
|
|
||||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
|
||||||
5. 创建 Pull Request
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📄 许可证
|
**License**: MIT
|
||||||
|
|
||||||
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📞 联系方式
|
|
||||||
|
|
||||||
- **问题反馈:** GitHub Issues
|
|
||||||
- **文档:** 参考 `docs/` 目录
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎉 致谢
|
|
||||||
|
|
||||||
感谢所有贡献者和使用者!
|
|
||||||
|
|
||||||
**开发团队:** 小虾 🦐
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**⭐ 如果这个项目对你有帮助,请给一个 Star!**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**最后更新**: 2026-06-19
|
|
||||||
**当前版本**: Phase 7 完成
|
|
||||||
ci test Fri Jun 26 06:59:37 PM CST 2026
|
|
||||||
debug test Fri Jun 26 07:05:38 PM CST 2026
|
|
||||||
docker daemon restart test
|
|
||||||
19:08:39 - final test
|
|
||||||
19:10:17 - new runner test
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Add video fingerprint and duplicate detection fields to generated_videos table.
|
"""Add video fingerprint and duplicate detection fields to generated_videos table.
|
||||||
|
|
||||||
Revision ID: 007
|
Revision ID: 008
|
||||||
Revises: 006
|
Revises: 007
|
||||||
Create Date: 2024-06-26
|
Create Date: 2024-06-26
|
||||||
"""
|
"""
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
@@ -9,7 +9,7 @@ import sqlalchemy as sa
|
|||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
revision = "008"
|
revision = "008"
|
||||||
down_revision = "006"
|
down_revision = "007"
|
||||||
branch_labels = None
|
branch_labels = None
|
||||||
depends_on = None
|
depends_on = None
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Remove workspace concept - Projects now directly under User
|
"""Remove workspace concept - Projects now directly under User
|
||||||
|
|
||||||
Revision ID: 007_remove_workspace_concept
|
Revision ID: 009
|
||||||
Revises: 008
|
Revises: 008
|
||||||
Create Date: 2026-06-26
|
Create Date: 2026-06-26
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ import sqlalchemy as sa
|
|||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
# revision identifiers
|
# revision identifiers
|
||||||
revision = "007_remove_workspace_concept"
|
revision = "009"
|
||||||
down_revision = "008"
|
down_revision = "008"
|
||||||
branch_labels = None
|
branch_labels = None
|
||||||
depends_on = None
|
depends_on = None
|
||||||
|
|||||||
@@ -166,7 +166,6 @@ async def verify_email_post(
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/password/forgot", response_model=MessageResponse, status_code=status.HTTP_202_ACCEPTED)
|
@router.post("/password/forgot", response_model=MessageResponse, status_code=status.HTTP_202_ACCEPTED)
|
||||||
@router.post("/forgot-password", response_model=MessageResponse, status_code=status.HTTP_202_ACCEPTED)
|
|
||||||
async def forgot_password(
|
async def forgot_password(
|
||||||
request: PasswordResetRequestModel,
|
request: PasswordResetRequestModel,
|
||||||
user_repository: UserRepository = Depends(get_user_repository),
|
user_repository: UserRepository = Depends(get_user_repository),
|
||||||
@@ -184,7 +183,6 @@ async def forgot_password(
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/password/reset", response_model=MessageResponse)
|
@router.post("/password/reset", response_model=MessageResponse)
|
||||||
@router.post("/reset-password", response_model=MessageResponse)
|
|
||||||
async def reset_password(
|
async def reset_password(
|
||||||
request: ResetPasswordModel,
|
request: ResetPasswordModel,
|
||||||
user_repository: UserRepository = Depends(get_user_repository),
|
user_repository: UserRepository = Depends(get_user_repository),
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ def get_issue_repo():
|
|||||||
|
|
||||||
class CreateTaskRequest(BaseModel):
|
class CreateTaskRequest(BaseModel):
|
||||||
project_id: str
|
project_id: str
|
||||||
workspace_id: str
|
workspace_id: str = ""
|
||||||
name: str
|
name: str
|
||||||
description: str = ""
|
description: str = ""
|
||||||
priority: TaskPriority = TaskPriority.MEDIUM
|
priority: TaskPriority = TaskPriority.MEDIUM
|
||||||
@@ -62,7 +62,7 @@ class CreateTaskRequest(BaseModel):
|
|||||||
class TaskResponse(BaseModel):
|
class TaskResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
project_id: str
|
project_id: str
|
||||||
workspace_id: str
|
workspace_id: str = ""
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
status: TaskStatus
|
status: TaskStatus
|
||||||
@@ -96,7 +96,7 @@ class UpdateTaskProgressRequest(BaseModel):
|
|||||||
|
|
||||||
class CreateMilestoneRequest(BaseModel):
|
class CreateMilestoneRequest(BaseModel):
|
||||||
project_id: str
|
project_id: str
|
||||||
workspace_id: str
|
workspace_id: str = ""
|
||||||
name: str
|
name: str
|
||||||
description: str = ""
|
description: str = ""
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ class CreateMilestoneRequest(BaseModel):
|
|||||||
class MilestoneResponse(BaseModel):
|
class MilestoneResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
project_id: str
|
project_id: str
|
||||||
workspace_id: str
|
workspace_id: str = ""
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
target_date: datetime | None
|
target_date: datetime | None
|
||||||
@@ -117,7 +117,7 @@ class MilestoneResponse(BaseModel):
|
|||||||
class CreateTaskIssueRequest(BaseModel):
|
class CreateTaskIssueRequest(BaseModel):
|
||||||
task_id: str
|
task_id: str
|
||||||
project_id: str
|
project_id: str
|
||||||
workspace_id: str
|
workspace_id: str = ""
|
||||||
title: str
|
title: str
|
||||||
description: str = ""
|
description: str = ""
|
||||||
created_by_user_id: str = ""
|
created_by_user_id: str = ""
|
||||||
@@ -127,7 +127,7 @@ class TaskIssueResponse(BaseModel):
|
|||||||
id: str
|
id: str
|
||||||
task_id: str
|
task_id: str
|
||||||
project_id: str
|
project_id: str
|
||||||
workspace_id: str
|
workspace_id: str = ""
|
||||||
title: str
|
title: str
|
||||||
description: str
|
description: str
|
||||||
resolved: bool
|
resolved: bool
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ def get_project(
|
|||||||
|
|
||||||
@router.get("", response_model=ListProjectsResponse)
|
@router.get("", response_model=ListProjectsResponse)
|
||||||
def list_projects(
|
def list_projects(
|
||||||
workspace_id: str,
|
workspace_id: str = "",
|
||||||
authenticated_user: AuthenticatedUser = Depends(get_current_user),
|
authenticated_user: AuthenticatedUser = Depends(get_current_user),
|
||||||
project_repository: Any = Depends(get_project_repository),
|
project_repository: Any = Depends(get_project_repository),
|
||||||
workspace_member_repository: WorkspaceMemberRepository = Depends(get_workspace_member_repository),
|
workspace_member_repository: WorkspaceMemberRepository = Depends(get_workspace_member_repository),
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ from app.schemas.upload import (
|
|||||||
DirectUploadCompleteResponse,
|
DirectUploadCompleteResponse,
|
||||||
DirectUploadPrepareRequest,
|
DirectUploadPrepareRequest,
|
||||||
DirectUploadPrepareResponse,
|
DirectUploadPrepareResponse,
|
||||||
|
UploadAssetRequest,
|
||||||
UploadAssetResponse,
|
UploadAssetResponse,
|
||||||
)
|
)
|
||||||
|
from typing import Annotated
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile, status
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile, status
|
||||||
|
|
||||||
from packages.application import GetProjectUseCase, SubmitIngestJobCommand, SubmitIngestJobUseCase
|
from packages.application import GetProjectUseCase, SubmitIngestJobCommand, SubmitIngestJobUseCase
|
||||||
@@ -184,12 +187,15 @@ async def complete_direct_upload(
|
|||||||
return DirectUploadCompleteResponse(storage_key=normalized_key, ingest_job_id=job.id)
|
return DirectUploadCompleteResponse(storage_key=normalized_key, ingest_job_id=job.id)
|
||||||
|
|
||||||
|
|
||||||
@router.post("", response_model=UploadAssetResponse)
|
@router.post(
|
||||||
|
"",
|
||||||
|
response_model=UploadAssetResponse,
|
||||||
|
summary="Upload Asset",
|
||||||
|
description="上传素材文件(multipart/form-data),支持视频、音频、图片。触发导入流水线自动处理。",
|
||||||
|
)
|
||||||
async def upload_asset(
|
async def upload_asset(
|
||||||
file: UploadFile = File(..., description="要上传的文件(视频、音频、图片等)"),
|
file: UploadFile = File(..., description="要上传的文件(视频、音频、图片等)"),
|
||||||
workspace_id: str = Form(..., description="工作空间 ID"),
|
form_data: Annotated[UploadAssetRequest, Form()],
|
||||||
project_id: str = Form(..., description="项目 ID"),
|
|
||||||
library_id: str = Form(..., description="素材库 ID"),
|
|
||||||
authenticated_user: AuthenticatedUser = Depends(get_current_user),
|
authenticated_user: AuthenticatedUser = Depends(get_current_user),
|
||||||
ingest_job_repository: Any = Depends(get_ingest_job_repository),
|
ingest_job_repository: Any = Depends(get_ingest_job_repository),
|
||||||
project_repository: Any = Depends(get_project_repository),
|
project_repository: Any = Depends(get_project_repository),
|
||||||
@@ -198,6 +204,9 @@ async def upload_asset(
|
|||||||
storage_service: OSSStorageService = Depends(get_storage_service),
|
storage_service: OSSStorageService = Depends(get_storage_service),
|
||||||
) -> UploadAssetResponse:
|
) -> UploadAssetResponse:
|
||||||
"""上传素材文件并触发导入流水线。"""
|
"""上传素材文件并触发导入流水线。"""
|
||||||
|
workspace_id = form_data.workspace_id
|
||||||
|
project_id = form_data.project_id
|
||||||
|
library_id = form_data.library_id
|
||||||
require_workspace_member(workspace_id, authenticated_user, workspace_member_repository)
|
require_workspace_member(workspace_id, authenticated_user, workspace_member_repository)
|
||||||
_require_project_and_library(workspace_id, project_id, library_id, project_repository, asset_library_repository)
|
_require_project_and_library(workspace_id, project_id, library_id, project_repository, asset_library_repository)
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ class AssetGapItem(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class ProjectAssetDiagnosisResponse(BaseModel):
|
class ProjectAssetDiagnosisResponse(BaseModel):
|
||||||
workspace_id: str = ""
|
|
||||||
project_id: str
|
project_id: str
|
||||||
readiness_score: int
|
readiness_score: int
|
||||||
readiness_label: str
|
readiness_label: str
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from pydantic import BaseModel, Field
|
|||||||
|
|
||||||
class EditTemplateResponse(BaseModel):
|
class EditTemplateResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
workspace_id: str = ""
|
|
||||||
project_id: str
|
project_id: str
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
@@ -28,7 +27,6 @@ class EditPlanClipResponse(BaseModel):
|
|||||||
|
|
||||||
class EditPlanResponse(BaseModel):
|
class EditPlanResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
workspace_id: str = ""
|
|
||||||
project_id: str
|
project_id: str
|
||||||
template_id: str
|
template_id: str
|
||||||
asset_library_id: str
|
asset_library_id: str
|
||||||
@@ -42,7 +40,6 @@ class EditPlanResponse(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class CreateEditPlanRequest(BaseModel):
|
class CreateEditPlanRequest(BaseModel):
|
||||||
workspace_id: str = ""
|
|
||||||
asset_library_id: str
|
asset_library_id: str
|
||||||
template_id: str = ""
|
template_id: str = ""
|
||||||
title_id: str = ""
|
title_id: str = ""
|
||||||
@@ -50,7 +47,6 @@ class CreateEditPlanRequest(BaseModel):
|
|||||||
|
|
||||||
class AutoGenerateEditPlanRequest(BaseModel):
|
class AutoGenerateEditPlanRequest(BaseModel):
|
||||||
"""智能生成剪辑计划请求"""
|
"""智能生成剪辑计划请求"""
|
||||||
workspace_id: str = ""
|
|
||||||
asset_library_id: str
|
asset_library_id: str
|
||||||
editing_mode: str = Field(
|
editing_mode: str = Field(
|
||||||
default="one_take",
|
default="one_take",
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ TitleCategory = Literal["default", "marketing", "tutorial", "story", "promo"]
|
|||||||
|
|
||||||
class ProjectTitleResponse(BaseModel):
|
class ProjectTitleResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
workspace_id: str = ""
|
|
||||||
project_id: str
|
project_id: str
|
||||||
text: str
|
text: str
|
||||||
category: str
|
category: str
|
||||||
@@ -24,7 +23,6 @@ class ListProjectTitlesResponse(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class CreateProjectTitleRequest(BaseModel):
|
class CreateProjectTitleRequest(BaseModel):
|
||||||
workspace_id: str = ""
|
|
||||||
text: str = Field(min_length=1, max_length=200)
|
text: str = Field(min_length=1, max_length=200)
|
||||||
category: TitleCategory = "default"
|
category: TitleCategory = "default"
|
||||||
favorite: bool = False
|
favorite: bool = False
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from pydantic import BaseModel, Field
|
|||||||
class ProjectTaskResponse(BaseModel):
|
class ProjectTaskResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
task_type: str
|
task_type: str
|
||||||
workspace_id: str = ""
|
|
||||||
project_id: str
|
project_id: str
|
||||||
status: str
|
status: str
|
||||||
progress: float
|
progress: float
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UploadAssetRequest(BaseModel):
|
||||||
|
"""素材上传请求(multipart form)"""
|
||||||
|
project_id: str = Field(..., min_length=1, description="项目 ID")
|
||||||
|
library_id: str = Field(..., min_length=1, description="素材库 ID")
|
||||||
|
workspace_id: str = Field(default="", description="工作空间 ID(已废弃,可传空)")
|
||||||
|
|
||||||
class UploadAssetResponse(BaseModel):
|
class UploadAssetResponse(BaseModel):
|
||||||
storage_key: str
|
storage_key: str
|
||||||
ingest_job_id: str
|
ingest_job_id: str
|
||||||
|
|||||||
Reference in New Issue
Block a user