feat(auth): 平台成唯一账号源——小程序微信登录进 users + OPC 报名资料字段
统一用户账号体系(阶段1,非破坏): - UserRepository 新增 get_by_wx_mini_openid。 - /auth/wx-login:小程序登录(X-Client=miniprogram)按 wx_mini_openid 建/复用 users 账号(role=opc_member, source=mini_program, auth_type=wx_openid), 并带 nickName/avatarUrl;非小程序仍走 wx_openid。 - /auth/me + 登录响应补齐小程序契约字段 name/phone/phoneBound。 - users 增 opc_status/topics 列(alembic 0010);desktop/web profile 也输出 name/phone/phoneBound/status/topics,且 update-profile 支持 name/status/topics 映射。 - scripts/db/seed.py 增 accounts→users 幂等迁移:旧小程序账号并入全局 users (wx_mini_openid/phone 关联 + opc_member 身份),由用户执行 migrate+seed。 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -47,6 +47,52 @@ def _seed_training() -> None:
|
||||
conn.close()
|
||||
|
||||
|
||||
async def _migrate_accounts_to_users(db) -> None:
|
||||
"""把培训端 accounts 存量并入平台 users(幂等:已存在账号跳过并保证身份)。
|
||||
|
||||
迁移旧小程序账号 → users(wx_mini_openid/phone 关联),使微信/手机登录仍命中,
|
||||
且并入全局唯一账号体系。迁移/种子由用户执行(铁律),禁止运行态调用。
|
||||
"""
|
||||
import secrets
|
||||
import sqlite3
|
||||
from app.training import db as tdb
|
||||
from app.api.routers.auth import _ensure_opc_identity
|
||||
|
||||
conn = sqlite3.connect(tdb.DB_PATH)
|
||||
conn.row_factory = sqlite3.Row
|
||||
migrated = created = 0
|
||||
try:
|
||||
for row in conn.execute("SELECT * FROM accounts"):
|
||||
username = (row["username"] or "").strip()
|
||||
wxid = (row["wxid"] or "").strip()
|
||||
phone = (row["phone"] or "").strip()
|
||||
if not username and not wxid and not phone:
|
||||
continue
|
||||
user = (await db.users.get_by_username(username)) if username else None
|
||||
if user is None and phone:
|
||||
user = await db.users.get_by_username(phone)
|
||||
if user is None and wxid:
|
||||
user = (await db.users.get_by_wx_mini_openid(wxid)
|
||||
or await db.users.get_by_wx_openid(wxid))
|
||||
if user is None:
|
||||
uname = (username or phone or f"wx_{wxid[:24]}")
|
||||
user = await db.users.create(
|
||||
username=uname, password=secrets.token_hex(16),
|
||||
nickname=(row["name"] or uname), avatar=(row["avatar"] or ""),
|
||||
phone=phone, wx_mini_openid=wxid or "",
|
||||
wx_openid=(f"wx_{wxid[:24]}" if wxid else ""),
|
||||
source="mini_program",
|
||||
auth_type=("wx_openid" if wxid else ("phone" if phone else "unknown")),
|
||||
role="opc_member",
|
||||
)
|
||||
created += 1
|
||||
await _ensure_opc_identity(db, user["id"])
|
||||
migrated += 1
|
||||
log.info("accounts→users 迁移完成:共 %s 条,新建 %s 条", migrated, created)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
async def run() -> None:
|
||||
db = Database()
|
||||
try:
|
||||
@@ -54,6 +100,7 @@ async def run() -> None:
|
||||
await db.session.commit()
|
||||
log.info("平台种子完成:%s", config.DATABASE_URL)
|
||||
_seed_training()
|
||||
await _migrate_accounts_to_users(db)
|
||||
except Exception as e: # noqa: BLE001
|
||||
await db.session.rollback()
|
||||
log.error("种子失败:%s", e)
|
||||
|
||||
Reference in New Issue
Block a user