PayrollManagementSystem/backend/app/core/db.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

65 lines
2.2 KiB
Python
Raw 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.

"""数据库连接管理。
**为什么用同步引擎**:本项目并发极低(内部系统,营销人员数十人),
异步拿不到吞吐收益;而重活——绩效批量核算、pandas 报表、Excel 导出——
全是同步阻塞代码,放在异步路由里会卡死事件循环,且这类问题在开发期
低并发下几乎不暴露。改用同步后,路由写 `def`,FastAPI 会**自动放入
线程池执行**,天然规避上述陷阱,也不会阻塞事件循环。
引擎采用**延迟创建**:应用启动时不连接 MySQL,
这样 Docker 未启动时仍能运行服务、查看接口文档。
"""
from collections.abc import Generator
from sqlalchemy import Engine, create_engine
from sqlalchemy.orm import Session, sessionmaker
from app.core.config import get_settings
_engine: Engine | None = None
_session_factory: sessionmaker[Session] | None = None
def get_engine() -> Engine:
"""获取同步引擎单例(首次调用时创建)。"""
global _engine, _session_factory
if _engine is None:
settings = get_settings()
_engine = create_engine(
settings.database_url,
# 连接池中的连接在使用前先探活,避免 MySQL 重启后取到死连接
pool_pre_ping=True,
pool_recycle=3600,
echo=False,
)
_session_factory = sessionmaker(_engine, expire_on_commit=False)
return _engine
def get_session_factory() -> sessionmaker[Session]:
"""获取会话工厂单例。"""
get_engine()
assert _session_factory is not None # get_engine 已确保初始化
return _session_factory
def get_db() -> Generator[Session, None, None]:
"""FastAPI 依赖:提供一个数据库会话。
只负责会话的创建与关闭,**事务边界由 service 层控制** ——
这样一次请求内的多个仓储调用可以共处同一事务。
"""
factory = get_session_factory()
with factory() as session:
yield session
def dispose_engine() -> None:
"""关闭连接池。应用退出时调用。"""
global _engine, _session_factory
if _engine is not None:
_engine.dispose()
_engine = None
_session_factory = None