Files

224 lines
8.3 KiB
Python
Raw Permalink Normal View History

# =============================================================================
# 企微IT智能服务台 — Mock适配器(开发期使用)
# =============================================================================
# 说明:
# 1. 在 Mock 模式下(EXT_MOCK_MODE=True),所有外部系统查询
# 返回预置的 Mock 数据,不调用任何真实 API
# 2. Mock 数据覆盖 P0 场景(终端查询、安全状态、VPN在线)
# 3. 凭证未配置时自动降级到 MockAdapter,保证开发期无外部依赖
#
# 使用方式:
# EXT_MOCK_MODE=True → 所有系统走 Mock
# 某系统凭证未配置 → 单个系统自动降级到 Mock(在 service.py 中处理)
# =============================================================================
import logging
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional
from app.services.external.base import (
ExternalSystemAdapter,
TerminalInfo,
SecurityStatus,
VpnSession,
)
logger = logging.getLogger(__name__)
# =============================================================================
# Mock 数据工厂
# =============================================================================
def _make_mock_terminal(username: str) -> TerminalInfo:
"""生成 Mock 终端信息
做什么:为指定用户生成一个逼真的模拟终端数据
为什么:开发期没有真实凭证时需要终端数据支撑会话排查流程
"""
return TerminalInfo(
source_system="mock",
terminal_id=f"mock-terminal-{username}",
computer_name=f"{username.upper()}-PC01",
ip_addresses=[f"192.168.{hash(username) % 255}.{100 + hash(username) % 155}"],
mac_addresses=[f"00:16:3E:{hash(username) % 256:02X}:{hash(username + 'a') % 256:02X}:{hash(username + 'b') % 256:02X}"],
os_version="Windows 11 专业版 23H2",
is_online=True,
logged_in_user=username,
logged_in_user_name=_username_to_display_name(username),
department=_guess_department(username),
hardware_summary={
"cpu": "Intel Core i7-12700",
"memory_total_gb": 16,
"memory_used_gb": 8,
"disk_total_gb": 512,
"disk_free_gb": 128,
"disk_usage_pct": 75, # 模拟磁盘使用率较高
},
last_seen=datetime.now() - timedelta(minutes=5),
raw_data=None, # Mock 数据不保留原始响应
)
def _make_mock_security_status(terminal_id: str) -> SecurityStatus:
"""生成 Mock 安全状态
做什么:生成一个模拟的安全状态数据
为什么:开发期需要验证安全状态卡片、漏洞警告等UI渲染
"""
return SecurityStatus(
source_system="mock",
terminal_id=terminal_id,
computer_name=f"MOCK-PC01",
virus_total=2,
virus_uncleaned=1,
vulnerabilities=[
{
"name": "Microsoft Windows 安全更新 (CVE-2025-12345)",
"level": "high",
"description": "远程代码执行漏洞,需立即修补",
"publish_time": (datetime.now() - timedelta(days=7)).isoformat(),
},
{
"name": "火绒安全漏洞扫描:弱密码检测",
"level": "medium",
"description": "账户密码强度不足,建议修改",
"publish_time": (datetime.now() - timedelta(days=3)).isoformat(),
},
],
high_vuln_count=1,
is_isolated=False,
isolation_source=None,
checked_at=datetime.now(),
)
def _make_mock_vpn_session(username: str) -> VpnSession:
"""生成 Mock VPN 会话"""
return VpnSession(
source_system="mock",
session_id=f"mock-session-{username}",
username=username,
display_name=_username_to_display_name(username),
remote_ip=f"1{hash(username) % 100}.{hash(username + 'r') % 256}.{hash(username + 's') % 256}.{hash(username + 't') % 256}",
vpn_ip=f"10.200.{hash(username) % 255}.{100 + hash(username) % 155}",
is_trusted=True,
os="Windows 11",
last_login=datetime.now() - timedelta(minutes=30),
domain="servyou.local",
)
def _username_to_display_name(username: str) -> str:
"""Mock 用户名转换(简单映射)"""
name_map = {
"songxian": "宋献",
"zhangsan": "张三",
"lisi": "李四",
"wangwu": "王五",
}
return name_map.get(username, username)
def _guess_department(username: str) -> str:
"""Mock 部门推断"""
dept_map = {
"songxian": "IT支持组",
"zhangsan": "财务部",
"lisi": "人力资源部",
"wangwu": "研发部",
}
return dept_map.get(username, "未知部门")
# =============================================================================
# MockAdapter 实现
# =============================================================================
class MockAdapter(ExternalSystemAdapter):
"""Mock 适配器 — 开发期替代所有外部系统
做什么:提供逼真的模拟数据,让开发期可以不依赖任何外部系统
为什么:阶段一MVP验证、前端开发、单元测试都需要稳定的数据来源
降级规则:
- 所有方法均返回 Mock 数据
- 支持常用测试用户:songxian / zhangsan / lisi / wangwu
- is_available 固定返回 TrueMock 永远可用)
"""
@property
def system_name(self) -> str:
return "mock"
@property
def is_available(self) -> bool:
"""Mock 永远可用"""
return True
async def health_check(self) -> bool:
"""Mock 健康检查永远通过"""
logger.debug("[MockAdapter] 健康检查 → OKMock模式)")
return True
# ── 终端查询能力 ──
async def get_terminal_by_user(self, username: str) -> Optional[TerminalInfo]:
"""Mock:通过账号查询终端
做什么:返回预置的 Mock 终端信息
为什么:开发期坐席打开会话时需要看到终端画像
"""
logger.info(f"[MockAdapter] get_terminal_by_user({username}) → Mock数据")
return _make_mock_terminal(username)
async def get_terminal_by_computer(self, computer_name: str) -> Optional[TerminalInfo]:
"""Mock:通过计算机名查询终端"""
logger.info(f"[MockAdapter] get_terminal_by_computer({computer_name}) → Mock数据")
# 从计算机名反推用户名(简单逻辑)
username = computer_name.split("-")[0].lower() if "-" in computer_name else "songxian"
return _make_mock_terminal(username)
async def get_terminal_detail(self, terminal_id: str) -> Optional[TerminalInfo]:
"""Mock:查询终端详细信息"""
logger.info(f"[MockAdapter] get_terminal_detail({terminal_id}) → Mock数据")
return _make_mock_terminal("songxian")
# ── 安全能力 ──
async def get_security_status(self, terminal_id: str) -> Optional[SecurityStatus]:
"""Mock:获取安全状态"""
logger.info(f"[MockAdapter] get_security_status({terminal_id}) → Mock数据")
return _make_mock_security_status(terminal_id)
async def isolate_terminal(self, terminal_id: str, reason: str) -> bool:
"""Mock:隔离终端(Mock 模式仅记录日志)"""
logger.warning(
f"[MockAdapter] 隔离终端(Mock,不真实执行): "
f"terminal={terminal_id}, reason={reason}"
)
return True # Mock 永远返回成功
async def unisolate_terminal(self, terminal_id: str) -> bool:
"""Mock:解除隔离"""
logger.warning(
f"[MockAdapter] 解除隔离(Mock,不真实执行): terminal={terminal_id}"
)
return True
# ── VPN/在线状态 ──
async def get_vpn_sessions(self, username: Optional[str] = None) -> List[VpnSession]:
"""Mock:查询VPN在线会话"""
if username:
return [_make_mock_vpn_session(username)]
# 返回多个 Mock 会话
return [
_make_mock_vpn_session("songxian"),
_make_mock_vpn_session("zhangsan"),
]
async def get_online_status(self, username: str) -> bool:
"""Mock:查询在线状态(Mock 永远返回 True)"""
return True