dev #1
@ -1,6 +1,7 @@
|
||||
"""Server-side DingTalk auth-code exchange and local account upsert."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
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"
|
||||
USERDETAIL_URL = "https://oapi.dingtalk.com/topapi/v2/user/get"
|
||||
DEPARTMENT_DETAIL_URL = "https://oapi.dingtalk.com/topapi/v2/department/get"
|
||||
logger = logging.getLogger("dieya_risk_prediction.dingtalk")
|
||||
|
||||
|
||||
class DingTalkServiceError(AppException):
|
||||
@ -122,12 +124,24 @@ class DingTalkAuthService:
|
||||
)
|
||||
response.raise_for_status()
|
||||
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}
|
||||
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}
|
||||
if not isinstance(detail, dict):
|
||||
detail = {"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)
|
||||
if dept_ids and not self._department_name(detail):
|
||||
dept_name = await self._department_name_from_id(access_token, dept_ids[0])
|
||||
@ -155,12 +169,12 @@ class DingTalkAuthService:
|
||||
|
||||
@staticmethod
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
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):
|
||||
raw = [raw]
|
||||
result: list[int] = []
|
||||
@ -173,7 +187,7 @@ class DingTalkAuthService:
|
||||
|
||||
@staticmethod
|
||||
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
|
||||
|
||||
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_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
|
||||
dept_id = detail.get("main_dept_id") or detail.get("dept_id") or user.dingtalk_dept_id
|
||||
if isinstance(dept_id, list):
|
||||
dept_id = dept_id[0] if dept_id else None
|
||||
dept_ids = self._department_ids(detail)
|
||||
dept_id = dept_ids[0] if dept_ids else user.dingtalk_dept_id
|
||||
try:
|
||||
user.dingtalk_dept_id = int(dept_id) if dept_id is not None else None
|
||||
except (TypeError, ValueError):
|
||||
|
||||
@ -21,3 +21,10 @@ def test_dingtalk_detail_fields_are_normalized() -> None:
|
||||
assert DingTalkAuthService._display_name(detail, "fallback") == "张三"
|
||||
assert DingTalkAuthService._department_ids(detail) == [123, 456]
|
||||
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