fix(auth): 登录 500——asyncpg 拒绝 aware datetime 写入 naive TIMESTAMP 列

批次5(64dc85b)datetime 弃用清零把 utcnow() 换成 aware 的
now(timezone.utc),而 users.last_login 列是 TIMESTAMP WITHOUT TIME
ZONE(sa.DateTime()),asyncpg 编码时抛 DataError → 未捕获 →
Starlette 纯文本 500。症状:密码错误正常 401,密码正确反而 500
(错误密码在 commit 前已 return)。

已在生产库实测确认(事务回滚零写入):aware 写入 REJECTED、
naive ACCEPTED、过去 1 小时 last_login 零记录。

- auth_service: last_login 改存 naive UTC(.replace(tzinfo=None)),
  JWT exp 不受影响仍用 aware
- experience_feedback_service: expires_at 同为 naive 列,提交/查询
  两处 aware now 一并修掉(线上尚无此表,属前瞻性修复)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-09-26 22:29:17 +08:00
parent 9e52c95400
commit f1d6a78f8a
2 changed files with 10 additions and 3 deletions
+5 -1
View File
@@ -142,7 +142,11 @@ async def authenticate_user(db_session: AsyncSession, username: str, password: s
if not verify_password(password, user.hashed_password):
return None
user.last_login = datetime.now(timezone.utc)
# last_login 列是 TIMESTAMP WITHOUT TIME ZONE(sa.DateTime()),asyncpg 拒绝写入
# 带 tzinfo 的 datetime(DataError → 登录 500):批次5 弃用清理把 utcnow() 换成
# aware 时间后,任何一次成功登录都会在 commit 处炸掉。存库统一 naive UTC,
# JWT exp(上方 create_access_token)不受影响,仍用 aware。
user.last_login = datetime.now(timezone.utc).replace(tzinfo=None)
await db_session.commit()
return user