sdk/dingdingSdk/dingding_attendance.py

169 lines
6.0 KiB
Python
Raw Normal View History

2025-08-19 10:20:23 +00:00
import concurrent.futures
import sys
import requests
from typing import List, Set, Dict, Tuple
class DingTalkDocsFetcher:
@staticmethod
def get_access_token(app_key: str, app_secret: str) -> str:
url = "https://oapi.dingtalk.com/gettoken"
params = {
"appkey": app_key,
"appsecret": app_secret
}
resp = requests.get(url, params=params)
data = resp.json()
if data.get("errcode") == 0:
print("✅ AccessToken 获取成功:", data.get("access_token"))
return data.get("access_token")
print("❌ 获取 AccessToken 失败:", data)
return ""
@staticmethod
def get_sub_departments(token: str, dept_id: int = None) -> List[Dict]:
url = "https://oapi.dingtalk.com/topapi/v2/department/listsub"
params = {"access_token": token}
payload = {}
if dept_id is not None:
payload["dept_id"] = dept_id
resp = requests.post(url, params=params, json=payload)
data = resp.json()
if data.get("errcode") == 0:
return data.get("result", [])
else:
print(f"❌ 获取部门列表失败 dept_id={dept_id}: {data}")
return []
@staticmethod
def get_users_by_dept(token: str, dept_id: int) -> List[str]:
url = "https://oapi.dingtalk.com/topapi/user/listid"
params = {"access_token": token}
payload = {"dept_id": dept_id}
resp = requests.post(url, params=params, json=payload)
data = resp.json()
if data.get("errcode") == 0:
return data.get("result", {}).get("userid_list", [])
else:
print(f"❌ 获取部门用户失败 dept_id={dept_id}: {data}")
return []
@staticmethod
def get_all_departments(token: str) -> List[Dict]:
all_depts = []
def recurse(dept_id: int = None, parent_id: int = 0):
depts = DingTalkDocsFetcher.get_sub_departments(token, dept_id)
for d in depts:
dept_info = {
"dept_id": d["dept_id"],
"name": d["name"],
"parent_id": parent_id # 这里用调用时的 parent_id
}
all_depts.append(dept_info)
recurse(d["dept_id"], parent_id=d["dept_id"]) # 递归时传当前dept_id作为父部门ID
recurse()
return all_depts
@staticmethod
def get_user_detail(token: str, userid: str) -> dict:
url = "https://oapi.dingtalk.com/topapi/v2/user/get"
params = {"access_token": token}
payload = {"userid": userid}
resp = requests.post(url, params=params, json=payload)
data = resp.json()
if data.get("errcode") == 0:
return data.get("result", {})
else:
print(f"获取用户详情失败: {data}")
return None
@staticmethod
def get_all_user_ids(token: str, departments: List[Dict]) -> Set[str]:
"""
根据部门列表获取所有部门用户ID集合去重
"""
user_ids = set()
for dept in departments:
dept_id = dept["dept_id"]
ids = DingTalkDocsFetcher.get_users_by_dept(token, dept_id)
user_ids.update(ids)
return user_ids
@staticmethod
def get_attendance_records(token: str, user_ids: List[str], date_from: str, date_to: str):
url = f"https://oapi.dingtalk.com/attendance/listRecord?access_token={token}"
payload = {
"checkDateFrom": date_from,
"checkDateTo": date_to,
"userIds": user_ids,
"isI18n": "false"
}
resp = requests.post(url, json=payload)
r = resp.json()
if r.get("errcode") == 0:
return r.get("recordresult", [])
else:
print(f"❌ 获取打卡记录失败: {r}")
return []
@staticmethod
def main(args: List[str]) -> None:
app_key = "dinguetojbaxvvhzpk3d"
app_secret = "lMFqns_ceLIcXvfLL8GKfa3ZiPKHcaZq0VbGtJXJlDuK8AEJ2WV3-PN8zv61ajm3"
token = DingTalkDocsFetcher.get_access_token(app_key, app_secret)
if not token:
return
print("开始获取所有部门...")
departments = DingTalkDocsFetcher.get_all_departments(token)
print(f"✅ 获取部门总数: {len(departments)}")
print("开始获取所有部门用户ID...")
user_ids_set = DingTalkDocsFetcher.get_all_user_ids(token, departments)
user_ids = list(user_ids_set)
print(f"✅ 获取用户总数: {len(user_ids)}")
date_from = "2025-08-10 12:00:00"
date_to = "2025-08-12 12:00:00"
print("开始获取打卡记录...")
records = DingTalkDocsFetcher.get_attendance_records(token, user_ids, date_from, date_to)
print(f"✅ 打卡记录数: {len(records)}")
user_cache = {}
def fetch_user_detail(userid):
if userid in user_cache:
return userid, user_cache[userid]
detail = DingTalkDocsFetcher.get_user_detail(token, userid)
user_cache[userid] = detail
return userid, detail
user_ids_in_records = list({r.get("userId") for r in records if r.get("userId")})
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(fetch_user_detail, uid) for uid in user_ids_in_records]
for future in concurrent.futures.as_completed(futures):
uid, detail = future.result()
user_cache[uid] = detail
enhanced_records = []
for r in records:
userid = r.get("userId")
user_detail = user_cache.get(userid)
if user_detail:
r["user_name"] = user_detail.get("name", "")
else:
r["user_name"] = ""
enhanced_records.append(r)
for r in enhanced_records:
print(r)
if __name__ == '__main__':
DingTalkDocsFetcher.main(sys.argv[1:])