首选要先理解钉钉的几个应用,因为应用不同,对应的接口也不一样,能够调用的权限也不一样,所以首先不要盲目的开发,看文档。
也就是这四个。总体来说,如果你只是想在钉钉上开个门,用来进到自己的H5应用,就直接选微应用,然后如果你是要上架到应用市场去就选第三方企业应用。如果选择小程序就比较麻烦,针对ISV接口会不同,第三方企业应用的前端接口更加丰富。咱们主要讲微应用。微应用的前端API也有发Ding功能
开发:
首先你要下载钉钉sdk下载地址:https://ding-doc.dingtalk.com/doc#/faquestions/vzbp02
你可以自己用钉钉账号创建一个企业,创建部门、员工、用作测试。然后创建一个企业内部微应用。
获取Token方法:
/// <summary> /// 获取token /// </summary> /// <returns></returns> public static string GetAccessToken() { var client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken"); OapiGettokenRequest tokenRequest = new OapiGettokenRequest(); tokenRequest.Corpid = Global.AppKey; tokenRequest.Corpsecret = Global.AppSecret; tokenRequest.SetHttpMethod("get"); Api.Response.OapiGettokenResponse retString = client.Execute(tokenRequest); ////根据CorpId和CorpSecret发送get请求获得access_token //string url = string.Format("https://oapi.dingtalk.com/gettoken?corpid={0}&corpsecret={1}", Global.CorpId, Global.CorpSecret); ////返回内容 //string retString = HttpMethod.HttpGet(url); //TokenEntity.TokenData obj = retString.ToObject<TokenEntity.TokenData>(); if (retString.Errmsg == "ok") { string accessToken = retString.AccessToken; Access_Token = accessToken; //DingTalk.Log.Info($"钉钉 获取TOKNE:{accessToken}"); return accessToken; } else { string message = "钉钉 获取TOKEN失败\r\n{" + retString.Errmsg + "}\r\nCORPID:{" + Global.AppKey + "}\r\nCORPSECRET:{" + Global.AppSecret + "}"; return message; } }
Global方法:
public class Global { private static string _AppKey = string.Empty; /// <summary> /// CorpId /// </summary> public static string AppKey { get { if (_AppKey.IsNullOrEmpty()) { try { _AppKey = ConfigurationManager.AppSettings["AppKey"]; } catch { } } return _AppKey; } } public static string _AppId = string.Empty; /// <summary> /// CorpSecret /// </summary> public static string AppId { get { if (_AppId.IsNullOrEmpty()) { try { _AppSecret = ConfigurationManager.AppSettings["AppId"]; } catch { } } return _AppSecret; } } public static string _AppSecret = string.Empty; /// <summary> /// CorpSecret /// </summary> public static string AppSecret { get { if (_AppSecret.IsNullOrEmpty()) { try { _AppSecret = ConfigurationManager.AppSettings["AppSecret"]; } catch { } } return _AppSecret; } } private static long _AgentId = 0; /// <summary> /// 微应用agentId /// </summary> public static long AgentId { get { if (_AgentId.Equals(0)) { try { _AgentId = Convert.ToInt64(System.Configuration.ConfigurationManager.AppSettings["AgentId"]); } catch { } } return _AgentId; } } private static string _suiteKey = string.Empty; public static string suiteKey { get { if (_suiteKey.IsNullOrEmpty()) { try { _suiteKey = ConfigurationManager.AppSettings["suiteKey"]; } catch { } } return _suiteKey; } } private static string _suiteSecret = string.Empty; public static string suiteSecret { get { if (_suiteSecret.IsNullOrEmpty()) { try { _suiteSecret = ConfigurationManager.AppSettings["suiteSecret"]; } catch { } } return _suiteSecret; } } private static long _suiteId = 0; public static long suiteId { get { if (_suiteId.Equals(0)) { try { _suiteId = Convert.ToInt64(System.Configuration.ConfigurationManager.AppSettings["suiteId"]); } catch { } } return _suiteId; } } /// <summary> /// <!--加密Token--> /// </summary> private static string _Token = string.Empty; public static string Token { get { if (_Token.IsNullOrEmpty()) { try { _Token = ConfigurationManager.AppSettings["Token"]; } catch { } } return _Token; } } /// <summary> /// <!--数据加密秘钥--> /// </summary> private static string _SecretKey = string.Empty; public static string SecretKey { get { if (_SecretKey.IsNullOrEmpty()) { try { _SecretKey = ConfigurationManager.AppSettings["SecretKey"]; } catch { } } return _SecretKey; } } /// <summary> /// 企业ID /// </summary> private static string _CorpId = string.Empty; public static string CorpId { get { if (_CorpId.IsNullOrEmpty()) { try { _CorpId = ConfigurationManager.AppSettings["CorpId"]; } catch { } } return _CorpId; } } }
企业内部应用主要用到:AppKey和AppSecret以及AgentId
获取部门用户列表,注意,先根据Token权限获取部门ID,然后获取该部门下面的用户列表,另外获取部门那个接口是获取一级部门,如果有二级部门就查不出来,需要递归去查询,然后根据部门ID集合去查询用户列表,另外建议自己写个实体类加上注释,用来反序列化存储钉钉返回的数据
public class UserUtil { public ArrayList al = new ArrayList(); public ArrayList al2 = new ArrayList(); /// <summary> /// 根据部门ID获取该部门下面的用户列表 /// </summary> /// <returns></returns> public List<userlist> GetUserInfoListByDepartment() { try { //获取access_token string access_token = TokenUtil.GetAccessToken(); if (string.IsNullOrEmpty(access_token)) return null; DepartmentResult departmentResult = DepartmentUtil.GetDepartmentInfo(); if (departmentResult.errcode != 0 || departmentResult.department.Count <= 0) return null; List<userlist> userList = new List<userlist>(); foreach (var item in departmentResult.department) { //根据部门ID获取部门人员详情 string url = "https://oapi.dingtalk.com/user/list?access_token=" + access_token + "&department_id=" + item.id; var client = new DefaultDingTalkClient(url); OapiUserGetDeptMemberRequest req = new OapiUserGetDeptMemberRequest(); req.SetHttpMethod("get"); var retString = client.Execute(req); //Log.Info($"获取钉钉用户列表结果:{retString.Body}"); var userResult = JsonConvert.DeserializeObject<UserResult>(retString.Body); if (userResult.errcode == 0 && userResult.userlist != null && userResult.userlist.Count > 0) { userList.AddRange(userResult.userlist);//添加人员信息到集合中 } } return userList; } catch (Exception ex) { //Log.Error("获取钉钉用户列表异常"); //Log.Error(ex); return new List<userlist>(); } } /// <summary> /// 先根据Token权限获取部门ID,然后获取该部门下面的用户列表 /// </summary> /// <returns></returns> public List<userlist> GetUserInfoListByDepartmentQx() { try { //获取access_token string access_token = TokenUtil.GetAccessToken(); if (string.IsNullOrEmpty(access_token)) return null; DepartmentPowerEntity departmentpowerentity = DepartmentUtil.GetPowerDepartmentInfo(); if (departmentpowerentity.errcode != 0) return null; List<userlist> userList = new List<userlist>(); foreach (var item in departmentpowerentity.auth_org_scopes.authed_dept) { //根据部门ID获取部门人员详情 string url = "https://oapi.dingtalk.com/user/list?access_token=" + access_token + "&department_id=" + item; var client = new DefaultDingTalkClient(url); OapiUserGetDeptMemberRequest req = new OapiUserGetDeptMemberRequest(); req.SetHttpMethod("get"); var retString = client.Execute(req); //Log.Info($"获取钉钉用户列表结果:{retString.Body}"); var userResult = JsonConvert.DeserializeObject<UserResult>(retString.Body); if (userResult.errcode == 0 && userResult.userlist != null && userResult.userlist.Count > 0) { userList.AddRange(userResult.userlist);//添加人员信息到集合中 } } return userList; } catch (Exception ex) { //Log.Error("获取钉钉用户列表异常"); //Log.Error(ex); return new List<userlist>(); } } /// <summary> /// 递归查询所有的用户 /// </summary> /// <returns></returns> public List<userlist> GetAllUserInfoListByDepartment() { try { //获取access_token string access_token = TokenUtil.GetAccessToken(); if (string.IsNullOrEmpty(access_token)) return null; DepartmentPowerEntity departmentpowerentity = DepartmentUtil.GetPowerDepartmentInfo(); if (departmentpowerentity.errcode != 0) return null; for (int i = 0; i < departmentpowerentity.auth_org_scopes.authed_dept.Length; i++) { al.Add(departmentpowerentity.auth_org_scopes.authed_dept[i]); } RecursionDepartment(al); List<userlist> userList = new List<userlist>(); foreach (var m in al) { al2.Add(m); } foreach (var n in al2) { //根据部门ID获取部门人员详情 string url = "https://oapi.dingtalk.com/user/list?access_token=" + access_token + "&department_id=" + n; var client = new DefaultDingTalkClient(url); OapiUserGetDeptMemberRequest req = new OapiUserGetDeptMemberRequest(); req.SetHttpMethod("get"); var retString = client.Execute(req); //Log.Info($"获取钉钉用户列表结果:{retString.Body}"); var userResult = JsonConvert.DeserializeObject<UserResult>(retString.Body); if (userResult.errcode == 0 && userResult.userlist != null && userResult.userlist.Count > 0) { userList.AddRange(userResult.userlist);//添加人员信息到集合中 } } return userList; } catch (Exception ex) { //Log.Error("获取钉钉用户列表异常"); //Log.Error(ex); return new List<userlist>(); } } /// <summary> ///体验组织————递归查询所有的用户 /// </summary> /// <returns></returns> public List<userlist> ISV_GetAllUserInfoListByDepartment() { try { //获取access_token string access_token = TokenUtil.ISV_GetAccessToken(); if (string.IsNullOrEmpty(access_token)) return null; DepartmentPowerEntity departmentpowerentity = DepartmentUtil.ISV_GetPowerDepartmentInfo(); if (departmentpowerentity.errcode != 0) return null; for (int i = 0; i < departmentpowerentity.auth_org_scopes.authed_dept.Length; i++) { al.Add(departmentpowerentity.auth_org_scopes.authed_dept[i]); } RecursionDepartment(al); List<userlist> userList = new List<userlist>(); foreach (var m in al) { al2.Add(m); } foreach (var n in al2) { //根据部门ID获取部门人员详情 string url = "https://oapi.dingtalk.com/user/list?access_token=" + access_token + "&department_id=" + n; var client = new DefaultDingTalkClient(url); OapiUserGetDeptMemberRequest req = new OapiUserGetDeptMemberRequest(); req.SetHttpMethod("get"); var retString = client.Execute(req); //SimpleLog.Log.Info($"获取钉钉用户列表结果:{retString.Body}"); var userResult = JsonConvert.DeserializeObject<UserResult>(retString.Body); if (userResult.errcode == 0 && userResult.userlist != null && userResult.userlist.Count > 0) { userList.AddRange(userResult.userlist);//添加人员信息到集合中 } } return userList; } catch (Exception ex) { //Log.Error("获取钉钉用户列表异常"); //Log.Error(ex); return new List<userlist>(); } } public void RecursionDepartment(ArrayList ids) { ArrayList list = new ArrayList(); //第一次循环所有权限部门Id foreach (var n in ids) { //al第一次循环存有权限部门下面的所有子部门 int[] kk = DepartmentUtil.GetAllAecondaryDepartmentInfo(n.ToString()).sub_dept_id_list; if (kk==null || kk.Length <= 0) continue; for (int i = 0; i < kk.Length; i++) { al2.Add(kk[i]); //循环刷新存放子部门的部门Id集合 list.Add(kk[i]); } } if (list.Count == 0) { return; } RecursionDepartment(list); } /// <summary> /// 根据OA用户的手机号在钉钉 /// </summary> /// <param name="mobile"></param> /// <returns></returns> public static userlist GetSingleUser(string mobile) { var userlist = UserList; userlist userInfo = new Model.userlist(); foreach (var item in userlist) { if (item.mobile.Equals(mobile)) { userInfo = item; break; } } return userInfo; } /// <summary> /// 将用户手机号分别映射成该用户在钉钉里面对应的USERID,便于用户后面发送短信 /// </summary> /// <param name="mobilelist">将用户手机号集合</param> /// <returns></returns> public static string MapperUserIDToDingding(List<string> mobilelist) { var dingdinguser = UserList; var list = new List<string>(); foreach (var oa in mobilelist) { foreach (var item in dingdinguser) { if (oa.Equals(item.mobile)) { list.Add(item.userid); continue; } } } return string.Join(",", from u in list select u); } #region 专用于存储钉钉用户 private static object LockObject = new object(); private static List<userlist> _userList = null; /// <summary> /// 用户保存所有用户信息 /// </summary> public static List<userlist> UserList { get { if (_userList.IsNull()) { lock (LockObject) { if (_userList.IsNull()) { //DingTalkLog.Log.Info($"开始获取钉钉用户列表。{_userList?.Count}"); _userList = new UserUtil().GetUserInfoListByDepartment(); // DingTalkLog.Log.Info("获取钉钉用户列表。{"++"_userList?.Count}"); } } } return _userList; } } #endregion }
审批接口:
创建审批实例去看审批接口。说一下注册审批回调接口。上代码
注册:
protected void Button1_Click(object sender, EventArgs e) { string _token = TokenUtil.GetAccessToken(); //获取Token DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/call_back/register_call_back"); OapiCallBackRegisterCallBackRequest request = new OapiCallBackRegisterCallBackRequest(); request.Url = "https://***"; //回调Url request.AesKey = ""; //数据加密密钥。用于回调数据的加密,长度固定为43个字符,从a-z, A-Z, 0-9共62个字符中选取,您可以随机生成 request.Token = ""; //加解密需要用到的token List<string> _arr = new List<string>(); _arr.Add("bpms_task_change"); //需要监听的事件类型,具体填写方法看这里 https://open-doc.dingtalk.com/microapp/serverapi2/skn8ld _arr.Add("bpms_instance_change"); request.CallBackTag = _arr; OapiCallBackRegisterCallBackResponse response = client.Execute(request, _token); string _str = response.Body; Response.Write(_str); }
当你随便注册了几个监听事件,想再加几个,发现提示已被注册,怎么办呢? https://ding-doc.dingtalk.com/doc#/serverapi2/pwz3r5
回调的接口代码,这个要上的话代码有点多,而且我官方的那个加密解密方法dll在我这报了错,我就自己copy过来写了,大概根据官方的那个JAVA去写,差不多,只是比如.setToken直接去掉Set,.Token=“Token”就可以了。
大概调用接口应该熟悉了,另外建议写个日志方法,在关键的地方加上日志,以便查错。这里发一个钉钉的Log方法吧
public static void WriteLog(string strMemo) { string directoryPath = HttpContext.Current.Server.MapPath(@"Logs"); string fileName = directoryPath + @"\log" + DateTime.Today.ToString("yyyyMMdd") + ".txt"; if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath); StreamWriter sr = null; try { if (!File.Exists(fileName)) { sr = File.CreateText(fileName); } else { sr = File.AppendText(fileName); } sr.WriteLine(DateTime.Now + ": " + strMemo); } catch (Exception ex) { } finally { if (sr != null) sr.Close(); } }