Files
wecom_it_smart_desk/backend/app/services/admin_user_service.py
T

319 lines
8.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# 企微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