From ff660b1438077748e9fba8eaffdbe88cc95870f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=81=B5=E5=BA=94?= Date: Sun, 5 Jul 2026 16:14:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A1=A5025=E5=8F=B7=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E6=96=87=E4=BB=B6=20=E2=80=94=20wechat=5Fopenid/wechat=5Funion?= =?UTF-8?q?id=20=E5=AD=97=E6=AE=B5=E7=BA=B3=E5=85=A5=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新建 alembic/versions/025_add_user_wechat_fields.py - revision=025, down_revision=024 - upgrade: 幂等加字段+唯一索引(检查列/索引是否存在再操作) - downgrade: 删除索引+删除字段 - UserModel 新增 wechat_openid / wechat_unionid 列定义 - 验证:upgrade 024→025 ✓ | downgrade 025→024 ✓ | 全量测试 994 passed Co-Authored-By: Claude Fable 5 --- .../versions/025_add_user_wechat_fields.py | 72 +++++++++++++++++++ packages/adapters/sqlalchemy_impl/models.py | 3 + 2 files changed, 75 insertions(+) create mode 100644 alembic/versions/025_add_user_wechat_fields.py diff --git a/alembic/versions/025_add_user_wechat_fields.py b/alembic/versions/025_add_user_wechat_fields.py new file mode 100644 index 000000000..2e0ef4ee0 --- /dev/null +++ b/alembic/versions/025_add_user_wechat_fields.py @@ -0,0 +1,72 @@ +"""Task: Add wechat_openid / wechat_unionid to users + +Revision ID: 025 +Revises: 024 +Create Date: 2026-07-05 + +补录微信小程序登录所需的 wechat 字段。 +生产数据库已手动添加过这些字段和索引,因此 upgrade 做幂等检查, +避免在已有字段的库上执行报错。 +""" + +import sqlalchemy as sa + +from alembic import op + +revision = "025" +down_revision = "024" +branch_labels = None +depends_on = None + + +def _column_exists(table: str, column: str) -> bool: + """检查 PostgreSQL 表中某列是否已存在。""" + conn = op.get_bind() + result = conn.execute( + sa.text( + "SELECT 1 FROM information_schema.columns " + "WHERE table_name = :table AND column_name = :column" + ), + {"table": table, "column": column}, + ) + return result.scalar() is not None + + +def _index_exists(index: str) -> bool: + """检查 PostgreSQL 中某索引是否已存在。""" + conn = op.get_bind() + result = conn.execute( + sa.text("SELECT 1 FROM pg_indexes WHERE indexname = :index"), + {"index": index}, + ) + return result.scalar() is not None + + +def upgrade() -> None: + # wechat_openid + if not _column_exists("users", "wechat_openid"): + op.add_column( + "users", + sa.Column("wechat_openid", sa.String(length=128), nullable=True), + ) + + # wechat_unionid + if not _column_exists("users", "wechat_unionid"): + op.add_column( + "users", + sa.Column("wechat_unionid", sa.String(length=128), nullable=True), + ) + + # 唯一索引 + if not _index_exists("ix_users_wechat_openid"): + op.create_index("ix_users_wechat_openid", "users", ["wechat_openid"], unique=True) + + if not _index_exists("ix_users_wechat_unionid"): + op.create_index("ix_users_wechat_unionid", "users", ["wechat_unionid"], unique=True) + + +def downgrade() -> None: + op.drop_index("ix_users_wechat_unionid", table_name="users") + op.drop_index("ix_users_wechat_openid", table_name="users") + op.drop_column("users", "wechat_unionid") + op.drop_column("users", "wechat_openid") diff --git a/packages/adapters/sqlalchemy_impl/models.py b/packages/adapters/sqlalchemy_impl/models.py index 94aaff0f4..982875e00 100755 --- a/packages/adapters/sqlalchemy_impl/models.py +++ b/packages/adapters/sqlalchemy_impl/models.py @@ -30,6 +30,9 @@ class UserModel(Base): used_storage_gb = Column(Integer, nullable=False, default=0) # 管理员标识 is_admin = Column(Boolean, nullable=False, default=False) + # 微信登录(小程序端) + wechat_openid = Column(String(128), nullable=True, unique=True, index=True) + wechat_unionid = Column(String(128), nullable=True, unique=True, index=True) created_at = Column(DateTime, nullable=False, default=lambda: datetime.now(timezone.utc))