194 lines
6.0 KiB
Python
194 lines
6.0 KiB
Python
from sqlalchemy import func, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.roles import ROLE_ADMIN, ROLE_AGENT, ROLE_OEM
|
|
from app.core.security import hash_password
|
|
from app.models.user import User
|
|
from app.schemas.admin_user import AdminUserCreate, AdminUserUpdate
|
|
|
|
DEFAULT_PAGE_SIZE = 20
|
|
MAX_PAGE_SIZE = 200
|
|
|
|
|
|
class AdminUserError(Exception):
|
|
def __init__(self, message: str) -> None:
|
|
self.message = message
|
|
super().__init__(message)
|
|
|
|
|
|
def _list_users_filters(
|
|
*,
|
|
username: str | None = None,
|
|
device_serial: str | None = None,
|
|
oem_id: int | None = None,
|
|
agent_id: int | None = None,
|
|
role_id: int | None = None,
|
|
) -> list:
|
|
conditions = []
|
|
if username and username.strip():
|
|
conditions.append(User.username.ilike(f"%{username.strip()}%"))
|
|
if device_serial and device_serial.strip():
|
|
conditions.append(User.device_serial.ilike(f"%{device_serial.strip()}%"))
|
|
if oem_id is not None:
|
|
conditions.append(User.oem_id == oem_id)
|
|
if agent_id is not None:
|
|
conditions.append(User.agent_id == agent_id)
|
|
if role_id is not None:
|
|
conditions.append(User.role_id == role_id)
|
|
return conditions
|
|
|
|
|
|
async def list_users(
|
|
db: AsyncSession,
|
|
*,
|
|
page: int = 1,
|
|
page_size: int = DEFAULT_PAGE_SIZE,
|
|
username: str | None = None,
|
|
device_serial: str | None = None,
|
|
oem_id: int | None = None,
|
|
agent_id: int | None = None,
|
|
role_id: int | None = None,
|
|
) -> tuple[list[User], int]:
|
|
page_size = min(max(page_size, 1), MAX_PAGE_SIZE)
|
|
page = max(page, 1)
|
|
offset = (page - 1) * page_size
|
|
conditions = _list_users_filters(
|
|
username=username,
|
|
device_serial=device_serial,
|
|
oem_id=oem_id,
|
|
agent_id=agent_id,
|
|
role_id=role_id,
|
|
)
|
|
|
|
count_stmt = select(func.count()).select_from(User)
|
|
list_stmt = select(User).order_by(User.id.desc())
|
|
if conditions:
|
|
count_stmt = count_stmt.where(*conditions)
|
|
list_stmt = list_stmt.where(*conditions)
|
|
|
|
total = await db.scalar(count_stmt) or 0
|
|
result = await db.scalars(list_stmt.offset(offset).limit(page_size))
|
|
return list(result.all()), total
|
|
|
|
|
|
async def _validate_owner_ids(
|
|
db: AsyncSession,
|
|
*,
|
|
oem_id: int | None,
|
|
agent_id: int | None,
|
|
) -> None:
|
|
if oem_id is not None:
|
|
oem = await db.get(User, oem_id)
|
|
if oem is None or oem.role_id != ROLE_OEM:
|
|
raise AdminUserError("所选 OEM 不存在")
|
|
if agent_id is not None:
|
|
agent = await db.get(User, agent_id)
|
|
if agent is None or agent.role_id != ROLE_AGENT:
|
|
raise AdminUserError("所选代理不存在")
|
|
|
|
|
|
async def create_user(db: AsyncSession, body: AdminUserCreate,oem: User) -> User:
|
|
existing = await db.scalar(select(User).where(User.username == body.username))
|
|
if existing is not None:
|
|
raise AdminUserError("用户名已存在")
|
|
|
|
if body.phone:
|
|
phone_taken = await db.scalar(select(User).where(User.phone == body.phone))
|
|
if phone_taken is not None:
|
|
raise AdminUserError("手机号已被使用")
|
|
|
|
await _validate_owner_ids(db, oem_id=oem.id, agent_id=body.agent_id)
|
|
|
|
user = User(
|
|
username=body.username,
|
|
password_hash=hash_password(body.password),
|
|
phone=body.phone,
|
|
role_id=body.role_id,
|
|
oem_id=oem.id,
|
|
agent_id=body.agent_id,
|
|
vip_end_time=body.vip_end_time,
|
|
)
|
|
db.add(user)
|
|
await db.flush()
|
|
await db.refresh(user)
|
|
return user
|
|
|
|
|
|
async def update_user(
|
|
db: AsyncSession,
|
|
oem: User,
|
|
user_id: int,
|
|
body: AdminUserUpdate,
|
|
*,
|
|
actor_id: int,
|
|
) -> User:
|
|
user = await db.get(User, user_id)
|
|
if user is None:
|
|
raise AdminUserError("用户不存在")
|
|
body.oem_id=oem.id
|
|
if body.username is not None and body.username != user.username:
|
|
taken = await db.scalar(select(User).where(User.username == body.username))
|
|
if taken is not None:
|
|
raise AdminUserError("用户名已存在")
|
|
user.username = body.username
|
|
|
|
if body.phone is not None:
|
|
if body.phone != user.phone:
|
|
taken = await db.scalar(select(User).where(User.phone == body.phone))
|
|
if taken is not None:
|
|
raise AdminUserError("手机号已被使用")
|
|
user.phone = body.phone
|
|
|
|
if body.role_id is not None:
|
|
if user_id == actor_id and body.role_id != ROLE_ADMIN:
|
|
raise AdminUserError("不能修改自己的管理员角色")
|
|
user.role_id = body.role_id
|
|
|
|
if body.oem_id is not None or body.agent_id is not None:
|
|
await _validate_owner_ids(
|
|
db,
|
|
oem_id=body.oem_id if body.oem_id is not None else user.oem_id,
|
|
agent_id=body.agent_id if body.agent_id is not None else user.agent_id,
|
|
)
|
|
if body.oem_id is not None:
|
|
user.oem_id = body.oem_id
|
|
if body.agent_id is not None:
|
|
user.agent_id = body.agent_id
|
|
|
|
if body.password:
|
|
user.password_hash = hash_password(body.password)
|
|
|
|
if body.clear_vip_end_time:
|
|
user.vip_end_time = None
|
|
elif body.vip_end_time is not None:
|
|
user.vip_end_time = body.vip_end_time
|
|
|
|
await db.flush()
|
|
await db.refresh(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("不能删除当前登录账号")
|
|
|
|
user = await db.get(User, user_id)
|
|
if user is None:
|
|
raise AdminUserError("用户不存在")
|
|
if user.oem_id != oem.id:
|
|
raise AdminUserError("无权限删除该用户")
|
|
await db.delete(user)
|