feat: 初始化FastAPI项目基础框架
添加项目基础结构,包括: - 核心模块(src/main.py) - 路由模块(users/items) - 数据库配置和模型 - 日志工具 - 测试用例 - 项目文档和依赖配置
This commit is contained in:
11
.env.example
Normal file
11
.env.example
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Database configuration
|
||||||
|
DATABASE_URL=sqlite:///./test.db
|
||||||
|
|
||||||
|
# API configuration
|
||||||
|
API_HOST=localhost
|
||||||
|
API_PORT=8000
|
||||||
|
|
||||||
|
# Security
|
||||||
|
SECRET_KEY=your-secret-key-here
|
||||||
|
ALGORITHM=HS256
|
||||||
|
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||||
234
.gitignore
vendored
Normal file
234
.gitignore
vendored
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak
|
||||||
|
venv.bak
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Project specific
|
||||||
|
test.db
|
||||||
|
|
||||||
|
# Additions for better Python project management
|
||||||
|
|
||||||
|
# Environment variables file
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# IDE and editor files
|
||||||
|
.vscode/settings.json
|
||||||
|
.vscode/tasks.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.idea/
|
||||||
|
*.sublime-project
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Operating System files
|
||||||
|
.DS_Store*
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Icon?
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.py[cod]
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# Database
|
||||||
|
*.sqlite3
|
||||||
|
*.db
|
||||||
|
*.dump
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
~*
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Compiled files
|
||||||
|
*.com
|
||||||
|
*.class
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Packages
|
||||||
|
*.7z
|
||||||
|
*.dmg
|
||||||
|
*.gz
|
||||||
|
*.iso
|
||||||
|
*.jar
|
||||||
|
*.rar
|
||||||
|
*.tar
|
||||||
|
*.zip
|
||||||
171
README.md
Normal file
171
README.md
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
# FastAPI项目模板 及 快速开发范式
|
||||||
|
|
||||||
|
|
||||||
|
## 1\. 核心原则
|
||||||
|
|
||||||
|
* **简洁至上**:避免过度设计。优先选择简单直接的方案。
|
||||||
|
* **一致性**:团队成员遵循相同的规范,降低沟通和维护成本。
|
||||||
|
* **自动化**:利用工具自动完成格式化、检查等重复性工作。
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
## 2\. 虚拟环境与依赖管理
|
||||||
|
|
||||||
|
**目标**:隔离项目依赖,保证环境一致性。
|
||||||
|
|
||||||
|
**规范**:
|
||||||
|
|
||||||
|
* **工具**:使用 Python 内置的 `venv` 模块。
|
||||||
|
|
||||||
|
* **创建虚拟环境**:在项目根目录执行 `python -m venv venv`。`.gitignore` 文件应包含 `venv/` 目录。
|
||||||
|
* **激活环境**:
|
||||||
|
* macOS/Linux: `source venv/bin/activate`
|
||||||
|
* Windows: `.\venv\Scripts\activate`
|
||||||
|
|
||||||
|
* **依赖管理**:
|
||||||
|
|
||||||
|
* 使用 `pip` 和 `requirements.txt` 文件。
|
||||||
|
* **安装依赖**:`pip install -r requirements.txt`
|
||||||
|
* **更新依赖文件**:完成开发或添加新包后,运行 `pip freeze > requirements.txt` 更新依赖列表。或手动修改依赖列表。
|
||||||
|
* **(可选) 开发依赖**:可以创建一个 `requirements-dev.txt` 文件,存放仅在开发时使用的工具(如 `pytest`, `black`),并将其 `include` 到主 `requirements.txt` 中或分开安装。
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
## 3\. 项目结构
|
||||||
|
|
||||||
|
**目标**:清晰、模块化,便于快速定位代码。
|
||||||
|
|
||||||
|
推荐一个可扩展的结构:
|
||||||
|
|
||||||
|
```
|
||||||
|
your_project_name/
|
||||||
|
├── src/ # 主要应用代码
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── main.py # FastAPI 应用实例、全局配置、根路由
|
||||||
|
│ ├── routers/ # 业务逻辑路由
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ ├── users.py # 用户相关的路由
|
||||||
|
│ │ └── items.py # 物品相关的路由
|
||||||
|
│ ├── services/ # 业务逻辑层 (当业务复杂时,将复杂逻辑拆分到此处)
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ ├── users.py
|
||||||
|
│ │ └── items.py
|
||||||
|
│ ├── models/ # 数据模型 (如 Pydantic 模型)
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ └── user.py # 用户数据模型
|
||||||
|
│ ├── db/ # 数据库相关配置与连接
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ └── database.py
|
||||||
|
│ └── utils/ # 常用工具函数
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── tools.py
|
||||||
|
│
|
||||||
|
├── tests/ # 测试代码
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── test_users.py # 用户模块的测试
|
||||||
|
│
|
||||||
|
├── .gitignore # Git 忽略文件
|
||||||
|
├── README.md # 项目说明文档
|
||||||
|
├── requirements.txt # 项目依赖列表
|
||||||
|
├── Dockerfile # Docker 配置文件
|
||||||
|
├── .env.example # 环境变量文件模板
|
||||||
|
└── .env # 环境变量文件 (不提交到Git)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
|
||||||
|
* `main.py`: 只做最核心的初始化和包含各个 `router`。
|
||||||
|
* `.env`: 存放环境变量,如数据库连接信息、密钥等。
|
||||||
|
* `.env.example`: 示例环境变量文件,用于创建 `.env` 文件。
|
||||||
|
* `Dockerfile`: Dockerfile 文件,用于构建镜像。
|
||||||
|
* `requirements.txt`: 项目依赖文件,用于安装依赖。
|
||||||
|
* `README.md`: 项目说明文档。
|
||||||
|
* `routers`: 路由模块,存放各个接口的实现。
|
||||||
|
* `models`: 模型模块,存放数据库模型定义。
|
||||||
|
* `utils`: 工具模块,存放工具函数。
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
#### 4\. 命名规范
|
||||||
|
|
||||||
|
**目标**:遵循 PEP 8,保持代码风格统一。
|
||||||
|
|
||||||
|
* **文件和目录**:使用小写蛇形命名法 (snake\_case),例如 `user_router.py`。
|
||||||
|
* **变量和函数**:使用小写蛇形命名法,例如 `get_user_by_id`, `db_session`。
|
||||||
|
* **类和 Pydantic 模型**:使用大驼峰命名法 (PascalCase),例如 `UserCreate`, `ItemSchema`。
|
||||||
|
* **常量**:使用全大写蛇形命名法,例如 `DATABASE_URL`, `API_PREFIX`。
|
||||||
|
* **FastAPI 路由函数**:建议以 `动作_资源` 的方式命名,例如 `read_users`, `create_item`。
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
#### 5\. 编码习惯与最佳实践
|
||||||
|
|
||||||
|
* **代码格式化**:
|
||||||
|
|
||||||
|
* **强制使用 `black`**。`black` 是一个无妥协的代码格式化工具,能消除所有关于格式的争论。
|
||||||
|
* **使用 `isort`** 对 `import` 语句进行排序。
|
||||||
|
* 在 CI/CD 或 Git pre-commit hook 中加入自动格式化检查。
|
||||||
|
|
||||||
|
* **代码质量检查 (Linter)**:
|
||||||
|
|
||||||
|
* 使用 `flake8` 或更现代的 `ruff` 进行代码风格和潜在错误的检查。
|
||||||
|
|
||||||
|
* **FastAPI 实践**:
|
||||||
|
|
||||||
|
* **善用依赖注入 (`Depends`)**:将数据库会话、用户认证等逻辑通过 `Depends` 注入到路由函数中,实现逻辑复用和解耦。
|
||||||
|
* **当router逻辑繁重时,考虑分离功能**:如果路由逻辑过于复杂,考虑将复杂逻辑提取到单独的函数中,放置在service层,并调用。
|
||||||
|
* **异步优先**:对于 I/O 密集型操作(如数据库查询、外部 API 请求),始终使用 `async def` 和 `await`。
|
||||||
|
* **统一日志输出**:使用 uvcrion 的 `from uvicorn.server import logger` 来捕获特定异常,并返回统一格式的错误响应。例子:`logger.error(f"Error: {e}")`
|
||||||
|
* **配置管理**:使用 os.getenv("ENV_VAR_NAME", "default_value") 从配置文件和环境变量中加载配置。
|
||||||
|
|
||||||
|
* **类型提示 (Type Hinting)**:
|
||||||
|
|
||||||
|
* **强制要求**:为所有函数参数和返回值添加类型提示。这是 FastAPI 的核心特性之一,能提供强大的编辑器支持和自动文档生成。
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
#### 6\. 版本控制 (Git)
|
||||||
|
|
||||||
|
* **分支模型**:
|
||||||
|
|
||||||
|
* `main` (或 `master`) 分支:用于部署的稳定版本。**只接受**来自 `develop` 的合并。
|
||||||
|
* `develop` 分支:用于集成各个功能开发的分支。
|
||||||
|
* `faet/xxx` 分支:从 `develop` 创建,用于开发新功能。开发完成后合并回 `develop`。
|
||||||
|
* `fix/xxx` 分支:用于修复 `main` 分支上的紧急 bug。
|
||||||
|
* `dev/xxx` 分支:从 `develop` 创建,用于开发新功能。开发完成后合并回 `develop`。
|
||||||
|
|
||||||
|
* **Commit Message**:
|
||||||
|
|
||||||
|
* **约定格式**:推荐使用 "Conventional Commits" 规范。
|
||||||
|
* `feat: add new user registration endpoint` (新功能)
|
||||||
|
* `fix: correct password hashing algorithm` (修复 Bug)
|
||||||
|
* `docs: update README with setup instructions` (文档)
|
||||||
|
* `style: format code with black` (格式化)
|
||||||
|
* `refactor: restructure database session handling` (重构)
|
||||||
|
* `test: add tests for user creation` (测试)
|
||||||
|
* 这样做的好处是提交历史清晰,便于追溯和自动生成更新日志。
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
#### 7\. 测试
|
||||||
|
|
||||||
|
* **框架**:使用 `pytest`。
|
||||||
|
* **工具**:结合 FastAPI 的 `TestClient` 和 `HTTPX` 来发送模拟请求。
|
||||||
|
* **要求**:核心业务逻辑和重要的 API 端点必须有单元测试或集成测试覆盖。
|
||||||
|
* **数据库**:测试时应使用一个独立的测试数据库,并在每次测试前后清理数据。
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
### 总结
|
||||||
|
|
||||||
|
这份范式旨在为小型 FastAPI 团队提供一个简单、易于上手的起点。最重要的不是规则本身,而是**团队达成共识并持续遵守**。随着项目和团队的发展,可以随时对这些规范进行调整和补充。
|
||||||
|
|
||||||
|
**工具链建议**:
|
||||||
|
|
||||||
|
* 虚拟环境: `venv`
|
||||||
|
* 依赖管理: `pip` + `requirements.txt`
|
||||||
|
* 代码格式化: `black`, `isort`
|
||||||
|
* 代码检查: `ruff` (推荐) 或 `flake8`
|
||||||
|
* 测试: `pytest`
|
||||||
|
* 版本控制: `Git`
|
||||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
fastapi==0.104.1
|
||||||
|
uvicorn[standard]==0.24.0
|
||||||
|
sqlalchemy==2.0.23
|
||||||
|
python-dotenv==1.0.0
|
||||||
|
pydantic==2.5.0
|
||||||
|
pydantic-settings==2.1.0
|
||||||
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
0
src/db/__init__.py
Normal file
0
src/db/__init__.py
Normal file
22
src/db/database.py
Normal file
22
src/db/database.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./test.db")
|
||||||
|
|
||||||
|
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||||
|
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
58
src/db/models.py
Normal file
58
src/db/models.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
"""
|
||||||
|
数据库模型模块
|
||||||
|
|
||||||
|
该模块定义了 SQLAlchemy ORM 模型,用于与数据库进行交互。
|
||||||
|
当前包含日志表的模型定义。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sqlalchemy import Column, String, Text, DateTime, BIGINT
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# 创建基类,所有模型都需要继承此类
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
class Log(Base):
|
||||||
|
"""
|
||||||
|
系统日志数据库模型
|
||||||
|
|
||||||
|
该模型定义了日志表的结构,用于存储系统的各种日志信息,
|
||||||
|
包括普通日志和异常日志。
|
||||||
|
"""
|
||||||
|
__tablename__ = "logs" # 数据库表名
|
||||||
|
|
||||||
|
# 日志ID,主键,自动递增
|
||||||
|
id = Column(BIGINT, primary_key=True, autoincrement=True, comment="日志ID,主键")
|
||||||
|
|
||||||
|
# 日志时间戳,默认为当前UTC时间
|
||||||
|
timestamp = Column(DateTime, default=datetime.utcnow, nullable=False, comment="日志时间戳")
|
||||||
|
|
||||||
|
# 日志级别 (INFO, WARNING, ERROR, DEBUG, CRITICAL)
|
||||||
|
level = Column(String(20), nullable=False, comment="日志级别 (INFO, WARNING, ERROR, DEBUG, CRITICAL)")
|
||||||
|
|
||||||
|
# 产生日志的模块名
|
||||||
|
module = Column(String(100), nullable=False, comment="产生日志的模块")
|
||||||
|
|
||||||
|
# 产生日志的函数名(可选)
|
||||||
|
function = Column(String(100), nullable=True, comment="产生日志的函数")
|
||||||
|
|
||||||
|
# 日志消息内容
|
||||||
|
message = Column(Text, nullable=False, comment="日志消息")
|
||||||
|
|
||||||
|
# 错误堆栈信息(可选,主要用于异常日志)
|
||||||
|
traceback = Column(Text, nullable=True, comment="错误堆栈信息")
|
||||||
|
|
||||||
|
# 请求URL(可选,用于记录HTTP请求相关信息)
|
||||||
|
request_url = Column(String(500), nullable=True, comment="请求URL")
|
||||||
|
|
||||||
|
# 请求方法(可选,如 GET, POST 等)
|
||||||
|
request_method = Column(String(10), nullable=True, comment="请求方法 (GET, POST等)")
|
||||||
|
|
||||||
|
# 用户代理信息(可选)
|
||||||
|
user_agent = Column(String(500), nullable=True, comment="用户代理")
|
||||||
|
|
||||||
|
# IP地址(可选)
|
||||||
|
ip_address = Column(String(45), nullable=True, comment="IP地址")
|
||||||
|
|
||||||
|
# 关联的用户ID(可选)
|
||||||
|
user_id = Column(BIGINT, nullable=True, comment="关联的用户ID")
|
||||||
59
src/main.py
Normal file
59
src/main.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
"""
|
||||||
|
FastAPI 应用主入口文件
|
||||||
|
|
||||||
|
该文件负责初始化 FastAPI 应用实例,配置中间件,
|
||||||
|
注册路由以及定义根路径和健康检查端点。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 将项目根目录添加到 Python 路径中,确保可以正确导入项目模块
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from src.routers import users, items
|
||||||
|
|
||||||
|
# 初始化 FastAPI 应用实例
|
||||||
|
app = FastAPI(
|
||||||
|
title="规范FastApi 开发基础框架",
|
||||||
|
description="规范的FastApi 开发基础框架",
|
||||||
|
version="1.0.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 配置 CORS 中间件,允许所有来源、凭证、方法和头部
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
allow_origin_regex=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 注册路由模块
|
||||||
|
# 用户相关路由,前缀为 /users,标签为 users
|
||||||
|
app.include_router(users.router, prefix="/users", tags=["users"])
|
||||||
|
# 物品相关路由,前缀为 /items,标签为 items
|
||||||
|
app.include_router(items.router, prefix="/items", tags=["items"])
|
||||||
|
|
||||||
|
# 根路径端点,返回欢迎信息
|
||||||
|
@app.get("/")
|
||||||
|
async def root():
|
||||||
|
return {"message": "Welcome to 规范FastApi 开发基础框架"}
|
||||||
|
|
||||||
|
# 健康检查端点,用于检查应用是否正常运行
|
||||||
|
@app.get("/health")
|
||||||
|
async def health_check():
|
||||||
|
return {"status": "healthy"}
|
||||||
|
|
||||||
|
# 当直接运行此文件时启动应用服务器
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
uvicorn.run(
|
||||||
|
"src.main:app",
|
||||||
|
host="0.0.0.0",
|
||||||
|
port=8000,
|
||||||
|
reload=True
|
||||||
|
)
|
||||||
0
src/models/__init__.py
Normal file
0
src/models/__init__.py
Normal file
36
src/models/item.py
Normal file
36
src/models/item.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
"""
|
||||||
|
物品数据模型模块
|
||||||
|
|
||||||
|
该模块定义了物品相关的数据模型,使用 Pydantic 进行数据验证和序列化。
|
||||||
|
包括基础物品模型、创建物品模型和完整物品模型。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class ItemBase(BaseModel):
|
||||||
|
"""
|
||||||
|
物品基础模型,定义了物品的基本信息字段
|
||||||
|
"""
|
||||||
|
name: str # 物品名称,必需字段
|
||||||
|
description: Optional[str] = None # 物品描述,可选字段
|
||||||
|
price: float # 物品价格,必需字段
|
||||||
|
|
||||||
|
class ItemCreate(ItemBase):
|
||||||
|
"""
|
||||||
|
物品创建模型,继承自 ItemBase
|
||||||
|
当前与 ItemBase 相同,但保留独立的类以便未来扩展
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Item(ItemBase):
|
||||||
|
"""
|
||||||
|
完整物品模型,继承自 ItemBase,增加了数据库相关字段
|
||||||
|
"""
|
||||||
|
id: int # 物品唯一标识符
|
||||||
|
created_at: datetime = None # 物品创建时间,可选字段
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
# 允许从 ORM 模型转换为 Pydantic 模型
|
||||||
|
from_attributes = True
|
||||||
35
src/models/user.py
Normal file
35
src/models/user.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
用户数据模型模块
|
||||||
|
|
||||||
|
该模块定义了用户相关的数据模型,使用 Pydantic 进行数据验证和序列化。
|
||||||
|
包括基础用户模型、创建用户模型和完整用户模型。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class UserBase(BaseModel):
|
||||||
|
"""
|
||||||
|
用户基础模型,定义了用户的基本信息字段
|
||||||
|
"""
|
||||||
|
email: str # 用户邮箱,必需字段
|
||||||
|
first_name: str # 用户名字,必需字段
|
||||||
|
last_name: str # 用户姓氏,必需字段
|
||||||
|
|
||||||
|
class UserCreate(UserBase):
|
||||||
|
"""
|
||||||
|
用户创建模型,继承自 UserBase,增加了密码字段
|
||||||
|
"""
|
||||||
|
password: str # 用户密码,必需字段
|
||||||
|
|
||||||
|
class User(UserBase):
|
||||||
|
"""
|
||||||
|
完整用户模型,继承自 UserBase,增加了数据库相关字段
|
||||||
|
"""
|
||||||
|
id: int # 用户唯一标识符
|
||||||
|
created_at: datetime = None # 用户创建时间,可选字段
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
# 允许从 ORM 模型转换为 Pydantic 模型
|
||||||
|
from_attributes = True
|
||||||
0
src/routers/__init__.py
Normal file
0
src/routers/__init__.py
Normal file
123
src/routers/items.py
Normal file
123
src/routers/items.py
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
"""
|
||||||
|
物品路由器模块
|
||||||
|
|
||||||
|
该模块定义了物品相关的 RESTful API 端点,
|
||||||
|
包括创建、读取、更新和删除物品等功能。
|
||||||
|
注意:当前实现使用内存存储,实际应用中应替换为数据库存储。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 将项目根目录添加到 Python 路径中,确保可以正确导入项目模块
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException
|
||||||
|
from typing import List
|
||||||
|
from src.models.item import Item, ItemCreate
|
||||||
|
|
||||||
|
# 创建 API 路由器实例
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
# 模拟数据库存储,实际应用中应使用真实数据库
|
||||||
|
items_db = []
|
||||||
|
|
||||||
|
# 创建物品端点
|
||||||
|
# 接收 ItemCreate 模型数据,返回创建的 Item 对象
|
||||||
|
@router.post("/", response_model=Item, status_code=201)
|
||||||
|
async def create_item(item: ItemCreate):
|
||||||
|
"""
|
||||||
|
创建新物品
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- item: ItemCreate 模型,包含物品创建所需信息
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- Item: 创建成功的物品对象
|
||||||
|
"""
|
||||||
|
# 创建新物品对象并添加到数据库
|
||||||
|
new_item = Item(id=len(items_db) + 1, **item.dict())
|
||||||
|
items_db.append(new_item)
|
||||||
|
return new_item
|
||||||
|
|
||||||
|
# 根据物品ID获取物品信息端点
|
||||||
|
@router.get("/{item_id}", response_model=Item)
|
||||||
|
async def read_item(item_id: int):
|
||||||
|
"""
|
||||||
|
根据物品ID获取物品信息
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- item_id: 物品ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- Item: 找到的物品对象
|
||||||
|
|
||||||
|
异常:
|
||||||
|
- HTTPException: 当物品不存在时返回 404 错误
|
||||||
|
"""
|
||||||
|
# 查找指定ID的物品
|
||||||
|
for item in items_db:
|
||||||
|
if item.id == item_id:
|
||||||
|
return item
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
# 获取物品列表端点,支持分页
|
||||||
|
@router.get("/", response_model=List[Item])
|
||||||
|
async def read_items(skip: int = 0, limit: int = 100):
|
||||||
|
"""
|
||||||
|
获取物品列表,支持分页
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- skip: 跳过的记录数,默认为 0
|
||||||
|
- limit: 返回的记录数,默认为 100
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- List[Item]: 物品对象列表
|
||||||
|
"""
|
||||||
|
return items_db[skip : skip + limit]
|
||||||
|
|
||||||
|
# 更新物品信息端点
|
||||||
|
@router.put("/{item_id}", response_model=Item)
|
||||||
|
async def update_item(item_id: int, item_update: ItemCreate):
|
||||||
|
"""
|
||||||
|
更新物品信息
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- item_id: 要更新的物品ID
|
||||||
|
- item_update: ItemCreate 模型,包含更新后的物品信息
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- Item: 更新后的物品对象
|
||||||
|
|
||||||
|
异常:
|
||||||
|
- HTTPException: 当物品不存在时返回 404 错误
|
||||||
|
"""
|
||||||
|
# 查找并更新指定ID的物品
|
||||||
|
for index, item in enumerate(items_db):
|
||||||
|
if item.id == item_id:
|
||||||
|
updated_item = Item(id=item_id, **item_update.dict())
|
||||||
|
items_db[index] = updated_item
|
||||||
|
return updated_item
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
# 删除物品端点
|
||||||
|
@router.delete("/{item_id}", status_code=204)
|
||||||
|
async def delete_item(item_id: int):
|
||||||
|
"""
|
||||||
|
删除物品
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- item_id: 要删除的物品ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 无内容,成功时返回 204 状态码
|
||||||
|
|
||||||
|
异常:
|
||||||
|
- HTTPException: 当物品不存在时返回 404 错误
|
||||||
|
"""
|
||||||
|
# 查找并删除指定ID的物品
|
||||||
|
for index, item in enumerate(items_db):
|
||||||
|
if item.id == item_id:
|
||||||
|
items_db.pop(index)
|
||||||
|
return
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
131
src/routers/users.py
Normal file
131
src/routers/users.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
"""
|
||||||
|
用户路由器模块
|
||||||
|
|
||||||
|
该模块定义了用户相关的 RESTful API 端点,
|
||||||
|
包括创建、读取、更新和删除用户等功能。
|
||||||
|
注意:当前实现使用内存存储,实际应用中应替换为数据库存储。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 将项目根目录添加到 Python 路径中,确保可以正确导入项目模块
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException
|
||||||
|
from typing import List
|
||||||
|
from src.models.user import User, UserCreate
|
||||||
|
|
||||||
|
# 创建 API 路由器实例
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
# 模拟数据库存储,实际应用中应使用真实数据库
|
||||||
|
users_db = []
|
||||||
|
|
||||||
|
# 创建用户端点
|
||||||
|
# 接收 UserCreate 模型数据,返回创建的 User 对象
|
||||||
|
@router.post("/", response_model=User, status_code=201)
|
||||||
|
async def create_user(user: UserCreate):
|
||||||
|
"""
|
||||||
|
创建新用户
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- user: UserCreate 模型,包含用户创建所需信息
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- User: 创建成功的用户对象
|
||||||
|
|
||||||
|
异常:
|
||||||
|
- HTTPException: 当邮箱已被注册时返回 400 错误
|
||||||
|
"""
|
||||||
|
# 检查邮箱是否已存在
|
||||||
|
for existing_user in users_db:
|
||||||
|
if existing_user.email == user.email:
|
||||||
|
raise HTTPException(status_code=400, detail="Email already registered")
|
||||||
|
|
||||||
|
# 创建新用户对象并添加到数据库
|
||||||
|
new_user = User(id=len(users_db) + 1, **user.dict())
|
||||||
|
users_db.append(new_user)
|
||||||
|
return new_user
|
||||||
|
|
||||||
|
# 根据用户ID获取用户信息端点
|
||||||
|
@router.get("/{user_id}", response_model=User)
|
||||||
|
async def read_user(user_id: int):
|
||||||
|
"""
|
||||||
|
根据用户ID获取用户信息
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- user_id: 用户ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- User: 找到的用户对象
|
||||||
|
|
||||||
|
异常:
|
||||||
|
- HTTPException: 当用户不存在时返回 404 错误
|
||||||
|
"""
|
||||||
|
# 查找指定ID的用户
|
||||||
|
for user in users_db:
|
||||||
|
if user.id == user_id:
|
||||||
|
return user
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
|
||||||
|
# 获取用户列表端点,支持分页
|
||||||
|
@router.get("/", response_model=List[User])
|
||||||
|
async def read_users(skip: int = 0, limit: int = 100):
|
||||||
|
"""
|
||||||
|
获取用户列表,支持分页
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- skip: 跳过的记录数,默认为 0
|
||||||
|
- limit: 返回的记录数,默认为 100
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- List[User]: 用户对象列表
|
||||||
|
"""
|
||||||
|
return users_db[skip : skip + limit]
|
||||||
|
|
||||||
|
# 更新用户信息端点
|
||||||
|
@router.put("/{user_id}", response_model=User)
|
||||||
|
async def update_user(user_id: int, user_update: UserCreate):
|
||||||
|
"""
|
||||||
|
更新用户信息
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- user_id: 要更新的用户ID
|
||||||
|
- user_update: UserCreate 模型,包含更新后的用户信息
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- User: 更新后的用户对象
|
||||||
|
|
||||||
|
异常:
|
||||||
|
- HTTPException: 当用户不存在时返回 404 错误
|
||||||
|
"""
|
||||||
|
# 查找并更新指定ID的用户
|
||||||
|
for index, user in enumerate(users_db):
|
||||||
|
if user.id == user_id:
|
||||||
|
updated_user = User(id=user_id, **user_update.dict())
|
||||||
|
users_db[index] = updated_user
|
||||||
|
return updated_user
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
|
||||||
|
# 删除用户端点
|
||||||
|
@router.delete("/{user_id}", status_code=204)
|
||||||
|
async def delete_user(user_id: int):
|
||||||
|
"""
|
||||||
|
删除用户
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- user_id: 要删除的用户ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 无内容,成功时返回 204 状态码
|
||||||
|
|
||||||
|
异常:
|
||||||
|
- HTTPException: 当用户不存在时返回 404 错误
|
||||||
|
"""
|
||||||
|
# 查找并删除指定ID的用户
|
||||||
|
for index, user in enumerate(users_db):
|
||||||
|
if user.id == user_id:
|
||||||
|
users_db.pop(index)
|
||||||
|
return
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
0
src/services/__init__.py
Normal file
0
src/services/__init__.py
Normal file
0
src/utils/__init__.py
Normal file
0
src/utils/__init__.py
Normal file
179
src/utils/logger.py
Normal file
179
src/utils/logger.py
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
"""
|
||||||
|
日志工具模块
|
||||||
|
|
||||||
|
该模块提供了完整的日志记录功能,包括:
|
||||||
|
1. 控制台日志输出
|
||||||
|
2. 数据库日志存储
|
||||||
|
3. 异常信息捕获和记录
|
||||||
|
4. 不同日志级别的记录函数
|
||||||
|
|
||||||
|
日志信息会被同时输出到控制台和存储到数据库中,便于问题排查和系统监控。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
from typing import Optional
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# 将项目根目录添加到 Python 路径中,确保可以正确导入项目模块
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
|
|
||||||
|
from src.db.database import get_db
|
||||||
|
from src.db.models import Log
|
||||||
|
|
||||||
|
# 配置基础日志设置
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 创建模块级日志记录器
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def log_to_database(
|
||||||
|
level: str,
|
||||||
|
message: str,
|
||||||
|
module: str,
|
||||||
|
function: Optional[str] = None,
|
||||||
|
traceback_info: Optional[str] = None,
|
||||||
|
request_url: Optional[str] = None,
|
||||||
|
request_method: Optional[str] = None,
|
||||||
|
user_agent: Optional[str] = None,
|
||||||
|
ip_address: Optional[str] = None,
|
||||||
|
user_id: Optional[int] = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
将日志信息保存到数据库
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- level: 日志级别 (INFO, WARNING, ERROR, DEBUG)
|
||||||
|
- message: 日志消息内容
|
||||||
|
- module: 产生日志的模块名
|
||||||
|
- function: 产生日志的函数名(可选)
|
||||||
|
- traceback_info: 异常堆栈信息(可选)
|
||||||
|
- request_url: 请求URL(可选)
|
||||||
|
- request_method: 请求方法(可选)
|
||||||
|
- user_agent: 用户代理信息(可选)
|
||||||
|
- ip_address: IP地址(可选)
|
||||||
|
- user_id: 用户ID(可选)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 获取数据库会话
|
||||||
|
db_generator = get_db()
|
||||||
|
db = next(db_generator)
|
||||||
|
|
||||||
|
# 创建日志条目对象
|
||||||
|
log_entry = Log(
|
||||||
|
level=level,
|
||||||
|
message=message,
|
||||||
|
module=module,
|
||||||
|
function=function,
|
||||||
|
traceback=traceback_info,
|
||||||
|
request_url=request_url,
|
||||||
|
request_method=request_method,
|
||||||
|
user_agent=user_agent,
|
||||||
|
ip_address=ip_address,
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# 保存到数据库
|
||||||
|
db.add(log_entry)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(log_entry)
|
||||||
|
db.close()
|
||||||
|
except Exception as e:
|
||||||
|
# 如果数据库记录失败,至少打印到控制台
|
||||||
|
logger.error(f"Failed to log to database: {str(e)}")
|
||||||
|
|
||||||
|
def capture_exception(
|
||||||
|
exception: Exception,
|
||||||
|
module: str,
|
||||||
|
function: Optional[str] = None,
|
||||||
|
request_url: Optional[str] = None,
|
||||||
|
request_method: Optional[str] = None,
|
||||||
|
user_agent: Optional[str] = None,
|
||||||
|
ip_address: Optional[str] = None,
|
||||||
|
user_id: Optional[int] = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
捕获并记录异常信息
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- exception: 捕获到的异常对象
|
||||||
|
- module: 产生异常的模块名
|
||||||
|
- function: 产生异常的函数名(可选)
|
||||||
|
- request_url: 请求URL(可选)
|
||||||
|
- request_method: 请求方法(可选)
|
||||||
|
- user_agent: 用户代理信息(可选)
|
||||||
|
- ip_address: IP地址(可选)
|
||||||
|
- user_id: 用户ID(可选)
|
||||||
|
"""
|
||||||
|
# 获取异常堆栈信息
|
||||||
|
tb_str = ''.join(traceback.format_exception(type(exception), exception, exception.__traceback__))
|
||||||
|
|
||||||
|
# 记录错误日志到数据库和控制台
|
||||||
|
log_to_database(
|
||||||
|
level="ERROR",
|
||||||
|
message=str(exception),
|
||||||
|
module=module,
|
||||||
|
function=function,
|
||||||
|
traceback_info=tb_str,
|
||||||
|
request_url=request_url,
|
||||||
|
request_method=request_method,
|
||||||
|
user_agent=user_agent,
|
||||||
|
ip_address=ip_address,
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# 同时打印到控制台
|
||||||
|
logger.error(f"[{module}] {str(exception)}", exc_info=True)
|
||||||
|
|
||||||
|
def info(message: str, module: str, function: Optional[str] = None):
|
||||||
|
"""
|
||||||
|
记录INFO级别日志
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- message: 日志消息内容
|
||||||
|
- module: 产生日志的模块名
|
||||||
|
- function: 产生日志的函数名(可选)
|
||||||
|
"""
|
||||||
|
logger.info(f"[{module}] {message}")
|
||||||
|
log_to_database("INFO", message, module, function)
|
||||||
|
|
||||||
|
def warning(message: str, module: str, function: Optional[str] = None):
|
||||||
|
"""
|
||||||
|
记录WARNING级别日志
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- message: 日志消息内容
|
||||||
|
- module: 产生日志的模块名
|
||||||
|
- function: 产生日志的函数名(可选)
|
||||||
|
"""
|
||||||
|
logger.warning(f"[{module}] {message}")
|
||||||
|
log_to_database("WARNING", message, module, function)
|
||||||
|
|
||||||
|
def error(message: str, module: str, function: Optional[str] = None):
|
||||||
|
"""
|
||||||
|
记录ERROR级别日志
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- message: 日志消息内容
|
||||||
|
- module: 产生日志的模块名
|
||||||
|
- function: 产生日志的函数名(可选)
|
||||||
|
"""
|
||||||
|
logger.error(f"[{module}] {message}")
|
||||||
|
log_to_database("ERROR", message, module, function)
|
||||||
|
|
||||||
|
def debug(message: str, module: str, function: Optional[str] = None):
|
||||||
|
"""
|
||||||
|
记录DEBUG级别日志
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- message: 日志消息内容
|
||||||
|
- module: 产生日志的模块名
|
||||||
|
- function: 产生日志的函数名(可选)
|
||||||
|
"""
|
||||||
|
logger.debug(f"[{module}] {message}")
|
||||||
|
log_to_database("DEBUG", message, module, function)
|
||||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
92
tests/test_users.py
Normal file
92
tests/test_users.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import pytest
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
from src.main import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
def test_create_user():
|
||||||
|
response = client.post(
|
||||||
|
"/users/",
|
||||||
|
json={
|
||||||
|
"email": "test@example.com",
|
||||||
|
"first_name": "Test",
|
||||||
|
"last_name": "User",
|
||||||
|
"password": "testpassword"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert response.status_code == 201
|
||||||
|
data = response.json()
|
||||||
|
assert data["email"] == "test@example.com"
|
||||||
|
assert "id" in data
|
||||||
|
|
||||||
|
def test_read_users():
|
||||||
|
response = client.get("/users/")
|
||||||
|
assert response.status_code == 200
|
||||||
|
data = response.json()
|
||||||
|
assert isinstance(data, list)
|
||||||
|
|
||||||
|
def test_read_user():
|
||||||
|
# First create a user
|
||||||
|
response = client.post(
|
||||||
|
"/users/",
|
||||||
|
json={
|
||||||
|
"email": "test2@example.com",
|
||||||
|
"first_name": "Test2",
|
||||||
|
"last_name": "User2",
|
||||||
|
"password": "testpassword"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert response.status_code == 201
|
||||||
|
user_id = response.json()["id"]
|
||||||
|
|
||||||
|
# Then read the user
|
||||||
|
response = client.get(f"/users/{user_id}")
|
||||||
|
assert response.status_code == 200
|
||||||
|
data = response.json()
|
||||||
|
assert data["id"] == user_id
|
||||||
|
|
||||||
|
def test_update_user():
|
||||||
|
# First create a user
|
||||||
|
response = client.post(
|
||||||
|
"/users/",
|
||||||
|
json={
|
||||||
|
"email": "test3@example.com",
|
||||||
|
"first_name": "Test3",
|
||||||
|
"last_name": "User3",
|
||||||
|
"password": "testpassword"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert response.status_code == 201
|
||||||
|
user_id = response.json()["id"]
|
||||||
|
|
||||||
|
# Then update the user
|
||||||
|
response = client.put(
|
||||||
|
f"/users/{user_id}",
|
||||||
|
json={
|
||||||
|
"email": "updated@example.com",
|
||||||
|
"first_name": "Updated",
|
||||||
|
"last_name": "User",
|
||||||
|
"password": "updatedpassword"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
data = response.json()
|
||||||
|
assert data["email"] == "updated@example.com"
|
||||||
|
|
||||||
|
def test_delete_user():
|
||||||
|
# First create a user
|
||||||
|
response = client.post(
|
||||||
|
"/users/",
|
||||||
|
json={
|
||||||
|
"email": "test4@example.com",
|
||||||
|
"first_name": "Test4",
|
||||||
|
"last_name": "User4",
|
||||||
|
"password": "testpassword"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert response.status_code == 201
|
||||||
|
user_id = response.json()["id"]
|
||||||
|
|
||||||
|
# Then delete the user
|
||||||
|
response = client.delete(f"/users/{user_id}")
|
||||||
|
assert response.status_code == 204
|
||||||
Reference in New Issue
Block a user