Files
group_fqcd_jr/app/infrastructure/db.py
T

37 lines
2.0 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.
from sqlalchemy.engine import make_url
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.pool import NullPool
from app.core.config import get_settings
# 存储层统一 UTC:`init_command` 是 asyncmy(driver 层)在**每次建立连接**时执行的
# 初始化 SQL,SQLAlchemy 的 asyncmy 方言会把 DSN 上的 query 参数原样透传给
# `asyncmy.connect`,因此写在 DSN 上即可生效,无需事件监听。
#
# 为什么不挂 `connect` 事件:`connect` 事件在 asyncmy 这套 asyncio 方言上**不会**
# 等待 async 监听器——实测注册后 `@@session.time_zone` 仍是 `SYSTEM`,同时抛
# `RuntimeWarning: coroutine ... was never awaited`。也就是说那种写法不报错、不生效,
# 是"看着修好了、其实照旧差 8 小时"的典型陷阱。
#
# 背景:建库时未显式指定时区,MySQL 会话继承系统时区(本机 Asia/Shanghai),
# 表上 `created_at DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6)` 因此写入**北京时间**,
# 而应用代码统一写 `datetime.now(UTC)`:同一行里 DB 默认值与应用写入值相差 8 小时。
# 更严重的真实故障是给 `sys_user_role.assigned_at` 用 MySQL `NOW()` 会超前 UTC 8 小时,
# 被 RBAC 的 `assigned_at <= now` 判为"尚未生效",接口直接 403。
#
# `.env` 的 `TIMEZONE=Asia/Shanghai` 只作用于展示/日志,改它**不会**影响 MySQL 会话时区,
# 所以必须在连接层设置。改这里即可:新增的连接一律 UTC,DB 默认值与应用 UTC 语义一致。
_SESSION_UTC_INIT_COMMAND = "SET time_zone = '+00:00'"
settings = get_settings()
_url = make_url(settings.mysql_dsn)
if "init_command" not in _url.query:
_url = _url.update_query_dict({"init_command": _SESSION_UTC_INIT_COMMAND})
engine = create_async_engine(
_url,
pool_pre_ping=settings.mysql_pool_pre_ping,
poolclass=NullPool,
)
SessionFactory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)