319 lines
8.8 KiB
Python
319 lines
8.8 KiB
Python
|
|
# =============================================================================
|
|||
|
|
# 企微IT智能服务台 — 管理员用户服务
|
|||
|
|
# =============================================================================
|
|||
|
|
# 说明:管理员用户的 CRUD 操作服务
|
|||
|
|
# =============================================================================
|
|||
|
|
|
|||
|
|
import secrets
|
|||
|
|
import logging
|
|||
|
|
from datetime import datetime
|
|||
|
|
from typing import List, Optional, Tuple
|
|||
|
|
|
|||
|
|
import bcrypt
|
|||
|
|
from sqlalchemy import select, func
|
|||
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|||
|
|
|
|||
|
|
from app.models.agent import Agent
|
|||
|
|
from app.utils.error_codes import ErrorCode
|
|||
|
|
from app.utils.response import AppException
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class AdminUserService:
|
|||
|
|
"""管理员用户服务。
|
|||
|
|
|
|||
|
|
提供管理员用户的增删改查、密码验证等功能。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(self, db: AsyncSession):
|
|||
|
|
"""初始化服务。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
db: 数据库会话
|
|||
|
|
"""
|
|||
|
|
self.db = db
|
|||
|
|
|
|||
|
|
async def get_user_by_user_id(self, user_id: str) -> Optional[Agent]:
|
|||
|
|
"""根据 user_id 查询管理员用户。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
user_id: 企微用户ID
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Optional[Agent]: 管理员用户,不存在返回 None
|
|||
|
|
"""
|
|||
|
|
stmt = select(Agent).where(
|
|||
|
|
Agent.user_id == user_id,
|
|||
|
|
Agent.role.in_(["admin", "super_admin"]),
|
|||
|
|
)
|
|||
|
|
result = await self.db.execute(stmt)
|
|||
|
|
return result.scalars().first()
|
|||
|
|
|
|||
|
|
async def get_user_by_id(self, id: str) -> Optional[Agent]:
|
|||
|
|
"""根据 ID 查询管理员用户。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
id: 用户ID
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Optional[Agent]: 管理员用户,不存在返回 None
|
|||
|
|
"""
|
|||
|
|
stmt = select(Agent).where(
|
|||
|
|
Agent.id == id,
|
|||
|
|
Agent.role.in_(["admin", "super_admin"]),
|
|||
|
|
)
|
|||
|
|
result = await self.db.execute(stmt)
|
|||
|
|
return result.scalars().first()
|
|||
|
|
|
|||
|
|
async def list_admin_users(
|
|||
|
|
self,
|
|||
|
|
page: int = 1,
|
|||
|
|
page_size: int = 20,
|
|||
|
|
is_active: Optional[bool] = None,
|
|||
|
|
) -> Tuple[List[Agent], int]:
|
|||
|
|
"""查询管理员用户列表。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
page: 页码(从1开始)
|
|||
|
|
page_size: 每页数量
|
|||
|
|
is_active: 按激活状态过滤
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Tuple[List[Agent], int]: (用户列表, 总数)
|
|||
|
|
"""
|
|||
|
|
# 构建查询
|
|||
|
|
stmt = select(Agent).where(Agent.role.in_(["admin", "super_admin"]))
|
|||
|
|
|
|||
|
|
if is_active is not None:
|
|||
|
|
stmt = stmt.where(Agent.status == ("online" if is_active else "offline"))
|
|||
|
|
|
|||
|
|
# 统计总数
|
|||
|
|
count_stmt = select(func.count()).select_from(stmt.subquery())
|
|||
|
|
total_result = await self.db.execute(count_stmt)
|
|||
|
|
total = total_result.scalar() or 0
|
|||
|
|
|
|||
|
|
# 分页查询
|
|||
|
|
stmt = stmt.order_by(Agent.created_at.desc())
|
|||
|
|
stmt = stmt.offset((page - 1) * page_size).limit(page_size)
|
|||
|
|
|
|||
|
|
result = await self.db.execute(stmt)
|
|||
|
|
items = list(result.scalars().all())
|
|||
|
|
|
|||
|
|
return items, total
|
|||
|
|
|
|||
|
|
async def create_admin_user(
|
|||
|
|
self,
|
|||
|
|
user_id: str,
|
|||
|
|
name: str,
|
|||
|
|
role: str = "admin",
|
|||
|
|
password: Optional[str] = None,
|
|||
|
|
) -> Agent:
|
|||
|
|
"""创建管理员用户。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
user_id: 企微用户ID
|
|||
|
|
name: 姓名
|
|||
|
|
role: 角色(admin/super_admin)
|
|||
|
|
password: 初始密码(可选,不传则生成随机密码)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Agent: 创建的用户
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
AppException: 用户已存在
|
|||
|
|
"""
|
|||
|
|
# 检查是否已存在
|
|||
|
|
existing = await self.get_user_by_user_id(user_id)
|
|||
|
|
if existing:
|
|||
|
|
raise AppException(ErrorCode.INVALID_PARAMETER, f"用户 {user_id} 已存在")
|
|||
|
|
|
|||
|
|
# 生成密码
|
|||
|
|
if not password:
|
|||
|
|
password = secrets.token_urlsafe(12) # 生成随机密码
|
|||
|
|
|
|||
|
|
# 密码哈希
|
|||
|
|
password_hash = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
|
|||
|
|
|
|||
|
|
# 创建用户
|
|||
|
|
agent = Agent(
|
|||
|
|
user_id=user_id,
|
|||
|
|
name=name,
|
|||
|
|
role=role,
|
|||
|
|
status="offline",
|
|||
|
|
password_hash=password_hash,
|
|||
|
|
current_load=0,
|
|||
|
|
max_load=5,
|
|||
|
|
)
|
|||
|
|
self.db.add(agent)
|
|||
|
|
await self.db.flush()
|
|||
|
|
|
|||
|
|
logger.info(f"创建管理员用户: user_id={user_id}, role={role}")
|
|||
|
|
|
|||
|
|
return agent
|
|||
|
|
|
|||
|
|
async def update_admin_user(
|
|||
|
|
self,
|
|||
|
|
id: str,
|
|||
|
|
name: Optional[str] = None,
|
|||
|
|
role: Optional[str] = None,
|
|||
|
|
is_active: Optional[bool] = None,
|
|||
|
|
) -> Agent:
|
|||
|
|
"""更新管理员用户。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
id: 用户ID
|
|||
|
|
name: 姓名(可选)
|
|||
|
|
role: 角色(可选)
|
|||
|
|
is_active: 是否激活(可选)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Agent: 更新后的用户
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
AppException: 用户不存在
|
|||
|
|
"""
|
|||
|
|
agent = await self.get_user_by_id(id)
|
|||
|
|
if not agent:
|
|||
|
|
raise AppException(ErrorCode.NOT_FOUND, "用户不存在")
|
|||
|
|
|
|||
|
|
if name is not None:
|
|||
|
|
agent.name = name
|
|||
|
|
if role is not None:
|
|||
|
|
agent.role = role
|
|||
|
|
if is_active is not None:
|
|||
|
|
agent.status = "online" if is_active else "offline"
|
|||
|
|
|
|||
|
|
agent.updated_at = datetime.now()
|
|||
|
|
self.db.add(agent)
|
|||
|
|
await self.db.flush()
|
|||
|
|
|
|||
|
|
logger.info(f"更新管理员用户: id={id}")
|
|||
|
|
|
|||
|
|
return agent
|
|||
|
|
|
|||
|
|
async def delete_admin_user(self, id: str) -> bool:
|
|||
|
|
"""删除管理员用户。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
id: 用户ID
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 是否删除成功
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
AppException: 用户不存在或无法删除超级管理员
|
|||
|
|
"""
|
|||
|
|
agent = await self.get_user_by_id(id)
|
|||
|
|
if not agent:
|
|||
|
|
raise AppException(ErrorCode.NOT_FOUND, "用户不存在")
|
|||
|
|
|
|||
|
|
# 不允许删除超级管理员
|
|||
|
|
if agent.role == "super_admin":
|
|||
|
|
raise AppException(ErrorCode.FORBIDDEN, "无法删除超级管理员")
|
|||
|
|
|
|||
|
|
await self.db.delete(agent)
|
|||
|
|
await self.db.flush()
|
|||
|
|
|
|||
|
|
logger.info(f"删除管理员用户: id={id}")
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
async def reset_password(self, id: str, new_password: str) -> Agent:
|
|||
|
|
"""重置密码。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
id: 用户ID
|
|||
|
|
new_password: 新密码
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Agent: 更新后的用户
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
AppException: 用户不存在
|
|||
|
|
"""
|
|||
|
|
agent = await self.get_user_by_id(id)
|
|||
|
|
if not agent:
|
|||
|
|
raise AppException(ErrorCode.NOT_FOUND, "用户不存在")
|
|||
|
|
|
|||
|
|
# 密码哈希
|
|||
|
|
agent.password_hash = bcrypt.hashpw(new_password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
|
|||
|
|
agent.updated_at = datetime.now()
|
|||
|
|
self.db.add(agent)
|
|||
|
|
await self.db.flush()
|
|||
|
|
|
|||
|
|
logger.info(f"重置密码: id={id}")
|
|||
|
|
|
|||
|
|
return agent
|
|||
|
|
|
|||
|
|
async def verify_password(self, user_id: str, password: str) -> Optional[Agent]:
|
|||
|
|
"""验证密码。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
user_id: 企微用户ID
|
|||
|
|
password: 密码
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Optional[Agent]: 验证成功返回用户,否则返回 None
|
|||
|
|
"""
|
|||
|
|
agent = await self.get_user_by_user_id(user_id)
|
|||
|
|
if not agent:
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
# 检查密码
|
|||
|
|
if not agent.password_hash:
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
if not bcrypt.checkpw(password.encode("utf-8"), agent.password_hash.encode("utf-8")):
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
return agent
|
|||
|
|
|
|||
|
|
|
|||
|
|
async def init_super_admin(db: AsyncSession) -> Optional[Agent]:
|
|||
|
|
"""初始化超级管理员。
|
|||
|
|
|
|||
|
|
从环境变量读取配置,创建超级管理员用户(如果不存在)。
|
|||
|
|
|
|||
|
|
环境变量:
|
|||
|
|
ADMIN_USERNAME: 超级管理员用户名(必填)
|
|||
|
|
ADMIN_PASSWORD: 超级管理员密码(必填)
|
|||
|
|
ADMIN_NAME: 超级管理员姓名(可选,默认"超级管理员")
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
db: 数据库会话
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Optional[Agent]: 创建/已有的超级管理员用户
|
|||
|
|
"""
|
|||
|
|
import os
|
|||
|
|
|
|||
|
|
admin_username = os.getenv("ADMIN_USERNAME")
|
|||
|
|
admin_password = os.getenv("ADMIN_PASSWORD")
|
|||
|
|
admin_name = os.getenv("ADMIN_NAME", "超级管理员")
|
|||
|
|
|
|||
|
|
if not admin_username or not admin_password:
|
|||
|
|
logger.info("未配置超级管理员,跳过初始化")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
service = AdminUserService(db)
|
|||
|
|
|
|||
|
|
# 检查是否已存在
|
|||
|
|
existing = await service.get_user_by_user_id(admin_username)
|
|||
|
|
if existing:
|
|||
|
|
logger.info(f"超级管理员已存在: user_id={admin_username}")
|
|||
|
|
return existing
|
|||
|
|
|
|||
|
|
# 创建超级管理员
|
|||
|
|
agent = await service.create_admin_user(
|
|||
|
|
user_id=admin_username,
|
|||
|
|
name=admin_name,
|
|||
|
|
role="super_admin",
|
|||
|
|
password=admin_password,
|
|||
|
|
)
|
|||
|
|
await db.commit()
|
|||
|
|
|
|||
|
|
logger.info(f"超级管理员初始化成功: user_id={admin_username}")
|
|||
|
|
|
|||
|
|
return agent
|