nex_docus/backend/README.md

107 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# NEX Docus 后端
FastAPI + SQLAlchemy 2.0(异步)+ MySQL + Redis 的文档管理后端,同时提供 RAG 知识库问答与 MCP 服务。
> **上手请先看 [`docs/quickstart.md`](../docs/quickstart.md)**(一键启动、环境变量、初始化、常见问题)。
> 部署/运维见 [`docs/deploy/README.md`](../docs/deploy/README.md),表结构见 [`docs/database.md`](../docs/database.md)。
> 本文件只描述**后端代码内部结构**与开发约定。
## 技术栈
| 用途 | 选型 |
| --- | --- |
| Web 框架 | FastAPI + Uvicorn |
| ORM | SQLAlchemy 2.0 异步(aiomysql),Alembic 迁移 |
| 数据库 / 缓存 | MySQL 8(utf8mb4)/ Redis |
| 认证 | JWT(python-jose)+ bcrypt(passlib) |
| 全文检索 | Whoosh 3 + jieba 分词 |
| 向量检索 | ZVec(远端 Embedding 或本地 Sentence-Transformers) |
| 文件与导出 | aiofiles、自研 Markdown/PDF 导出服务 |
| Git 同步 | 通过命令行调用 `git`(主机需自行安装) |
| 测试 | pytest + pytest-asyncio |
## 目录结构
```
backend/
├── app/
│ ├── api/v1/ # HTTP 路由(前缀 /api/v1),__init__.py 汇总注册
│ ├── core/ # config / database / deps / security / enums / migrations / redis_client
│ ├── mcp/ # MCP server 与请求上下文(挂载在 /mcp)
│ ├── models/ # SQLAlchemy 模型(__init__.py 必须导入全部模型,见下方陷阱)
│ ├── schemas/ # Pydantic Schema 与统一响应包装
│ └── services/ # 业务逻辑:project/file/search/rag/zvec/git/vectorization/notification/log/storage/llm…
├── scripts/ # init_db.py(建表+种子数据)、generate_password.py,详见 scripts/README.md
├── tests/ # pytest 用例
├── models/ # 本地向量模型目录(gitignore,需自行放置 HF 模型,如 m3e-small)
├── main.py # 应用入口:/api/v1 路由、/mcp 挂载、/ 与 /health
├── requirements.txt # 运行依赖
├── requirements-dev.txt # 开发/测试依赖(含 pytest)
├── pytest.ini # testpaths=tests, pythonpath=.
└── .env # 本地配置(不入库;键名见下表)
```
## 路由一览
`main.py` 暴露:`GET /`(名称/版本/状态)、`GET /health`(`{"status":"healthy"}`)、`/docs`、`/openapi.json`、`/mcp`(MCP Streamable HTTP,需 `X-Bot-Id` / `X-Bot-Secret`)。
`app/api/v1/__init__.py` 注册的子路由前缀:
| 前缀 | 模块 | 说明 |
| --- | --- | --- |
| `/auth` | auth | 登录、当前用户、资料/密码/头像、MCP 凭证签发与轮换 |
| `/projects` | projects | 项目 CRUD、成员、所有权转移、分享开关、Git pull/push 与目录选择 |
| (同 `/projects/{id}/git-repos`) | git_repos | 仓库配置的增删改查与连通性测试(路由内部自带前缀) |
| `/files` | files | 目录树、读写文件、文件操作、上传、导入导出、PDF 导出、文档与静态资源流式访问 |
| `/preview` | preview | 项目预览 |
| `/shares` | shares | 分享链接创建/校验/访问 |
| `/search` | search | 全文 + 向量双引擎检索 |
| `/chat` | chat | 会话管理、SSE 流式问答、引用与中断 |
| `/llm-model-configs` | llm_model_configs | chat / embedding 模型配置与连通性测试 |
| `/dashboard` | dashboard | 管理员统计 |
| `/users` `/roles` `/role-permissions` `/menu` | users/roles/role_permissions/menu | 用户、角色、角色权限、菜单树 |
| `/notifications` | notifications | 站内通知 |
| `/logs` | logs | 系统日志查询 |
## 环境变量(`backend/.env`)
完整键表与说明见 [`docs/quickstart.md`](../docs/quickstart.md);此处只列必填项与常见坑:
- 必填:`DB_HOST` `DB_USER` `DB_PASSWORD` `DB_NAME`、`REDIS_HOST` `REDIS_PASSWORD`、`SECRET_KEY`。
- 文件存储:`STORAGE_ROOT` / `PROJECTS_PATH` / `USERS_PATH` / `TEMP_PATH`(**本地开发用这组绝对路径**;Docker 部署用根 `.env` 的 `STORAGE_PATH`,两套体系不同,别混用)。
- `DEBUG=True` 时 SQLAlchemy 会打印全量 SQL,**生产必须 `false`**。
- `DB_PASSWORD` 含特殊字符无需手动转义(连接串构造时已 `quote_plus`)。
> ⚠️ **不要在代码、文档或提交里写真实环境的账号密码。** 需要示例时用占位值(如 `password_change_me`)。
## 开发命令
```bash
cd backend
python3 -m venv venv
./venv/bin/pip install -r requirements-dev.txt # 含运行依赖 + pytest
cp .env.example .env # 若无,按 docs/quickstart.md 的键表创建
./venv/bin/python scripts/init_db.py # 建表 + 种子数据(幂等)
./venv/bin/python scripts/generate_password.py # 生成 bcrypt 哈希
./venv/bin/python -m pytest # 单元测试
./venv/bin/uvicorn main:app --reload --port 8000 # 单独起服务(推荐用仓库根 ./scripts/start.sh)
```
## 必须知道的实现约定
1. **新增模型要在 `app/models/__init__.py` 里导入。** 只建表逻辑而不导入,`Base.metadata` 就看不到该表,`init_db.py` 会**静默漏建表**,线上表现为 `Table ... doesn't exist`。
2. **项目权限只走 `app/services/project_service.py`**(`require_project_read_access` / `require_project_write_access` / `normalize_project_role`)。不要在路由里手写角色判断:历史数据里 `project_members.role` 存在大写(`ADMIN`/`EDITOR`/`VIEWER`),直接 `== "admin"` 比较会静默失效。返回给前端的角色必须先 `normalize_project_role`。
3. **异步 Session 配了 `expire_on_commit=False`**,但 `onupdate=func.now()` 的列(如 `updated_at`)在 UPDATE 提交后仍会被标记过期;提交后若还要序列化整个 ORM 对象,**必须 `await db.refresh(obj)`**,否则会在异步上下文触发同步 IO 抛 `MissingGreenlet`(HTTP 500)。
4. **统一响应**用 `app/schemas/response.py` 的 `success_response()`;业务错误抛 `HTTPException` 并用 4xx 表达(不要用 500 表达"参数/状态不合法")。
5. **SSE 接口**(`/chat`)要求网关关闭响应缓冲,见 `docs/deploy/README.md` 的 nginx 示例。
6. **本地向量模型**放在 `backend/models/<模型名>/`(含 `config.json`、`1_Pooling/config.json`、`model.safetensors` 或 `pytorch_model.bin`),由 `LocalEmbeddingService` 扫描列出;该目录已 gitignore,不要提交。
## 测试
```bash
cd backend && ./venv/bin/python -m pytest
```
覆盖范围(v1.0.0):项目权限与角色归一化、Git 服务命令构造、搜索服务、模型与向量配置、RAG 引用解析。**前端目前没有自动化测试**,界面回归需手工验证,重点清单见 [`docs/sdd/releases/v1.0.0.md`](../docs/sdd/releases/v1.0.0.md)。