Files
wecom_it_smart_desk/backend/app/integrations/ehr.py
T

77 lines
3.1 KiB
Python
Raw Normal View History

# =============================================================================
# 企微IT智能服务台 — 阶段5 自动化 北森 EHR 客户端(静态映射兜底)
# =============================================================================
# 说明:当联软(主映射源)无法解析员工→终端时,使用北森 EHR 提供的
# 员工-部门-资产静态映射作为兜底。
#
# 认证:北森开放 API 通常使用 App Key + App SecretBearer 或签名)。
# 此处用占位实现:AUTOMATION_EHR_BASE_URL / AUTOMATION_EHR_API_KEY。
# 具体签名方式以真实环境文档为准,结构上可被单测 mock。
#
# 接口设计为占位骨架:方法签名完整、异常可捕获、出入参可被 ActionLog 记录,
# 本地无真实密钥/环境时仅结构正确,可被单元测试 mock。
# =============================================================================
from __future__ import annotations
import logging
from typing import Any, Dict, Optional
from app.config import settings
from app.integrations.base import BaseClient, BaseClientError
logger = logging.getLogger(__name__)
class EhrClient(BaseClient):
"""北森 EHR 客户端(静态映射兜底)。"""
system_name = "ehr"
def __init__(self, api_key: str, base_url: str, timeout=None, audit=None):
super().__init__(base_url=base_url, timeout=timeout, audit=audit)
self.api_key = api_key
def _headers(self) -> Dict[str, str]:
return {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}
async def get_employee_profile(self, employee_id: str) -> Dict[str, Any]:
"""查询员工档案(部门、岗位、资产编号等),作为映射兜底。"""
return await self.request(
"GET",
f"/api/v1/employees/{employee_id}",
headers=self._headers(),
event="ehr.get_employee_profile",
)
async def get_terminal_by_employee(self, employee_id: str) -> Optional[Dict[str, Any]]:
"""根据员工查兜底终端信息。
北森通常只给资产编号/部门,真正的终端 IP 仍需联软;
此处返回 hint(如 last_known_hostname / asset_no),供 mapping_resolver 合并。
"""
try:
profile = await self.get_employee_profile(employee_id)
return {
"employee_id": employee_id,
"department": profile.get("department", ""),
"asset_no": profile.get("asset_no", ""),
"terminal_hint": profile.get("last_known_hostname", ""),
"source": "ehr",
}
except BaseClientError as e:
logger.warning(f"EHR 映射兜底失败 employee={employee_id}: {e}")
return None
async def get_ehr_client(audit=None) -> Optional[EhrClient]:
"""从 settings 构建 EHR 客户端;未配置返回 None。"""
base_url = settings.automation_ehr_base_url
api_key = settings.automation_ehr_api_key
if not base_url or not api_key:
return None
return EhrClient(api_key=api_key, base_url=base_url, audit=audit)