├── README.md ├── Auth ├── IAuthHelper.cs └── AuthHelper.cs ├── Message ├── IMessage.cs ├── OAMessage │ ├── OAMessage.cs │ └── OAMessageContent.cs ├── LinkMessage │ ├── LinkMessage.cs │ └── LinkMessageContent.cs ├── FileMessage │ ├── FileMessage.cs │ └── FileMessageContent.cs ├── TextMessage │ ├── TextMessage.cs │ └── TextMessageContent.cs ├── ImageMessage │ ├── ImageMessage.cs │ └── ImageMessageContent.cs ├── VoiceMessage │ ├── VoiceMessage.cs │ └── VoiceMessageContent.cs ├── MarkdownMessage │ ├── MarkdownMessage.cs │ └── MarkdownMessageContent.cs ├── ActionCardMessage │ ├── ActionCardMessage.cs │ └── ActionCardMessageContent.cs ├── MessageSentResult.cs └── MessageHelper.cs ├── Util ├── CheckParameterHelper.cs └── HttpHelper.cs ├── DingDingException ├── DingDingApiResultException.cs └── DingDingApiException.cs ├── DingTalkSDK.sln ├── DingTalkSDK.csproj ├── Env.cs ├── Department ├── Department.cs └── DepartmentHelper.cs ├── User ├── User.cs └── UserHelper.cs └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # 钉钉服务端SDK 2 | https://open-doc.dingtalk.com/microapp/serverapi2 3 | 4 | nuget可以直接安装:Install-Package DingTalkSDK 5 | -------------------------------------------------------------------------------- /Auth/IAuthHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Auth 6 | { 7 | public interface IAuthHelper 8 | { 9 | public string getAccessToke(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Message/IMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message 6 | { 7 | public interface IMessage 8 | { 9 | public string msgtype { get;} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Util/CheckParameterHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DingTalkSDK.Util 8 | { 9 | public sealed class CheckParameterHelper 10 | { 11 | // private void CheckTokenNotNull 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Message/OAMessage/OAMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.OAMessage 6 | { 7 | /// 8 | /// OA消息 9 | /// 10 | public class OAMessage : IMessage 11 | { 12 | public string msgtype { get =>"oa";} 13 | public OAMessageContent oa { get; set; } 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Message/LinkMessage/LinkMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.LinkMessage 6 | { 7 | /// 8 | /// 链接消息 9 | /// 10 | public class LinkMessage : IMessage 11 | { 12 | public string msgtype { get => "link"; } 13 | public LinkMessageContent link { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Message/FileMessage/FileMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.FileMessage 6 | { 7 | /// 8 | /// 附件类消息 9 | /// 10 | public class FileMessage : IMessage 11 | { 12 | public string msgtype { get => "file"; } 13 | public FileMessageContent file { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Message/TextMessage/TextMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.TextMessage 6 | { 7 | /// 8 | /// 文字类消息 9 | /// 10 | public class TextMessage : IMessage 11 | { 12 | public string msgtype { get => "text"; } 13 | public TextMessageContent text { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Message/ImageMessage/ImageMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.ImageMessage 6 | { 7 | /// 8 | /// 图片类消息 9 | /// 10 | public class ImageMessage : IMessage 11 | { 12 | public string msgtype { get => "image"; } 13 | public ImageMessageContent image { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Message/VoiceMessage/VoiceMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.VoiceMessage 6 | { 7 | /// 8 | /// 语音类消息 9 | /// 10 | public class VoiceMessage : IMessage 11 | { 12 | public string msgtype { get => "voice"; } 13 | public VoiceMessageContent voice { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Message/TextMessage/TextMessageContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.TextMessage 6 | { 7 | /// 8 | /// 文字消息的内容 9 | /// 10 | public class TextMessageContent 11 | { 12 | /// 13 | /// 消息内容,建议500字符以内 14 | /// 15 | public string content { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Message/MarkdownMessage/MarkdownMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.MarkdownMessage 6 | { 7 | /// 8 | /// Markdown消息 9 | /// 10 | public class MarkdownMessage : IMessage 11 | { 12 | public string msgtype { get => "markdown"; } 13 | public MarkdownMessageContent markdown { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Message/FileMessage/FileMessageContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.FileMessage 6 | { 7 | /// 8 | /// 附件消息的内容 9 | /// 10 | public class FileMessageContent 11 | { 12 | /// 13 | /// 媒体文件id,引用的媒体文件最大10MB 14 | /// 15 | public string media_id { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Message/ActionCardMessage/ActionCardMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.ActionCardMessage 6 | { 7 | /// 8 | /// 卡片消息 9 | /// 10 | public class ActionCardMessage : IMessage 11 | { 12 | public string msgtype { get => "action_card"; } 13 | public ActionCardMessageContent action_card { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Message/ImageMessage/ImageMessageContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.ImageMessage 6 | { 7 | /// 8 | /// 图片类消息的内容 9 | /// 10 | public class ImageMessageContent 11 | { 12 | /// 13 | /// 媒体文件id,可以通过媒体文件接口上传图片获取。建议宽600像素 x 400像素,宽高比3 : 2 14 | /// 15 | public string media_id { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Message/VoiceMessage/VoiceMessageContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.VoiceMessage 6 | { 7 | /// 8 | /// 语音消息的内容 9 | /// 10 | public class VoiceMessageContent 11 | { 12 | /// 13 | /// 媒体文件id。2MB,播放长度不超过60s,AMR格式 14 | /// 15 | public string media_id { get; set; } 16 | /// 17 | /// 正整数,小于60,表示音频时长 18 | /// 19 | public int duration { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Message/MarkdownMessage/MarkdownMessageContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.MarkdownMessage 6 | { 7 | /// 8 | /// markdown消息内容 9 | /// 10 | public class MarkdownMessageContent 11 | { 12 | /// 13 | /// 首屏会话透出的展示内容 14 | /// 15 | public string title { get; set; } 16 | /// 17 | /// markdown格式的消息,建议500字符以内 18 | /// 19 | public string text { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Message/MessageSentResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message 6 | { 7 | /// 8 | /// 消息发送后的返回结果 9 | /// 10 | public class MessageSentResult 11 | { 12 | /// 13 | /// 返回码 14 | /// 15 | public string errcode { get; set; } 16 | /// 17 | /// 对返回码的文本描述内容 18 | /// 19 | public string errmsg { get; set; } 20 | /// 21 | /// 创建的发送任务id 22 | /// 23 | public string task_id { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /DingDingException/DingDingApiResultException.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DingTalkSDK.DingDingException 9 | { 10 | public class DingDingApiResultException : DingDingApiException 11 | { 12 | public static readonly int ERR_RESULT_RESOLUTION = -2; 13 | 14 | public DingDingApiResultException(String field) : base(ERR_RESULT_RESOLUTION, "Cannot resolve field " + field + " from oapi resonpse") 15 | { 16 | } 17 | public DingDingApiResultException(int errCode,string errMsg):base(errCode,errMsg) 18 | { 19 | 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DingDingException/DingDingApiException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DingTalkSDK.DingDingException 8 | { 9 | public class DingDingApiException:Exception 10 | { 11 | public int ErrCode { get; set; } 12 | public string ErrMsg { get; set; } 13 | public DingDingApiException(int errCode, String errMsg) : base("error code: " + errCode + ", error message: " + errMsg) 14 | { 15 | ErrCode = errCode; 16 | ErrMsg = errMsg; 17 | } 18 | 19 | public void printStackTrace() 20 | { 21 | Console.WriteLine(string.Format("errCode={0} errMsg={1}", ErrCode, ErrMsg)); 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Message/LinkMessage/LinkMessageContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.LinkMessage 6 | { 7 | /// 8 | /// 链接消息的内容 9 | /// 10 | public class LinkMessageContent 11 | { 12 | /// 13 | /// 消息点击链接地址,当发送消息为小程序时支持小程序跳转链接 14 | /// 15 | public string messageUrl { get; set; } 16 | /// 17 | /// 图片地址 18 | /// 19 | public string picUrl { get; set; } 20 | /// 21 | /// 消息标题,建议100字符以内 22 | /// 23 | public string title { get; set; } 24 | /// 25 | /// 消息描述,建议500字符以内 26 | /// 27 | public string text { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DingTalkSDK.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29613.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DingTalkSDK", "DingTalkSDK.csproj", "{DA70F4CC-C8E6-4E11-AD0B-598E7675D65B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {DA70F4CC-C8E6-4E11-AD0B-598E7675D65B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {DA70F4CC-C8E6-4E11-AD0B-598E7675D65B}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {DA70F4CC-C8E6-4E11-AD0B-598E7675D65B}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {DA70F4CC-C8E6-4E11-AD0B-598E7675D65B}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {692B1BB7-BAAC-48E9-82FB-444A2BAFBD7D} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Auth/AuthHelper.cs: -------------------------------------------------------------------------------- 1 | using DingTalkSDK.DingDingException; 2 | using DingTalkSDK.Util; 3 | using Microsoft.Extensions.Caching.Memory; 4 | using MongoDB.Bson; 5 | using System; 6 | 7 | namespace DingTalkSDK.Auth 8 | { 9 | public class AuthHelper 10 | { 11 | public static String getAccessToken() 12 | { 13 | string accessonToken; 14 | if (String.IsNullOrWhiteSpace(Env.CorpId)) 15 | { 16 | throw new ArgumentException("请先初始化Env中的CorpId"); 17 | } 18 | if (String.IsNullOrWhiteSpace(Env.CorpSecret)) 19 | { 20 | throw new ArgumentException("请先初始化Env中的CorpSecret"); 21 | } 22 | 23 | String url = Env.OAPI_HOST + "/gettoken?" + 24 | "corpid=" + Env.CorpId + "&corpsecret=" + Env.CorpSecret; 25 | BsonDocument response = HttpHelper.httpGet(url); 26 | if (response.Contains("access_token")) 27 | { 28 | accessonToken = response["access_token"].ToString(); 29 | } 30 | else 31 | { 32 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 33 | } 34 | 35 | return accessonToken; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /DingTalkSDK.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | true 6 | 0.9.5 7 | 钉钉的服务端SDK 8 | https://github.com/joychin/DingDingSDK 9 | https://github.com/joychin/DingDingSDK 10 | Github 11 | https://raw.githubusercontent.com/joychin/DingDingSDK/master/LICENSE 12 | https://raw.githubusercontent.com/joychin/DingDingSDK/master/LICENSE 13 | 14 | false 15 | 0.9.5.0 16 | 0.9.5.0 17 | 18 | 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Env.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using DingTalkSDK.Auth; 7 | 8 | namespace DingTalkSDK 9 | { 10 | /// 11 | /// 钉钉的环境配置 12 | /// 13 | public class Env 14 | { 15 | public const string OAPI_HOST = "https://oapi.dingtalk.com"; 16 | 17 | /// 18 | /// 企业Id 19 | /// 20 | public static string CorpId { get; set; } 21 | 22 | /// 23 | /// 企业应用的凭证密钥 24 | /// 25 | public static string CorpSecret { get; set; } 26 | /// 27 | /// Token 更新日期 28 | /// 29 | private static DateTime Accesson_token_UpdateTime 30 | { 31 | get;set; 32 | } 33 | 34 | /// 35 | /// Access_Token 36 | /// 37 | public static string Access_token 38 | { 39 | get 40 | { 41 | //钉钉规定7200秒过期,这里我们用6900秒过期 42 | 43 | if ( 44 | Accesson_token_UpdateTime == DateTime.MinValue 45 | || Accesson_token_UpdateTime == null 46 | || (DateTime.Now - Accesson_token_UpdateTime).TotalSeconds > 6900 47 | ) 48 | { 49 | var accessonToken = AuthHelper.getAccessToken(); 50 | Env.Accesson_token_UpdateTime = DateTime.Now; 51 | return accessonToken; 52 | } 53 | else 54 | { 55 | return Access_token; 56 | } 57 | } 58 | set => Access_token = value; 59 | } 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Message/ActionCardMessage/ActionCardMessageContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.ActionCardMessage 6 | { 7 | public class ActionCardMessageContent_btn_json_list 8 | { 9 | /// 10 | /// 使用独立跳转ActionCard样式时的按钮的标题,最长20个字符 11 | /// 12 | public string title { get; set; } 13 | /// 14 | /// 消息点击链接地址,当发送消息为小程序时支持小程序跳转链接,最长500个字符 15 | /// 16 | public string action_url { get; set; } 17 | } 18 | /// 19 | /// 卡片消息内容 20 | /// 卡片消息支持整体跳转ActionCard样式和独立跳转ActionCard样式: 21 | ///(1)整体跳转ActionCard样式,支持一个点击Action,需要传入参数 single_title和 single_url; 22 | ///(2)独立跳转ActionCard样式,支持多个点击Action,需要传入参数 btn_orientation 和 btn_json_list; 23 | /// 24 | public class ActionCardMessageContent 25 | { 26 | /// 27 | /// 透出到会话列表和通知的文案,最长64个字符 28 | /// 29 | public string title { get; set; } 30 | /// 31 | /// 消息内容,支持markdown,语法参考标准markdown语法。建议1000个字符以内 32 | /// 33 | public string markdown { get; set; } 34 | /// 35 | /// 使用整体跳转ActionCard样式时的标题,必须与single_url同时设置,最长20个字符 36 | /// 37 | public string single_title { get; set; } 38 | /// 39 | /// 消息点击链接地址,当发送消息为小程序时支持小程序跳转链接,最长500个字符 40 | /// 41 | public string single_url { get; set; } 42 | /// 43 | /// 使用独立跳转ActionCard样式时的按钮排列方式,竖直排列(0),横向排列(1);必须与btn_json_list同时设置 44 | /// 45 | public string btn_orientation { get; set; } 46 | /// 47 | /// 使用独立跳转ActionCard样式时的按钮列表;必须与btn_orientation同时设置 48 | /// 49 | public ActionCardMessageContent_btn_json_list btn_json_list { get; set; } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Message/MessageHelper.cs: -------------------------------------------------------------------------------- 1 | using DingTalkSDK.DingDingException; 2 | using DingTalkSDK.Util; 3 | using MongoDB.Bson; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace DingTalkSDK.Message 9 | { 10 | /// 11 | /// 消息通知 12 | /// 13 | public class MessageHelper 14 | { 15 | /// 16 | /// 发送工作通知消息 17 | /// 18 | /// accessToken 19 | /// 应用agentId 20 | /// 是否发送给企业全部用户 21 | /// 消息内容,消息类型和样例参考“消息类型与数据格式”。最长不超过2048个字节 22 | /// 接收者的用户userid列表,最大列表长度:100 23 | /// 接收者的部门id列表,最大列表长度:20, 接收者是部门id下(包括子部门下)的所有用户 24 | /// 消息task_id 25 | public static string SendCorpconversation(String accessToken, string agent_id,Boolean to_all_user, IMessage msg,string userid_list,string dept_id_list) 26 | { 27 | if (String.IsNullOrWhiteSpace(accessToken)) 28 | { 29 | throw new ArgumentException("accessToken 不能为空"); 30 | } 31 | if (String.IsNullOrWhiteSpace(agent_id)) 32 | { 33 | throw new ArgumentException("应用agentId 不能为空"); 34 | } 35 | 36 | String url = Env.OAPI_HOST + "/topapi/message/corpconversation/asyncsend_v2?" + 37 | "access_token=" + accessToken; 38 | #region 创建参数 39 | BsonDocument args = new BsonDocument(); 40 | args["agent_id"] = agent_id; 41 | args["userid_list"] = userid_list; 42 | args["dept_id_list"] = dept_id_list; 43 | args["to_all_user"] = to_all_user; 44 | args["msg"] = msg; 45 | #endregion 46 | BsonDocument response = HttpHelper.httpPost(url, args); 47 | if (HttpHelper.CheckResponseOk(response)) 48 | { 49 | return response["task_id"].ToString(); 50 | } 51 | else 52 | { 53 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Department/Department.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DingTalkSDK.Department 8 | { 9 | public class Department 10 | { 11 | public String lang = "zh_CN"; 12 | /// 13 | /// 部门id,创建部门的时候不用填写 14 | /// 15 | public long id; 16 | /// 17 | /// 部门名称。长度限制为1~64个字符。不允许包含字符‘-’‘,’以及‘,’。 18 | /// 19 | public String name; 20 | /// 21 | /// 父部门id。根部门id为1,如果创建的是根部门,则不传这个参数 22 | /// 23 | public String parentid; 24 | /// 25 | /// 在父部门中的次序值,order值小的排序靠前 26 | /// 27 | public string order; 28 | /// 29 | /// 是否创建一个关联此部门的企业群,默认为false 30 | /// 31 | public bool createDeptGroup = false; 32 | 33 | /// 34 | /// 是否隐藏部门,true表示隐藏,false表示显示,默认为false 35 | /// 36 | public bool deptHiding = false; 37 | 38 | /// 39 | /// 可以查看指定隐藏部门的其他部门列表,如果部门隐藏,则此值生效,取值为其他的部门id组成的字符串,使用“|”符号进行分割。总数不能超过200 40 | /// 41 | public string deptPerimits; 42 | /// 43 | /// 可以查看指定隐藏部门的其他人员列表,如果部门隐藏,则此值生效,取值为其他的人员userid组成的的字符串,使用“|”符号进行分割。总数不能超过200 44 | /// 45 | public string userPerimits; 46 | 47 | /// 48 | /// 是否本部门的员工仅可见员工自己,为true时,本部门员工默认只能看到员工自己,默认为false 49 | /// 50 | public bool outerDept = false; 51 | /// 52 | /// 本部门的员工仅可见员工自己为true时,可以配置额外可见部门,值为部门id组成的的字符串,使用“|”符号进行分割。总数不能超过200 53 | /// 54 | public string outerPermitDepts; 55 | /// 56 | /// 本部门的员工仅可见员工自己为true时,可以配置额外可见人员,值为userid组成的的字符串,使用“|”符号进行分割。总数不能超过200 57 | /// 58 | public string outerPermitUsers; 59 | /// 60 | /// 部门标识字段,开发者可用该字段来唯一标识一个部门,并与钉钉外部通讯录里的部门做映射 61 | /// 62 | public string sourceIdentifier; 63 | public override string ToString() 64 | { 65 | return "Department[id:" + id + " Name:" + name + " Parentid:" + parentid + "]"; 66 | } 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /User/User.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DingTalkSDK.User 9 | { 10 | public class User 11 | { 12 | /// 13 | /// 员工唯一标识ID(不可修改),企业内必须唯一。长度为1~64个字符,如果不传,服务器将自动生成一个userid 14 | /// 15 | public String userid; 16 | /// 17 | /// 成员名称。长度为1~64个字符 18 | /// 19 | public String name; 20 | /// 21 | /// 在对应的部门中的排序, Map结构的json字符串, key是部门的Id, value是人员在这个部门的排序值 22 | /// 23 | public BsonDocument orderInDepts; 24 | /// 25 | /// 数组类型,数组里面值为整型,成员所属部门id列表 26 | /// 27 | public List department; 28 | /// 29 | /// 职位信息。长度为0~64个字符 30 | /// 31 | public String position; 32 | /// 33 | /// 手机号码,企业内必须唯一,不可重复 34 | /// 35 | public String mobile; 36 | /// 37 | /// 电话号码 38 | /// 39 | public String tel; 40 | /// 41 | /// 邮箱 42 | /// 43 | public String email; 44 | /// 45 | /// 工作地 46 | /// 47 | public String workPlace; 48 | /// 49 | /// 员工工号。对应显示到OA后台和客户端个人资料的工号栏目。长度为0~64个字符 50 | /// 51 | public String jobnumber; 52 | /// 53 | /// 企业邮箱 54 | /// 55 | public String orgEmail; 56 | /// 57 | /// 是否号码隐藏, true表示隐藏, false表示不隐藏。隐藏手机号后,手机号在个人资料页隐藏,但仍可对其发DING、发起钉钉免费商务电话。 58 | /// 59 | public bool isHide; 60 | /// 61 | /// 是否高管模式,true表示是,false表示不是。开启后,手机号码对所有员工隐藏。普通员工无法对其发DING、发起钉钉免费商务电话。高管之间不受影响。 62 | /// 63 | public bool isSenior; 64 | /// 65 | /// 扩展属性,可以设置多种属性(但手机上最多只能显示10个扩展属性,具体显示哪些属性,请到OA管理后台->设置->通讯录信息设置和OA管理后台->设置->手机端显示信息设置) 66 | /// 67 | public BsonDocument extattr; 68 | 69 | public User() 70 | { 71 | } 72 | 73 | public User(String userid, String name) 74 | { 75 | this.userid = userid; 76 | this.name = name; 77 | } 78 | public override string ToString() 79 | { 80 | //List users; 81 | return "User[userid:" + userid + ", name:" + name + ", department:" + department + 82 | ", position:" + position + ", mobile:" + mobile + ", email:" + email + 83 | ", extattr:" + extattr + "]"; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Message/OAMessage/OAMessageContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DingTalkSDK.Message.OAMessage 6 | { 7 | public class OAMessageContent_head 8 | { 9 | /// 10 | /// 消息头部的背景颜色。长度限制为8个英文字符,其中前2为表示透明度,后6位表示颜色值。不要添加0x 11 | /// 12 | public string bgcolor { get;set;} 13 | /// 14 | /// 消息的头部标题 (向普通会话发送时有效,向企业会话发送时会被替换为微应用的名字),长度限制为最多10个字符 15 | /// 16 | public string text { get; set; } 17 | } 18 | public class OAMessageContent_body_form 19 | { 20 | /// 21 | /// 消息体的关键字 22 | /// 23 | public string key { get; set; } 24 | /// 25 | /// 消息体的关键字对应的值 26 | /// 27 | public string value { get; set; } 28 | } 29 | public class OAMessageContent_body_rich 30 | { 31 | /// 32 | /// 单行富文本信息的数目 33 | /// 34 | public string num { get; set; } 35 | /// 36 | /// 单行富文本信息的单位 37 | /// 38 | public string unit { get; set; } 39 | } 40 | public class OAMessageContent_body 41 | { 42 | /// 43 | /// 消息体的标题,建议50个字符以内 44 | /// 45 | public string title { get; set; } 46 | /// 47 | /// 消息体的表单,最多显示6个,超过会被隐藏 48 | /// 49 | public List form { get; set; } 50 | /// 51 | /// 单行富文本信息 52 | /// 53 | public OAMessageContent_body_rich rich { get; set; } 54 | /// 55 | /// 消息体的内容,最多显示3行 56 | /// 57 | public string content { get; set; } 58 | /// 59 | /// 消息体中的图片,支持图片资源@mediaId 60 | /// 61 | public string image { get; set; } 62 | /// 63 | /// 自定义的附件数目。此数字仅供显示,钉钉不作验证 64 | /// 65 | public string file_count { get; set; } 66 | /// 67 | /// 自定义的作者名字 68 | /// 69 | public string author { get; set; } 70 | } 71 | /// 72 | /// OA类消息的内容 73 | /// 74 | public class OAMessageContent 75 | { 76 | /// 77 | /// 消息点击链接地址,当发送消息为小程序时支持小程序跳转链接 78 | /// 79 | public string message_url { get;set;} 80 | /// 81 | /// PC端点击消息时跳转到的地址 82 | /// 83 | public string pc_message_url { get; set; } 84 | /// 85 | /// 消息头部内容 86 | /// 87 | public OAMessageContent_head head { get; set; } 88 | /// 89 | /// 消息体 90 | /// 91 | public OAMessageContent_body body { get; set; } 92 | 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Util/HttpHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | using MongoDB.Bson; 5 | using Newtonsoft.Json.Bson; 6 | 7 | namespace DingTalkSDK.Util 8 | { 9 | internal class HttpHelper 10 | { 11 | private static CookieContainer cookie = new CookieContainer(); 12 | 13 | /// 14 | /// 使用GET从url获取数据 15 | /// 16 | /// 获取数据的url 17 | /// 附带的data,可以不传,直接在url里面拼接参数,如果传了,url里面的参数就会被忽略 18 | /// 格式化的BsonDocument 19 | public static BsonDocument httpGet(String url, BsonDocument data = null) 20 | { 21 | WebClient client = new WebClient(); 22 | client.Headers.Add(HttpRequestHeader.ContentType, "application/json"); 23 | if (data != null) 24 | { 25 | foreach (var item in data) 26 | { 27 | client.QueryString.Add(item.Name, item.Value.ToString()); 28 | } 29 | } 30 | var respostData = client.DownloadData(url); 31 | return BsonDocument.Parse(System.Text.UTF8Encoding.UTF8.GetString(respostData)); 32 | } 33 | 34 | /// 35 | /// 使用GET从url获取数据 36 | /// 37 | /// 获取数据的url 38 | /// 附带的data,可以不传,直接在url里面拼接参数,如果传了,url里面的参数就会被忽略 39 | /// 格式化的BsonDocument 40 | public static async Task httpGetAsync(String url, BsonDocument data = null) 41 | { 42 | WebClient client = new WebClient(); 43 | client.Headers.Add(HttpRequestHeader.ContentType, "application/json"); 44 | if (data != null) 45 | { 46 | foreach (var item in data) 47 | { 48 | client.QueryString.Add(item.Name, item.Value.ToString()); 49 | } 50 | } 51 | 52 | var respostData = await client.DownloadDataTaskAsync(url); 53 | return BsonDocument.Parse(System.Text.UTF8Encoding.UTF8.GetString(respostData)); 54 | } 55 | 56 | /// 57 | /// 使用POST从url获取数据 58 | /// 59 | /// 获取数据的url 60 | /// 附带的data 61 | /// 格式化的BsonDocument 62 | public static BsonDocument httpPost(String url, BsonDocument data) 63 | { 64 | WebClient client = new WebClient(); 65 | client.Headers.Add(HttpRequestHeader.ContentType, "application/json"); 66 | var postdata = System.Text.UTF8Encoding.UTF8.GetBytes((data.ToString())); 67 | var respostData = client.UploadData(url, postdata); 68 | return BsonDocument.Parse(System.Text.UTF8Encoding.UTF8.GetString(respostData)); 69 | } 70 | 71 | /// 72 | /// 使用POST从url获取数据 73 | /// 74 | /// 获取数据的url 75 | /// 附带的data 76 | /// 格式化的BsonDocument 77 | public static async Task httpPostAsync(String url, BsonDocument data) 78 | { 79 | WebClient client = new WebClient(); 80 | client.Headers.Add(HttpRequestHeader.ContentType, "application/json"); 81 | var postdata = System.Text.UTF8Encoding.UTF8.GetBytes((data.ToString())); 82 | var respostData = await client.UploadDataTaskAsync(url, postdata); 83 | return BsonDocument.Parse(System.Text.UTF8Encoding.UTF8.GetString(respostData)); 84 | } 85 | /// 86 | /// 检查请求是否执行成功 87 | /// 88 | /// 89 | /// 90 | public static bool CheckResponseOk(BsonDocument response) 91 | { 92 | if (response.Contains("errcode") && response["errcode"].ToInt64() == 0) 93 | { 94 | return true; 95 | } 96 | else 97 | { 98 | return false; 99 | } 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | 56 | # StyleCop 57 | StyleCopReport.xml 58 | 59 | # Files built by Visual Studio 60 | *_i.c 61 | *_p.c 62 | *_i.h 63 | *.ilk 64 | *.meta 65 | *.obj 66 | *.iobj 67 | *.pch 68 | *.pdb 69 | *.ipdb 70 | *.pgc 71 | *.pgd 72 | *.rsp 73 | *.sbr 74 | *.tlb 75 | *.tli 76 | *.tlh 77 | *.tmp 78 | *.tmp_proj 79 | *.log 80 | *.vspscc 81 | *.vssscc 82 | .builds 83 | *.pidb 84 | *.svclog 85 | *.scc 86 | 87 | # Chutzpah Test files 88 | _Chutzpah* 89 | 90 | # Visual C++ cache files 91 | ipch/ 92 | *.aps 93 | *.ncb 94 | *.opendb 95 | *.opensdf 96 | *.sdf 97 | *.cachefile 98 | *.VC.db 99 | *.VC.VC.opendb 100 | 101 | # Visual Studio profiler 102 | *.psess 103 | *.vsp 104 | *.vspx 105 | *.sap 106 | 107 | # Visual Studio Trace Files 108 | *.e2e 109 | 110 | # TFS 2012 Local Workspace 111 | $tf/ 112 | 113 | # Guidance Automation Toolkit 114 | *.gpState 115 | 116 | # ReSharper is a .NET coding add-in 117 | _ReSharper*/ 118 | *.[Rr]e[Ss]harper 119 | *.DotSettings.user 120 | 121 | # JustCode is a .NET coding add-in 122 | .JustCode 123 | 124 | # TeamCity is a build add-in 125 | _TeamCity* 126 | 127 | # DotCover is a Code Coverage Tool 128 | *.dotCover 129 | 130 | # AxoCover is a Code Coverage Tool 131 | .axoCover/* 132 | !.axoCover/settings.json 133 | 134 | # Visual Studio code coverage results 135 | *.coverage 136 | *.coveragexml 137 | 138 | # NCrunch 139 | _NCrunch_* 140 | .*crunch*.local.xml 141 | nCrunchTemp_* 142 | 143 | # MightyMoose 144 | *.mm.* 145 | AutoTest.Net/ 146 | 147 | # Web workbench (sass) 148 | .sass-cache/ 149 | 150 | # Installshield output folder 151 | [Ee]xpress/ 152 | 153 | # DocProject is a documentation generator add-in 154 | DocProject/buildhelp/ 155 | DocProject/Help/*.HxT 156 | DocProject/Help/*.HxC 157 | DocProject/Help/*.hhc 158 | DocProject/Help/*.hhk 159 | DocProject/Help/*.hhp 160 | DocProject/Help/Html2 161 | DocProject/Help/html 162 | 163 | # Click-Once directory 164 | publish/ 165 | 166 | # Publish Web Output 167 | *.[Pp]ublish.xml 168 | *.azurePubxml 169 | # Note: Comment the next line if you want to checkin your web deploy settings, 170 | # but database connection strings (with potential passwords) will be unencrypted 171 | *.pubxml 172 | *.publishproj 173 | 174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 175 | # checkin your Azure Web App publish settings, but sensitive information contained 176 | # in these scripts will be unencrypted 177 | PublishScripts/ 178 | 179 | # NuGet Packages 180 | *.nupkg 181 | # The packages folder can be ignored because of Package Restore 182 | **/[Pp]ackages/* 183 | # except build/, which is used as an MSBuild target. 184 | !**/[Pp]ackages/build/ 185 | # Uncomment if necessary however generally it will be regenerated when needed 186 | #!**/[Pp]ackages/repositories.config 187 | # NuGet v3's project.json files produces more ignorable files 188 | *.nuget.props 189 | *.nuget.targets 190 | 191 | # Microsoft Azure Build Output 192 | csx/ 193 | *.build.csdef 194 | 195 | # Microsoft Azure Emulator 196 | ecf/ 197 | rcf/ 198 | 199 | # Windows Store app package directories and files 200 | AppPackages/ 201 | BundleArtifacts/ 202 | Package.StoreAssociation.xml 203 | _pkginfo.txt 204 | *.appx 205 | 206 | # Visual Studio cache files 207 | # files ending in .cache can be ignored 208 | *.[Cc]ache 209 | # but keep track of directories ending in .cache 210 | !*.[Cc]ache/ 211 | 212 | # Others 213 | ClientBin/ 214 | ~$* 215 | *~ 216 | *.dbmdl 217 | *.dbproj.schemaview 218 | *.jfm 219 | *.pfx 220 | *.publishsettings 221 | orleans.codegen.cs 222 | 223 | # Including strong name files can present a security risk 224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 225 | #*.snk 226 | 227 | # Since there are multiple workflows, uncomment next line to ignore bower_components 228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 229 | #bower_components/ 230 | 231 | # RIA/Silverlight projects 232 | Generated_Code/ 233 | 234 | # Backup & report files from converting an old project file 235 | # to a newer Visual Studio version. Backup files are not needed, 236 | # because we have git ;-) 237 | _UpgradeReport_Files/ 238 | Backup*/ 239 | UpgradeLog*.XML 240 | UpgradeLog*.htm 241 | ServiceFabricBackup/ 242 | *.rptproj.bak 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | *.ndf 248 | 249 | # Business Intelligence projects 250 | *.rdl.data 251 | *.bim.layout 252 | *.bim_*.settings 253 | *.rptproj.rsuser 254 | 255 | # Microsoft Fakes 256 | FakesAssemblies/ 257 | 258 | # GhostDoc plugin setting file 259 | *.GhostDoc.xml 260 | 261 | # Node.js Tools for Visual Studio 262 | .ntvs_analysis.dat 263 | node_modules/ 264 | 265 | # Visual Studio 6 build log 266 | *.plg 267 | 268 | # Visual Studio 6 workspace options file 269 | *.opt 270 | 271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 272 | *.vbw 273 | 274 | # Visual Studio LightSwitch build output 275 | **/*.HTMLClient/GeneratedArtifacts 276 | **/*.DesktopClient/GeneratedArtifacts 277 | **/*.DesktopClient/ModelManifest.xml 278 | **/*.Server/GeneratedArtifacts 279 | **/*.Server/ModelManifest.xml 280 | _Pvt_Extensions 281 | 282 | # Paket dependency manager 283 | .paket/paket.exe 284 | paket-files/ 285 | 286 | # FAKE - F# Make 287 | .fake/ 288 | 289 | # JetBrains Rider 290 | .idea/ 291 | *.sln.iml 292 | 293 | # CodeRush 294 | .cr/ 295 | 296 | # Python Tools for Visual Studio (PTVS) 297 | __pycache__/ 298 | *.pyc 299 | 300 | # Cake - Uncomment if you are using it 301 | # tools/** 302 | # !tools/packages.config 303 | 304 | # Tabs Studio 305 | *.tss 306 | 307 | # Telerik's JustMock configuration file 308 | *.jmconfig 309 | 310 | # BizTalk build output 311 | *.btp.cs 312 | *.btm.cs 313 | *.odx.cs 314 | *.xsd.cs 315 | 316 | # OpenCover UI analysis results 317 | OpenCover/ 318 | 319 | # Azure Stream Analytics local run output 320 | ASALocalRun/ 321 | 322 | # MSBuild Binary and Structured Log 323 | *.binlog 324 | 325 | # NVidia Nsight GPU debugger configuration file 326 | *.nvuser 327 | 328 | # MFractors (Xamarin productivity tool) working folder 329 | .mfractor/ 330 | 331 | # Local History for Visual Studio 332 | .localhistory/ 333 | -------------------------------------------------------------------------------- /Department/DepartmentHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using DingTalkSDK.DingDingException; 5 | using DingTalkSDK.Util; 6 | using MongoDB.Bson; 7 | 8 | namespace DingTalkSDK.Department 9 | { 10 | public class DepartmentHelper 11 | { 12 | /// 13 | /// 创建部门 14 | /// 15 | /// token 16 | /// 部门名称 17 | /// 父部门名称 18 | /// 19 | /// 创建成功的部门ID 20 | public static long CreateDepartment(String accessToken, Department department) 21 | { 22 | 23 | if (String.IsNullOrWhiteSpace(accessToken)) 24 | { 25 | throw new ArgumentException("accessToken 不能为空"); 26 | } 27 | if (String.IsNullOrWhiteSpace(department.name)) 28 | { 29 | throw new ArgumentException("部门名称不能为空"); 30 | } 31 | if (String.IsNullOrWhiteSpace(department.parentid)) 32 | { 33 | throw new ArgumentException("父部门id不能为空"); 34 | } 35 | 36 | String url = Env.OAPI_HOST + "/department/create?" + 37 | "access_token=" + accessToken; 38 | 39 | #region 创建参数 40 | BsonDocument args = new BsonDocument(); 41 | args["name"] = department.name; 42 | args["parentid"] = department.parentid; 43 | if (!string.IsNullOrWhiteSpace(department.order)) 44 | { 45 | args["order"] = department.order; 46 | } 47 | args["createDeptGroup"] = department.createDeptGroup; 48 | args["deptHiding"] = department.deptHiding; 49 | if (!string.IsNullOrWhiteSpace(department.deptPerimits)) 50 | { 51 | args["deptPerimits"] = department.deptPerimits; 52 | } 53 | if (!string.IsNullOrWhiteSpace(department.userPerimits)) 54 | { 55 | args["userPerimits"] = department.userPerimits; 56 | } 57 | args["outerDept"] = department.outerDept; 58 | if (!string.IsNullOrWhiteSpace(department.outerPermitDepts)) 59 | { 60 | args["outerPermitDepts"] = department.outerPermitDepts; 61 | } 62 | if (!string.IsNullOrWhiteSpace(department.outerPermitUsers)) 63 | { 64 | args["outerPermitUsers"] = department.outerPermitUsers; 65 | } 66 | if (!string.IsNullOrWhiteSpace(department.sourceIdentifier)) 67 | { 68 | args["sourceIdentifier"] = department.sourceIdentifier; 69 | } 70 | #endregion 71 | 72 | BsonDocument response = HttpHelper.httpPost(url, args); 73 | if (HttpHelper.CheckResponseOk(response)) 74 | { 75 | return response["id"].ToInt64(); 76 | } 77 | else 78 | { 79 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 80 | } 81 | } 82 | /// 83 | /// 创建部门 84 | /// 85 | /// token 86 | /// 部门名称 87 | /// 父部门名称 88 | /// 89 | /// 创建成功的部门ID 90 | public static async Task CreateDepartmentAsync(String accessToken, Department department) 91 | { 92 | 93 | if (String.IsNullOrWhiteSpace(accessToken)) 94 | { 95 | throw new ArgumentException("accessToken 不能为空"); 96 | } 97 | if (String.IsNullOrWhiteSpace(department.name)) 98 | { 99 | throw new ArgumentException("部门名称不能为空"); 100 | } 101 | if (String.IsNullOrWhiteSpace(department.parentid)) 102 | { 103 | throw new ArgumentException("父部门id不能为空"); 104 | } 105 | 106 | String url = Env.OAPI_HOST + "/department/create?" + 107 | "access_token=" + accessToken; 108 | 109 | #region 创建参数 110 | BsonDocument args = new BsonDocument(); 111 | args["name"] = department.name; 112 | args["parentid"] = department.parentid; 113 | if (!string.IsNullOrWhiteSpace(department.order)) 114 | { 115 | args["order"] = department.order; 116 | } 117 | args["createDeptGroup"] = department.createDeptGroup; 118 | args["deptHiding"] = department.deptHiding; 119 | if (!string.IsNullOrWhiteSpace(department.deptPerimits)) 120 | { 121 | args["deptPerimits"] = department.deptPerimits; 122 | } 123 | if (!string.IsNullOrWhiteSpace(department.userPerimits)) 124 | { 125 | args["userPerimits"] = department.userPerimits; 126 | } 127 | args["outerDept"] = department.outerDept; 128 | if (!string.IsNullOrWhiteSpace(department.outerPermitDepts)) 129 | { 130 | args["outerPermitDepts"] = department.outerPermitDepts; 131 | } 132 | if (!string.IsNullOrWhiteSpace(department.outerPermitUsers)) 133 | { 134 | args["outerPermitUsers"] = department.outerPermitUsers; 135 | } 136 | if (!string.IsNullOrWhiteSpace(department.sourceIdentifier)) 137 | { 138 | args["sourceIdentifier"] = department.sourceIdentifier; 139 | } 140 | #endregion 141 | 142 | BsonDocument response = await HttpHelper.httpPostAsync(url, args); 143 | if (HttpHelper.CheckResponseOk(response)) 144 | { 145 | return response["id"].ToInt64(); 146 | } 147 | else 148 | { 149 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 150 | } 151 | } 152 | /// 153 | /// 列出所有的部门 154 | /// 155 | /// accessToken 156 | /// 部门列表 157 | public static List ListDepartments(String accessToken) 158 | { 159 | if (String.IsNullOrWhiteSpace(accessToken)) 160 | { 161 | throw new ArgumentException("accessToken 不能为空"); 162 | } 163 | String url = Env.OAPI_HOST + "/department/list?" + 164 | "access_token=" + accessToken; 165 | BsonDocument response = HttpHelper.httpGet(url); 166 | if (HttpHelper.CheckResponseOk(response)) 167 | { 168 | BsonArray arr = response["department"].AsBsonArray; 169 | List list = new List(); 170 | for (int i = 0; i < arr.Count; i++) 171 | { 172 | list.Add(Newtonsoft.Json.JsonConvert.DeserializeObject(arr[i].ToString())); 173 | } 174 | return list; 175 | } 176 | else 177 | { 178 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 179 | } 180 | } 181 | 182 | /// 183 | /// 列出所有的部门 184 | /// 185 | /// accessToken 186 | /// 部门列表 187 | public static async Task> ListDepartmentsAsync(String accessToken) 188 | { 189 | if (String.IsNullOrWhiteSpace(accessToken)) 190 | { 191 | throw new ArgumentException("accessToken 不能为空"); 192 | } 193 | String url = Env.OAPI_HOST + "/department/list?" + 194 | "access_token=" + accessToken; 195 | BsonDocument response = await HttpHelper.httpGetAsync(url); 196 | if (HttpHelper.CheckResponseOk(response)) 197 | { 198 | BsonArray arr = response["department"].AsBsonArray; 199 | List list = new List(); 200 | for (int i = 0; i < arr.Count; i++) 201 | { 202 | list.Add(Newtonsoft.Json.JsonConvert.DeserializeObject(arr[i].ToString())); 203 | } 204 | return list; 205 | } 206 | else 207 | { 208 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 209 | } 210 | } 211 | /// 212 | /// 删除部门 213 | /// 214 | /// accessontoken 215 | /// 部门id 216 | /// 是否删除成功 217 | public static bool DeleteDepartment(String accessToken, long id) 218 | { 219 | if (String.IsNullOrWhiteSpace(accessToken)) 220 | { 221 | throw new ArgumentException("accessToken 不能为空"); 222 | } 223 | String url = Env.OAPI_HOST + "/department/delete?" + 224 | "access_token=" + accessToken + "&id=" + id; 225 | BsonDocument response = HttpHelper.httpGet(url); 226 | if (!HttpHelper.CheckResponseOk(response)) 227 | { 228 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 229 | } 230 | return true; 231 | } 232 | /// 233 | /// 删除部门 234 | /// 235 | /// accessontoken 236 | /// 部门id 237 | /// 是否删除成功 238 | public static async Task DeleteDepartmentAsync(String accessToken, long id) 239 | { 240 | if (String.IsNullOrWhiteSpace(accessToken)) 241 | { 242 | throw new ArgumentException("accessToken 不能为空"); 243 | } 244 | String url = Env.OAPI_HOST + "/department/delete?" + 245 | "access_token=" + accessToken + "&id=" + id; 246 | BsonDocument response = await HttpHelper.httpGetAsync(url); 247 | if (!HttpHelper.CheckResponseOk(response)) 248 | { 249 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 250 | } 251 | return true; 252 | } 253 | /// 254 | /// 更新部门 255 | /// 256 | /// accessToken 257 | /// 部门实体 258 | /// 更新的部门id 259 | public static long UpdateDepartment(String accessToken, Department department) 260 | { 261 | if (String.IsNullOrWhiteSpace(accessToken)) 262 | { 263 | throw new ArgumentException("accessToken 不能为空"); 264 | } 265 | String url = Env.OAPI_HOST + "/department/update?" + 266 | "access_token=" + accessToken; 267 | #region 创建参数 268 | BsonDocument args = new BsonDocument(); 269 | args["name"] = department.name; 270 | args["parentid"] = department.parentid; 271 | if (!string.IsNullOrWhiteSpace(department.order)) 272 | { 273 | args["order"] = department.order; 274 | } 275 | args["createDeptGroup"] = department.createDeptGroup; 276 | args["deptHiding"] = department.deptHiding; 277 | if (!string.IsNullOrWhiteSpace(department.deptPerimits)) 278 | { 279 | args["deptPerimits"] = department.deptPerimits; 280 | } 281 | if (!string.IsNullOrWhiteSpace(department.userPerimits)) 282 | { 283 | args["userPerimits"] = department.userPerimits; 284 | } 285 | args["outerDept"] = department.outerDept; 286 | if (!string.IsNullOrWhiteSpace(department.outerPermitDepts)) 287 | { 288 | args["outerPermitDepts"] = department.outerPermitDepts; 289 | } 290 | if (!string.IsNullOrWhiteSpace(department.outerPermitUsers)) 291 | { 292 | args["outerPermitUsers"] = department.outerPermitUsers; 293 | } 294 | if (!string.IsNullOrWhiteSpace(department.sourceIdentifier)) 295 | { 296 | args["sourceIdentifier"] = department.sourceIdentifier; 297 | } 298 | #endregion 299 | BsonDocument response = HttpHelper.httpPost(url, args); 300 | if (HttpHelper.CheckResponseOk(response)) 301 | { 302 | return response["id"].ToInt64(); 303 | } 304 | else 305 | { 306 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 307 | } 308 | } 309 | /// 310 | /// 更新部门 311 | /// 312 | /// accessToken 313 | /// 部门实体 314 | /// 更新的部门id 315 | public static async Task UpdateDepartmentAsync(String accessToken, Department department) 316 | { 317 | if (String.IsNullOrWhiteSpace(accessToken)) 318 | { 319 | throw new ArgumentException("accessToken 不能为空"); 320 | } 321 | String url = Env.OAPI_HOST + "/department/update?" + 322 | "access_token=" + accessToken; 323 | #region 创建参数 324 | BsonDocument args = new BsonDocument(); 325 | args["name"] = department.name; 326 | args["parentid"] = department.parentid; 327 | if (!string.IsNullOrWhiteSpace(department.order)) 328 | { 329 | args["order"] = department.order; 330 | } 331 | args["createDeptGroup"] = department.createDeptGroup; 332 | args["deptHiding"] = department.deptHiding; 333 | if (!string.IsNullOrWhiteSpace(department.deptPerimits)) 334 | { 335 | args["deptPerimits"] = department.deptPerimits; 336 | } 337 | if (!string.IsNullOrWhiteSpace(department.userPerimits)) 338 | { 339 | args["userPerimits"] = department.userPerimits; 340 | } 341 | args["outerDept"] = department.outerDept; 342 | if (!string.IsNullOrWhiteSpace(department.outerPermitDepts)) 343 | { 344 | args["outerPermitDepts"] = department.outerPermitDepts; 345 | } 346 | if (!string.IsNullOrWhiteSpace(department.outerPermitUsers)) 347 | { 348 | args["outerPermitUsers"] = department.outerPermitUsers; 349 | } 350 | if (!string.IsNullOrWhiteSpace(department.sourceIdentifier)) 351 | { 352 | args["sourceIdentifier"] = department.sourceIdentifier; 353 | } 354 | #endregion 355 | BsonDocument response = await HttpHelper.httpPostAsync(url, args); 356 | if (HttpHelper.CheckResponseOk(response)) 357 | { 358 | return response["id"].ToInt64(); 359 | } 360 | else 361 | { 362 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 363 | } 364 | } 365 | /// 366 | /// 获取钉钉上某个企业/组织内的人数。 367 | /// 368 | /// accessToken 369 | /// 0:包含未激活钉钉的人员数量,1:不包含未激活钉钉的人员数量 370 | /// 371 | public static long GetOrgUserCount(String accessToken, int onlyActive) 372 | { 373 | String url = Env.OAPI_HOST + "/department/get_org_user_count"; 374 | if (String.IsNullOrWhiteSpace(accessToken)) 375 | { 376 | throw new ArgumentException("accessToken 不能为空"); 377 | } 378 | 379 | if (onlyActive != 0 && onlyActive != 1) 380 | { 381 | throw new ArgumentException("onlyActive,只能是1或者是0。0:包含未激活钉钉的人员数量,1:不包含未激活钉钉的人员数量"); 382 | } 383 | BsonDocument args = new BsonDocument(); 384 | args["access_token"] = "accessToken"; 385 | args["onlyActive"] = onlyActive; 386 | var response = HttpHelper.httpGet(url, args); 387 | if (HttpHelper.CheckResponseOk(response)) 388 | { 389 | return response["count"].ToInt64(); 390 | } 391 | else 392 | { 393 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 394 | } 395 | } 396 | /// 397 | /// 获取钉钉上某个企业/组织内的人数。 398 | /// 399 | /// accessToken 400 | /// 0:包含未激活钉钉的人员数量,1:不包含未激活钉钉的人员数量 401 | /// 402 | public static async Task GetOrgUserCountAsync(String accessToken, int onlyActive) 403 | { 404 | String url = Env.OAPI_HOST + "/department/get_org_user_count"; 405 | if (String.IsNullOrWhiteSpace(accessToken)) 406 | { 407 | throw new ArgumentException("accessToken 不能为空"); 408 | } 409 | 410 | if (onlyActive != 0 && onlyActive != 1) 411 | { 412 | throw new ArgumentException("onlyActive,只能是1或者是0。0:包含未激活钉钉的人员数量,1:不包含未激活钉钉的人员数量"); 413 | } 414 | BsonDocument args = new BsonDocument(); 415 | args["access_token"] = "accessToken"; 416 | args["onlyActive"] = onlyActive; 417 | var response = await HttpHelper.httpGetAsync(url, args); 418 | if (HttpHelper.CheckResponseOk(response)) 419 | { 420 | return response["count"].ToInt64(); 421 | } 422 | else 423 | { 424 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 425 | } 426 | } 427 | } 428 | } 429 | -------------------------------------------------------------------------------- /User/UserHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using DingTalkSDK.DingDingException; 6 | using DingTalkSDK.Util; 7 | using MongoDB.Bson; 8 | 9 | namespace DingTalkSDK.User 10 | { 11 | public class UserHelper 12 | { 13 | /// 14 | /// 15 | /// 创建成员 16 | /// 17 | /// access_token 18 | /// user 19 | /// 创建的用户ID 20 | public static string CreateUser(String accessToken, User user) 21 | { 22 | 23 | if (String.IsNullOrWhiteSpace(accessToken)) 24 | { 25 | throw new ArgumentException("accessToken 不能为空"); 26 | } 27 | if (String.IsNullOrWhiteSpace(user.name)) 28 | { 29 | throw new ArgumentException("name 必须不能为空"); 30 | } 31 | if (String.IsNullOrWhiteSpace(user.mobile)) 32 | { 33 | throw new ArgumentException("mobile 必须不能为空"); 34 | } 35 | if (String.IsNullOrWhiteSpace(user.userid)) 36 | { 37 | throw new ArgumentException("userid 必须不能为空"); 38 | } 39 | if (user.department.Count == 0) 40 | { 41 | throw new ArgumentException("user 的构造参数 department 必须不能为空"); 42 | } 43 | 44 | String url = Env.OAPI_HOST + "/user/create"; 45 | 46 | BsonDocument args = new BsonDocument(); 47 | args["access_token"] = accessToken; 48 | args["name"] = user.name; 49 | args["mobile"] = user.mobile; 50 | args["userid"] = user.userid; 51 | var deptlist = "["; 52 | foreach (var item in user.department) 53 | { 54 | deptlist += item.ToString() + ","; 55 | } 56 | deptlist = deptlist.Remove(deptlist.Count() - 1) + "]"; 57 | args["department"] = deptlist; 58 | 59 | BsonDocument response = HttpHelper.httpPost(url, args); 60 | 61 | if (HttpHelper.CheckResponseOk(response)) 62 | { 63 | return response["userid"].ToString(); 64 | } 65 | else 66 | { 67 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 68 | } 69 | } 70 | /// 71 | /// 72 | /// 创建成员 73 | /// 74 | /// access_token 75 | /// user 76 | /// 创建的用户ID 77 | public static async Task CreateUserAsync(String accessToken, User user) 78 | { 79 | 80 | if (String.IsNullOrWhiteSpace(accessToken)) 81 | { 82 | throw new ArgumentException("accessToken 不能为空"); 83 | } 84 | if (String.IsNullOrWhiteSpace(user.name)) 85 | { 86 | throw new ArgumentException("name 必须不能为空"); 87 | } 88 | if (String.IsNullOrWhiteSpace(user.mobile)) 89 | { 90 | throw new ArgumentException("mobile 必须不能为空"); 91 | } 92 | if (String.IsNullOrWhiteSpace(user.userid)) 93 | { 94 | throw new ArgumentException("userid 必须不能为空"); 95 | } 96 | if (user.department.Count == 0) 97 | { 98 | throw new ArgumentException("user 的构造参数 department 必须不能为空"); 99 | } 100 | 101 | String url = Env.OAPI_HOST + "/user/create"; 102 | 103 | BsonDocument args = new BsonDocument(); 104 | args["access_token"] = accessToken; 105 | args["name"] = user.name; 106 | args["mobile"] = user.mobile; 107 | args["userid"] = user.userid; 108 | var deptlist = "["; 109 | foreach (var item in user.department) 110 | { 111 | deptlist += item.ToString() + ","; 112 | } 113 | deptlist = deptlist.Remove(deptlist.Count() - 1) + "]"; 114 | args["department"] = deptlist; 115 | 116 | var response = await HttpHelper.httpPostAsync(url, args); 117 | if (HttpHelper.CheckResponseOk(response)) 118 | { 119 | return response["userid"].ToString(); 120 | } 121 | else 122 | { 123 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 124 | } 125 | } 126 | 127 | /// 128 | /// 更新成员 129 | /// 130 | /// access_token 131 | /// user 132 | /// 是否更新成功 133 | public static bool UpdateUser(String accessToken, User user) 134 | { 135 | 136 | if (String.IsNullOrWhiteSpace(accessToken)) 137 | { 138 | throw new ArgumentException("accessToken 不能为空"); 139 | } 140 | if (String.IsNullOrWhiteSpace(user.userid)) 141 | { 142 | throw new ArgumentException("userid 必须不能为空"); 143 | } 144 | 145 | String url = Env.OAPI_HOST + "/user/update"; 146 | BsonDocument args = new BsonDocument(); 147 | args["access_token"] = accessToken; 148 | args["name"] = user.name; 149 | args["mobile"] = user.mobile; 150 | args["userid"] = user.userid; 151 | var deptlist = "["; 152 | foreach (var item in user.department) 153 | { 154 | deptlist += item.ToString() + ","; 155 | } 156 | deptlist = deptlist.Remove(deptlist.Count() - 1) + "]"; 157 | args["department"] = deptlist; 158 | 159 | BsonDocument response = HttpHelper.httpPost(url, args); 160 | 161 | if (!HttpHelper.CheckResponseOk(response)) 162 | { 163 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 164 | } 165 | return true; 166 | } 167 | 168 | /// 169 | /// 更新成员 170 | /// 171 | /// access_token 172 | /// user 173 | /// 是否更新成功 174 | public static async Task UpdateUserAsync(String accessToken, User user) 175 | { 176 | if (String.IsNullOrWhiteSpace(accessToken)) 177 | { 178 | throw new ArgumentException("accessToken 不能为空"); 179 | } 180 | if (String.IsNullOrWhiteSpace(user.userid)) 181 | { 182 | throw new ArgumentException("userid 必须不能为空"); 183 | } 184 | 185 | String url = Env.OAPI_HOST + "/user/update"; 186 | BsonDocument args = new BsonDocument(); 187 | args["access_token"] = accessToken; 188 | args["name"] = user.name; 189 | args["mobile"] = user.mobile; 190 | args["userid"] = user.userid; 191 | var deptlist = "["; 192 | foreach (var item in user.department) 193 | { 194 | deptlist += item.ToString() + ","; 195 | } 196 | deptlist = deptlist.Remove(deptlist.Count() - 1) + "]"; 197 | args["department"] = deptlist; 198 | 199 | BsonDocument response = await HttpHelper.httpPostAsync(url, args); 200 | 201 | if (!HttpHelper.CheckResponseOk(response)) 202 | { 203 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 204 | } 205 | return true; 206 | } 207 | /// 208 | /// 删除 209 | /// 210 | /// access_token 211 | /// userid 212 | /// 是否删除成功 213 | public static bool DeleteUser(String accessToken, String userid) 214 | { 215 | if (String.IsNullOrWhiteSpace(accessToken)) 216 | { 217 | throw new ArgumentException("accessToken 不能为空"); 218 | } 219 | if (String.IsNullOrWhiteSpace(userid)) 220 | { 221 | throw new ArgumentException("userid 必须不能为空"); 222 | } 223 | String url = Env.OAPI_HOST + "/user/delete?" + 224 | "access_token=" + accessToken + "&userid=" + userid; 225 | BsonDocument response = HttpHelper.httpGet(url); 226 | 227 | if (!HttpHelper.CheckResponseOk(response)) 228 | { 229 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 230 | } 231 | return true; 232 | } 233 | /// 234 | /// 删除 235 | /// 236 | /// access_token 237 | /// userid 238 | /// 是否删除成功 239 | public static async Task DeleteUserAsync(String accessToken, String userid) 240 | { 241 | if (String.IsNullOrWhiteSpace(accessToken)) 242 | { 243 | throw new ArgumentException("accessToken 不能为空"); 244 | } 245 | if (String.IsNullOrWhiteSpace(userid)) 246 | { 247 | throw new ArgumentException("userid 必须不能为空"); 248 | } 249 | String url = Env.OAPI_HOST + "/user/delete?" + 250 | "access_token=" + accessToken + "&userid=" + userid; 251 | BsonDocument response = await HttpHelper.httpGetAsync(url); 252 | 253 | if (!HttpHelper.CheckResponseOk(response)) 254 | { 255 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 256 | } 257 | return true; 258 | } 259 | 260 | 261 | /// 262 | /// 获取用户信息 263 | /// 264 | /// access_token 265 | /// userid 266 | public static User GetUser(String accessToken, String userid) 267 | { 268 | if (String.IsNullOrWhiteSpace(accessToken)) 269 | { 270 | throw new ArgumentException("accessToken 不能为空"); 271 | } 272 | if (String.IsNullOrWhiteSpace(userid)) 273 | { 274 | throw new ArgumentException("userid 不能为空"); 275 | } 276 | String url = Env.OAPI_HOST + "/user/get?" + 277 | "access_token=" + accessToken + "&userid=" + userid; 278 | BsonDocument json = HttpHelper.httpGet(url); 279 | return Newtonsoft.Json.JsonConvert.DeserializeObject(json.ToString()); 280 | } 281 | /// 282 | /// 获取用户信息 283 | /// 284 | /// access_token 285 | /// userid 286 | public static async Task GetUserAsync(String accessToken, String userid) 287 | { 288 | if (String.IsNullOrWhiteSpace(accessToken)) 289 | { 290 | throw new ArgumentException("accessToken 不能为空"); 291 | } 292 | if (String.IsNullOrWhiteSpace(userid)) 293 | { 294 | throw new ArgumentException("userid 不能为空"); 295 | } 296 | String url = Env.OAPI_HOST + "/user/get?" + 297 | "access_token=" + accessToken + "&userid=" + userid; 298 | BsonDocument json = await HttpHelper.httpGetAsync(url); 299 | return Newtonsoft.Json.JsonConvert.DeserializeObject(json.ToString()); 300 | } 301 | 302 | /// 303 | /// 批量删除成员 304 | /// 305 | /// access_token 306 | /// 用户列表 307 | /// 是否删除成功 308 | public static bool BatchDeleteUser(String accessToken, List useridlist) 309 | { 310 | if (String.IsNullOrWhiteSpace(accessToken)) 311 | { 312 | throw new ArgumentException("accessToken 不能为空"); 313 | } 314 | if (useridlist.Count == 0) 315 | { 316 | throw new ArgumentException("useridlist 不能为空"); 317 | } 318 | String url = Env.OAPI_HOST + "/user/batchdelete?" + 319 | "access_token=" + accessToken; 320 | BsonDocument args = new BsonDocument(); 321 | args["useridlist"] = new BsonArray(useridlist); 322 | BsonDocument response = HttpHelper.httpPost(url, args); 323 | if (!HttpHelper.CheckResponseOk(response)) 324 | { 325 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 326 | } 327 | return true; 328 | } 329 | 330 | /// 331 | /// 批量删除成员 332 | /// 333 | /// access_token 334 | /// 用户列表 335 | /// 是否删除成功 336 | public static async Task BatchDeleteUserAsync(String accessToken, List useridlist) 337 | { 338 | if (String.IsNullOrWhiteSpace(accessToken)) 339 | { 340 | throw new ArgumentException("accessToken 不能为空"); 341 | } 342 | if (useridlist.Count == 0) 343 | { 344 | throw new ArgumentException("useridlist 不能为空"); 345 | } 346 | String url = Env.OAPI_HOST + "/user/batchdelete?" + 347 | "access_token=" + accessToken; 348 | BsonDocument args = new BsonDocument(); 349 | args["useridlist"] = new BsonArray(useridlist); 350 | BsonDocument response = await HttpHelper.httpPostAsync(url, args); 351 | if (!HttpHelper.CheckResponseOk(response)) 352 | { 353 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 354 | } 355 | return true; 356 | } 357 | /// 358 | /// 获取部门成员 359 | /// 360 | /// access_token 361 | /// 部门id 362 | /// 部门成员list 363 | public static List GetDepartmentUser(String accessToken, long department_id) 364 | { 365 | if (String.IsNullOrWhiteSpace(accessToken)) 366 | { 367 | throw new ArgumentException("accessToken 不能为空"); 368 | } 369 | String url = Env.OAPI_HOST + "/user/simplelist?" + 370 | "access_token=" + accessToken + "&department_id=" + department_id; 371 | BsonDocument response = HttpHelper.httpGet(url); 372 | if (HttpHelper.CheckResponseOk(response)) 373 | { 374 | List list = new List(); 375 | BsonArray arr = response["userlist"].AsBsonArray; 376 | for (int i = 0; i < arr.Count; i++) 377 | { 378 | list.Add(Newtonsoft.Json.JsonConvert.DeserializeObject(arr[i].ToString())); 379 | } 380 | return list; 381 | } 382 | else 383 | { 384 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 385 | } 386 | } 387 | 388 | /// 389 | /// 获取部门成员的简单信息,此接口只包含userid和name 390 | /// 391 | /// access_token 392 | /// 部门id 393 | /// 部门成员list 394 | public static async Task> GetDepartmentUserAsync(String accessToken, long department_id) 395 | { 396 | if (String.IsNullOrWhiteSpace(accessToken)) 397 | { 398 | throw new ArgumentException("accessToken 不能为空"); 399 | } 400 | String url = Env.OAPI_HOST + "/user/simplelist?" + 401 | "access_token=" + accessToken + "&department_id=" + department_id; 402 | BsonDocument response = await HttpHelper.httpGetAsync(url); 403 | if (HttpHelper.CheckResponseOk(response)) 404 | { 405 | List list = new List(); 406 | BsonArray arr = response["userlist"].AsBsonArray; 407 | for (int i = 0; i < arr.Count; i++) 408 | { 409 | list.Add(Newtonsoft.Json.JsonConvert.DeserializeObject(arr[i].ToString())); 410 | } 411 | return list; 412 | } 413 | else 414 | { 415 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 416 | } 417 | } 418 | 419 | /// 420 | /// 获取部门成员的详细信息 421 | /// 422 | /// access_token 423 | /// 部门id 424 | /// 部门成员list 425 | public static List GettDepartmentUserDetail(String accessToken, long department_id) 426 | { 427 | if (String.IsNullOrWhiteSpace(accessToken)) 428 | { 429 | throw new ArgumentException("accessToken 不能为空"); 430 | } 431 | String url = Env.OAPI_HOST + "/user/list?" + 432 | "access_token=" + accessToken + "&department_id=" + department_id; 433 | BsonDocument response = HttpHelper.httpGet(url); 434 | if (HttpHelper.CheckResponseOk(response)) 435 | { 436 | BsonArray arr = response["userlist"].AsBsonArray; 437 | List list = new List(); 438 | for (int i = 0; i < arr.Count; i++) 439 | { 440 | var bd = arr[i].ToBsonDocument(); 441 | if (bd.Contains("order")) 442 | { 443 | bd.Set("order", bd.GetValue("order").AsInt64.ToString()); 444 | } 445 | list.Add(Newtonsoft.Json.JsonConvert.DeserializeObject(arr[i].ToString())); 446 | } 447 | return list; 448 | } 449 | else 450 | { 451 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 452 | } 453 | } 454 | /// 455 | /// 获取部门成员的详细信息 456 | /// 457 | /// access_token 458 | /// 部门id 459 | /// 部门成员list 460 | public static async Task> GettDepartmentUserDetailAsync(String accessToken, long department_id) 461 | { 462 | if (String.IsNullOrWhiteSpace(accessToken)) 463 | { 464 | throw new ArgumentException("accessToken 不能为空"); 465 | } 466 | String url = Env.OAPI_HOST + "/user/list?" + 467 | "access_token=" + accessToken + "&department_id=" + department_id; 468 | BsonDocument response = await HttpHelper.httpGetAsync(url); 469 | if (rHttpHelper.CheckResponseOk(response)) 470 | { 471 | BsonArray arr = response["userlist"].AsBsonArray; 472 | List list = new List(); 473 | for (int i = 0; i < arr.Count; i++) 474 | { 475 | var bd = arr[i].ToBsonDocument(); 476 | if (bd.Contains("order")) 477 | { 478 | bd.Set("order", bd.GetValue("order").AsInt64.ToString()); 479 | } 480 | list.Add(Newtonsoft.Json.JsonConvert.DeserializeObject(arr[i].ToString())); 481 | } 482 | return list; 483 | } 484 | else 485 | { 486 | throw new DingDingApiResultException(response["errcode"].AsInt32, response["errmsg"].AsString); 487 | } 488 | } 489 | } 490 | } 491 | --------------------------------------------------------------------------------