Skip to content

Integrate with DingTalk to Synchronize Organizational Structure and Members

Overview

In enterprises, instant messaging tools like DingTalk are commonly used for daily office communication. Such tools typically provide functions like organizational structure management, user management, notifications, and to-do items. The following are the steps to integrate with DingTalk and synchronize organizational structure and members to Informat.

Implementation Steps

  • Before synchronizing the DingTalk organizational structure, you need to complete the integration of "Third-Party Integration >> DingTalk".
  • Enable address book permissions. In the application - Basic Information - Permission Management, you need to add the following permissions:
    • Address book personal information reading permission
    • Member information reading permission
    • Address book department information reading permission
    • Personal information such as email
    • Enterprise employee phone number information
    • Personal phone number information

Implementation Code

javascript
const defaultRole = "normal"; //Team role authorized to new users
const defaultPassword = "123456"; //Password set for new users, username is phone number
const syncMembers = true; //Whether to sync members
let rootDeptId = null;

export function syncDept() {
  var deptList = getDeptList();
  console.log("deptList---------------->", JSON.stringify(deptList));
  rootDeptId = (
    informat.dept.queryDeptList({
      pageSize: 1,
      filter: {
        conditionList: [{ fieldId: "parentId", opt: "isnull" }],
      },
    })[0] || {}
  ).id;
  console.log("rootDeptId", rootDeptId);
  if (deptList != null) {
    deptList.forEach((item) => {
      var dept_id = `${item.dept_id}`;
      var dept = informat.dept.getDept(dept_id);
      console.log("dept_id", dept_id);
      if (dept_id === "1") return;
      if (dept == null) {
        console.log("Add department", item);
        informat.dept.addDept({
          id: dept_id,
          name: item.name,
          remark: item.name,
          parentId: item.parent_id === 1 ? rootDeptId : `${item.parent_id}`,
        });
      } else {
        console.log("Edit department", item);
        informat.dept.updateDept({
          id: dept_id,
          name: item.name,
          parentId: item.parent_id === 1 ? rootDeptId : `${item.parent_id}`,
        });
      }
    });
  }
  //Sync members
  if (syncMembers) {
    syncMemberList([{ dept_id: 1 }].concat(deptList));
  }
}

function syncMemberList(deptList) {
  if (Array.isArray(deptList)) {
    deptList.forEach((dept) => {
      const memberList = getMemberList(dept.dept_id);
      memberList.forEach((member) => {
        var memberList = informat.company.queryCompanyMemberList({
          pageIndex: 1,
          pageSize: 1,
          filter: {
            conditionList: [
              {
                fieldId: "dingtalkUserId",
                opt: "eq",
                value: member.userid,
              },
            ],
          },
        });
        var departmentList = getDeptIdListWithDingtalkDeptIdList(member.dept_id_list);
        if (memberList.length == 0) {
          //Add new
          var accountId = addAccount(member);
          var roleList = defaultRole == null ? [] : [defaultRole];
          console.log("Add team member", member, departmentList);
          informat.company.addCompanyMember(accountId, departmentList, roleList);
          informat.company.updateCompanyMember({
            id: accountId,
            dingtalkUserId: member.userid,
          });
        } else {
          //Edit
          console.log("Edit team member", member, departmentList);
          var member = memberList[0];
          informat.company.updateCompanyMember({
            id: member.id,
            departmentList: departmentList,
          });
        }
      });
    });
  } else {
    console.log("syncMemberList, deptList is not array. deptList: ", deptList);
  }
}

function getDeptIdListWithDingtalkDeptIdList(idList) {
  const rootIndex = idList.indexOf(1);
  if (rootIndex > -1) {
    idList.splice(rootIndex, 1, rootDeptId);
  }
  return idList.map((id) => `${id}`);
}

function getMemberList(dept_id) {
  var dingtalkAccessToken = informat.app.dingtalkAccessToken();
  var url = `https://oapi.dingtalk.com/topapi/user/listid?access_token=${dingtalkAccessToken}`;
  var rs = httpPost(
    url,
    JSON.stringify({
      dept_id: dept_id,
    })
  );
  const memberList = [];
  if (rs.errcode === 0) {
    rs.result.userid_list.forEach((id) => {
      memberList.push(getMemeberInfo(id));
    });
  }
  return memberList;
}

function getMemeberInfo(user_id) {
  var dingtalkAccessToken = informat.app.dingtalkAccessToken();
  var url = `https://oapi.dingtalk.com/topapi/v2/user/get?access_token=${dingtalkAccessToken}`;
  var rs = httpPost(
    url,
    JSON.stringify({
      language: "zh_CN",
      userid: user_id,
    })
  );
  return rs.result;
}

function getDeptList(dept_id, result = []) {
  var dingtalkAccessToken = informat.app.dingtalkAccessToken();
  var url = `https://oapi.dingtalk.com/topapi/v2/department/listsub?access_token=${dingtalkAccessToken}`;
  //Passing the corresponding department id will identify all sub-department ids under that department
  const data = {
    language: "zh_CN",
  };
  if (dept_id) {
    data.dept_id = dept_id;
  }
  var rs = httpPost(url, JSON.stringify(data));
  if (rs.errcode) return result;
  const deptList = rs.result;
  deptList.forEach((item) => {
    // var detail = getDeptInfo(id);
    result.push(item);
    getDeptList(item.dept_id, result);
  });
  return result;
}

function getDeptInfo(dept_id) {
  var dingtalkAccessToken = informat.app.dingtalkAccessToken();
  var url = `https://oapi.dingtalk.com/topapi/v2/department/get?access_token=${dingtalkAccessToken}`;
  var rs = httpPost(
    url,
    JSON.stringify({
      language: "zh_CN",
      dept_id: dept_id,
    })
  );
  return rs.result;
}

function addAccount(item) {
  var mobile = item.mobile;
  if (mobile == null) {
    throw new Error("Employee phone number cannot be empty. Please enable phone number viewing permission.");
  }
  if (mobile.length == 14) {
    mobile = mobile.substr(3);
  }
  var accountList = informat.system.queryAccountList({
    pageIndex: 1,
    pageSize: 1,
    filter: {
      conditionList: [
        {
          fieldId: "mobileNo",
          opt: "eq",
          value: mobile,
        },
      ],
    },
  });
  var accountId = null;
  if (accountList.length == 0) {
    //Account does not exist
    console.log("Add account", item);
    accountId = informat.system.addAccount({
      oid: item.userid,
      name: item.name,
      avatar: "pic15.png",
      userName: mobile,
      mobileNo: mobile,
      email: item.email,
      password: defaultPassword,
    });
  } else {
    var oldAccount = accountList[0];
    accountId = oldAccount.id;
    console.log("Edit account", item, oldAccount);
    informat.system.updateAccount({
      id: oldAccount.id,
      name: item.name,
      userName: mobile,
      mobileNo: mobile,
    });
  }
  return accountId;
}

function httpPost(url, body) {
  console.log("url----------------->", url);
  console.log("body----------------->", body);
  var resp = informat.http.request({
    headers: {
      "Content-Type": "application/json;chartset=utf-8",
    },
    method: "POST",
    timeout: 30000,
    url: url,
    body: body,
  });
  if (200 != resp.statusCode()) {
    console.log("url", url);
    console.log("resp statusCode", resp.statusCode());
    console.log("resp body", resp.body());
    throw new Error("Failed to initiate HTTP request. Please contact the developer.");
  }
  console.log("url------->", url);
  console.log("resp----------->", resp.body());
  return JSON.parse(resp.body());
}

Common Errors