514 lines
24 KiB
Markdown
514 lines
24 KiB
Markdown
# Speakr - 定制版
|
||
|
||
> 基于 [murtaza-nasir/speakr](https://github.com/murtaza-nasir/speakr) 二次开发的语音转录与智能笔记平台。
|
||
|
||
## 项目简介
|
||
|
||
本项目是一个自托管的 AI 语音转录和智能笔记平台,支持:
|
||
|
||
- **语音录制与上传** - 浏览器直接录音或上传已有音频文件
|
||
- **AI 转录** - 高准确率语音转文字,支持说话人识别
|
||
- **交互式对话** - 对录音提问,获取 AI 回答
|
||
- **智能标签** - 支持带自定义 AI 提示词的标签
|
||
- **安全分享** - 生成安全链接分享录音
|
||
- **草稿功能** - 本地草稿保存与上传
|
||
- **声纹管理** - 自定义说话人声纹识别
|
||
- **标准词映射** - 自动纠正专业术语和人名
|
||
|
||
---
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
speakr/
|
||
├── app.py # 入口文件(启动脚本)
|
||
├── src/
|
||
│ ├── app.py # Flask 应用主逻辑(create_app 工厂模式)
|
||
│ ├── config.py # 统一配置文件(所有环境变量集中管理)
|
||
│ ├── logging_config.py # 日志配置模块(格式化、输出目标、日志级别)
|
||
│ ├── flask_ext.py # Flask 扩展实例集合(LoginManager、Bcrypt、Limiter、CSRFProtect)
|
||
│ ├── clients/ # 外部服务客户端层
|
||
│ │ ├── __init__.py # 统一导出所有客户端和配置
|
||
│ │ ├── database.py # 数据库客户端:db 实例 + 配置 + 初始化
|
||
│ │ ├── llm_client.py # LLM 客户端:OpenAI 客户端初始化
|
||
│ │ └── voiceprint_client.py # 声纹客户端:VoiceprintClient
|
||
│ ├── api/ # API 路由层(Flask 蓝图)
|
||
│ │ ├── __init__.py # 统一蓝图注册函数 register_blueprints()
|
||
│ │ ├── template_filters.py # 模板过滤器注册(now、localdatetime)
|
||
│ │ ├── auth.py # 认证路由(登录、注册、账户管理)
|
||
│ │ ├── recording.py # 录音管理(CRUD、上传、转录、下载)
|
||
│ │ ├── share.py # 分享功能
|
||
│ │ ├── tag.py # 标签管理
|
||
│ │ ├── speaker.py # 说话人管理
|
||
│ │ ├── admin.py # 管理后台
|
||
│ │ ├── search.py # 搜索和 Inquire
|
||
│ │ ├── voiceprint.py # 声纹管理
|
||
│ │ ├── config.py # 系统配置管理
|
||
│ │ ├── hotword.py # 热词管理
|
||
│ │ ├── template.py # 转录模板
|
||
│ │ ├── event.py # 事件管理
|
||
│ │ └── main.py # 主页和杂项
|
||
│ ├── models/ # 数据模型层(每个模型独立文件)
|
||
│ │ ├── __init__.py # 统一导出所有模型
|
||
│ │ ├── user.py # 用户模型
|
||
│ │ ├── speaker.py # 说话人模型
|
||
│ │ ├── recording.py # 录音模型
|
||
│ │ ├── tag.py # 标签模型
|
||
│ │ ├── recording_tag.py # 录音-标签关联模型
|
||
│ │ ├── event.py # 事件模型
|
||
│ │ ├── share.py # 分享模型
|
||
│ │ ├── transcript_chunk.py # 转录块模型
|
||
│ │ ├── transcript_template.py # 转录模板模型
|
||
│ │ ├── inquire_session.py # 询问会话模型
|
||
│ │ ├── voiceprint.py # 声纹模型
|
||
│ │ ├── system_setting.py # 系统设置模型
|
||
│ │ ├── system_config.py # 系统配置模型
|
||
│ │ ├── hotword.py # 热词模型
|
||
│ │ ├── draft_recording.py # 草稿录音模型
|
||
│ │ ├── draft_segment.py # 草稿片段模型
|
||
│ │ └── forms.py # 表单类(注册、登录)
|
||
│ ├── services/ # 业务逻辑层
|
||
│ │ ├── __init__.py # 统一导出所有服务函数
|
||
│ │ ├── llm_service.py # LLM 服务
|
||
│ │ ├── transcription_service.py # 转录服务
|
||
│ │ ├── summary_service.py # 摘要服务
|
||
│ │ ├── embedding_service.py # 嵌入向量服务
|
||
│ │ ├── event_service.py # 事件服务
|
||
│ │ ├── document_service.py # 文档服务
|
||
│ │ ├── speaker_service.py # 说话人服务
|
||
│ │ ├── voiceprint_service.py # 声纹服务(业务逻辑)
|
||
│ │ ├── auto_process_service.py # 自动处理服务
|
||
│ │ ├── auth_service.py # 认证服务
|
||
│ │ ├── sync_service.py # 远程数据同步服务
|
||
│ │ ├── audio_chunking.py # 音频分块服务
|
||
│ │ └── file_monitor.py # 文件监控服务
|
||
│ ├── utils/ # 工具函数层
|
||
│ │ ├── __init__.py
|
||
│ │ ├── markdown.py # Markdown 转 HTML 工具
|
||
│ │ ├── datetime.py # 时区转换工具
|
||
│ │ ├── text_utils.py # 文本处理工具
|
||
│ │ ├── json_utils.py # JSON 处理工具
|
||
│ │ └── standard_word_mappings.py # 标准词映射工具
|
||
├── templates/ # Jinja2 模板
|
||
├── static/ # 静态资源(CSS/JS/图片)
|
||
├── instance/ # SQLite 数据库文件
|
||
├── uploads/ # 上传的音频文件
|
||
└── .env # 环境变量配置
|
||
```
|
||
|
||
---
|
||
|
||
## 启动方式
|
||
|
||
### 前置条件
|
||
|
||
- Python 环境(已安装 conda 环境 `speakr`)
|
||
- Nginx 反向代理(必须启动)
|
||
|
||
### 启动步骤
|
||
|
||
```bash
|
||
# 1. 进入项目根目录(speakr 2 目录)
|
||
cd "c:\Work\zdht\python-projects\speakr 2"
|
||
|
||
# 2. 启动 Nginx 反向代理(必须先启动)
|
||
# Nginx 负责将 https://localhost:8083/tool/speakr/ 请求转发到 Flask 后端
|
||
# ⚠️ 注意:不启动 Nginx 将无法访问应用
|
||
nginx # 或根据你的安装方式启动 nginx
|
||
|
||
# 3. 激活 conda 环境并启动应用
|
||
conda activate speakr
|
||
python .\speakr\app.py
|
||
```
|
||
|
||
Flask 后端默认监听 **5000 端口**,Nginx 负责将 `https://localhost:8083/tool/speakr/` 请求转发到 `http://localhost:5000`。
|
||
|
||
### 访问地址
|
||
|
||
- 主页面:`https://localhost:8083/tool/speakr/`
|
||
- 管理后台:`https://localhost:8083/tool/speakr/admin`
|
||
- 账户设置:`https://localhost:8083/tool/speakr/account`
|
||
|
||
### 常见错误
|
||
|
||
| 问题 | 原因 | 解决 |
|
||
|------|------|------|
|
||
| 页面无法访问 | Nginx 未启动 | 启动 Nginx 反向代理 |
|
||
| `ModuleNotFoundError: No module named 'xxx'` | conda 环境未激活 | 执行 `conda activate speakr` |
|
||
| 数据库文件位置错误 | 从错误目录启动 | 确保从 `speakr 2/` 目录执行 `python .\speakr\app.py` |
|
||
|
||
---
|
||
|
||
## 环境配置
|
||
|
||
编辑 `.env` 文件,主要配置项:
|
||
|
||
```ini
|
||
# ASR 服务配置
|
||
USE_ASR_ENDPOINT=true
|
||
ASR_BASE_URL=http://10.100.3.22:6688
|
||
ASR_DIARIZE=true
|
||
|
||
# 文本生成模型配置
|
||
TEXT_MODEL_BASE_URL=http://10.100.3.22:9800/v1
|
||
TEXT_MODEL_API_KEY=EMPTY
|
||
TEXT_MODEL_NAME=minimax-m2.5
|
||
|
||
# 数据库配置
|
||
SQLALCHEMY_DATABASE_URI=sqlite:///transcriptions.db
|
||
# PostgreSQL 配置示例(切换时只需修改 URI 即可)
|
||
# SQLALCHEMY_DATABASE_URI=postgresql://user:password@localhost/speakr
|
||
# DB_POOL_SIZE=10
|
||
# DB_MAX_OVERFLOW=20
|
||
# DB_POOL_TIMEOUT=30
|
||
# DB_POOL_RECYCLE=3600
|
||
|
||
# 外部访问端口(Nginx 监听端口)
|
||
EXTERNAL_PORT=8083
|
||
|
||
# 时区配置
|
||
TIMEZONE=Asia/Shanghai
|
||
```
|
||
|
||
---
|
||
|
||
## 重构计划与当前进度
|
||
|
||
### 拆分原则
|
||
|
||
1. ⭐ 标记的模块是业务特色,拆分时只改代码组织结构,不改业务逻辑
|
||
2. 拆分顺序:
|
||
- **第一步**:抽出 Models(最安全,纯数据定义)
|
||
- **第二步**:抽出 Services(把 app.py 中的业务逻辑函数提取到独立文件)
|
||
- **第三步**:抽出 API 蓝图(把路由按功能域分组注册)
|
||
- **第四步**:抽出 Utils(工具函数独立化)
|
||
- **第五步**:模板组件化(可选,优先级较低)
|
||
3. `app.py` 最终只保留:Flask 初始化、数据库初始化、蓝图注册、全局错误处理
|
||
4. 保持向后兼容:拆分过程中所有 API 路径不变,前端无需改动
|
||
|
||
### 需要特别注意的业务逻辑
|
||
|
||
| 功能 | 说明 | 拆分注意事项 |
|
||
|------|------|-------------|
|
||
| ⭐ 声纹管理 | VoiceprintClient 类、声纹注册/比对/同步 | 保持 VoiceprintClient 与外部平台的交互逻辑不变 |
|
||
| ⭐ 系统配置 | SystemConfig 动态配置读写 | 保持配置缓存和热加载机制 |
|
||
| ⭐ 草稿 API | 已有独立蓝图 draft_bp | 直接迁移,几乎不需改动 |
|
||
| ⭐ 标准词映射 | 转写后的文本纠正 | 保持映射规则和应用逻辑 |
|
||
| ⭐ 自动处理 | 定时/触发式自动转写 | 保持调度逻辑和状态管理 |
|
||
| ⭐ 收件箱 | 录音的收件箱/高亮状态 | 保持查询和状态切换逻辑 |
|
||
| ⭐ 热词 | 热词管理和应用 | 保持与转写服务的集成 |
|
||
|
||
### 当前进度
|
||
|
||
#### ✅ 已完成:Phase 1 - Models 层抽取
|
||
|
||
已将 `src/app.py` 中的 **16 个数据模型** 和 **2 个表单类** 从主文件中抽取到独立模块:
|
||
|
||
| 状态 | 任务 | 说明 |
|
||
|------|------|------|
|
||
| ✅ | `src/database.py`(已移至 clients) | 创建独立数据库实例文件,集中管理 SQLAlchemy db 对象(Phase 2.5 移至 clients/database.py) |
|
||
| ✅ | `src/models/` | 每个模型独立文件(user.py, speaker.py, recording.py 等 16 个模型) |
|
||
| ✅ | `src/models/forms.py` | 表单类独立(RegistrationForm, LoginForm, password_check) |
|
||
| ✅ | `src/utils/markdown.py` | Markdown 转 HTML 工具函数 |
|
||
| ✅ | `src/utils/datetime.py` | 时区转换工具函数 |
|
||
| ✅ | `src/app.py` | 删除模型定义,改为从 `src.models` 导入 |
|
||
|
||
**验证结果**:应用可正常启动,数据库文件路径正确(`speakr/instance/transcriptions.db`)
|
||
|
||
#### ✅ 已完成:Phase 2 - Services 层抽取
|
||
|
||
**服务文件已全部创建**(`src/services/` 目录下 11 个服务文件),app.py 中的旧函数定义已全部删除。
|
||
|
||
| 状态 | 服务文件 | 包含函数 |
|
||
|------|---------|---------|
|
||
| ✅ | `llm_service.py` | `call_llm_completion`, `clean_llm_response`, `format_transcription_for_llm`, `process_streaming_with_thinking`, `extract_thinking_content`, `preprocess_long_transcription`, `extract_corrected_text`, `format_api_error_message` |
|
||
| ✅ | `transcription_service.py` | `transcribe_audio_asr`, `transcribe_audio_task`, `transcribe_single_file`, `transcribe_with_chunking`, `extract_audio_from_video`, `convert_to_wav` |
|
||
| ✅ | `summary_service.py` | `generate_title_task`, `generate_summary_only_task` |
|
||
| ✅ | `embedding_service.py` | `get_embedding_model`, `chunk_transcription`, `generate_embeddings`, `serialize_embedding`, `deserialize_embedding`, `process_recording_chunks`, `basic_text_search_chunks`, `semantic_search_chunks` |
|
||
| ✅ | `event_service.py` | `extract_events_from_transcript`, `generate_ics_content`, `escape_ical_text` |
|
||
| ✅ | `document_service.py` | `process_markdown_to_docx`, `get_recording_display_title`, `sanitize_download_filename_component`, `build_docx_download_filename`, `set_download_filename_header` |
|
||
| ✅ | `speaker_service.py` | `update_speaker_usage`, `identify_speakers_from_text`, `identify_unidentified_speakers_from_text` |
|
||
| ✅ | `voiceprint_service.py` | `sync_voiceprint_to_platform`(VoiceprintClient 已移至 clients 层) |
|
||
| ✅ | `auto_process_service.py` | `initialize_file_monitor`, `get_file_monitor_functions` |
|
||
| ✅ | `auth_service.py` | `is_safe_url`, `auto_login` |
|
||
| ✅ | `__init__.py` | 统一导出所有服务函数 |
|
||
|
||
**已完成的工作:**
|
||
|
||
1. ✅ 删除了 app.py 中的 22 个重复函数定义,改为从服务层导入
|
||
2. ✅ 创建了 `src/utils/text_utils.py` 和 `src/utils/json_utils.py`,将工具函数移出 app.py
|
||
3. ✅ 添加了服务层和工具层的导入
|
||
4. ✅ 核心功能测试通过,应用运行正常
|
||
|
||
#### ✅ 已完成:Phase 2.5 - Clients 层创建
|
||
|
||
按照行业规范创建了 `src/clients/` 层,统一管理与外部服务的连接客户端:
|
||
|
||
| 状态 | 客户端 | 说明 |
|
||
|------|--------|------|
|
||
| ✅ | `database.py` | 数据库客户端:`db` 实例 + 配置 + `init_database()` 初始化逻辑 |
|
||
| ✅ | `llm_client.py` | LLM 客户端:OpenAI 客户端初始化(client, TEXT_MODEL_NAME, TEXT_MODEL_API_KEY 等) |
|
||
| ✅ | `voiceprint_client.py` | 声纹客户端:VoiceprintClient 类 + voiceprint_client() 延迟初始化 |
|
||
| ✅ | `__init__.py` | 统一导出所有客户端 |
|
||
|
||
**重构成果:**
|
||
|
||
- 删除了旧 `src/database.py`(空实例文件),合并到 `clients/database.py`
|
||
- 删除了 `services/voiceprint_service.py` 中的 `VoiceprintClient` 类,移到 `clients/voiceprint_client.py`
|
||
- 17 个模型文件 + 6 个服务文件的 `db` 导入统一改为 `from src.clients import db`
|
||
- 消除了层层传递 `client` 参数的问题
|
||
- 外部服务客户端与业务逻辑分离,架构更清晰
|
||
|
||
#### ✅ 已完成:Phase 2.6 - 服务层注释中文化
|
||
|
||
将所有服务层文件中的英文注释和日志消息翻译为中文,提升代码可读性:
|
||
|
||
| 文件 | 翻译内容 |
|
||
|------|---------|
|
||
| `clients/llm_client.py` | URL 注释清理逻辑、客户端初始化逻辑 |
|
||
| `clients/voiceprint_client.py` | 声纹客户端注释和日志(新建文件,全中文) |
|
||
| `clients/database.py` | 数据库配置和初始化注释(新建文件,全中文) |
|
||
| `services/llm_service.py` | 思考内容提取、流式处理、API 错误格式化、分段总结等函数的注释和日志 |
|
||
| `services/summary_service.py` | 摘要生成全流程的注释和日志(标签提示词、语言要求、系统/用户消息构建等) |
|
||
| `services/transcription_service.py` | 音频提取、ASR 转录、分块转录、Whisper 调用等全流程注释和日志 |
|
||
| `services/embedding_service.py` | 文本分块、嵌入向量生成、语义搜索等全流程注释和日志 |
|
||
| `services/voiceprint_service.py` | 声纹同步业务逻辑注释 |
|
||
|
||
#### ✅ 已完成:Phase 3 - API 蓝图抽取
|
||
|
||
将 `src/app.py` 中的约 100 个路由定义按功能域分组抽取到独立的 Flask 蓝图中,app.py 从约 10000 行精简到约 700 行:
|
||
|
||
| 状态 | 蓝图文件 | 路由数量 | 说明 |
|
||
|------|---------|---------|------|
|
||
| ✅ | `api/auth.py` | 5 | 注册、登录、登出、账户管理、修改密码 |
|
||
| ✅ | `api/recording.py` | 24 | 录音 CRUD、上传、转录、下载、状态管理、ASR 校正 |
|
||
| ✅ | `api/share.py` | 7 | 分享创建、查看、访问 |
|
||
| ✅ | `api/tag.py` | 6 | 标签 CRUD、应用到录音 |
|
||
| ✅ | `api/speaker.py` | 5 | 说话人管理、搜索 |
|
||
| ✅ | `api/admin.py` | 18 | 管理后台、统计分析、录音管理、配置管理 |
|
||
| ✅ | `api/search.py` | 7 | 搜索、Inquire 对话、收件箱 |
|
||
| ✅ | `api/voiceprint.py` | 6 | 声纹注册、管理、同步 |
|
||
| ✅ | `api/config.py` | 6 | 系统配置读写、分段服务管理 |
|
||
| ✅ | `api/hotword.py` | 2 | 热词管理 |
|
||
| ✅ | `api/template.py` | 5 | 转录模板 CRUD |
|
||
| ✅ | `api/event.py` | 3 | 事件提取、ICS 导出 |
|
||
| ✅ | `api/main.py` | 5 | 主页、摘要生成、对话 |
|
||
|
||
**架构改进:**
|
||
- 创建 `api/__init__.py` 中的 `register_blueprints()` 函数,统一管理所有蓝图注册和初始化
|
||
- 使用 `init_xxx_bp()` 延迟注入模式解决循环导入问题(limiter、csrf、bcrypt 等全局对象)
|
||
- 所有 API 路径保持不变,前端无需任何修改
|
||
- `app.py` 仅保留:Flask 初始化、配置加载、蓝图注册、全局错误处理、启动逻辑
|
||
|
||
**重构后修复的问题:**
|
||
|
||
| 问题 | 原因 | 解决方案 |
|
||
|------|------|---------|
|
||
| 重定向循环 | `main.py` 中的 `index()` 路由误加了 `@login_required` 装饰器,丢失了自动登录逻辑 | 恢复备份中的 token 验证和自动登录逻辑,移除装饰器 |
|
||
| WebSocket 连接失败 | `getUserConfig` 路由缺少 `FUNASR_WEBSOCKET_IP` 和 `SCREEN_PUSH_IP` 配置 | 在 `main.py` 的 `get_user_config()` 中补充 WebSocket 配置字段 |
|
||
| 循环导入 bcrypt | `main.py` 在模块级别导入 `bcrypt` 导致循环依赖 | 改为在 `init_main_bp()` 中延迟注入 `bcrypt_instance` |
|
||
|
||
#### ✅ 已完成:统一配置管理
|
||
|
||
创建 `src/config.py` 统一配置文件,将所有散落的环境变量读取集中管理:
|
||
|
||
| 配置域 | 包含变量 |
|
||
|--------|---------|
|
||
| 基础配置 | `SECRET_KEY`, `UPLOAD_FOLDER`, `LOG_LEVEL`, `MAX_FILE_SIZE_MB` |
|
||
| ASR 配置 | `USE_ASR_ENDPOINT`, `ASR_BASE_URL`, `ASR_DIARIZE`, `ASR_MIN_SPEAKERS`, `ASR_MAX_SPEAKERS` |
|
||
| LLM 配置 | `TEXT_MODEL_API_KEY`, `TEXT_MODEL_BASE_URL`, `TEXT_MODEL_NAME` |
|
||
| 分段配置 | `ENABLE_CHUNKING` |
|
||
| Inquire 配置 | `ENABLE_INQUIRE_MODE` |
|
||
| 数据库配置 | `SQLALCHEMY_DATABASE_URI`, `DB_POOL_SIZE`, `DB_MAX_OVERFLOW`, `DB_POOL_TIMEOUT`, `DB_POOL_RECYCLE` |
|
||
| 外部 API | `TRANSCRIPTION_BASE_URL`, `VALID_TOKEN`, `EMBEDDINGS_AVAILABLE` |
|
||
| 代理配置 | `HTTP_PROXY`, `HTTPS_PROXY` |
|
||
|
||
**重构成果:**
|
||
- `clients/llm_client.py`、`clients/voiceprint_client.py` 从 `src.config` 导入配置
|
||
- `clients/__init__.py` 统一导出所有配置项
|
||
- `app.py` 通过 `from src.clients import ...` 统一导入配置,消除散落的环境变量读取
|
||
- 提供 `validate_config()` 函数在启动时验证关键配置
|
||
|
||
#### ✅ 已完成:Phase 3.5 - app.py 清理与同步服务抽取
|
||
|
||
将 `src/app.py` 中残留的约 400 行业务逻辑彻底清理,app.py 从约 700 行精简到约 234 行:
|
||
|
||
| 状态 | 任务 | 说明 |
|
||
|------|------|------|
|
||
| ✅ | 删除重复的 preprocess 函数 | 删除了 `preprocess_long_transcription` 等 6 个重复定义的函数(已在 llm_service.py 中) |
|
||
| ✅ | 抽取远程同步服务 | 创建了 `services/sync_service.py`,将约 180 行同步逻辑从 app.py 移出 |
|
||
| ✅ | 清理无用 import | 删除了 30+ 个未使用的导入(render_template、OpenAI、httpx、subprocess、markdown 等) |
|
||
| ✅ | 注释中文化 | 将所有英文注释翻译为中文,删除无意义注释 |
|
||
| ✅ | 修复函数签名 | `start_sync_thread()` 增加 `app` 参数,调用处同步更新 |
|
||
| ✅ | 优化配置读取 | `preprocess_long_transcription` 等函数直接从 `src.config` 读取配置,不再通过参数传递 |
|
||
|
||
#### ✅ 已完成:Phase 4 - app.py 工厂模式重构
|
||
|
||
采用 Flask `create_app()` 工厂模式,将 `src/app.py` 从 234 行进一步精简到 60 行,各层职责清晰分离:
|
||
|
||
**新建模块:**
|
||
|
||
| 文件 | 职责 |
|
||
|------|------|
|
||
| `src/logging_config.py` | 日志配置模块 |
|
||
| `src/flask_ext.py` | Flask 扩展实例集合(login_manager、bcrypt、limiter、csrf) |
|
||
| `src/services/register.py` | 服务层统一初始化(音频分段、同步线程、文件监控、表结构迁移) |
|
||
| `src/api/template_filters.py` | 模板过滤器注册 |
|
||
|
||
**修改模块:**
|
||
|
||
| 文件 | 修改内容 |
|
||
|------|---------|
|
||
| `src/config.py` | 新增 `get_app_config()` 函数,一次性返回所有需写入 `app.config` 的配置项。新增配置只需改此函数,无需修改 `app.py` |
|
||
| `src/clients/__init__.py` | 集中管理 `chunking_service` 和 `EMBEDDINGS_AVAILABLE` 的初始化与导出 |
|
||
| `src/api/__init__.py` | 简化 `register_blueprints()` 函数,直接从 `src.flask_ext` 导入扩展实例 |
|
||
| `src/app.py` | 采用 `create_app()` 工厂模式,仅负责编排各层初始化顺序 |
|
||
|
||
**`app.py` 最终结构(60 行):**
|
||
|
||
```
|
||
create_app()
|
||
├── 1. configure_logging() ← src/logging_config.py
|
||
├── 2. 创建 Flask 应用实例
|
||
├── 3. 加载应用配置 ← src/config.py 的 get_app_config()
|
||
├── 4. 配置反向代理支持
|
||
├── 5. 初始化 Flask 扩展 ← src/flask_ext.py
|
||
├── 6. 初始化数据库 ← src/clients/database.py
|
||
├── 7. 注册蓝图 ← src/api/__init__.py
|
||
├── 8. 注册模板过滤器 ← src/api/template_filters.py
|
||
└── 9. 初始化服务层 ← src/services/register.py
|
||
```
|
||
|
||
**关键设计决策:**
|
||
|
||
| 决策 | 理由 |
|
||
|------|------|
|
||
| `flask_ext.py` 独立 | Flask 扩展是全局单例,不应放在 clients 或 services 中 |
|
||
| `get_app_config()` 集中管理 | 新增配置只需改 config.py,app.py 永远不需要改动 |
|
||
| `register_blueprints` 自行读扩展 | 蓝图注册最清楚自己需要什么扩展,无需 app.py 中转 |
|
||
| `init_services` 放在最后 | 服务依赖数据库和蓝图,必须在它们之后初始化 |
|
||
|
||
---
|
||
|
||
## 开发规范
|
||
|
||
### API 接口返回格式规范
|
||
|
||
> **重要:所有新建/修改的 API 接口必须使用 `ApiResponse` 工具类统一返回格式。旧接口暂不强制迁移。**
|
||
|
||
#### 工具位置
|
||
|
||
`src/api/api_response.py`
|
||
|
||
#### 使用方式
|
||
|
||
```python
|
||
from src.api.api_response import ApiResponse
|
||
|
||
# 成功响应(返回数据)
|
||
@blueprint.route('/xxx', methods=['POST'])
|
||
def create_xxx():
|
||
result = do_something()
|
||
return ApiResponse.success({'xxx': result.to_dict()})
|
||
|
||
# 成功响应(带提示消息)
|
||
return ApiResponse.success({'xxx': result.to_dict()}, '创建成功')
|
||
|
||
# 成功响应(自定义状态码,如 201 Created)
|
||
return ApiResponse.success({'xxx': result.to_dict()}, '创建成功', status_code=201)
|
||
|
||
# 错误响应
|
||
if not xxx:
|
||
return ApiResponse.error('资源未找到', 404)
|
||
|
||
# 错误响应(带详情)
|
||
if not valid:
|
||
return ApiResponse.error('参数校验失败', 400, detail='email 格式不正确')
|
||
|
||
# 分页响应
|
||
return ApiResponse.paginated(
|
||
items=[item.to_dict() for item in items],
|
||
pagination={
|
||
'page': page,
|
||
'per_page': per_page,
|
||
'total': total,
|
||
'total_pages': total_pages,
|
||
'has_next': has_next,
|
||
'has_prev': has_prev
|
||
}
|
||
)
|
||
```
|
||
|
||
#### 响应格式约定
|
||
|
||
**成功响应结构:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "操作成功(可选)",
|
||
"xxx": {...} // 业务数据直接合并到顶层
|
||
}
|
||
```
|
||
|
||
**分页响应结构:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"items": [...],
|
||
"pagination": {
|
||
"page": 1,
|
||
"total": 100,
|
||
...
|
||
}
|
||
}
|
||
```
|
||
|
||
**错误响应结构:**
|
||
```json
|
||
{
|
||
"error": "用户友好的错误提示"
|
||
}
|
||
```
|
||
|
||
#### 注意事项
|
||
|
||
1. `data` 参数必须是字典类型,列表数据请使用 `paginated()` 或直接合并到字典中
|
||
2. 前端通过 `response.ok`(HTTP 状态码)判断成功/失败,通过 `data.error` 获取错误信息
|
||
3. 旧接口暂不迁移,新接口和修改的接口必须使用 `ApiResponse`
|
||
4. 不要在 `ApiResponse` 中直接传递原始异常信息给用户(`str(e)`),应转为友好提示
|
||
|
||
---
|
||
|
||
## 技术栈
|
||
|
||
- **后端**: Python/Flask + SQLAlchemy
|
||
- **前端**: Vue.js 3 + Tailwind CSS
|
||
- **AI**: OpenAI Whisper (ASR) + Ollama/OpenRouter (LLM)
|
||
- **数据库**: SQLite(默认)/ PostgreSQL(支持环境变量切换)
|
||
- **部署**: Nginx 反向代理 + Flask 开发服务器
|
||
|
||
---
|
||
|
||
## 依赖安装
|
||
|
||
```bash
|
||
conda activate speakr
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
主要依赖:
|
||
- `flask==2.3.3`
|
||
- `flask-sqlalchemy==3.1.1`
|
||
- `flask-login==0.6.3`
|
||
- `flask-wtf==1.2.2`
|
||
- `flask-bcrypt==1.0.1`
|
||
- `openai==1.3.0`
|
||
- `markdown==3.5.1`
|
||
- `python-docx==1.1.0`
|
||
|
||
---
|
||
|
||
## 许可证
|
||
|
||
本项目基于 AGPL v3.0 开源协议。
|