778 lines
18 KiB
Markdown
778 lines
18 KiB
Markdown
# ZQ Platform - FastAPI Backend
|
||
|
||
基于 FastAPI 的现代化异步后端服务,使用 SQLAlchemy 异步 ORM + Alembic 数据库迁移 + PostgreSQL。
|
||
|
||
## 技术栈
|
||
|
||
- **框架**: FastAPI 0.115+
|
||
- **数据库**: PostgreSQL 16+
|
||
- **ORM**: SQLAlchemy 2.0+ (异步)
|
||
- **迁移**: Alembic
|
||
- **认证**: JWT
|
||
- **缓存**: Redis
|
||
- **Python**: 3.12+
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
backend-fastapi/
|
||
├── app/ # 核心应用模块
|
||
│ ├── config.py # 配置管理
|
||
│ ├── database.py # 数据库连接
|
||
│ ├── base_model.py # BaseModel 基类
|
||
│ ├── base_schema.py # 通用 Schema
|
||
│ ├── base_service.py # BaseService 基类
|
||
│ ├── redis.py # Redis 缓存
|
||
│ └── excel.py # Excel 工具
|
||
├── core/ # 核心业务模块
|
||
│ ├── user/ # 用户管理
|
||
│ ├── role/ # 角色管理
|
||
│ ├── menu/ # 菜单管理
|
||
│ ├── dept/ # 部门管理
|
||
│ ├── permission/ # 权限管理
|
||
│ └── ...
|
||
├── scheduler/ # 定时任务模块
|
||
│ ├── model.py
|
||
│ ├── service.py
|
||
│ └── tasks.py
|
||
├── zq_demo/ # 示例模块
|
||
│ ├── demo/
|
||
│ └── demo_cache/
|
||
├── scripts/ # 工具脚本
|
||
│ ├── dumpdata.py # 数据导出
|
||
│ └── loaddata.py # 数据导入
|
||
├── alembic/ # 数据库迁移
|
||
│ ├── versions/
|
||
│ └── env.py
|
||
├── env/ # 环境配置
|
||
│ ├── dev.env
|
||
│ ├── uat.env
|
||
│ └── prod.env
|
||
├── main.py # 应用入口
|
||
├── requirements.txt # 依赖列表
|
||
└── README.md
|
||
```
|
||
|
||
## 快速开始
|
||
|
||
### 1. 环境准备
|
||
|
||
```bash
|
||
# 创建虚拟环境
|
||
conda create -n zq-fastapi python=3.12
|
||
conda activate zq-fastapi
|
||
|
||
# 或使用 venv
|
||
python -m venv venv
|
||
source venv/bin/activate # Linux/Mac
|
||
# venv\Scripts\activate # Windows
|
||
```
|
||
|
||
### 2. 安装依赖
|
||
|
||
```bash
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
### 3. 配置环境变量
|
||
|
||
复制环境配置文件:
|
||
|
||
```bash
|
||
cp env/example.env env/dev.env
|
||
```
|
||
|
||
编辑 `env/dev.env`,配置数据库连接:
|
||
|
||
```env
|
||
# 数据库配置
|
||
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/dbname
|
||
|
||
# Redis 配置
|
||
REDIS_HOST=localhost
|
||
REDIS_PORT=6379
|
||
REDIS_DB=0
|
||
|
||
# JWT 配置
|
||
SECRET_KEY=your-secret-key-here
|
||
ALGORITHM=HS256
|
||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||
```
|
||
|
||
### 4. 数据库迁移
|
||
|
||
```bash
|
||
# 首次使用:生成初始迁移
|
||
alembic revision --autogenerate -m "init tables"
|
||
|
||
# 执行迁移
|
||
alembic upgrade head
|
||
|
||
# 导入数据
|
||
python scripts/loaddata.py db_init.json
|
||
```
|
||
|
||
### 5. 启动服务
|
||
|
||
```bash
|
||
# 开发模式(自动重载)
|
||
python main.py
|
||
|
||
# 或使用 uvicorn
|
||
uvicorn main:app --reload --host 0.0.0.0 --port 8000
|
||
```
|
||
|
||
### 6. 访问 API 文档
|
||
|
||
- **Swagger UI**: http://localhost:8000/docs
|
||
- **ReDoc**: http://localhost:8000/redoc
|
||
|
||
## 数据库操作
|
||
|
||
### 迁移命令
|
||
|
||
```bash
|
||
# 查看当前版本
|
||
alembic current
|
||
|
||
# 查看迁移历史
|
||
alembic history
|
||
|
||
# 生成新的迁移文件
|
||
alembic revision --autogenerate -m "描述信息"
|
||
|
||
# 升级到最新版本
|
||
alembic upgrade head
|
||
|
||
# 回滚一个版本
|
||
alembic downgrade -1
|
||
|
||
# 回滚到指定版本
|
||
alembic downgrade <revision_id>
|
||
```
|
||
|
||
### 数据导入导出
|
||
|
||
#### 导出数据(dumpdata.py)
|
||
|
||
```bash
|
||
# 导出所有数据到文件
|
||
python scripts/dumpdata.py -o db_init.json -f
|
||
|
||
# 导出指定模块(如 core)
|
||
python scripts/dumpdata.py core -o core_data.json -f
|
||
|
||
# 导出到标准输出(不指定 -o 参数)
|
||
python scripts/dumpdata.py > data.json
|
||
|
||
# 导出指定模块到标准输出
|
||
python scripts/dumpdata.py core > core_data.json
|
||
```
|
||
|
||
**参数说明:**
|
||
|
||
- `app_name`(位置参数,可选):指定要导出的应用/模块名称
|
||
- 例如:`core`、`scheduler`、`zq_demo`
|
||
- 不指定则导出所有数据
|
||
|
||
- `-o, --output`:指定输出文件路径
|
||
- 例如:`-o db_init.json`
|
||
- 不指定则输出到标准输出(stdout)
|
||
|
||
- `-f, --force`:强制覆盖已存在的文件
|
||
- 如果输出文件已存在且未使用此参数,脚本会报错并退出
|
||
- 使用此参数可以强制覆盖现有文件
|
||
|
||
**示例:**
|
||
|
||
```bash
|
||
# 导出所有数据,如果文件存在则覆盖
|
||
python scripts/dumpdata.py -o db_init.json -f
|
||
|
||
# 导出 core 模块数据,不覆盖已存在文件(文件存在会报错)
|
||
python scripts/dumpdata.py core -o core_data.json
|
||
|
||
# 导出 scheduler 模块数据到标准输出,然后重定向到文件
|
||
python scripts/dumpdata.py scheduler > scheduler_data.json
|
||
```
|
||
|
||
#### 导入数据(loaddata.py)
|
||
|
||
```bash
|
||
# 导入数据
|
||
python scripts/loaddata.py db_init.json
|
||
|
||
# 导入多个文件
|
||
python scripts/loaddata.py core_data.json scheduler_data.json
|
||
```
|
||
|
||
**参数说明:**
|
||
|
||
- `files`(位置参数,必需):要导入的 JSON 文件路径,可以指定多个文件
|
||
|
||
## 开发指南
|
||
|
||
### 新建模块
|
||
|
||
按照以下步骤创建新的业务模块(以 `example` 为例):
|
||
|
||
#### 1. 创建模块目录
|
||
|
||
```bash
|
||
mkdir -p core/example
|
||
touch core/example/__init__.py
|
||
touch core/example/model.py
|
||
touch core/example/schema.py
|
||
touch core/example/service.py
|
||
touch core/example/api.py
|
||
```
|
||
|
||
#### 2. 定义模型 (model.py)
|
||
|
||
```python
|
||
from sqlalchemy import Column, String, Boolean
|
||
from app.base_model import BaseModel
|
||
|
||
class Example(BaseModel):
|
||
__tablename__ = "core_example"
|
||
|
||
name = Column(String(100), nullable=False, comment="名称")
|
||
description = Column(String(500), comment="描述")
|
||
is_active = Column(Boolean, default=True, comment="是否激活")
|
||
```
|
||
|
||
#### 3. 定义 Schema (schema.py)
|
||
|
||
```python
|
||
from pydantic import BaseModel, ConfigDict
|
||
from typing import Optional
|
||
from datetime import datetime
|
||
|
||
class ExampleBase(BaseModel):
|
||
name: str
|
||
description: Optional[str] = None
|
||
is_active: bool = True
|
||
|
||
class ExampleCreate(ExampleBase):
|
||
pass
|
||
|
||
class ExampleUpdate(BaseModel):
|
||
name: Optional[str] = None
|
||
description: Optional[str] = None
|
||
is_active: Optional[bool] = None
|
||
|
||
class ExampleResponse(ExampleBase):
|
||
id: str
|
||
sort: int = 0
|
||
is_deleted: bool = False
|
||
sys_create_datetime: Optional[datetime] = None
|
||
sys_update_datetime: Optional[datetime] = None
|
||
|
||
model_config = ConfigDict(from_attributes=True)
|
||
```
|
||
|
||
#### 4. 定义服务 (service.py)
|
||
|
||
```python
|
||
from app.base_service import BaseService
|
||
from core.example.model import Example
|
||
from core.example.schema import ExampleCreate, ExampleUpdate
|
||
|
||
class ExampleService(BaseService[Example, ExampleCreate, ExampleUpdate]):
|
||
model = Example
|
||
```
|
||
|
||
#### 5. 定义 API (api.py)
|
||
|
||
```python
|
||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from app.database import get_db
|
||
from app.base_schema import PaginatedResponse, ResponseModel
|
||
from core.example.schema import ExampleCreate, ExampleUpdate, ExampleResponse
|
||
from core.example.service import ExampleService
|
||
|
||
router = APIRouter(prefix="/example", tags=["示例管理"])
|
||
|
||
@router.post("", response_model=ExampleResponse, summary="创建")
|
||
async def create(data: ExampleCreate, db: AsyncSession = Depends(get_db)):
|
||
return await ExampleService.create(db=db, data=data)
|
||
|
||
@router.get("", response_model=PaginatedResponse[ExampleResponse], summary="获取列表")
|
||
async def get_list(
|
||
page: int = Query(default=1, ge=1),
|
||
page_size: int = Query(default=20, ge=1, le=100, alias="pageSize"),
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
items, total = await ExampleService.get_list(db, page=page, page_size=page_size)
|
||
return PaginatedResponse(items=items, total=total)
|
||
|
||
@router.get("/{record_id}", response_model=ExampleResponse, summary="获取详情")
|
||
async def get_by_id(record_id: str, db: AsyncSession = Depends(get_db)):
|
||
result = await ExampleService.get_by_id(db, record_id=record_id)
|
||
if not result:
|
||
raise HTTPException(status_code=404, detail="记录不存在")
|
||
return result
|
||
|
||
@router.put("/{record_id}", response_model=ExampleResponse, summary="更新")
|
||
async def update(record_id: str, data: ExampleUpdate, db: AsyncSession = Depends(get_db)):
|
||
result = await ExampleService.update(db, record_id=record_id, data=data)
|
||
if not result:
|
||
raise HTTPException(status_code=404, detail="记录不存在")
|
||
return result
|
||
|
||
@router.delete("/{record_id}", response_model=ResponseModel, summary="删除")
|
||
async def delete(record_id: str, db: AsyncSession = Depends(get_db)):
|
||
success = await ExampleService.delete(db, record_id=record_id)
|
||
if not success:
|
||
raise HTTPException(status_code=404, detail="记录不存在")
|
||
return ResponseModel(message="删除成功")
|
||
```
|
||
|
||
#### 6. 注册路由
|
||
|
||
在 `core/router.py` 中添加:
|
||
|
||
```python
|
||
from core.example.api import router as example_router
|
||
|
||
router.include_router(example_router)
|
||
```
|
||
|
||
#### 7. 生成数据库迁移
|
||
|
||
```bash
|
||
alembic revision --autogenerate -m "add example table"
|
||
alembic upgrade head
|
||
```
|
||
|
||
## 核心功能
|
||
|
||
### BaseModel
|
||
|
||
所有模型继承自 `BaseModel`,自动包含以下字段:
|
||
|
||
- `id`: UUID 主键
|
||
- `sort`: 排序字段
|
||
- `is_deleted`: 软删除标记
|
||
- `sys_create_datetime`: 创建时间
|
||
- `sys_update_datetime`: 更新时间
|
||
- `sys_creator_id`: 创建人ID
|
||
- `sys_modifier_id`: 修改人ID
|
||
|
||
### BaseService
|
||
|
||
提供通用 CRUD 操作:
|
||
|
||
- `create()`: 创建记录
|
||
- `get_by_id()`: 根据ID获取
|
||
- `get_list()`: 分页查询
|
||
- `update()`: 更新记录
|
||
- `delete()`: 删除记录(软删除/硬删除)
|
||
- `check_unique()`: 唯一性检查
|
||
- `export_to_excel()`: 导出Excel
|
||
- `import_from_excel()`: 导入Excel
|
||
|
||
### 缓存支持
|
||
|
||
使用 Redis 缓存,继承 `CacheService` 获得缓存功能:
|
||
|
||
```python
|
||
from app.cache_service import CacheService
|
||
|
||
class ExampleService(CacheService[Example, ExampleCreate, ExampleUpdate]):
|
||
model = Example
|
||
cache_prefix = "example"
|
||
cache_ttl = 3600 # 1小时
|
||
```
|
||
|
||
## 环境配置
|
||
|
||
项目支持多环境配置:
|
||
|
||
- `env/dev.env`: 开发环境
|
||
- `env/uat.env`: UAT环境
|
||
- `env/prod.env`: 生产环境
|
||
|
||
通过环境变量 `ENV` 切换:
|
||
|
||
```bash
|
||
export ENV=prod # 使用生产环境配置
|
||
python main.py
|
||
```
|
||
|
||
## API 规范
|
||
|
||
### 响应格式
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {...}
|
||
}
|
||
```
|
||
|
||
分页响应:
|
||
|
||
```json
|
||
{
|
||
"items": [...],
|
||
"total": 100
|
||
}
|
||
```
|
||
|
||
错误响应:
|
||
|
||
```json
|
||
{
|
||
"detail": "错误信息"
|
||
}
|
||
```
|
||
|
||
### 路由命名规范
|
||
|
||
- 使用小写短横线:`/api/core/user-profile`
|
||
- 静态路由在前:`/api/core/menu/check/name`
|
||
- 动态路由在后:`/api/core/menu/{menu_id}`
|
||
|
||
## 常见问题
|
||
|
||
### 1. 迁移文件为空
|
||
|
||
确保 `alembic/env.py` 中的 `auto_import_models()` 函数正确扫描了所有模型文件。
|
||
|
||
### 2. 路由重定向 307
|
||
|
||
检查路由定义,使用 `@router.post("")` 而不是 `@router.post("/")`。
|
||
|
||
### 3. 数据库连接失败
|
||
|
||
检查 `env/dev.env` 中的 `DATABASE_URL` 配置是否正确。
|
||
|
||
# WeasyPrint 安装与配置指南
|
||
|
||
本文档介绍如何在不同操作系统上安装和配置 WeasyPrint 及其依赖。
|
||
|
||
## 目录
|
||
|
||
- [macOS](#macos)
|
||
- [Linux (Ubuntu/Debian)](#linux-ubuntudebian)
|
||
- [Linux (CentOS/RHEL)](#linux-centosrhel)
|
||
- [Windows](#windows)
|
||
- [验证安装](#验证安装)
|
||
- [常见问题](#常见问题)
|
||
|
||
---
|
||
|
||
## macOS
|
||
|
||
### 1. 安装系统依赖
|
||
|
||
使用 Homebrew 安装所需的系统库:
|
||
|
||
```bash
|
||
brew install pango glib gobject-introspection harfbuzz cairo fontconfig freetype
|
||
```
|
||
|
||
### 2. 安装 Python 依赖
|
||
|
||
```bash
|
||
pip install weasyprint==62.3
|
||
```
|
||
|
||
### 3. 配置环境变量
|
||
|
||
WeasyPrint 需要能够找到系统库。将以下内容添加到 `~/.zshrc`(如果使用 bash,则添加到 `~/.bash_profile`):
|
||
|
||
```bash
|
||
export DYLD_LIBRARY_PATH="/opt/homebrew/opt/glib/lib:/opt/homebrew/opt/pango/lib:/opt/homebrew/opt/harfbuzz/lib:/opt/homebrew/opt/cairo/lib:/opt/homebrew/opt/fontconfig/lib:/opt/homebrew/opt/freetype/lib:$DYLD_LIBRARY_PATH"
|
||
```
|
||
|
||
**自动添加方法:**
|
||
|
||
```bash
|
||
echo 'export DYLD_LIBRARY_PATH="/opt/homebrew/opt/glib/lib:/opt/homebrew/opt/pango/lib:/opt/homebrew/opt/harfbuzz/lib:/opt/homebrew/opt/cairo/lib:/opt/homebrew/opt/fontconfig/lib:/opt/homebrew/opt/freetype/lib:$DYLD_LIBRARY_PATH"' >> ~/.zshrc
|
||
source ~/.zshrc
|
||
```
|
||
|
||
### 4. 重启终端或 IDE
|
||
|
||
关闭并重新打开终端窗口,或者重启 IDE,使环境变量生效。
|
||
|
||
---
|
||
|
||
## Linux (Ubuntu/Debian)
|
||
|
||
### 1. 安装系统依赖
|
||
|
||
```bash
|
||
sudo apt-get update
|
||
sudo apt-get install -y \
|
||
libpango-1.0-0 \
|
||
libpangocairo-1.0-0 \
|
||
libgdk-pixbuf2.0-0 \
|
||
libffi-dev \
|
||
libcairo2 \
|
||
libglib2.0-0 \
|
||
libharfbuzz0b \
|
||
libfontconfig1 \
|
||
libfreetype6
|
||
```
|
||
|
||
### 2. 安装 Python 依赖
|
||
|
||
```bash
|
||
pip install weasyprint==62.3
|
||
```
|
||
|
||
### 3. 配置环境变量(通常不需要)
|
||
|
||
在 Linux 上,系统库通常已经在标准路径中,不需要额外配置环境变量。
|
||
|
||
如果遇到库加载问题,可以添加到 `~/.bashrc`:
|
||
|
||
```bash
|
||
export LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH"
|
||
source ~/.bashrc
|
||
```
|
||
|
||
---
|
||
|
||
## Linux (CentOS/RHEL)
|
||
|
||
### 1. 安装系统依赖
|
||
|
||
```bash
|
||
sudo yum install -y \
|
||
pango \
|
||
pango-devel \
|
||
cairo \
|
||
cairo-devel \
|
||
glib2 \
|
||
glib2-devel \
|
||
harfbuzz \
|
||
harfbuzz-devel \
|
||
fontconfig \
|
||
fontconfig-devel \
|
||
freetype \
|
||
freetype-devel \
|
||
libffi-devel
|
||
```
|
||
|
||
或者使用 dnf(CentOS 8+):
|
||
|
||
```bash
|
||
sudo dnf install -y \
|
||
pango \
|
||
pango-devel \
|
||
cairo \
|
||
cairo-devel \
|
||
glib2 \
|
||
glib2-devel \
|
||
harfbuzz \
|
||
harfbuzz-devel \
|
||
fontconfig \
|
||
fontconfig-devel \
|
||
freetype \
|
||
freetype-devel \
|
||
libffi-devel
|
||
```
|
||
|
||
### 2. 安装 Python 依赖
|
||
|
||
```bash
|
||
pip install weasyprint==62.3
|
||
```
|
||
|
||
### 3. 配置环境变量(如果需要)
|
||
|
||
```bash
|
||
export LD_LIBRARY_PATH="/usr/lib64:$LD_LIBRARY_PATH"
|
||
echo 'export LD_LIBRARY_PATH="/usr/lib64:$LD_LIBRARY_PATH"' >> ~/.bashrc
|
||
source ~/.bashrc
|
||
```
|
||
|
||
---
|
||
|
||
## Windows
|
||
|
||
### 方法 1:使用 GTK3 Runtime(推荐)
|
||
|
||
1. **下载并安装 GTK3 Runtime**
|
||
|
||
访问 [GTK for Windows Runtime](https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer/releases),下载最新版本的安装程序(例如 `gtk3-runtime-3.24.31-2022-01-04-ts-win64.exe`)。
|
||
|
||
2. **运行安装程序**
|
||
|
||
双击安装程序,按照提示完成安装。默认安装路径为 `C:\Program Files\GTK3-Runtime Win64`。
|
||
|
||
3. **添加到系统 PATH**
|
||
|
||
- 右键点击"此电脑" → "属性" → "高级系统设置" → "环境变量"
|
||
- 在"系统变量"中找到 `Path`,点击"编辑"
|
||
- 添加以下路径:
|
||
```
|
||
C:\Program Files\GTK3-Runtime Win64\bin
|
||
```
|
||
- 点击"确定"保存
|
||
|
||
4. **安装 Python 依赖**
|
||
|
||
```cmd
|
||
pip install weasyprint==62.3
|
||
```
|
||
|
||
5. **重启命令提示符或 PowerShell**
|
||
|
||
### 方法 2:使用 MSYS2(开发者推荐)
|
||
|
||
1. **安装 MSYS2**
|
||
|
||
访问 [MSYS2 官网](https://www.msys2.org/),下载并安装 MSYS2。
|
||
|
||
2. **安装依赖包**
|
||
|
||
打开 MSYS2 终端,运行:
|
||
|
||
```bash
|
||
pacman -S mingw-w64-x86_64-pango mingw-w64-x86_64-cairo mingw-w64-x86_64-glib2
|
||
```
|
||
|
||
3. **添加到系统 PATH**
|
||
|
||
将 MSYS2 的 bin 目录添加到系统 PATH:
|
||
```
|
||
C:\msys64\mingw64\bin
|
||
```
|
||
|
||
4. **安装 Python 依赖**
|
||
|
||
```cmd
|
||
pip install weasyprint==62.3
|
||
```
|
||
|
||
---
|
||
|
||
## 验证安装
|
||
|
||
运行以下 Python 代码验证 WeasyPrint 是否正确安装:
|
||
|
||
```python
|
||
from weasyprint import HTML
|
||
|
||
html_content = """
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<style>
|
||
body { font-family: "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||
h1 { color: #333; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1>测试中文字体</h1>
|
||
<p>这是一个测试文档,用于验证 WeasyPrint 是否正确安装。</p>
|
||
<p>Test English text and 中文文本。</p>
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
try:
|
||
pdf_bytes = HTML(string=html_content).write_pdf()
|
||
with open('test.pdf', 'wb') as f:
|
||
f.write(pdf_bytes)
|
||
print("✓ WeasyPrint 安装成功!已生成 test.pdf")
|
||
except Exception as e:
|
||
print(f"✗ WeasyPrint 安装失败:{e}")
|
||
```
|
||
|
||
如果成功,会在当前目录生成 `test.pdf` 文件。
|
||
|
||
---
|
||
|
||
## 常见问题
|
||
|
||
### 1. macOS: `OSError: cannot load library 'libgobject-2.0-0'`
|
||
|
||
**原因**:环境变量未正确设置。
|
||
|
||
**解决方案**:
|
||
- 确保已添加环境变量到 `~/.zshrc`
|
||
- 重启终端或运行 `source ~/.zshrc`
|
||
- 如果使用 IDE,需要重启 IDE
|
||
|
||
### 2. Linux: `ImportError: cannot import name 'HTML' from 'weasyprint'`
|
||
|
||
**原因**:系统依赖未安装。
|
||
|
||
**解决方案**:
|
||
```bash
|
||
sudo apt-get install -y libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0
|
||
```
|
||
|
||
### 3. Windows: `OSError: no library called "cairo" was found`
|
||
|
||
**原因**:GTK3 Runtime 未安装或未添加到 PATH。
|
||
|
||
**解决方案**:
|
||
- 确保已安装 GTK3 Runtime
|
||
- 检查 `C:\Program Files\GTK3-Runtime Win64\bin` 是否在系统 PATH 中
|
||
- 重启命令提示符
|
||
|
||
### 4. 中文字体显示为方块
|
||
|
||
**原因**:系统缺少中文字体。
|
||
|
||
**解决方案**:
|
||
|
||
**macOS**:
|
||
```bash
|
||
# 系统自带中文字体,通常不需要额外安装
|
||
```
|
||
|
||
**Linux**:
|
||
```bash
|
||
sudo apt-get install fonts-noto-cjk fonts-wqy-zenhei
|
||
```
|
||
|
||
**Windows**:
|
||
- 确保系统已安装中文字体(如微软雅黑、宋体等)
|
||
- Windows 10/11 默认已包含中文字体
|
||
|
||
### 5. Conda 环境中的问题
|
||
|
||
如果在 Conda 环境中遇到库加载问题,尝试:
|
||
|
||
```bash
|
||
# 安装 conda-forge 版本
|
||
conda install -c conda-forge weasyprint
|
||
```
|
||
|
||
或者确保环境变量在激活 Conda 环境后仍然有效。
|
||
|
||
---
|
||
|
||
## 项目启动
|
||
|
||
配置完成后,启动后端服务:
|
||
|
||
```bash
|
||
cd /path/to/backend-fastapi
|
||
python -m uvicorn main:app --reload
|
||
```
|
||
|
||
如果一切正常,服务应该能够成功启动,并且 PDF 预览功能可以正常使用。
|
||
|
||
---
|
||
|
||
## 参考链接
|
||
|
||
- [WeasyPrint 官方文档](https://doc.courtbouillon.org/weasyprint/stable/)
|
||
- [WeasyPrint 安装指南](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#installation)
|
||
- [WeasyPrint 故障排除](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#troubleshooting)
|
||
|