From 5aafba8eb99428dc2cff5ef8f7adefc99f45fb20 Mon Sep 17 00:00:00 2001 From: "fengchuanhn@gmail.com" Date: Fri, 22 May 2026 14:59:33 +0800 Subject: [PATCH] 11 --- app/api/v1/admin_users.py | 18 ++++++++++++++++++ app/models/user.py | 2 +- app/schemas/admin_user.py | 1 + app/services/admin_user.py | 13 +++++++++++++ app/services/auth.py | 8 +++++++- 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/app/api/v1/admin_users.py b/app/api/v1/admin_users.py index 24c4ef7..3ccae2f 100644 --- a/app/api/v1/admin_users.py +++ b/app/api/v1/admin_users.py @@ -89,6 +89,24 @@ async def update_user( ) +@router.post("/{user_id}/clear-device-serial", response_model=ApiResponse[AdminUserOut]) +async def clear_device_serial( + user_id: int, + _admin: AdminUser, + db: DbSession, +) -> ApiResponse[AdminUserOut]: + try: + user = await admin_user_service.clear_device_serial(db, user_id) + except admin_user_service.AdminUserError as exc: + return ApiResponse(ok=False, message=exc.message) + + return ApiResponse( + ok=True, + message="设备已解绑", + data=AdminUserOut.model_validate(user), + ) + + @router.delete("/{user_id}", response_model=ApiResponse[None]) async def delete_user( user_id: int, diff --git a/app/models/user.py b/app/models/user.py index e5b8538..f67f178 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -25,4 +25,4 @@ class User(Base): ) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) - device_serial: Mapped[str] = mapped_column(String(255), default="", server_default="") \ No newline at end of file + device_serial: Mapped[str] = mapped_column(String(255), default="", server_default="",index=True) \ No newline at end of file diff --git a/app/schemas/admin_user.py b/app/schemas/admin_user.py index d7d4ba4..3cb72d3 100644 --- a/app/schemas/admin_user.py +++ b/app/schemas/admin_user.py @@ -13,6 +13,7 @@ class AdminUserOut(BaseModel): oem_id: int | None = None agent_id: int | None = None vip_end_time: datetime | None = None + device_serial: str = "" created_at: datetime updated_at: datetime diff --git a/app/services/admin_user.py b/app/services/admin_user.py index 55259c0..abe8d36 100644 --- a/app/services/admin_user.py +++ b/app/services/admin_user.py @@ -163,6 +163,19 @@ async def update_user( return user +async def clear_device_serial(db: AsyncSession, user_id: int) -> User: + """管理员解绑:将用户 device_serial 置空。""" + user = await db.get(User, user_id) + if user is None: + raise AdminUserError("用户不存在") + if not (user.device_serial or "").strip(): + raise AdminUserError("该用户未绑定设备") + user.device_serial = "" + await db.flush() + await db.refresh(user) + return user + + async def delete_user(db: AsyncSession, oem: User, user_id: int, *, actor_id: int) -> None: if user_id == actor_id: raise AdminUserError("不能删除当前登录账号") diff --git a/app/services/auth.py b/app/services/auth.py index f5ac44b..101e66b 100644 --- a/app/services/auth.py +++ b/app/services/auth.py @@ -66,12 +66,18 @@ async def register_user( if existing is not None: raise AuthError("用户名已存在") + serial = _normalize_device_serial(device_serial) + if serial: + bound = await db.scalar(select(User).where(User.device_serial == serial)) + if bound is not None: + raise AuthError("该电脑已绑定其它用户") + user = User( username=username, password_hash=hash_password(password), role_id=0, phone=None, - device_serial=_normalize_device_serial(device_serial), + device_serial=serial, ) db.add(user) await db.flush()