WIP-CHECKPOINT[auth-refactor]: 固化工程师崩溃前部分成果 + 同树其他未提交WIP(仅源码,不含密钥/二进制)-- 待重激活工程师续作
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
# =============================================================================
|
||||
# 企微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
|
||||
Reference in New Issue
Block a user