增强钉钉用户详情字段解析
This commit is contained in:
parent
ca4c0e2d58
commit
0eecb9dc3c
@ -1,6 +1,7 @@
|
|||||||
"""Server-side DingTalk auth-code exchange and local account upsert."""
|
"""Server-side DingTalk auth-code exchange and local account upsert."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import time
|
import time
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ TOKEN_URL = "https://oapi.dingtalk.com/gettoken"
|
|||||||
USERINFO_URL = "https://oapi.dingtalk.com/topapi/v2/user/getuserinfo"
|
USERINFO_URL = "https://oapi.dingtalk.com/topapi/v2/user/getuserinfo"
|
||||||
USERDETAIL_URL = "https://oapi.dingtalk.com/topapi/v2/user/get"
|
USERDETAIL_URL = "https://oapi.dingtalk.com/topapi/v2/user/get"
|
||||||
DEPARTMENT_DETAIL_URL = "https://oapi.dingtalk.com/topapi/v2/department/get"
|
DEPARTMENT_DETAIL_URL = "https://oapi.dingtalk.com/topapi/v2/department/get"
|
||||||
|
logger = logging.getLogger("dieya_risk_prediction.dingtalk")
|
||||||
|
|
||||||
|
|
||||||
class DingTalkServiceError(AppException):
|
class DingTalkServiceError(AppException):
|
||||||
@ -122,12 +124,24 @@ class DingTalkAuthService:
|
|||||||
)
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
data = response.json()
|
||||||
except httpx.HTTPError:
|
except httpx.HTTPError as exc:
|
||||||
|
logger.warning("钉钉用户详情请求失败 user_id=%s error=%s", user_id, exc.__class__.__name__)
|
||||||
return {"userid": user_id}
|
return {"userid": user_id}
|
||||||
|
if data.get("errcode") not in (None, 0, "0"):
|
||||||
|
logger.warning(
|
||||||
|
"钉钉用户详情接口返回错误 user_id=%s errcode=%s errmsg=%s",
|
||||||
|
user_id,
|
||||||
|
data.get("errcode"),
|
||||||
|
data.get("errmsg") or data.get("message"),
|
||||||
|
)
|
||||||
detail = data.get("result") or data.get("user") or {"userid": user_id}
|
detail = data.get("result") or data.get("user") or {"userid": user_id}
|
||||||
if not isinstance(detail, dict):
|
if not isinstance(detail, dict):
|
||||||
detail = {"userid": user_id}
|
detail = {"userid": user_id}
|
||||||
detail.setdefault("userid", user_id)
|
detail.setdefault("userid", user_id)
|
||||||
|
# Some DingTalk responses wrap the actual user object one level deeper.
|
||||||
|
nested = detail.get("user") or detail.get("data")
|
||||||
|
if isinstance(nested, dict):
|
||||||
|
detail = {**detail, **nested}
|
||||||
dept_ids = self._department_ids(detail)
|
dept_ids = self._department_ids(detail)
|
||||||
if dept_ids and not self._department_name(detail):
|
if dept_ids and not self._department_name(detail):
|
||||||
dept_name = await self._department_name_from_id(access_token, dept_ids[0])
|
dept_name = await self._department_name_from_id(access_token, dept_ids[0])
|
||||||
@ -155,12 +169,12 @@ class DingTalkAuthService:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _department_name(detail: dict) -> str | None:
|
def _department_name(detail: dict) -> str | None:
|
||||||
value = detail.get("dept_name") or detail.get("deptName") or detail.get("department_name")
|
value = detail.get("dept_name") or detail.get("deptName") or detail.get("department_name") or detail.get("departmentName")
|
||||||
return str(value).strip() if value else None
|
return str(value).strip() if value else None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _department_ids(detail: dict) -> list[int]:
|
def _department_ids(detail: dict) -> list[int]:
|
||||||
raw = (detail.get("dept_id_list") or detail.get("deptIdList") or detail.get("department_ids") or detail.get("dept_id") or detail.get("main_dept_id") or detail.get("mainDeptId") or [])
|
raw = (detail.get("dept_id_list") or detail.get("deptIdList") or detail.get("dept_id_list_str") or detail.get("department_ids") or detail.get("dept_id") or detail.get("main_dept_id") or detail.get("mainDeptId") or [])
|
||||||
if not isinstance(raw, list):
|
if not isinstance(raw, list):
|
||||||
raw = [raw]
|
raw = [raw]
|
||||||
result: list[int] = []
|
result: list[int] = []
|
||||||
@ -173,7 +187,7 @@ class DingTalkAuthService:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _display_name(detail: dict, fallback: str) -> str:
|
def _display_name(detail: dict, fallback: str) -> str:
|
||||||
value = detail.get("name") or detail.get("nick") or detail.get("nickname") or detail.get("real_name") or detail.get("display_name") or fallback
|
value = detail.get("name") or detail.get("nick") or detail.get("nickname") or detail.get("real_name") or detail.get("display_name") or detail.get("user_name") or detail.get("userName") or fallback
|
||||||
return str(value).strip()[:50] or fallback
|
return str(value).strip()[:50] or fallback
|
||||||
|
|
||||||
async def _upsert_user(self, user_id: str, detail: dict) -> UserAccount:
|
async def _upsert_user(self, user_id: str, detail: dict) -> UserAccount:
|
||||||
@ -200,9 +214,8 @@ class DingTalkAuthService:
|
|||||||
user.dingtalk_avatar = str(detail.get("avatar") or user.dingtalk_avatar or "")[:512] or None
|
user.dingtalk_avatar = str(detail.get("avatar") or user.dingtalk_avatar or "")[:512] or None
|
||||||
user.dingtalk_job_number = str(detail.get("job_number") or user.dingtalk_job_number or "")[:64] or None
|
user.dingtalk_job_number = str(detail.get("job_number") or user.dingtalk_job_number or "")[:64] or None
|
||||||
user.dingtalk_title = str(detail.get("title") or user.dingtalk_title or "")[:128] or None
|
user.dingtalk_title = str(detail.get("title") or user.dingtalk_title or "")[:128] or None
|
||||||
dept_id = detail.get("main_dept_id") or detail.get("dept_id") or user.dingtalk_dept_id
|
dept_ids = self._department_ids(detail)
|
||||||
if isinstance(dept_id, list):
|
dept_id = dept_ids[0] if dept_ids else user.dingtalk_dept_id
|
||||||
dept_id = dept_id[0] if dept_id else None
|
|
||||||
try:
|
try:
|
||||||
user.dingtalk_dept_id = int(dept_id) if dept_id is not None else None
|
user.dingtalk_dept_id = int(dept_id) if dept_id is not None else None
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
|
|||||||
@ -21,3 +21,10 @@ def test_dingtalk_detail_fields_are_normalized() -> None:
|
|||||||
assert DingTalkAuthService._display_name(detail, "fallback") == "张三"
|
assert DingTalkAuthService._display_name(detail, "fallback") == "张三"
|
||||||
assert DingTalkAuthService._department_ids(detail) == [123, 456]
|
assert DingTalkAuthService._department_ids(detail) == [123, 456]
|
||||||
assert DingTalkAuthService._is_dingtalk_manager(detail)
|
assert DingTalkAuthService._is_dingtalk_manager(detail)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dingtalk_nested_detail_fields_are_supported() -> None:
|
||||||
|
detail = {"user": {"userName": "李四", "deptIdList": [789]}}
|
||||||
|
nested = detail["user"]
|
||||||
|
assert DingTalkAuthService._display_name(nested, "fallback") == "李四"
|
||||||
|
assert DingTalkAuthService._department_ids(nested) == [789]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user