# ExternalSystemAdapter 抽象层设计文档 > 版本:V1.0 | 日期:2026-06-11 | 作者:IT智能服务台项目组 --- ## 一、设计目标 为联软、火绒、aTrust、eHR 四个外部系统提供**统一适配层**,实现: 1. **接口统一**:上层业务代码只依赖抽象接口,不感知底层系统差异 2. **可替换性**:Mock数据开发 → 真实API无缝切换,只需改配置 3. **缓存透明**:外部数据自动缓存+定时刷新,业务层无感 4. **降级安全**:外部系统不可用时自动降级,不阻断主流程 5. **横向扩展**:新增系统只需实现一个 Adapter,零改动业务层 --- ## 二、系统角色与优先级 | 系统 | 角色 | 核心能力 | 认证方式 | 凭证状态 | |------|------|---------|---------|---------| | 联软LV7000 | 主映射源(P0) | 终端查询(含strusername)、硬件详情、在线状态 | IP白名单+账号密码+Token | 明天可拿 | | 火绒企业版 | 安全源(P0) | 终端列表、漏洞/病毒事件、一键隔离 | HMAC-SHA1 AccessKey | 现在可拿 | | aTrust | VPN源(P1) | 在线用户+VPN IP、终端查询、踢出用户 | HMAC-SHA256签名 | 约一周 | | 北森eHR | 辅助静态数据(P2) | 员工基础信息、任职信息 | OAuth2.0 | 待对接HR | --- ## 三、架构分层 ``` ┌─────────────────────────────────────────────────┐ │ 上层业务代码(AI Wingman等) │ ├─────────────────────────────────────────────────┤ │ ExternalSystemService(统一门面) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ 缓存层 │ │ 降级策略 │ │ 配置管理 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├─────────────────────────────────────────────────┤ │ ExternalSystemAdapter(抽象基类) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────┐│ │ │ LianRuan │ │ HuoRong │ │ aTrust │ │eHR ││ │ │ Adapter │ │ Adapter │ │ Adapter │ │ 适配││ │ └──────────┘ └──────────┘ └──────────┘ └────┘│ ├─────────────────────────────────────────────────┤ │ MockAdapter(开发期) │ └─────────────────────────────────────────────────┘ ``` --- ## 四、核心抽象接口 ### 4.1 数据模型(统一DTO) ```python class TerminalInfo(BaseModel): """统一终端信息模型 — 所有Adapter返回同一结构""" source_system: str # 数据来源系统标识 computer_name: str # 计算机名 ip_addresses: List[str] # IP地址列表(含VPN虚拟IP) mac_addresses: List[str] # MAC地址列表 os_version: Optional[str] # 操作系统版本 is_online: bool # 是否在线 logged_in_user: Optional[str] # 当前登录用户账号(映射核心字段) logged_in_user_name: Optional[str] # 用户姓名 department: Optional[str] # 所属部门 hardware_summary: Optional[Dict] # 硬件摘要(CPU/内存/磁盘) last_seen: Optional[datetime] # 最后在线时间 raw_data: Optional[Dict] # 原始响应(调试用,生产可关闭) class SecurityStatus(BaseModel): """统一安全状态模型""" source_system: str terminal_id: str virus_events: Optional[Dict] # 病毒事件统计 vulnerabilities: Optional[List] # 高危漏洞列表 is_isolated: bool # 是否被隔离 isolation_source: Optional[str] # 隔离来源系统 class VpnSession(BaseModel): """VPN会话模型(仅aTrust)""" source_system: str = "atrust" username: str display_name: Optional[str] remote_ip: str vpn_ip: Optional[str] # 虚拟内网IP is_trusted: bool last_login: Optional[datetime] ``` ### 4.2 Adapter抽象基类 ```python from abc import ABC, abstractmethod from typing import Optional, List class ExternalSystemAdapter(ABC): """外部系统适配器抽象基类 每个外部系统实现此接口,上层业务只依赖此接口。 """ @property @abstractmethod def system_name(self) -> str: """系统标识名称,如 'lianruan' / 'huorong' / 'atrust' / 'ehr'""" ... @property @abstractmethod def is_available(self) -> bool: """当前系统是否可用(凭证已配置+网络可达)""" ... @abstractmethod async def health_check(self) -> bool: """健康检查 — 验证凭证和网络连通性""" ... # ── 终端查询能力 ── async def get_terminal_by_user(self, username: str) -> Optional[TerminalInfo]: """通过员工账号查询终端信息(映射核心方法) 联软:queryDevByParams(strusername=xxx) 火绒:_list(ip=xxx) 需配合联软IP交叉匹配 aTrust:queryAll(bindUserList) 终端绑定用户 eHR:不提供终端数据,返回None """ return None # 默认不支持,子类按需覆写 async def get_terminal_by_computer(self, computer_name: str) -> Optional[TerminalInfo]: """通过计算机名查询终端信息""" return None async def get_terminal_detail(self, terminal_id: str) -> Optional[TerminalInfo]: """查询终端详细信息(硬件/软件/网络配置)""" return None # ── 安全能力 ── async def get_security_status(self, terminal_id: str) -> Optional[SecurityStatus]: """获取终端安全状态(病毒/漏洞/隔离状态)""" return None async def isolate_terminal(self, terminal_id: str, reason: str) -> bool: """隔离终端(仅火绒支持,需admin角色二次确认)""" raise NotImplementedError(f"{self.system_name} 不支持终端隔离") async def unisolate_terminal(self, terminal_id: str) -> bool: """解除终端隔离""" raise NotImplementedError(f"{self.system_name} 不支持解除隔离") # ── VPN/在线状态 ── async def get_vpn_sessions(self, username: Optional[str] = None) -> List[VpnSession]: """查询VPN在线会话(仅aTrust支持)""" return [] async def get_online_status(self, username: str) -> bool: """查询用户是否在线""" return False ``` ### 4.3 统一门面服务 ```python class ExternalSystemService: """外部系统统一门面 — 上层业务只调用此类""" def __init__(self, adapters: Dict[str, ExternalSystemAdapter], cache: CacheService): self._adapters = adapters # {"lianruan": LianRuanAdapter, ...} self._cache = cache async def find_user_terminal(self, username: str) -> Optional[TerminalInfo]: """查找用户终端 — 优先联软,降级aTrust,最后eHR 做什么:按映射优先级依次查询,任一系统返回即停止 为什么:联软strusername精确匹配最可靠,aTrust次之 """ # 1. 联软(主源,strusername精确匹配) result = await self._query_with_cache("lianruan", "get_terminal_by_user", username) if result: return result # 2. aTrust(VPN源,bindUserList匹配) result = await self._query_with_cache("atrust", "get_terminal_by_user", username) if result: return result # 3. eHR(静态辅助,无终端数据) return None async def get_terminal_security(self, terminal_id: str) -> Optional[SecurityStatus]: """获取终端安全状态 — 仅火绒""" return await self._query_with_cache("huorong", "get_security_status", terminal_id) async def isolate_terminal(self, terminal_id: str, reason: str, operator: str) -> bool: """隔离终端 — 仅火绒,需operator记录审计日志""" logger.warning(f"终端隔离操作: terminal={terminal_id}, operator={operator}, reason={reason}") return await self._adapters["huorong"].isolate_terminal(terminal_id, reason) ``` --- ## 五、缓存策略 | 数据类型 | 缓存TTL | 刷新策略 | 说明 | |---------|---------|---------|------| | 终端映射(员工→终端) | 30分钟 | 定时刷新+访问时检查 | 映射关系不常变 | | 终端详情(硬件/软件) | 60分钟 | 懒加载 | 硬件配置极少变 | | 安全状态(漏洞/病毒) | 5分钟 | 短TTL+事件驱动 | 安全状态需近实时 | | VPN在线状态 | 1分钟 | 短TTL | 在线状态变化快 | | eHR员工信息 | 24小时 | 每日凌晨全量同步 | 静态数据 | 缓存key格式:`ext:{system}:{method}:{param_hash}` --- ## 六、降级策略 | 故障场景 | 处理方式 | 用户影响 | |---------|---------|---------| | 单个系统不可用 | 跳过该系统,尝试下一优先级 | 部分数据缺失,不阻断 | | 所有外部系统不可用 | 返回缓存数据(如有)+ 明确标注"数据可能过时" | 信息可能过时 | | 缓存+外部系统均不可用 | 返回空结果+告警通知坐席 | 无法获取外部数据 | | 火绒隔离操作失败 | 重试1次 → 失败则记录待执行队列 → 告警坐席 | 安全操作不静默失败 | --- ## 七、配置管理 ```python class ExternalSystemConfig(BaseModel): """外部系统连接配置 — 从环境变量或配置中心读取""" # 联软 lianruan_base_url: str = "http://192.168.x.x:30098" lianruan_api_account: Optional[str] = None lianruan_api_password: Optional[str] = None # 火绒 huorong_base_url: str = "http://huorong.oa.servyou-it.com:8080" huorong_access_key_id: Optional[str] = None huorong_access_key_secret: Optional[str] = None # aTrust atrust_base_url: str = "https://atrust.servyou-it.com:4433" atrust_api_id: Optional[str] = None atrust_api_secret: Optional[str] = None atrust_directory_domain: Optional[str] = None # eHR ehr_base_url: Optional[str] = None ehr_client_id: Optional[str] = None ehr_client_secret: Optional[str] = None # 全局 cache_enabled: bool = True mock_mode: bool = False # True时所有请求走MockAdapter ``` --- ## 八、目录结构 ``` backend/app/services/external/ ├── __init__.py # 模块导出 ├── base.py # 抽象基类 ExternalSystemAdapter + 数据模型 ├── config.py # 配置管理 ExternalSystemConfig ├── cache.py # 缓存装饰器和策略 ├── mock.py # MockAdapter(开发期使用) ├── lianruan_adapter.py # 联软适配器 ├── huorong_adapter.py # 火绒适配器 ├── atrust_adapter.py # aTrust适配器 ├── ehr_adapter.py # eHR适配器 └── service.py # ExternalSystemService 统一门面 ``` --- ## 九、实施路径 | 阶段 | 内容 | 依赖 | |------|------|------| | Step 1 | base.py + config.py + mock.py + service.py + cache.py | 无,立即可做 | | Step 2 | huorong_adapter.py | 凭证现在可拿 | | Step 3 | lianruan_adapter.py | 凭证明天可拿 | | Step 4 | atrust_adapter.py | 凭证约一周 | | Step 5 | ehr_adapter.py | 待对接HR团队 | --- ## 十、与项目阶段的对应关系 | 项目阶段 | Adapter用途 | 对接系统 | |---------|------------|---------| | 阶段一(1C) | 不使用 — MVP只跑会话管理 | 无 | | 阶段二(2B) | 联软终端查询 + 火绒安全状态 | 联软+火绒 | | 阶段二(2C) | 火绒漏洞/病毒/隔离 | 火绒 | | 阶段三(3B) | aTrust VPN数据 + AI混合排查 | aTrust | | 阶段三(3C) | eHR员工信息 + 标注体系 | eHR |