PayrollManagementSystem/backend/alembic/env.py
liuwenxin 273ebf4657 chore: 初始化项目结构与登录鉴权模块
后端 FastAPI 六层结构(api/services/repositories/models/schemas/core),
统一响应信封、全局异常、日志脱敏;6 张表经 Alembic 迁移建库,SQL 留存
db/migration;登录鉴权含图形验证码(Redis GETDEL 防重放)、JWT、登录限流、
登出黑名单(TTL=令牌剩余有效期)、登录日志。

前端 Vue3 + TS + Element Plus 骨架,含登录页、路由守卫、请求封装与 Vite 代理。

工程配置:uv 锁定 Python 3.12、ruff 检查与格式化、editorconfig、gitattributes。

测试 184 项(单元 155 / 集成 29)全绿,浏览器端到端 8 项通过。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-09 18:46:36 +08:00

75 lines
2.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Alembic 迁移环境。
**连接串取自 backend/.env**,不写在 alembic.ini —— 避免密码进版本库,
也避免运行期配置与迁移配置两处漂移。运行期与迁移共用同一份同步连接串
(驱动 pymysql),不存在同步/异步两套配置。
"""
import sys
from logging.config import fileConfig
from pathlib import Path
from alembic import context
from sqlalchemy import engine_from_config, pool
# 使 `app` 包在 alembic 执行时可导入(alembic 从 backend/ 目录运行)
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from app.core.config import get_settings
from app.models import Base
config = context.config
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# autogenerate 的比对基准
target_metadata = Base.metadata
# 运行期把 .env 里的连接串注入配置,覆盖 alembic.ini 中的占位值
config.set_main_option(
"sqlalchemy.url",
get_settings().database_url.render_as_string(hide_password=False),
)
def run_migrations_offline() -> None:
"""离线模式:不连库,只输出 SQL。
用于导出 `db/migration/V{n}__*.sql` 留存脚本。
"""
context.configure(
url=config.get_main_option("sqlalchemy.url"),
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
# 类型变更也要被识别,否则改列类型不会生成迁移
compare_type=True,
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""在线模式:连库执行迁移。"""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
# 迁移是一次性任务,用完即弃,不需要连接池
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
compare_type=True,
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()