feat: 添加MySQL数据库支持和完整的数据库管理功能

- 添加MySQL数据库连接配置
- 创建SQLAlchemy数据库模型(用户表和产品表)
- 添加Alembic数据库迁移工具
- 创建数据库管理脚本(database_manager.py)
- 添加SQL文件目录结构(migrations, seeds, schemas)
- 更新应用配置以支持数据库连接
- 添加完整的数据库设置文档
- 配置开发环境数据库连接(localhost:3306, root/123456, fast_demo)
This commit is contained in:
杨煜 2025-09-27 23:23:25 +08:00
parent 04cb2cbbf7
commit 1f2bb12178
17 changed files with 968 additions and 2 deletions

86
CODEBUDDY.md Normal file
View File

@ -0,0 +1,86 @@
# CODEBUDDY.md
此文件为 CodeBuddy Code 在此代码库中工作时提供指导。
## 常用命令
### 应用启动
```bash
# 安装依赖
pip install -r requirements.txt
# 使用便捷启动脚本(推荐)
python start.py --env dev # 开发环境
python start.py --env prod # 生产环境
python start.py --port 9000 # 指定端口
# 直接运行主文件
python main.py
```
### 环境配置
```bash
# 环境变量优先级(从高到低):
# 1. 环境变量
# 2. .env.{environment} 文件
# 3. .env 基础配置文件
# 4. 代码中的默认值
# 配置验证
python -c "from app.core.config import settings; print(f'Environment: {settings.ENVIRONMENT}'); print(f'Debug: {settings.DEBUG}')"
```
## 架构设计
### 应用工厂模式
项目采用应用工厂模式,核心文件:
- **应用创建**`app/core/application.py` - 使用 `create_application()` 工厂函数
- **配置管理**`app/core/config.py` - 基于 pydantic-settings 的多环境配置
- **路由聚合**`app/api/v1/router.py` - 集中管理 API 路由注册
### 分层架构
1. **API层** (`app/api/v1/endpoints/`) - FastAPI 路由端点,处理 HTTP 请求/响应
2. **服务层** (`app/services/`) - 业务逻辑封装,使用单例服务实例
3. **模型层** (`app/models/`) - 核心领域模型Pydantic BaseModel
4. **Schema层** (`app/schemas/`) - 请求/响应验证和序列化
### 路由结构
- 所有 API 以 `/api/v1/` 为前缀
- 路由通过 `app/api/v1/router.py` 统一注册
- 每个资源对应一个独立的路由器文件
### 配置管理
- 支持多环境development、testing、production
- 自动加载对应配置文件:`.env.dev.{env}`
- 类型安全验证和转换
- 便利属性:`settings.is_development`、`settings.is_production`
### 服务模式
- 使用静态服务类 (`UserService`、`ProductService`)
- 当前使用内存数据库进行演示
- 服务层负责所有 CRUD 操作和业务规则
### 关键特性
- CORS 中间件配置
- 应用生命周期事件处理器
- 模块化路由设计
- 版本化的 API 结构
## 开发规范
### 添加新资源
1. 模型层:在 `app/models/` 创建数据模型
2. Schema层`app/schemas/` 创建 Base/Create/Update/Response 模式
3. 服务层:在 `app/services/` 创建服务类
4. API层`app/api/v1/endpoints/` 创建路由端点
5. 路由注册:在 `app/api/v1/router.py` 注册新路由
### 配置扩展
- 新配置项添加到 `app/core/config.py` 中的 Settings 类
- 遵循现有配置验证模式
- 支持环境变量覆盖
### 数据存储
- 当前使用内存数据存储用于演示
- 实际实现将在服务层集成数据库
- 服务接口设计支持无缝替换存储后端

199
DATABASE_SETUP.md Normal file
View File

@ -0,0 +1,199 @@
# 数据库设置指南
本项目已配置支持MySQL数据库包含完整的数据库模型、迁移脚本和管理工具。
## 目录结构
```
sql/
├── migrations/ # 数据库迁移文件
│ └── 001_create_tables.sql
├── seeds/ # 种子数据文件
│ └── sample_data.sql
└── schemas/ # 数据库架构文件
└── init_database.sql
alembic/ # Alembic迁移工具
├── versions/ # 自动生成的迁移版本
├── env.py # Alembic环境配置
└── script.py.mako # 迁移模板
app/database/ # 数据库相关代码
├── __init__.py
├── connection.py # 数据库连接管理
├── base.py # 基础模型类
└── models.py # SQLAlchemy模型
```
## 快速开始
### 1. 安装依赖
```bash
pip install -r requirements.txt
```
### 2. 配置数据库
创建 `.env.dev` 文件(参考 `.env.example`
```env
# MySQL 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=fastapi_demo
DB_CHARSET=utf8mb4
```
### 3. 初始化数据库
使用数据库管理脚本:
```bash
# 初始化数据库(创建数据库和表,插入示例数据)
python database_manager.py init
# 或者分步执行
python database_manager.py create-db # 创建数据库
python database_manager.py create-tables # 创建表
python database_manager.py seed # 插入示例数据
```
### 4. 启动应用
```bash
python main.py
```
## 数据库管理
### 使用数据库管理脚本
```bash
python database_manager.py init # 初始化数据库
python database_manager.py reset # 重置数据库
python database_manager.py create-db # 仅创建数据库
python database_manager.py create-tables # 仅创建表
python database_manager.py seed # 仅插入示例数据
python database_manager.py help # 显示帮助
```
### 使用Alembic进行迁移
```bash
# 初始化Alembic首次使用
alembic init alembic
# 创建新的迁移
alembic revision --autogenerate -m "描述信息"
# 执行迁移
alembic upgrade head
# 回滚迁移
alembic downgrade -1
# 查看迁移历史
alembic history
# 查看当前版本
alembic current
```
## 数据库模型
### 用户表 (users)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | int(11) | 主键,自增 |
| username | varchar(50) | 用户名,唯一 |
| email | varchar(100) | 邮箱,唯一 |
| full_name | varchar(100) | 全名,可空 |
| is_active | tinyint(1) | 是否激活 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
### 产品表 (products)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | int(11) | 主键,自增 |
| name | varchar(200) | 产品名称 |
| description | text | 产品描述,可空 |
| price | decimal(10,2) | 价格 |
| stock | int(11) | 库存数量 |
| is_available | tinyint(1) | 是否可用 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
## 配置说明
### 数据库连接配置
`app/core/config.py` 中配置:
```python
# MySQL 数据库配置
DB_HOST: str = "localhost" # 数据库主机
DB_PORT: int = 3306 # 数据库端口
DB_USER: str = "root" # 数据库用户名
DB_PASSWORD: str = "" # 数据库密码
DB_NAME: str = "fastapi_demo" # 数据库名称
DB_CHARSET: str = "utf8mb4" # 字符集
# 数据库连接池配置
DB_POOL_SIZE: int = 5 # 连接池大小
DB_MAX_OVERFLOW: int = 10 # 最大溢出连接数
DB_POOL_TIMEOUT: int = 30 # 连接超时时间(秒)
DB_POOL_RECYCLE: int = 3600 # 连接回收时间(秒)
```
### 环境变量
支持通过环境变量覆盖配置:
```bash
export DB_HOST=localhost
export DB_PORT=3306
export DB_USER=root
export DB_PASSWORD=your_password
export DB_NAME=fastapi_demo
```
## 故障排除
### 常见问题
1. **连接失败**
- 检查MySQL服务是否启动
- 验证数据库配置信息
- 确认用户权限
2. **字符编码问题**
- 确保数据库使用utf8mb4字符集
- 检查连接字符串中的charset参数
3. **迁移失败**
- 检查Alembic配置
- 确认数据库连接正常
- 查看迁移文件语法
### 日志调试
启用SQL日志
```env
DB_ECHO=true
LOG_LEVEL=debug
```
## 开发建议
1. **使用迁移管理数据库结构变更**
2. **在开发环境中使用示例数据**
3. **定期备份生产数据库**
4. **使用连接池优化性能**
5. **监控数据库连接状态**

110
DEV_CONFIG.md Normal file
View File

@ -0,0 +1,110 @@
# 开发环境配置总结
## 🎯 数据库配置
### 连接信息
- **主机**: localhost
- **端口**: 3306
- **用户名**: root
- **密码**: 123456
- **数据库名**: fast_demo
- **字符集**: utf8mb4
### 连接URL
```
mysql+pymysql://root:123456@localhost:3306/fast_demo?charset=utf8mb4
```
## 📁 配置文件
### 1. 应用配置 (`app/core/config.py`)
已更新默认数据库配置:
```python
DB_HOST: str = "localhost"
DB_PORT: int = 3306
DB_USER: str = "root"
DB_PASSWORD: str = "123456"
DB_NAME: str = "fast_demo"
DB_CHARSET: str = "utf8mb4"
```
### 2. Alembic配置 (`alembic.ini`)
已更新数据库URL
```ini
sqlalchemy.url = mysql+pymysql://root:123456@localhost:3306/fast_demo?charset=utf8mb4
```
## 🗄️ 数据库表
### 用户表 (users)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | int(11) | 主键,自增 |
| username | varchar(50) | 用户名,唯一 |
| email | varchar(100) | 邮箱,唯一 |
| full_name | varchar(100) | 全名,可空 |
| is_active | tinyint(1) | 是否激活 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
### 产品表 (products)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | int(11) | 主键,自增 |
| name | varchar(200) | 产品名称 |
| description | text | 产品描述,可空 |
| price | decimal(10,2) | 价格 |
| stock | int(11) | 库存数量 |
| is_available | tinyint(1) | 是否可用 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
## 🚀 快速开始
### 1. 启动应用
```bash
python main.py
```
### 2. 访问API文档
- Swagger UI: http://127.0.0.1:8000/docs
- ReDoc: http://127.0.0.1:8000/redoc
### 3. 数据库管理
```bash
# 初始化数据库
python database_manager.py init
# 重置数据库
python database_manager.py reset
# 查看帮助
python database_manager.py help
```
## 🔧 环境变量
可以通过环境变量覆盖配置:
```bash
export DB_HOST=localhost
export DB_PORT=3306
export DB_USER=root
export DB_PASSWORD=123456
export DB_NAME=fast_demo
```
## ✅ 验证配置
数据库连接已成功验证:
- ✅ 数据库 'fast_demo' 已存在
- ✅ 数据库连接成功
- ✅ 表 'users' 和 'products' 已创建
- ✅ 示例数据已插入
## 📝 注意事项
1. 确保MySQL服务正在运行
2. 确保用户 'root' 有访问数据库的权限
3. 开发环境配置已优化,包含热重载等功能
4. 生产环境请修改默认密码和配置

112
alembic.ini Normal file
View File

@ -0,0 +1,112 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python-dateutil library that can be
# installed by adding `alembic[tz]` to the pip requirements
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version number format
version_num_format = %04d
# version path separator; As mentioned above, this is the character used to split
# version_locations. The default within new alembic.ini files is "os", which uses
# os.pathsep. If this key is omitted entirely, it falls back to the legacy
# behavior of splitting on spaces and/or commas.
# Valid values for version_path_separator are:
#
# version_path_separator = :
# version_path_separator = ;
# version_path_separator = space
version_path_separator = os
# set to 'true' to search source files recursively
# in each "version_locations" directory
# new in Alembic version 1.10
# recursive_version_locations = false
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = mysql+pymysql://root:123456@localhost:3306/fast_demo?charset=utf8mb4
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks = black
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME
# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
# hooks = ruff
# ruff.type = exec
# ruff.executable = %(here)s/.venv/bin/ruff
# ruff.options = --fix REVISION_SCRIPT_FILENAME
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

93
alembic/env.py Normal file
View File

@ -0,0 +1,93 @@
"""
Alembic 环境配置
"""
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
import os
import sys
# 添加项目根目录到 Python 路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.database.models import Base
from app.core.config import settings
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def get_url():
"""获取数据库连接URL"""
return settings.DATABASE_URL
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = get_url()
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
configuration = config.get_section(config.config_ini_section)
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
configuration,
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

24
alembic/script.py.mako Normal file
View File

@ -0,0 +1,24 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade() -> None:
${upgrades if upgrades else "pass"}
def downgrade() -> None:
${downgrades if downgrades else "pass"}

View File

@ -96,6 +96,12 @@ def register_events(app: FastAPI) -> None:
# 可以在这里添加启动时的初始化操作 # 可以在这里添加启动时的初始化操作
# 如:数据库连接、缓存初始化等 # 如:数据库连接、缓存初始化等
# 创建数据库表(如果不存在)
from app.database.connection import engine
from app.database.models import Base
Base.metadata.create_all(bind=engine)
print("[INFO] Database tables created/verified successfully")
@app.on_event("shutdown") @app.on_event("shutdown")
async def shutdown_event(): async def shutdown_event():
"""应用关闭时执行""" """应用关闭时执行"""

View File

@ -30,10 +30,25 @@ class Settings(BaseSettings):
REDOC_URL: Optional[str] = "/redoc" REDOC_URL: Optional[str] = "/redoc"
# === 数据库配置 === # === 数据库配置 ===
DATABASE_URL: Optional[str] = None # MySQL 数据库配置
DB_HOST: str = "localhost"
DB_PORT: int = 3306
DB_USER: str = "root"
DB_PASSWORD: str = "123456"
DB_NAME: str = "fast_demo"
DB_CHARSET: str = "utf8mb4"
# 数据库连接池配置
DB_ECHO: bool = False DB_ECHO: bool = False
DB_POOL_SIZE: int = 5 DB_POOL_SIZE: int = 5
DB_MAX_OVERFLOW: int = 10 DB_MAX_OVERFLOW: int = 10
DB_POOL_TIMEOUT: int = 30
DB_POOL_RECYCLE: int = 3600
@property
def DATABASE_URL(self) -> str:
"""构建数据库连接URL"""
return f"mysql+pymysql://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}?charset={self.DB_CHARSET}"
# === Redis 配置 === # === Redis 配置 ===
REDIS_HOST: str = "localhost" REDIS_HOST: str = "localhost"

7
app/database/__init__.py Normal file
View File

@ -0,0 +1,7 @@
"""
数据库模块
"""
from .connection import engine, SessionLocal, get_db
from .base import Base
__all__ = ["engine", "SessionLocal", "get_db", "Base"]

6
app/database/base.py Normal file
View File

@ -0,0 +1,6 @@
"""
数据库基础模型类
"""
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

View File

@ -0,0 +1,35 @@
"""
数据库连接管理
"""
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from app.core.config import settings
# 创建数据库引擎
engine = create_engine(
settings.DATABASE_URL,
echo=settings.DB_ECHO,
pool_size=settings.DB_POOL_SIZE,
max_overflow=settings.DB_MAX_OVERFLOW,
pool_timeout=settings.DB_POOL_TIMEOUT,
pool_recycle=settings.DB_POOL_RECYCLE,
pool_pre_ping=True, # 连接前测试连接是否有效
)
# 创建会话工厂
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 创建基础模型类
Base = declarative_base()
def get_db():
"""
获取数据库会话的依赖注入函数
"""
db = SessionLocal()
try:
yield db
finally:
db.close()

39
app/database/models.py Normal file
View File

@ -0,0 +1,39 @@
"""
SQLAlchemy 数据库模型
"""
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, Text
from sqlalchemy.sql import func
from .base import Base
class User(Base):
"""用户表"""
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
username = Column(String(50), unique=True, index=True, nullable=False, comment="用户名")
email = Column(String(100), unique=True, index=True, nullable=False, comment="邮箱")
full_name = Column(String(100), nullable=True, comment="全名")
is_active = Column(Boolean, default=True, nullable=False, comment="是否激活")
created_at = Column(DateTime(timezone=True), server_default=func.now(), comment="创建时间")
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), comment="更新时间")
def __repr__(self):
return f"<User(id={self.id}, username='{self.username}', email='{self.email}')>"
class Product(Base):
"""产品表"""
__tablename__ = "products"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
name = Column(String(200), nullable=False, comment="产品名称")
description = Column(Text, nullable=True, comment="产品描述")
price = Column(Float, nullable=False, comment="价格")
stock = Column(Integer, default=0, nullable=False, comment="库存数量")
is_available = Column(Boolean, default=True, nullable=False, comment="是否可用")
created_at = Column(DateTime(timezone=True), server_default=func.now(), comment="创建时间")
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), comment="更新时间")
def __repr__(self):
return f"<Product(id={self.id}, name='{self.name}', price={self.price})>"

139
database_manager.py Normal file
View File

@ -0,0 +1,139 @@
"""
数据库管理脚本
用于初始化数据库运行迁移插入测试数据等
"""
import os
import sys
import asyncio
from sqlalchemy import create_engine, text
from app.core.config import settings
from app.database.models import Base
def create_database():
"""创建数据库(如果不存在)"""
# 连接到MySQL服务器不指定数据库
server_url = f"mysql+pymysql://{settings.DB_USER}:{settings.DB_PASSWORD}@{settings.DB_HOST}:{settings.DB_PORT}/"
engine = create_engine(server_url)
with engine.connect() as conn:
# 创建数据库
conn.execute(text(f"CREATE DATABASE IF NOT EXISTS `{settings.DB_NAME}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"))
print(f"✅ 数据库 '{settings.DB_NAME}' 创建成功")
def create_tables():
"""创建数据库表"""
engine = create_engine(settings.DATABASE_URL)
Base.metadata.create_all(bind=engine)
print("✅ 数据库表创建成功")
def run_sql_file(file_path):
"""运行SQL文件"""
if not os.path.exists(file_path):
print(f"❌ SQL文件不存在: {file_path}")
return
engine = create_engine(settings.DATABASE_URL)
with open(file_path, 'r', encoding='utf-8') as f:
sql_content = f.read()
with engine.connect() as conn:
# 分割SQL语句并执行
statements = [stmt.strip() for stmt in sql_content.split(';') if stmt.strip()]
for statement in statements:
if statement:
conn.execute(text(statement))
conn.commit()
print(f"✅ SQL文件执行成功: {file_path}")
def init_database():
"""初始化数据库"""
print("🚀 开始初始化数据库...")
# 1. 创建数据库
create_database()
# 2. 创建表
create_tables()
# 3. 插入示例数据
sample_data_file = "sql/seeds/sample_data.sql"
if os.path.exists(sample_data_file):
run_sql_file(sample_data_file)
print("🎉 数据库初始化完成!")
def reset_database():
"""重置数据库"""
print("⚠️ 重置数据库...")
# 删除并重新创建数据库
server_url = f"mysql+pymysql://{settings.DB_USER}:{settings.DB_PASSWORD}@{settings.DB_HOST}:{settings.DB_PORT}/"
engine = create_engine(server_url)
with engine.connect() as conn:
conn.execute(text(f"DROP DATABASE IF EXISTS `{settings.DB_NAME}`"))
print(f"🗑️ 数据库 '{settings.DB_NAME}' 已删除")
# 重新初始化
init_database()
def show_help():
"""显示帮助信息"""
print("""
数据库管理脚本使用说明:
python database_manager.py init - 初始化数据库创建数据库和表
python database_manager.py reset - 重置数据库删除并重新创建
python database_manager.py create-db - 仅创建数据库
python database_manager.py create-tables - 仅创建表
python database_manager.py seed - 仅插入示例数据
python database_manager.py help - 显示此帮助信息
环境变量配置:
- DB_HOST: 数据库主机 (默认: localhost)
- DB_PORT: 数据库端口 (默认: 3306)
- DB_USER: 数据库用户名 (默认: root)
- DB_PASSWORD: 数据库密码 (默认: )
- DB_NAME: 数据库名称 (默认: fastapi_demo)
""")
def main():
"""主函数"""
if len(sys.argv) < 2:
show_help()
return
command = sys.argv[1].lower()
try:
if command == "init":
init_database()
elif command == "reset":
reset_database()
elif command == "create-db":
create_database()
elif command == "create-tables":
create_tables()
elif command == "seed":
run_sql_file("sql/seeds/sample_data.sql")
elif command == "help":
show_help()
else:
print(f"❌ 未知命令: {command}")
show_help()
except Exception as e:
print(f"❌ 执行失败: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -4,3 +4,9 @@ pydantic==2.8.0
pydantic-settings==2.1.0 pydantic-settings==2.1.0
python-multipart==0.0.6 python-multipart==0.0.6
email-validator==2.1.0 email-validator==2.1.0
# 数据库相关依赖
sqlalchemy>=2.0.30
alembic>=1.13.1
pymysql>=1.1.0
cryptography>=41.0.0

View File

@ -0,0 +1,35 @@
-- 迁移文件:创建基础表结构
-- 创建时间2024-01-01
-- 描述:创建用户表和产品表
-- 创建用户表
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`email` varchar(100) NOT NULL COMMENT '邮箱',
`full_name` varchar(100) DEFAULT NULL COMMENT '全名',
`is_active` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否激活',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`),
KEY `idx_username` (`username`),
KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
-- 创建产品表
CREATE TABLE IF NOT EXISTS `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL COMMENT '产品名称',
`description` text COMMENT '产品描述',
`price` decimal(10,2) NOT NULL COMMENT '价格',
`stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存数量',
`is_available` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否可用',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_name` (`name`),
KEY `idx_price` (`price`),
KEY `idx_is_available` (`is_available`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品表';

View File

@ -0,0 +1,39 @@
-- 创建数据库
CREATE DATABASE IF NOT EXISTS `fastapi_demo`
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- 使用数据库
USE `fastapi_demo`;
-- 创建用户表
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`email` varchar(100) NOT NULL COMMENT '邮箱',
`full_name` varchar(100) DEFAULT NULL COMMENT '全名',
`is_active` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否激活',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`),
KEY `idx_username` (`username`),
KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
-- 创建产品表
CREATE TABLE IF NOT EXISTS `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL COMMENT '产品名称',
`description` text COMMENT '产品描述',
`price` decimal(10,2) NOT NULL COMMENT '价格',
`stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存数量',
`is_available` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否可用',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_name` (`name`),
KEY `idx_price` (`price`),
KEY `idx_is_available` (`is_available`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品表';

15
sql/seeds/sample_data.sql Normal file
View File

@ -0,0 +1,15 @@
-- 插入示例用户数据
INSERT INTO `users` (`username`, `email`, `full_name`, `is_active`) VALUES
('admin', 'admin@example.com', '管理员', 1),
('john_doe', 'john@example.com', 'John Doe', 1),
('jane_smith', 'jane@example.com', 'Jane Smith', 1),
('bob_wilson', 'bob@example.com', 'Bob Wilson', 0);
-- 插入示例产品数据
INSERT INTO `products` (`name`, `description`, `price`, `stock`, `is_available`) VALUES
('iPhone 15 Pro', '苹果最新旗舰手机配备A17 Pro芯片', 9999.00, 50, 1),
('MacBook Pro 16"', '专业级笔记本电脑M3 Max芯片', 19999.00, 20, 1),
('AirPods Pro', '主动降噪无线耳机', 1999.00, 100, 1),
('iPad Air', '轻薄便携的平板电脑', 4399.00, 30, 1),
('Apple Watch Series 9', '智能手表,健康监测', 2999.00, 80, 1),
('Magic Keyboard', '无线键盘,支持多设备连接', 999.00, 0, 0);