fix: use requester name (CN) instead of email for SD API - fallback to AI when name fails

This commit is contained in:
Денис Кривоченко
2026-07-29 01:12:48 +07:00
parent ddbcb11685
commit 84a03f79ee
+43 -17
View File
@@ -106,35 +106,46 @@ def get_ad_user_info(user_id: str) -> dict | None:
return None return None
async def _send_sd_api_request(requester_email: str, subject: str, description: str, city: str) -> str | None: async def _send_sd_api_request(
requester_cn: str,
requester_email: str,
subject: str,
description: str,
city: str
) -> str | None:
""" """
Автономная прямая отправка HTTP-запроса в Service Desk API v3. Автономная прямая отправка HTTP-запроса в Service Desk API v3.
Автоматически ищет ID группы и техника по именам из config. Умеет привязывать пользователя по ФИО, Email или Fallback.
""" """
# Если запрашивает системный ИИ — ищем по ИМЕНИ # 1. Формируем структуру заявителя (приоритет: ФИО -> Email -> ИИ)
if requester_email == DEFAULT_REQUESTER or requester_email == "ai@sibcem.ru": if requester_cn and requester_cn != "N/A":
requester_payload = {"name": "Искусственный Интеллект"} requester_payload = {"name": requester_cn}
else: elif requester_email and requester_email not in (DEFAULT_REQUESTER, "ai@sibcem.ru"):
requester_payload = {"email_id": requester_email} requester_payload = {"email_id": requester_email}
else:
requester_payload = {"name": "Искусственный Интеллект"}
# Ищем ID группы # 2. Поиск ID группы (пробуем /groups и /group)
group_id = None group_id = None
try: try:
url_groups = f"{SD_URL.rstrip('/')}/api/v3/groups"
headers = {"TECHNICIAN_KEY": SD_TOKEN} headers = {"TECHNICIAN_KEY": SD_TOKEN}
async with httpx.AsyncClient(verify=False) as client: async with httpx.AsyncClient(verify=False) as client:
for endpoint in ["/api/v3/groups", "/api/v3/group"]:
url_groups = f"{SD_URL.rstrip('/')}{endpoint}"
resp = await client.get(url_groups, headers=headers, timeout=10.0) resp = await client.get(url_groups, headers=headers, timeout=10.0)
if resp.status_code == 200: if resp.status_code == 200:
groups = resp.json().get("groups", []) groups = resp.json().get("groups", []) or resp.json().get("group", [])
for g in groups: for g in groups:
if g.get("name") == SD_INSTRUCT_GROUP_NAME: if g.get("name") == SD_INSTRUCT_GROUP_NAME:
group_id = g.get("id") group_id = g.get("id")
logger.info(f"SD group found: name={SD_INSTRUCT_GROUP_NAME}, id={group_id}") logger.info(f"SD group found: name={SD_INSTRUCT_GROUP_NAME}, id={group_id}")
break break
if group_id:
break
except Exception as e: except Exception as e:
logger.warning(f"Failed to fetch SD groups: {e}") logger.warning(f"Failed to fetch SD groups: {e}")
# Ищем ID техника # 3. Поиск ID техника
tech_id = None tech_id = None
try: try:
url_techs = f"{SD_URL.rstrip('/')}/api/v3/technicians" url_techs = f"{SD_URL.rstrip('/')}/api/v3/technicians"
@@ -151,6 +162,7 @@ async def _send_sd_api_request(requester_email: str, subject: str, description:
except Exception as e: except Exception as e:
logger.warning(f"Failed to fetch SD technicians: {e}") logger.warning(f"Failed to fetch SD technicians: {e}")
# 4. Формируем тело запроса
payload = { payload = {
"request": { "request": {
"subject": subject, "subject": subject,
@@ -161,7 +173,6 @@ async def _send_sd_api_request(requester_email: str, subject: str, description:
} }
} }
} }
# Добавляем группу и исполнителя если найдены
if group_id: if group_id:
payload["request"]["group"] = {"id": group_id} payload["request"]["group"] = {"id": group_id}
if tech_id: if tech_id:
@@ -178,7 +189,22 @@ async def _send_sd_api_request(requester_email: str, subject: str, description:
res_json = response.json() res_json = response.json()
ticket_id = res_json.get("request", {}).get("id") ticket_id = res_json.get("request", {}).get("id")
return str(ticket_id) if ticket_id else None return str(ticket_id) if ticket_id else None
else:
# Fallback: Если по ФИО не нашло, пробуем через DEFAULT_REQUESTER
elif response.status_code == 400 and requester_payload != {"name": "Искусственный Интеллект"}:
logger.warning(
f"ServiceDesk не смог привязать requester={requester_payload}. "
f"Повторяем запрос от имени системного заявителя..."
)
payload["request"]["requester"] = {"name": "Искусственный Интеллект"}
payload["request"]["description"] = f"[Пользователь: {requester_cn}]\n\n{description}"
data_fb = {"input_data": json.dumps(payload)}
resp_fb = await client.post(url, headers=headers, data=data_fb, timeout=15.0)
if resp_fb.status_code in (200, 201):
ticket_id = resp_fb.json().get("request", {}).get("id")
return str(ticket_id) if ticket_id else None
logger.error(f"Service Desk API Error ({response.status_code}): {response.text}") logger.error(f"Service Desk API Error ({response.status_code}): {response.text}")
return None return None
@@ -214,9 +240,9 @@ async def create_sd_ticket_for_trueconf(user_id: str, cn: str) -> str | None:
""" """
subject = "Запрос доступа к Trueconf" subject = "Запрос доступа к Trueconf"
# Получаем email из AD # Получаем данные пользователя из AD
ad_info = get_ad_user_info(user_id) ad_info = get_ad_user_info(user_id)
requester_email = ad_info.get("mail") or DEFAULT_REQUESTER requester_email = (ad_info.get("mail") if ad_info else None) or DEFAULT_REQUESTER
city = ad_info.get("city", "Кемерово") if ad_info else "Кемерово" city = ad_info.get("city", "Кемерово") if ad_info else "Кемерово"
description = ( description = (
@@ -225,10 +251,10 @@ async def create_sd_ticket_for_trueconf(user_id: str, cn: str) -> str | None:
) )
try: try:
logger.info(f"Calling SD API directly: requester={requester_email}, city={city}") logger.info(f"Calling SD API directly: cn='{cn}', email='{requester_email}', city='{city}'")
ticket_id = await _send_sd_api_request(requester_email, subject, description, city) ticket_id = await _send_sd_api_request(cn, requester_email, subject, description, city)
if ticket_id: if ticket_id:
logger.info(f"SD ticket created for {cn}: #{ticket_id} (requester={requester_email})") logger.info(f"SD ticket created for {cn}: #{ticket_id}")
else: else:
logger.error(f"Failed to create SD ticket for {cn}") logger.error(f"Failed to create SD ticket for {cn}")
return ticket_id return ticket_id