diff --git a/Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj b/Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj index d436744b..05bec77b 100644 --- a/Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj +++ b/Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj @@ -42,6 +42,7 @@ + - + \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Application/Configuration/HttpRemotes.json b/Admin.NET/Admin.NET.Application/Configuration/HttpRemotes.json index d6c49a10..bd28fc8d 100644 --- a/Admin.NET/Admin.NET.Application/Configuration/HttpRemotes.json +++ b/Admin.NET/Admin.NET.Application/Configuration/HttpRemotes.json @@ -9,7 +9,7 @@ "Account": "", "Password": "" }, - "HttpBin": { // 测试接口服务配置 + "HttpBin": { // 获取Ip "EnabledLog": true, "UseCookies": false, "EnabledProxy": false, @@ -22,6 +22,17 @@ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" }, "Timeout": 5 + }, + "WorkWeixin": { // 企业微信 + "EnabledLog": true, + "UseCookies": false, + "EnabledProxy": false, + "HttpName": "WORK_WEIXIN", + "BaseAddress": "https://qyapi.weixin.qq.com", + "Headers": { + "User-Agent": "WORK_WEIXIN" + }, + "Timeout": 60 } } } \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Application/Service/Test/TestService.cs b/Admin.NET/Admin.NET.Application/Service/Test/TestService.cs index 1b546135..6699f4ef 100644 --- a/Admin.NET/Admin.NET.Application/Service/Test/TestService.cs +++ b/Admin.NET/Admin.NET.Application/Service/Test/TestService.cs @@ -4,6 +4,7 @@ // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! +using Admin.NET.Plugin.WorkWeixin; using Furion.Localization; using Furion.Logging; using Microsoft.Extensions.Logging; @@ -19,11 +20,13 @@ public class TestService : IDynamicApiController private readonly UserManager _userManager; private readonly IEventPublisher _eventPublisher; private readonly ILogger _logger; + private readonly WorkWxUserService _workWxUserService; - public TestService(UserManager userManager, IEventPublisher eventPublisher, ILoggerFactory loggerFactory) + public TestService(UserManager userManager, IEventPublisher eventPublisher, WorkWxUserService workWxUserService, ILoggerFactory loggerFactory) { _userManager = userManager; _eventPublisher = eventPublisher; + _workWxUserService = workWxUserService; _logger = loggerFactory.CreateLogger(CommonConst.SysLogCategoryName); // 日志过滤标识(会写入数据库) } @@ -68,6 +71,15 @@ public class TestService : IDynamicApiController _logger.LogWarning($"信息{DateTime.Now}"); } + /// + /// 企业微信测试 + /// + /// + public async Task> UserIdList() + { + return (await _workWxUserService.GetUserIdList())?.DeptUser; + } + /// /// 匿名上传文件测试 /// diff --git a/Admin.NET/Admin.NET.Core/Option/HttpRemotesOptions.cs b/Admin.NET/Admin.NET.Core/Option/HttpRemotesOptions.cs index 2faf4f6e..c4c3d704 100644 --- a/Admin.NET/Admin.NET.Core/Option/HttpRemotesOptions.cs +++ b/Admin.NET/Admin.NET.Core/Option/HttpRemotesOptions.cs @@ -20,6 +20,11 @@ public sealed class HttpRemotesOptions : IConfigurableOptions /// 获取Ip地址接口 /// public HttpRemoteItem HttpBin { get; set; } + + /// + /// 企业微信接口配置 + /// + public HttpRemoteItem WorkWeixin { get; set; } } /// diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Configuration/WorkWeixin.json b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Configuration/WorkWeixin.json deleted file mode 100644 index 349015f5..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Configuration/WorkWeixin.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json", - - "WorkWeixin": { - "CorpId": "xxxx", // 企业ID - "CorpSecret": "xxxx" // 企业微信凭证密钥 - } -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Const/WorkWeixinConst.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Const/WorkWeixinConst.cs index b5a18580..b706e7bb 100644 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Const/WorkWeixinConst.cs +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Const/WorkWeixinConst.cs @@ -9,7 +9,22 @@ namespace Admin.NET.Plugin.WorkWeixin.Const; public class WorkWeixinConst { /// - /// API分组名称 + /// 企业微信缓存建 /// - public const string GroupName = "WorkWeixin"; + public const string KeyWorkWeixinToken = "work_weixin_token"; + + /// + /// 企业微信CorpId + /// + public const string WorkWeixinCorpId = "work_weixin_corp_id"; + + /// + /// 企业微信CorpSecret + /// + public const string WorkWeixinCorpSecret = "work_weixin_corp_secret"; + + /// + /// 企业微信分布式锁 + /// + public const string KeyLockWorkWeixin = "dis_lock_work_weixin"; } \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/GlobalUsings.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/GlobalUsings.cs index 54a4e670..8442a677 100644 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/GlobalUsings.cs +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/GlobalUsings.cs @@ -5,8 +5,14 @@ // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! global using Admin.NET.Core; +global using Admin.NET.Core.Service; global using Furion; +global using Furion.DependencyInjection; +global using Furion.EventBus; global using Furion.HttpRemote; +global using Lazy.Captcha.Core; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Mvc; global using Newtonsoft.Json; global using System.ComponentModel.DataAnnotations; global using System.Text.Json.Serialization; \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Option/WorkWeixinOptions.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Option/WorkWeixinOptions.cs deleted file mode 100644 index 6c1008da..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Option/WorkWeixinOptions.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -using Furion.ConfigurableOptions; - -namespace Admin.NET.Plugin.WorkWeixin.Option; - -/// -/// 企业微信配置项 -/// -public class WorkWeixinOptions : IConfigurableOptions -{ - /// - /// 企业ID - /// - public string CorpId { get; set; } - - /// - /// 企业微信凭证密钥 - /// - public string CorpSecret { get; set; } -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/AppChat/Dto/AppChatHttpInput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/AppChat/Dto/AppChatHttpInput.cs deleted file mode 100644 index a5655e81..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/AppChat/Dto/AppChatHttpInput.cs +++ /dev/null @@ -1,425 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy; - -/// -/// 创建群聊会话输入参数 -/// -public class CreatAppChatInput -{ - /// - /// 群名称 - /// - [JsonProperty("name")] - [JsonPropertyName("name")] - [Required(ErrorMessage = "群名称不能为空"), MaxLength(50, ErrorMessage = "群名称最多不能超过50个字符")] - public string Name { get; set; } - - /// - /// 群主Id - /// - [JsonProperty("owner")] - [JsonPropertyName("owner")] - [Required(ErrorMessage = "群主Id不能为空")] - public string Owner { get; set; } - - /// - /// 群成员Id列表 - /// - [JsonProperty("userlist")] - [JsonPropertyName("userlist")] - [NotEmpty(ErrorMessage = "群成员列表不能为空")] - public List UserList { get; set; } - - /// - /// 群Id - /// - [JsonProperty("chatid")] - [JsonPropertyName("chatid")] - [Required(ErrorMessage = "群Id不能为空"), MaxLength(32, ErrorMessage = "群Id最多不能超过32个字符")] - public string ChatId { get; set; } -} - -/// -/// 修改群聊会话输入参数 -/// -public class UpdateAppChatInput -{ - /// - /// 群Id - /// - [JsonProperty("chatid")] - [JsonPropertyName("chatid")] - [Required(ErrorMessage = "群Id不能为空"), MaxLength(32, ErrorMessage = "群Id最多不能超过32个字符")] - public string ChatId { get; set; } - - /// - /// 群名称 - /// - [JsonProperty("name")] - [JsonPropertyName("name")] - [Required(ErrorMessage = "群名称不能为空"), MaxLength(50, ErrorMessage = "群名称最多不能超过50个字符")] - public string Name { get; set; } - - /// - /// 群主Id - /// - [JsonProperty("owner")] - [JsonPropertyName("owner")] - [Required(ErrorMessage = "群主Id不能为空")] - public string Owner { get; set; } - - /// - /// 添加成员的id列表 - /// - [JsonProperty("add_user_list")] - [JsonPropertyName("add_user_list")] - public List AddUserList { get; set; } - - /// - /// 踢出成员的id列表 - /// - [JsonProperty("del_user_list")] - [JsonPropertyName("del_user_list")] - public List DelUserList { get; set; } -} - -/// -/// 应用消息推送输入基类参数 -/// -public class SendBaseAppChatInput -{ - /// - /// 群Id - /// - [JsonProperty("chatid")] - [JsonPropertyName("chatid")] - [Required(ErrorMessage = "群Id不能为空"), MaxLength(32, ErrorMessage = "群Id最多不能超过32个字符")] - public string ChatId { get; set; } - - /// - /// 消息类型 - /// - /// text:文本消息 - /// image:图片消息 - /// voice:图片消息 - /// video:视频消息 - /// file:文件消息 - /// textcard:文本卡片 - /// news:图文消息 - /// mpnews:图文消息(存储在企业微信) - /// markdown:markdown消息 - [JsonProperty("msgtype")] - [JsonPropertyName("msgtype")] - [Required(ErrorMessage = "消息类型不能为空")] - protected string MsgType { get; set; } - - /// - /// 是否是保密消息 - /// - [JsonProperty("safe")] - [JsonPropertyName("safe")] - [Required(ErrorMessage = "消息类型不能为空")] - public int Safe { get; set; } - - public SendBaseAppChatInput(string chatId, string msgType, bool safe = false) - { - ChatId = chatId; - MsgType = msgType; - Safe = safe ? 1 : 0; - } -} - -/// -/// 推送文本消息输入参数 -/// -public class SendTextAppChatInput : SendBaseAppChatInput -{ - /// - /// 消息内容 - /// - [JsonProperty("text")] - [JsonPropertyName("text")] - public object Text { get; set; } - - /// - /// 文本消息 - /// - /// - /// - /// - public SendTextAppChatInput(string chatId, string content, bool safe = false) : base(chatId, "text", safe) - { - Text = new { content }; - } -} - -/// -/// 推送图片消息输入参数 -/// -public class SendImageAppChatInput : SendBaseAppChatInput -{ - /// - /// 消息内容 - /// - [JsonProperty("image")] - [JsonPropertyName("image")] - public object Image { get; set; } - - /// - /// 图片消息 - /// - /// - /// - /// - public SendImageAppChatInput(string chatId, string mediaId, bool safe = false) : base(chatId, "image", safe) - { - Image = new { media_id = mediaId }; - } -} - -/// -/// 推送语音消息输入参数 -/// -public class SendVoiceAppChatInput : SendBaseAppChatInput -{ - /// - /// 消息内容 - /// - [JsonProperty("voice")] - [JsonPropertyName("voice")] - public object Voice { get; set; } - - /// - /// 语音消息 - /// - /// - /// - /// - public SendVoiceAppChatInput(string chatId, string mediaId, bool safe = false) : base(chatId, "voice", safe) - { - Voice = new { media_id = mediaId }; - } -} - -/// -/// 推送视频消息输入参数 -/// -public class SendVideoAppChatInput : SendBaseAppChatInput -{ - /// - /// 消息内容 - /// - [JsonProperty("video")] - [JsonPropertyName("video")] - public object Video { get; set; } - - /// - /// 视频消息 - /// - /// - /// - /// - /// - /// - public SendVideoAppChatInput(string chatId, string title, string description, string mediaId, bool safe = false) : base(chatId, "video", safe) - { - Video = new - { - media_id = mediaId, - description, - title - }; - } -} - -/// -/// 推送视频消息输入参数 -/// -public class SendFileAppChatInput : SendBaseAppChatInput -{ - /// - /// 消息内容 - /// - [JsonProperty("file")] - [JsonPropertyName("file")] - public object File { get; set; } - - /// - /// 文件消息 - /// - /// - /// - /// - public SendFileAppChatInput(string chatId, string mediaId, bool safe = false) : base(chatId, "video", safe) - { - File = new { media_id = mediaId }; - } -} - -/// -/// 推送文本卡片消息输入参数 -/// -public class SendTextCardAppChatInput : SendBaseAppChatInput -{ - /// - /// 消息内容 - /// - [JsonProperty("textcard")] - [JsonPropertyName("textcard")] - public object TextCard { get; set; } - - /// - /// 文本卡片消息 - /// - /// - /// 标题 - /// 描述 - /// 点击后跳转的链接 - /// 按钮文字 - /// - public SendTextCardAppChatInput(string chatId, string title, string description, string url, string btnTxt, bool safe = false) : base(chatId, "textcard", safe) - { - TextCard = new - { - title, - description, - url, - btntxt = btnTxt - }; - } -} - -/// -/// 图文消息项 -/// -public class SendNewsItem -{ - /// - /// 标题 - /// - [JsonProperty("title")] - [JsonPropertyName("title")] - public string Title { get; set; } - - /// - /// 描述 - /// - [JsonProperty("description")] - [JsonPropertyName("description")] - public string Description { get; set; } - - /// - /// 描述 - /// - [JsonProperty("url")] - [JsonPropertyName("url")] - public string Url { get; set; } - - /// - /// 图文消息的图片链接(推荐大图1068 * 455,小图150 * 150) - /// - [JsonProperty("picurl")] - [JsonPropertyName("picurl")] - public string PicUrl { get; set; } -} - -/// -/// 推送图文消息输入参数 -/// -public class SendNewsAppChatInput : SendBaseAppChatInput -{ - /// - /// 消息内容 - /// - [JsonProperty("news")] - [JsonPropertyName("news")] - public object News { get; set; } - - /// - /// 图文消息 - /// - /// - /// 图文消息列表 - /// - public SendNewsAppChatInput(string chatId, List newsList, bool safe = false) : base(chatId, "news", safe) - { - News = new { articles = newsList }; - } -} - -/// -/// 图文消息项 -/// -public class SendMpNewsItem -{ - /// - /// 标题 - /// - [JsonProperty("title")] - [JsonPropertyName("title")] - public string Title { get; set; } - - /// - /// 缩略图media_id - /// - [JsonProperty("thumb_media_id")] - [JsonPropertyName("thumb_media_id")] - public string ThumbMediaId { get; set; } - - /// - /// 作者 - /// - [JsonProperty("author")] - [JsonPropertyName("author")] - public string Author { get; set; } - - /// - /// 点击“阅读原文”之后的页面链接 - /// - [JsonProperty("content_source_url")] - [JsonPropertyName("content_source_url")] - public string ContentSourceUrl { get; set; } - - /// - /// 图文消息的内容 - /// - [JsonProperty("content")] - [JsonPropertyName("content")] - public string Content { get; set; } - - /// - /// 图文消息的描述 - /// - [JsonProperty("digest")] - [JsonPropertyName("digest")] - public string Digest { get; set; } -} - -/// -/// 推送图文消息(存储在企业微信)输入参数 -/// -public class SendMpNewsAppChatInput : SendBaseAppChatInput -{ - /// - /// 消息内容 - /// - [JsonProperty("mpnews")] - [JsonPropertyName("mpnews")] - public object MpNews { get; set; } - - /// - /// 图文消息 - /// - /// - /// 图文消息列表 - /// - public SendMpNewsAppChatInput(string chatId, List mpNewsList, bool safe = false) : base(chatId, "mpnews", safe) - { - MpNews = new { articles = mpNewsList }; - } -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/AppChat/Dto/AppChatHttpOutput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/AppChat/Dto/AppChatHttpOutput.cs deleted file mode 100644 index 888912f5..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/AppChat/Dto/AppChatHttpOutput.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy; - -public class CreatAppChatOutput : BaseWorkOutput -{ - /// - /// 群聊的唯一标志 - /// - [JsonProperty("chatid")] - [JsonPropertyName("chatid")] - public string ChatId { get; set; } -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/AppChat/IWorkWeixinAppChatHttp.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/AppChat/IWorkWeixinAppChatHttp.cs deleted file mode 100644 index 89ac90c6..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/AppChat/IWorkWeixinAppChatHttp.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy.AppChat; - -/// -/// 群聊会话远程调用服务 -/// -public interface IWorkWeixinAppChatHttp : IHttpDeclarative -{ - /// - /// 创建群聊会话 - /// - /// - /// - /// - /// - [Post("https://qyapi.weixin.qq.com/cgi-bin/appchat/create")] - Task Create([Query("access_token")] string accessToken, [Body] CreatAppChatInput body); - - /// - /// 修改群聊会话 - /// - /// - /// - /// - /// - [Post("https://qyapi.weixin.qq.com/cgi-bin/appchat/update")] - Task Update([Query("access_token")] string accessToken, [Body] UpdateAppChatInput body); - - /// - /// 获取群聊会话 - /// - /// - /// - /// - /// - [Get("https://qyapi.weixin.qq.com/cgi-bin/appchat/get")] - Task Get([Query("access_token")] string accessToken, [Query("chatid")] string chatId); - - /// - /// 应用推送消息 - /// - /// - /// - /// - /// - [Post("https://qyapi.weixin.qq.com/cgi-bin/appchat/send")] - Task Send([Query("access_token")] string accessToken, [Body] SendBaseAppChatInput body); -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Auth/Dto/WorkWeixinAuthHttpOutput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Auth/Dto/WorkWeixinAuthHttpOutput.cs deleted file mode 100644 index 34851408..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Auth/Dto/WorkWeixinAuthHttpOutput.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy; - -public class AuthAccessTokenHttpOutput : BaseWorkOutput -{ - /// - /// 获取到的凭证 - /// - [JsonProperty("access_token")] - [JsonPropertyName("access_token")] - public string AccessToken { get; set; } - - /// - /// 凭证的有效时间(秒) - /// - [JsonProperty("expires_in")] - [JsonPropertyName("expires_in")] - public int ExpiresIn { get; set; } -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Auth/IWorkWeixinAuthHttp.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Auth/IWorkWeixinAuthHttp.cs deleted file mode 100644 index 9db100e1..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Auth/IWorkWeixinAuthHttp.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy.AppChat; - -/// -/// 授权会话远程服务 -/// -public interface IWorkWeixinAuthHttp : IHttpDeclarative -{ - /// - /// 获取接口凭证 - /// - /// 企业ID - /// 应用的凭证密钥 - /// - /// - [Post("https://qyapi.weixin.qq.com/cgi-bin/gettoken")] - Task GetToken([Query("corpid")] string corpId, [Query("corpsecret")] string corpSecret); -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Department/Dto/DepartmentHttpInput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Department/Dto/DepartmentHttpInput.cs deleted file mode 100644 index ce87de7d..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Department/Dto/DepartmentHttpInput.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy; - -/// -/// 创建部门输入参数 -/// -public class DepartmentHttpInput -{ - /// - /// 部门名称 - /// - [JsonProperty("id")] - [JsonPropertyName("id")] - public long? Id { get; set; } - - /// - /// 父部门id - /// - [JsonProperty("parentid")] - [JsonPropertyName("parentid")] - public long? ParentId { get; set; } - - /// - /// 部门名称 - /// - [JsonProperty("name")] - [JsonPropertyName("name")] - public string Name { get; set; } - - /// - /// 英文名称 - /// - [JsonProperty("name_en")] - [JsonPropertyName("name_en")] - public string NameEn { get; set; } - - /// - /// 序号 - /// - [JsonProperty("order")] - [JsonPropertyName("order")] - public int? Order { get; set; } -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Department/Dto/DepartmentHttpOutput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Department/Dto/DepartmentHttpOutput.cs deleted file mode 100644 index 151e93c8..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Department/Dto/DepartmentHttpOutput.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy; - -/// -/// 部门Id列表输出参数 -/// -public class DepartmentIdOutput : BaseWorkOutput -{ - /// - /// id - /// - [JsonProperty("department_id")] - [JsonPropertyName("department_id")] - public List DepartmentList { get; set; } -} - -/// -/// 部门Id输出参数 -/// -public class DepartmentItemOutput -{ - /// - /// 部门名称 - /// - [JsonProperty("id")] - [JsonPropertyName("id")] - public long? Id { get; set; } - - /// - /// 父部门id - /// - [JsonProperty("parentid")] - [JsonPropertyName("parentid")] - public long? ParentId { get; set; } - - /// - /// 序号 - /// - [JsonProperty("order")] - [JsonPropertyName("order")] - public int? Order { get; set; } -} - -/// -/// 部门输出参数 -/// -public class DepartmentOutput -{ - /// - /// 部门名称 - /// - [JsonProperty("id")] - [JsonPropertyName("id")] - public long? Id { get; set; } - - /// - /// 父部门id - /// - [JsonProperty("parentid")] - [JsonPropertyName("parentid")] - public long? ParentId { get; set; } - - /// - /// 部门名称 - /// - [JsonProperty("name")] - [JsonPropertyName("name")] - public string Name { get; set; } - - /// - /// 英文名称 - /// - [JsonProperty("name_en")] - [JsonPropertyName("name_en")] - public string NameEn { get; set; } - - /// - /// 部门负责人列表 - /// - [JsonProperty("department_leader")] - [JsonPropertyName("department_leader")] - public List Leaders { get; set; } - - /// - /// 序号 - /// - [JsonProperty("order")] - [JsonPropertyName("order")] - public int? Order { get; set; } -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Department/IDepartmentHttp.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Department/IDepartmentHttp.cs deleted file mode 100644 index 72172e63..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Department/IDepartmentHttp.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy.AppChat; - -/// -/// 部门远程调用服务 -/// -public interface IDepartmentHttp : IHttpDeclarative -{ - /// - /// 创建部门 - /// https://developer.work.weixin.qq.com/document/path/90205 - /// - /// - /// - /// - [Post("https://qyapi.weixin.qq.com/cgi-bin/department/create")] - Task Create([Query("access_token")] string accessToken, [Body] DepartmentHttpInput body); - - /// - /// 修改部门 - /// https://developer.work.weixin.qq.com/document/path/90206 - /// - /// - /// - /// - [Post("https://qyapi.weixin.qq.com/cgi-bin/department/update")] - Task Update([Query("access_token")] string accessToken, [Body] DepartmentHttpInput body); - - /// - /// 删除部门 - /// https://developer.work.weixin.qq.com/document/path/90207 - /// - /// - /// - /// - [Get("https://qyapi.weixin.qq.com/cgi-bin/department/delete")] - Task Delete([Query("access_token")] string accessToken, [Query] long id); - - /// - /// 获取部门Id列表 - /// https://developer.work.weixin.qq.com/document/path/90208 - /// - /// - /// - /// - [Get("https://qyapi.weixin.qq.com/cgi-bin/department/simplelist")] - Task SimpleList([Query("access_token")] string accessToken, [Query] long id); - - /// - /// 获取部门详情 - /// https://developer.work.weixin.qq.com/document/path/90208 - /// - /// - /// - /// - [Get("https://qyapi.weixin.qq.com/cgi-bin/department/get")] - Task Get([Query("access_token")] string accessToken, [Query] long id); -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Tag/Dto/TagHttpInput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Tag/Dto/TagHttpInput.cs deleted file mode 100644 index 81e0adde..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Tag/Dto/TagHttpInput.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy; - -/// -/// 标签输入参数 -/// -public class TagHttpInput -{ - /// - /// 标签id - /// - [JsonProperty("tagid")] - [JsonPropertyName("tagid")] - public long? TagId { get; set; } - - /// - /// 标签名称 - /// - [JsonProperty("tagname")] - [JsonPropertyName("tagname")] - public string TagName { get; set; } -} - -/// -/// 增加标签成员输入参数 -/// -public class TagUsersTagInput -{ - /// - /// 标签id - /// - [JsonProperty("tagid")] - [JsonPropertyName("tagid")] - public long TagId { get; set; } - - /// - /// 企业成员ID列表 - /// - [JsonProperty("userlist")] - [JsonPropertyName("userlist")] - public List UserList { get; set; } - - /// - /// 企业部门ID列表 - /// - [JsonProperty("partylist")] - [JsonPropertyName("partylist")] - public List PartyList { get; set; } -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Tag/Dto/TagHttpOutput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Tag/Dto/TagHttpOutput.cs deleted file mode 100644 index 30c0ae25..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Tag/Dto/TagHttpOutput.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy; - -/// -/// 新增标签输出参数 -/// -public class TagIdHttpOutput : BaseWorkOutput -{ - /// - /// 标签Id - /// - [JsonProperty("tagid")] - [JsonPropertyName("tagid")] - public long? TagId { get; set; } -} - -/// -/// 标签列表输出参数 -/// -public class TagListHttpOutput : BaseWorkOutput -{ - /// - /// 标签Id - /// - [JsonProperty("taglist")] - [JsonPropertyName("taglist")] - public List TagList { get; set; } -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Tag/ITagHttp.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Tag/ITagHttp.cs deleted file mode 100644 index c72b5015..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Proxy/Tag/ITagHttp.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin.Proxy; - -/// -/// 标签远程调用服务 -/// -public interface ITagHttp : IHttpDeclarative -{ - /// - /// 创建标签 - /// https://developer.work.weixin.qq.com/document/path/90210 - /// - /// - /// - /// - [Post("https://qyapi.weixin.qq.com/cgi-bin/tag/create")] - Task Create([Query("access_token")] string accessToken, [Body] TagHttpInput body); - - /// - /// 更新标签名字 - /// https://developer.work.weixin.qq.com/document/path/90211 - /// - /// - /// - /// - [Post("https://qyapi.weixin.qq.com/cgi-bin/tag/update")] - Task Update([Query("access_token")] string accessToken, [Body] TagHttpInput body); - - /// - /// 删除标签 - /// https://developer.work.weixin.qq.com/document/path/90212 - /// - /// - /// - /// - [Get("https://qyapi.weixin.qq.com/cgi-bin/tag/delete")] - Task Delete([Query("access_token")] string accessToken, [Query("tagid")] long tagId); - - /// - /// 获取标签详情 - /// https://developer.work.weixin.qq.com/document/path/90213 - /// - /// - /// - /// - [Get("https://qyapi.weixin.qq.com/cgi-bin/tag/get")] - Task Get([Query("access_token")] string accessToken, [Query("tagid")] long tagId); - - /// - /// 增加标签成员 - /// https://developer.work.weixin.qq.com/document/path/90214 - /// - /// - /// - /// - [Post("https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers")] - Task AddTagUsers([Query("access_token")] string accessToken, [Body] TagUsersTagInput body); - - /// - /// 删除标签成员 - /// https://developer.work.weixin.qq.com/document/path/90215 - /// - /// - /// - /// - [Post("https://qyapi.weixin.qq.com/cgi-bin/tag/deltagusers")] - Task DelTagUsers([Query("access_token")] string accessToken, [Body] TagUsersTagInput body); - - /// - /// 获取标签列表 - /// https://developer.work.weixin.qq.com/document/path/90216 - /// - /// - /// - [Get("https://qyapi.weixin.qq.com/cgi-bin/tag/list")] - Task List([Query("access_token")] string accessToken); -} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/SeedData/SysConfigSeedData.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/SeedData/SysConfigSeedData.cs new file mode 100644 index 00000000..e9a6d283 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/SeedData/SysConfigSeedData.cs @@ -0,0 +1,29 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +using Admin.NET.Plugin.WorkWeixin.Const; + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 系统配置表种子数据 +/// +[IgnoreUpdateSeed] +public class SysConfigSeedData : ISqlSugarEntitySeedData +{ + /// + /// 种子数据 + /// + /// + public IEnumerable HasData() + { + return + [ + new SysConfig{ Id=1330000000001, Name="企业微信CorpId", Code=WorkWeixinConst.WorkWeixinCorpId, Value="", SysFlag=YesNoEnum.Y, Remark= "企业微信接口获取凭证的CorpId", OrderNo=1000, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2025-04-10 00:00:00") }, + new SysConfig{ Id=1330000000002, Name="企业微信CorpSecret", Code=WorkWeixinConst.WorkWeixinCorpSecret, Value="", SysFlag=YesNoEnum.Y, Remark= "企业微信接口获取凭证的CorpSecret", OrderNo=1000, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2025-04-10 00:00:00") }, + ]; + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/BaseWorkWxDto.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/BaseWorkWxDto.cs new file mode 100644 index 00000000..bc4bea2b --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/BaseWorkWxDto.cs @@ -0,0 +1,94 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 需授权基类,继承此类,自动追加令牌 +/// +public class AuthWorkWxInput +{ +} + +/// +/// 企业微信接口输出基类 +/// +public class BaseWorkWxOutput +{ + /// + /// 返回码 + /// + [CustomJsonProperty("errcode")] + public int ErrCode { get; set; } + + /// + /// 对返回码的文本描述内容 + /// + [CustomJsonProperty("errmsg")] + public string ErrMsg { get; set; } +} + +/// +/// 企业微信接口输出基类 +/// +public class BasePageWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 分页游标,下次请求时填写以获取之后分页的记录。如果该字段返回空则表示已没有更多数据 + /// + [CustomJsonProperty("next_cursor")] + public string NextCursor { get; set; } +} + +/// +/// 带id的输出参数 +/// +public class BaseIdWorkWxOutput : BaseWorkWxOutput +{ + /// + /// id + /// + [CustomJsonProperty("id")] + public long? Id { get; set; } +} + +/// +/// 获取接口凭证输入参数 +/// +/// https://developer.work.weixin.qq.com/document/path/91039 +[HttpRemoteApi(Action = "gettoken", Desc = "获取接口凭证", HttpMethod = HttpMethodEnum.Get)] +public class TokenWorkWxInput +{ + /// + /// 企业标识 + /// + [CustomJsonProperty("corpid")] + public string CorpId { get; set; } + + /// + /// 企业密钥 + /// + [CustomJsonProperty("corpsecret")] + public string CorpSecret { get; set; } +} + +/// +/// 获取接口凭证输出参数 +/// +public class TokenWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 获取到的凭证 + /// + [CustomJsonProperty("access_token")] + public string AccessToken { get; set; } + + /// + /// 凭证的有效时间(秒) + /// + [CustomJsonProperty("expires_in")] + public int ExpiresIn { get; set; } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/AppChatMessageFactory.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/AppChatMessageFactory.cs new file mode 100644 index 00000000..6dbacfd7 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/AppChatMessageFactory.cs @@ -0,0 +1,102 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 应用推送消息工厂类 +/// +public static class AppChatMessageFactory +{ + /// + /// 文本消息 + /// + public static TextAppChatMessage CreateTextMessage(string chatId, string content, int? safe = null) + { + return new TextAppChatMessage + { + ChatId = chatId, + Text = new TextContent { Content = content }, + Safe = safe + }; + } + + /// + /// 图片消息 + /// + public static ImageAppChatMessage CreateImageMessage(string chatId, string mediaId, int? safe = null) + { + return new ImageAppChatMessage + { + ChatId = chatId, + Image = new MediaContent { MediaId = mediaId }, + Safe = safe + }; + } + + /// + /// 语音消息 + /// + public static VoiceAppChatMessage CreateVoiceMessage(string chatId, string mediaId) + { + return new VoiceAppChatMessage + { + ChatId = chatId, + Voice = new MediaContent { MediaId = mediaId } + }; + } + + /// + /// 视频消息 + /// + public static VideoAppChatMessage CreateVideoMessage(string chatId, string mediaId, string title = null, string description = null, int? safe = null) + { + return new VideoAppChatMessage + { + ChatId = chatId, + Video = new VideoContent { MediaId = mediaId, Title = title, Description = description }, + Safe = safe + }; + } + + /// + /// 文件消息 + /// + public static FileAppChatMessage CreateFileMessage(string chatId, string mediaId, int? safe = null) + { + return new FileAppChatMessage + { + ChatId = chatId, + File = new MediaContent { MediaId = mediaId }, + Safe = safe + }; + } + + /// + /// 文本卡片消息 + /// + public static TextCardAppChatMessage CreateTextCardMessage(string chatId, string title, string description, string url, string btnTxt = null, int? safe = null) + { + return new TextCardAppChatMessage + { + ChatId = chatId, + TextCard = new TextCardContent { Title = title, Description = description, Url = url, BtnTxt = btnTxt }, + Safe = safe + }; + } + + /// + /// Markdown消息 + /// + public static MarkdownAppChatMessage CreateMarkdownMessage(string chatId, string content) + { + return new MarkdownAppChatMessage + { + ChatId = chatId, + Markdown = new MarkdownContent { Content = content } + }; + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/Dto/AppChatMessageInput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/Dto/AppChatMessageInput.cs new file mode 100644 index 00000000..01f87eda --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/Dto/AppChatMessageInput.cs @@ -0,0 +1,410 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 应用推送消息基类 +/// +[HttpRemoteApi(Action = "appchat/send", Desc = "应用消息推送", HttpMethod = HttpMethodEnum.Post)] +public abstract class AppChatMessageInput : AuthWorkWxInput +{ + /// + /// 群聊id + /// + /// 是否必填:是 + [CustomJsonProperty("chatid")] + [Required(ErrorMessage = "群聊ID不能为空")] + public string ChatId { get; set; } + + /// + /// 消息类型 + /// + /// 是否必填:是 + [CustomJsonProperty("msgtype")] + [Required(ErrorMessage = "消息类型不能为空")] + public abstract string MsgType { get; } + + /// + /// 表示是否是保密消息,0表示否,1表示是,默认0 + /// + /// 是否必填:否 + [CustomJsonProperty("safe")] + [Range(0, 1, ErrorMessage = "安全标识只能是0或1")] + public int? Safe { get; set; } +} + +/// +/// 文本消息 +/// +/// +///
特殊说明:content字段可以支持换行,换行符请用转义过的'\n' +///
+public class TextAppChatMessage : AppChatMessageInput +{ + public override string MsgType => "text"; + + /// + /// 文本消息内容 + /// + [CustomJsonProperty("text")] + [Required(ErrorMessage = "文本消息内容不能为空")] + public TextContent Text { get; set; } = new TextContent(); +} + +/// +/// 图片消息 +/// +public class ImageAppChatMessage : AppChatMessageInput +{ + public override string MsgType => "image"; + + /// + /// 图片消息内容 + /// + [CustomJsonProperty("image")] + [Required(ErrorMessage = "图片消息内容不能为空")] + public MediaContent Image { get; set; } = new MediaContent(); +} + +/// +/// 语音消息 +/// +public class VoiceAppChatMessage : AppChatMessageInput +{ + public override string MsgType => "voice"; + + /// + /// 语音消息内容 + /// + [CustomJsonProperty("voice")] + [Required(ErrorMessage = "语音消息内容不能为空")] + public MediaContent Voice { get; set; } = new MediaContent(); +} + +/// +/// 视频消息 +/// +public class VideoAppChatMessage : AppChatMessageInput +{ + public override string MsgType => "video"; + + /// + /// 视频消息内容 + /// + [CustomJsonProperty("video")] + [Required(ErrorMessage = "视频消息内容不能为空")] + public VideoContent Video { get; set; } = new VideoContent(); +} + +/// +/// 文件消息 +/// +public class FileAppChatMessage : AppChatMessageInput +{ + public override string MsgType => "file"; + + /// + /// 文件消息内容 + /// + [CustomJsonProperty("file")] + [Required(ErrorMessage = "文件消息内容不能为空")] + public MediaContent File { get; set; } = new MediaContent(); +} + +/// +/// 文本卡片消息 +/// +public class TextCardAppChatMessage : AppChatMessageInput +{ + public override string MsgType => "textcard"; + + /// + /// 文本卡片消息内容 + /// + [CustomJsonProperty("textcard")] + [Required(ErrorMessage = "文本卡片消息内容不能为空")] + public TextCardContent TextCard { get; set; } = new TextCardContent(); +} + +/// +/// 图文消息 +/// +public class NewsAppChatMessage : AppChatMessageInput +{ + public override string MsgType => "news"; + + /// + /// 图文消息内容 + /// + [CustomJsonProperty("news")] + [Required(ErrorMessage = "图文消息内容不能为空")] + public NewsContent News { get; set; } = new NewsContent(); +} + +/// +/// 图文消息(mpnews) +/// +public class MpNewsAppChatMessage : AppChatMessageInput +{ + public override string MsgType => "mpnews"; + + /// + /// 图文消息内容 + /// + [CustomJsonProperty("mpnews")] + [Required(ErrorMessage = "图文消息内容不能为空")] + public MpNewsContent MpNews { get; set; } = new MpNewsContent(); +} + +/// +/// Markdown消息 +/// +public class MarkdownAppChatMessage : AppChatMessageInput +{ + public override string MsgType => "markdown"; + + /// + /// Markdown消息内容 + /// + [CustomJsonProperty("markdown")] + [Required(ErrorMessage = "Markdown消息内容不能为空")] + public MarkdownContent Markdown { get; set; } = new MarkdownContent(); +} + +/// +/// 文本消息内容 +/// +public class TextContent +{ + /// + /// 消息内容,最长不超过2048个字节 + /// + /// 是否必填:是 + [CustomJsonProperty("content")] + [Required(ErrorMessage = "消息内容不能为空")] + [StringLength(2048, ErrorMessage = "消息内容长度不能超过2048个字节")] + public string Content { get; set; } +} + +/// +/// 媒体内容基类 +/// +public class MediaContent +{ + /// + /// 媒体文件id,可以调用上传临时素材接口获取 + /// + /// 是否必填:是 + [CustomJsonProperty("media_id")] + [Required(ErrorMessage = "媒体文件ID不能为空")] + public string MediaId { get; set; } +} + +/// +/// 视频消息内容 +/// +public class VideoContent : MediaContent +{ + /// + /// 视频消息的标题,不超过128个字节,超过会自动截断 + /// + /// 是否必填:否 + [CustomJsonProperty("title")] + [StringLength(128, ErrorMessage = "视频标题长度不能超过128个字节")] + public string Title { get; set; } + + /// + /// 视频消息的描述,不超过512个字节,超过会自动截断 + /// + /// 是否必填:否 + [CustomJsonProperty("description")] + [StringLength(512, ErrorMessage = "视频描述长度不能超过512个字节")] + public string Description { get; set; } +} + +/// +/// 文本卡片消息内容 +/// +public class TextCardContent +{ + /// + /// 标题,不超过128个字节,超过会自动截断 + /// + /// 是否必填:是 + [CustomJsonProperty("title")] + [Required(ErrorMessage = "标题不能为空")] + [StringLength(128, ErrorMessage = "标题长度不能超过128个字节")] + public string Title { get; set; } + + /// + /// 描述,不超过512个字节,超过会自动截断 + /// + /// 是否必填:是 + [CustomJsonProperty("description")] + [Required(ErrorMessage = "描述不能为空")] + [StringLength(512, ErrorMessage = "描述长度不能超过512个字节")] + public string Description { get; set; } + + /// + /// 点击后跳转的链接 + /// + /// 是否必填:是 + [CustomJsonProperty("url")] + [Required(ErrorMessage = "跳转链接不能为空")] + [Url(ErrorMessage = "链接格式不正确")] + public string Url { get; set; } + + /// + /// 按钮文字。默认为"详情",不超过4个文字,超过自动截断 + /// + /// 是否必填:否 + [CustomJsonProperty("btntxt")] + [StringLength(4, ErrorMessage = "按钮文字长度不能超过4个字符")] + public string BtnTxt { get; set; } +} + +/// +/// 图文消息文章 +/// +public class Article +{ + /// + /// 标题,不超过128个字节,超过会自动截断 + /// + /// 是否必填:是 + [CustomJsonProperty("title")] + [Required(ErrorMessage = "文章标题不能为空")] + [StringLength(128, ErrorMessage = "文章标题长度不能超过128个字节")] + public string Title { get; set; } + + /// + /// 描述,不超过512个字节,超过会自动截断 + /// + /// 是否必填:否 + [CustomJsonProperty("description")] + [StringLength(512, ErrorMessage = "文章描述长度不能超过512个字节")] + public string Description { get; set; } + + /// + /// 点击后跳转的链接 + /// + /// 是否必填:是 + [CustomJsonProperty("url")] + [Required(ErrorMessage = "文章链接不能为空")] + [Url(ErrorMessage = "文章链接格式不正确")] + public string Url { get; set; } + + /// + /// 图文消息的图片链接,支持JPG、PNG格式 + /// + /// 是否必填:否 + [CustomJsonProperty("picurl")] + [Url(ErrorMessage = "图片链接格式不正确")] + public string PicUrl { get; set; } +} + +/// +/// 图文消息内容 +/// +public class NewsContent +{ + /// + /// 图文消息,一个图文消息支持1到8条图文 + /// + /// 是否必填:是 + [CustomJsonProperty("articles")] + [Required(ErrorMessage = "图文消息不能为空")] + [MinLength(1, ErrorMessage = "至少需要1条图文消息")] + [MaxLength(8, ErrorMessage = "最多只能有8条图文消息")] + public List
Articles { get; set; } = new List
(); +} + +/// +/// 图文消息(mpnews)文章 +/// +public class MpArticle +{ + /// + /// 标题,不超过128个字节,超过会自动截断 + /// + /// 是否必填:是 + [CustomJsonProperty("title")] + [Required(ErrorMessage = "文章标题不能为空")] + [StringLength(128, ErrorMessage = "文章标题长度不能超过128个字节")] + public string Title { get; set; } + + /// + /// 图文消息缩略图的media_id,可以通过素材管理接口获得 + /// + /// 是否必填:是 + [CustomJsonProperty("thumb_media_id")] + [Required(ErrorMessage = "缩略图媒体ID不能为空")] + public string ThumbMediaId { get; set; } + + /// + /// 图文消息的作者,不超过64个字节 + /// + /// 是否必填:否 + [CustomJsonProperty("author")] + [StringLength(64, ErrorMessage = "作者长度不能超过64个字节")] + public string Author { get; set; } + + /// + /// 图文消息点击"阅读原文"之后的页面链接 + /// + /// 是否必填:否 + [CustomJsonProperty("content_source_url")] + [Url(ErrorMessage = "原文链接格式不正确")] + public string ContentSourceUrl { get; set; } + + /// + /// 图文消息的内容,支持html标签,不超过666K个字节 + /// + /// 是否必填:是 + [CustomJsonProperty("content")] + [Required(ErrorMessage = "文章内容不能为空")] + public string Content { get; set; } + + /// + /// 图文消息的描述,不超过512个字节,超过会自动截断 + /// + /// 是否必填:否 + [CustomJsonProperty("digest")] + [StringLength(512, ErrorMessage = "摘要长度不能超过512个字节")] + public string Digest { get; set; } +} + +/// +/// 图文消息(mpnews)内容 +/// +public class MpNewsContent +{ + /// + /// 图文消息,一个图文消息支持1到8条图文 + /// + /// 是否必填:是 + [CustomJsonProperty("articles")] + [Required(ErrorMessage = "图文消息不能为空")] + [MinLength(1, ErrorMessage = "至少需要1条图文消息")] + [MaxLength(8, ErrorMessage = "最多只能有8条图文消息")] + public List Articles { get; set; } = new List(); +} + +/// +/// Markdown消息内容 +/// +public class MarkdownContent +{ + /// + /// markdown内容,最长不超过2048个字节,必须是utf8编码 + /// + /// 是否必填:是 + [CustomJsonProperty("content")] + [Required(ErrorMessage = "Markdown内容不能为空")] + [StringLength(2048, ErrorMessage = "Markdown内容长度不能超过2048个字节")] + public string Content { get; set; } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/Dto/AppChatWorkWxInput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/Dto/AppChatWorkWxInput.cs new file mode 100644 index 00000000..14e9a667 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/Dto/AppChatWorkWxInput.cs @@ -0,0 +1,148 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 创建群聊会话输入参数 +/// +/// +///
最后更新:2024/11/29 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/appchat/create?access_token=ACCESS_TOKEN +///
权限说明:只允许企业自建应用调用,且应用的可见范围必须是根部门 +///
限制说明:群成员人数不可超过管理端配置的"群成员人数上限",最大不可超过2000人,每企业创建群数不可超过1000/天 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90245 +///
+[HttpRemoteApi(Action = "appchat/create", Desc = "创建群聊会话", HttpMethod = HttpMethodEnum.Post)] +public class CreateChatWorkWxInput : AuthWorkWxInput +{ + /// + /// 群聊名,最多50个utf8字符,超过将截断 + /// + /// 是否必填:否 + [CustomJsonProperty("name")] + [StringLength(50, ErrorMessage = "群聊名长度不能超过50个字符")] + public string Name { get; set; } + + /// + /// 指定群主的id。如果不指定,系统会随机从userlist中选一人作为群主 + /// + /// 是否必填:否 + [Required(ErrorMessage = "群主id不能为空")] + [CustomJsonProperty("owner")] + public string Owner { get; set; } + + /// + /// 群成员id列表。至少2人,至多2000人 + /// + /// 是否必填:是 + [CustomJsonProperty("userlist")] + [NotEmpty(ErrorMessage = "群成员列表不能为空")] + [MinLength(2, ErrorMessage = "群成员至少需要2人")] + [MaxLength(2000, ErrorMessage = "群成员最多不能超过2000人")] + public List UserList { get; set; } + + /// + /// 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id + /// + /// 是否必填:否 + [CustomJsonProperty("chatid")] + [RegularExpression("^[a-zA-Z0-9]{0,32}$", ErrorMessage = "群ID只能包含字母和数字,且长度不能超过32个字符")] + public string ChatId { get; set; } +} + +/// +/// 修改群聊会话输入参数 +/// +/// +///
最后更新:2023/04/18 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/appchat/update?access_token=ACCESS_TOKEN +///
权限说明:只允许企业自建应用调用,且应用的可见范围必须是根部门 +///
限制说明:chatid所代表的群必须是该应用所创建,群成员人数不可超过2000人,每企业变更群的次数不可超过1000次/小时 +///
文档地址:https://developer.work.weixin.qq.com/document/path/98913 +///
+[HttpRemoteApi(Action = "appchat/update", Desc = "修改群聊会话", HttpMethod = HttpMethodEnum.Post)] +public class UpdateChatWorkWxInput : AuthWorkWxInput +{ + /// + /// 群聊id + /// + /// 是否必填:是 + [CustomJsonProperty("chatid")] + [Required(ErrorMessage = "群聊ID不能为空")] + public string ChatId { get; set; } + + /// + /// 新的群聊名。若不需更新,请忽略此参数。最多50个utf8字符,超过将截断 + /// + /// 是否必填:否 + [CustomJsonProperty("name")] + [StringLength(50, ErrorMessage = "群聊名长度不能超过50个字符")] + public string Name { get; set; } + + /// + /// 新群主的id。若不需更新,请忽略此参数。课程群聊群主必须拥有课程群创建权限,del_user_list包含群主时本字段必填 + /// + /// 是否必填:否 + [CustomJsonProperty("owner")] + public string Owner { get; set; } + + /// + /// 添加成员的id列表 + /// + /// 是否必填:否 + [CustomJsonProperty("add_user_list")] + [MaxLength(2000, ErrorMessage = "添加成员列表不能超过2000人")] + public List AddUserList { get; set; } + + /// + /// 踢出成员的id列表 + /// + /// 是否必填:否 + [CustomJsonProperty("del_user_list")] + [MaxLength(2000, ErrorMessage = "踢出成员列表不能超过2000人")] + public List DelUserList { get; set; } +} + +/// +/// 获取群聊会话输入参数 +/// +/// +///
最后更新:2023/04/20 +///
请求方式:GET(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/appchat/get?access_token=ACCESS_TOKEN&chatid=CHATID +///
权限说明:只允许企业自建应用调用,且应用的可见范围必须是根部门;chatid所代表的群必须是该应用所创建;第三方不可调用 +///
文档地址:https://developer.work.weixin.qq.com/document/path/98914 +///
+[HttpRemoteApi(Action = "appchat/get", Desc = "获取群聊会话", HttpMethod = HttpMethodEnum.Get)] +public class GetChatWorkWxInput : AuthWorkWxInput +{ + /// + /// 群聊id + /// + /// 是否必填:是 + [CustomJsonProperty("chatid")] + [Required(ErrorMessage = "群聊ID不能为空")] + public string ChatId { get; set; } +} + +/// +/// 应用消息推送输入参数 +/// +/// +///
最后更新:2024/07/11 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/appchat/send?access_token=ACCESS_TOKEN +///
权限说明:只允许企业自建应用调用,且应用的可见范围必须是根部门 +///
限制说明:chatid所代表的群必须是该应用所创建,消息发送量有限制,成员接收消息有限制 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90248 +///
+[HttpRemoteApi(Action = "appchat/send", Desc = "应用消息推送", HttpMethod = HttpMethodEnum.Post)] +public class SendChatWorkWxInput +{ +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/Dto/AppChatWorkWxOutput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/Dto/AppChatWorkWxOutput.cs new file mode 100644 index 00000000..64a4cdb1 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/Dto/AppChatWorkWxOutput.cs @@ -0,0 +1,67 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 创建群聊会话输出参数 +/// +public class CreateChatWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 群聊的唯一标志 + /// + [CustomJsonProperty("chatid")] + public string ChatId { get; set; } +} + +/// +/// 获取群聊会话输出参数 +/// +public class GetChatWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 群聊信息 + /// + [CustomJsonProperty("chat_info")] + public ChatInfoDto ChatInfo { get; set; } + + /// + /// 群聊信息 + /// + public class ChatInfoDto + { + /// + /// 群聊唯一标志 + /// + [CustomJsonProperty("chatid")] + public string ChatId { get; set; } + + /// + /// 群聊名 + /// + [CustomJsonProperty("name")] + public string Name { get; set; } + + /// + /// 群主id + /// + [CustomJsonProperty("owner")] + public string Owner { get; set; } + + /// + /// 群成员id列表 + /// + [CustomJsonProperty("userlist")] + public List UserList { get; set; } + + /// + /// 群聊类型,0表示普通群聊,1表示课程群聊 + /// + [CustomJsonProperty("chat_type")] + public int ChatType { get; set; } + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/WorkWxAppChatService.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/WorkWxAppChatService.cs new file mode 100644 index 00000000..977ecf00 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Chat/WorkWxAppChatService.cs @@ -0,0 +1,121 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 企业微信群聊会话服务 🧩 +/// +public class WorkWxAppChatService(WorkWxBaseService baseService) : ITransient +{ + /// + /// 创建群聊会话 + /// + /// + /// + public async Task CreateChat(CreateChatWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 修改群聊会话 + /// + /// + /// + public async Task UpdateChat(UpdateChatWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 推送纯文本消息 + /// + /// 群聊Id + /// 内容 + /// 安全标识 + /// + public async Task SendText(string chatId, string content, int? safe = null) + { + return await baseService.SendAsync(AppChatMessageFactory.CreateTextMessage(chatId, content, safe)); + } + + /// + /// 推送文本卡片消息 + /// + /// 群聊Id + /// 标题 + /// 内容 + /// 跳转链接 + /// 按钮文本 + /// 安全标识 + /// + public async Task SendTextCard(string chatId, string title, string description, string url, string btnTxt = null, int? safe = null) + { + return await baseService.SendAsync(AppChatMessageFactory.CreateTextCardMessage(chatId, title, description, url, btnTxt, safe)); + } + + /// + /// 推送Markdown消息 + /// + /// 群聊Id + /// 内容 + /// + public async Task SendMarkdown(string chatId, string content) + { + return await baseService.SendAsync(AppChatMessageFactory.CreateMarkdownMessage(chatId, content)); + } + + /// + /// 推送图片消息 + /// + /// 群聊Id + /// 媒体文件ID + /// 安全标识 + /// + public async Task SendImage(string chatId, string mediaId, int? safe = null) + { + return await baseService.SendAsync(AppChatMessageFactory.CreateImageMessage(chatId, mediaId, safe)); + } + + /// + /// 推送语音消息 + /// + /// 群聊Id + /// 媒体文件ID + /// + public async Task SendVoice(string chatId, string mediaId) + { + return await baseService.SendAsync(AppChatMessageFactory.CreateVoiceMessage(chatId, mediaId)); + } + + /// + /// 推送视频消息 + /// + /// 群聊Id + /// 媒体文件ID + /// 标题 + /// 描述 + /// 安全标识 + /// + public async Task SendVideo(string chatId, string mediaId, string title = null, string description = null, int? safe = null) + { + return await baseService.SendAsync(AppChatMessageFactory.CreateVideoMessage(chatId, mediaId, title, description, safe)); + } + + /// + /// 推送文件消息 + /// + /// 群聊Id + /// 媒体文件ID + /// 安全标识 + /// + public async Task SendFile(string chatId, string mediaId, int? safe = null) + { + return await baseService.SendAsync(AppChatMessageFactory.CreateFileMessage(chatId, mediaId, safe)); + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Dept/Dto/DeptWorkWxInput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Dept/Dto/DeptWorkWxInput.cs new file mode 100644 index 00000000..1a49cf01 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Dept/Dto/DeptWorkWxInput.cs @@ -0,0 +1,168 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 创建部门 +/// +/// +///
最后更新:2023/08/15 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/department/create?access_token=ACCESS_TOKEN +///
权限说明:第三方仅通讯录应用可以调用 +///
限制说明:部门最大层级15层,部门总数不超过3万个,每个部门下节点不超过3万个 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90205 +///
+[HttpRemoteApi(Action = "department/create", Desc = "创建部门", HttpMethod = HttpMethodEnum.Post)] +public class CreateDeptWorkWxInput : AuthWorkWxInput +{ + /// + /// 部门名称。同一个层级的部门名称不能重复。长度限制为1~64个UTF-8字符,字符不能包括\:*?"<>| + /// + /// 是否必填:是 + [CustomJsonProperty("name")] + [Required(ErrorMessage = "部门名称不能为空")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "部门名称长度必须在1-64个字符之间")] + [RegularExpression(@"^[^\\:*?""<>|]+$", ErrorMessage = "部门名称不能包含\\:*?\"<>|字符")] + public string Name { get; set; } + + /// + /// 英文名称。同一个层级的部门名称不能重复。需要在管理后台开启多语言支持才能生效 + /// + /// 是否必填:否 + [CustomJsonProperty("name_en")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "英文名称长度必须在1-64个字符之间")] + [RegularExpression(@"^[^\\:*?""<>|]*$", ErrorMessage = "英文名称不能包含\\:*?\"<>|字符")] + public string NameEn { get; set; } + + /// + /// 父部门id,32位整型 + /// + /// 是否必填:是 + [CustomJsonProperty("parentid")] + [Required(ErrorMessage = "父部门ID不能为空")] + [Range(1, long.MaxValue, ErrorMessage = "父部门ID必须大于0")] + public long ParentId { get; set; } + + /// + /// 在父部门中的次序值。order值大的排序靠前 + /// + /// 是否必填:否 + [CustomJsonProperty("order")] + [Range(0, 4294967295, ErrorMessage = "排序值必须在0-4294967295之间")] + public long? Order { get; set; } + + /// + /// 部门id,32位整型,指定时必须大于1 + /// + /// 是否必填:否 + [CustomJsonProperty("id")] + [Range(2, long.MaxValue, ErrorMessage = "部门ID必须大于1")] + public long? Id { get; set; } +} + +/// +/// 更新部门 +/// +/// +///
最后更新:2023/08/15 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/department/update?access_token=ACCESS_TOKEN +///
权限说明:应用须拥有指定部门的管理权限,第三方仅通讯录应用可以调用 +///
限制说明:部门最大层级15层,部门总数不超过3万个,每个部门下节点不超过3万个 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90206 +///
+[HttpRemoteApi(Action = "department/update", Desc = "更新部门", HttpMethod = HttpMethodEnum.Post)] +public class UpdateDeptWorkWxInput : AuthWorkWxInput +{ + /// + /// 部门id + /// + /// 是否必填:是 + [CustomJsonProperty("id")] + [Required(ErrorMessage = "部门ID不能为空")] + [Range(1, long.MaxValue, ErrorMessage = "部门ID必须大于0")] + public long Id { get; set; } + + /// + /// 部门名称。长度限制为1~64个UTF-8字符,字符不能包括\:*?"<>| + /// + /// 是否必填:否 + [CustomJsonProperty("name")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "部门名称长度必须在1-64个字符之间")] + [RegularExpression(@"^[^\\:*?""<>|]+$", ErrorMessage = "部门名称不能包含\\:*?\"<>|字符")] + public string Name { get; set; } + + /// + /// 英文名称,需要在管理后台开启多语言支持才能生效 + /// + /// 是否必填:否 + [CustomJsonProperty("name_en")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "英文名称长度必须在1-64个字符之间")] + [RegularExpression(@"^[^\\:*?""<>|]*$", ErrorMessage = "英文名称不能包含\\:*?\"<>|字符")] + public string NameEn { get; set; } + + /// + /// 父部门id + /// + /// 是否必填:否 + [CustomJsonProperty("parentid")] + [Range(1, long.MaxValue, ErrorMessage = "父部门ID必须大于0")] + public long? ParentId { get; set; } + + /// + /// 在父部门中的次序值。order值大的排序靠前 + /// + /// 是否必填:否 + [CustomJsonProperty("order")] + [Range(0, 4294967295, ErrorMessage = "排序值必须在0-4294967295之间")] + public long? Order { get; set; } +} + +/// +/// 删除部门 +/// +/// +///
最后更新:2020/03/30 +///
请求方式:GET(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/department/delete?access_token=ACCESS_TOKEN&id=ID +///
权限说明:应用须拥有指定部门的管理权限,第三方仅通讯录应用可以调用 +///
限制说明:不能删除根部门;不能删除含有子部门、成员的部门 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90207 +///
+[HttpRemoteApi(Action = "department/delete", Desc = "删除部门", HttpMethod = HttpMethodEnum.Get)] +public class DeleteDeptWorkWxInput : AuthWorkWxInput +{ + /// + /// 部门id。(注:不能删除根部门;不能删除含有子部门、成员的部门) + /// + /// 是否必填:是 + [CustomJsonProperty("id")] + [Required(ErrorMessage = "部门ID不能为空")] + [Range(2, long.MaxValue, ErrorMessage = "部门ID必须大于1(不能删除根部门)")] + public long Id { get; set; } +} + +/// +/// 获取子部门ID列表 +/// +/// +///
最后更新:2023/09/06 +///
请求方式:GET(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/department/simplelist?access_token=ACCESS_TOKEN&id=ID +///
文档地址:https://developer.work.weixin.qq.com/document/path/95350 +///
+[HttpRemoteApi(Action = "department/simplelist", Desc = "获取子部门ID列表", HttpMethod = HttpMethodEnum.Get)] +public class DeptSimpleListWorkWxInput : AuthWorkWxInput +{ + /// + /// 部门id。获取指定部门及其下的子部门(递归) + /// + /// 是否必填:否 + [CustomJsonProperty("id")] + public long? Id { get; set; } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Dept/Dto/DeptWorkWxOutput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Dept/Dto/DeptWorkWxOutput.cs new file mode 100644 index 00000000..03129407 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Dept/Dto/DeptWorkWxOutput.cs @@ -0,0 +1,86 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 创建部门输出参数 +/// +public class CreateDepartmentWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 创建的部门id + /// + [CustomJsonProperty("id")] + public long Id { get; set; } +} + +/// +/// 部门基础信息 +/// +public class DeptWorkWxBaseInfo +{ + /// + /// 部门id + /// + [CustomJsonProperty("id")] + public long Id { get; set; } + + /// + /// 父部门id + /// + [CustomJsonProperty("parentid")] + public long ParentId { get; set; } + + /// + /// 排序值 + /// + [CustomJsonProperty("order")] + public long Order { get; set; } +} + +/// +/// 获取子部门ID列表输出参数 +/// +public class DeptSimpleListWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 部门列表数据 + /// + [CustomJsonProperty("department_id")] + public List DeptList { get; set; } = new List(); + + /// + /// 部门数量 + /// + public int Count => DeptList?.Count ?? 0; + + /// + /// 获取部门ID列表 + /// + public List DeptIds => DeptList?.Select(d => d.Id).ToList() ?? new List(); + + /// + /// 按父部门分组 + /// + public Dictionary> GroupByParent() + { + var result = new Dictionary>(); + + if (DeptList == null) return result; + + foreach (var dept in DeptList) + { + if (!result.ContainsKey(dept.ParentId)) + { + result[dept.ParentId] = new List(); + } + result[dept.ParentId].Add(dept); + } + + return result; + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Dept/WorkWxDeptService.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Dept/WorkWxDeptService.cs new file mode 100644 index 00000000..a2f462d0 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Dept/WorkWxDeptService.cs @@ -0,0 +1,54 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 企业微信部门服务 🧩 +/// +public class WorkWxDeptService(WorkWxBaseService baseService) : ITransient +{ + /// + /// 创建部门 + /// + /// + /// + public async Task Create(CreateDeptWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 更新部门 + /// + /// + /// + public async Task Update(UpdateDeptWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 删除部门 + /// + /// 部门id + /// + public async Task Delete(long id) + { + return await baseService.SendAsync(new() { Id = id }); + } + + /// + /// 获取子部门ID列表 + /// + /// 部门id。获取指定部门及其下的子部门(递归) + /// + public async Task Get(long id) + { + return await baseService.SendAsync(new() { Id = id }); + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Tag/Dto/TagWorkWxInput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Tag/Dto/TagWorkWxInput.cs new file mode 100644 index 00000000..f24b278c --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Tag/Dto/TagWorkWxInput.cs @@ -0,0 +1,166 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 创建标签 +/// +/// +///
最后更新:2020/06/08 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/tag/create?access_token=ACCESS_TOKEN +///
权限说明:创建的标签属于该应用,只有该应用的secret才可以增删成员 +///
限制说明:标签总数不能超过3000个 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90210 +///
+[HttpRemoteApi(Action = "tag/create", Desc = "创建标签", HttpMethod = HttpMethodEnum.Post)] +public class CreateTagWorkWxInput : AuthWorkWxInput +{ + /// + /// 标签名称,长度限制为32个字以内(汉字或英文字母),标签名不可与其他标签重名 + /// + [CustomJsonProperty("tagname")] + [Required] + [StringLength(32, MinimumLength = 1)] + public string TagName { get; set; } + + /// + /// 标签id,非负整型,指定此参数时新增的标签会生成对应的标签id,不指定时则以目前最大的id自增 + /// + [CustomJsonProperty("tagid")] + public long? TagId { get; set; } +} + +/// +/// 更新标签名字 +/// +/// +///
最后更新:2020/04/02 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/tag/update?access_token=ACCESS_TOKEN +///
权限说明:调用的应用必须是指定标签的创建者 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90211 +///
+[HttpRemoteApi(Action = "tag/update", Desc = "更新标签名字", HttpMethod = HttpMethodEnum.Post)] +public class UpdateTagWorkWxInput : AuthWorkWxInput +{ + /// + /// 标签ID + /// + [CustomJsonProperty("tagid")] + [Required] + public long TagId { get; set; } + + /// + /// 标签名称,长度限制为32个字(汉字或英文字母),标签不可与其他标签重名 + /// + [CustomJsonProperty("tagname")] + [Required] + [StringLength(32, MinimumLength = 1)] + public string TagName { get; set; } +} + +/// +/// 删除标签 +/// +/// +///
最后更新:2020/04/02 +///
请求方式:GET(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/tag/delete?access_token=ACCESS_TOKEN&tagid=TAGID +///
权限说明:调用的应用必须是指定标签的创建者 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90212 +///
+[HttpRemoteApi(Action = "tag/delete", Desc = "删除标签", HttpMethod = HttpMethodEnum.Get)] +public class DeleteTagWorkWxInput : AuthWorkWxInput +{ + /// + /// 标签ID + /// + /// 是否必填:是 + [CustomJsonProperty("tagid")] + [Required(ErrorMessage = "标签ID不能为空")] + [Range(1, long.MaxValue, ErrorMessage = "标签ID必须大于0")] + public long TagId { get; set; } +} + +/// +/// 获取标签成员 +/// +[HttpRemoteApi(Action = "tag/get", Desc = "获取标签成员", HttpMethod = HttpMethodEnum.Get)] +public class TagMembersWorkWxInput : AuthWorkWxInput +{ + /// + /// 标签ID + /// + [CustomJsonProperty("tagid")] + [Required] + public long TagId { get; set; } +} + +/// +/// 增加标签成员 +/// +[HttpRemoteApi(Action = "tag/addtagusers", Desc = "增加标签成员", HttpMethod = HttpMethodEnum.Post)] +public class AddTagMembersWorkWxInput : AuthWorkWxInput +{ + /// + /// 标签ID + /// + [CustomJsonProperty("tagid")] + [Required] + public long TagId { get; set; } + + /// + /// 企业成员ID列表 + /// + [CustomJsonProperty("userlist")] + [MaxLength(1000)] + public List UserList { get; set; } + + /// + /// 企业部门ID列表 + /// + [CustomJsonProperty("partylist")] + [MaxLength(100)] + public List PartyList { get; set; } +} + +/// +/// 删除标签成员 +/// +[HttpRemoteApi(Action = "tag/deltagusers", Desc = "删除标签成员", HttpMethod = HttpMethodEnum.Post)] +public class DeleteTagMembersWorkWxInput : AuthWorkWxInput +{ + /// + /// 标签ID + /// + [CustomJsonProperty("tagid")] + [Required] + public long TagId { get; set; } + + /// + /// 企业成员ID列表 + /// + [CustomJsonProperty("userlist")] + [MaxLength(1000)] + public List UserList { get; set; } + + /// + /// 企业部门ID列表 + /// + [CustomJsonProperty("partylist")] + [MaxLength(100)] + public List PartyList { get; set; } +} + +/// +/// 获取标签列表 +/// +[HttpRemoteApi(Action = "tag/list", Desc = "获取标签列表", HttpMethod = HttpMethodEnum.Get)] +public class TagListWorkWxInput : AuthWorkWxInput +{ +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Tag/Dto/TagWorkWxOutput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Tag/Dto/TagWorkWxOutput.cs new file mode 100644 index 00000000..edc85d45 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Tag/Dto/TagWorkWxOutput.cs @@ -0,0 +1,127 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 创建标签输出 +/// +public class CreateTagWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 标签id + /// + [CustomJsonProperty("tagid")] + public long TagId { get; set; } +} + +/// +/// 标签成员信息 +/// +public class TagMemberWorkWxInfo +{ + /// + /// 成员账号 + /// + [CustomJsonProperty("userid")] + public string UserId { get; set; } + + /// + /// 成员名称 + /// + [CustomJsonProperty("name")] + public string Name { get; set; } +} + +/// +/// 获取标签成员输出 +/// +public class TagMembersWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 标签名 + /// + [CustomJsonProperty("tagname")] + public string TagName { get; set; } + + /// + /// 标签中包含的成员列表 + /// + [CustomJsonProperty("userlist")] + public List UserList { get; set; } = new List(); + + /// + /// 标签中包含的部门id列表 + /// + [CustomJsonProperty("partylist")] + public List PartyList { get; set; } = new List(); +} + +/// +/// 增加标签成员输出 +/// +public class AddTagMembersWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 非法的成员账号列表 + /// + [CustomJsonProperty("invalidlist")] + public string InvalidList { get; set; } + + /// + /// 非法的部门id列表 + /// + [CustomJsonProperty("invalidparty")] + public List InvalidParty { get; set; } = new List(); +} + +/// +/// 删除标签成员输出 +/// +public class DeleteTagMembersWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 非法的成员账号列表 + /// + [CustomJsonProperty("invalidlist")] + public string InvalidList { get; set; } + + /// + /// 非法的部门id列表 + /// + [CustomJsonProperty("invalidparty")] + public List InvalidParty { get; set; } = new List(); +} + +/// +/// 标签基本信息 +/// +public class TagInfoWorkWx +{ + /// + /// 标签id + /// + [CustomJsonProperty("tagid")] + public long TagId { get; set; } + + /// + /// 标签名 + /// + [CustomJsonProperty("tagname")] + public string TagName { get; set; } +} + +/// +/// 获取标签列表输出 +/// +public class TagListWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 标签列表 + /// + [CustomJsonProperty("taglist")] + public List TagList { get; set; } = new List(); +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Tag/WorkWxTagService.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Tag/WorkWxTagService.cs new file mode 100644 index 00000000..41d98b2c --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/Tag/WorkWxTagService.cs @@ -0,0 +1,83 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 企业微信标签服务 🧩 +/// +public class WorkWxTagService(WorkWxBaseService baseService) : ITransient +{ + /// + /// 创建标签 + /// + /// + /// + public async Task Create(CreateTagWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 更新标签 + /// + /// + /// + public async Task Update(UpdateTagWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 删除标签 + /// + /// 标签ID + /// + public async Task Delete(long id) + { + return await baseService.SendAsync(new(){ TagId = id }); + } + + /// + /// 获取标签成员 + /// + /// 标签ID + /// + public async Task Get(long id) + { + return await baseService.SendAsync(new(){ TagId = id }); + } + + /// + /// 增加标签成员 + /// + /// + /// + public async Task AddTagMembers(AddTagMembersWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 删除标签成员 + /// + /// + /// + public async Task DeleteTagMembers(DeleteTagMembersWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 获取标签列表 + /// + /// + public async Task GetList() + { + return await baseService.SendAsync(new()); + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/Dto/UserWorkWxInput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/Dto/UserWorkWxInput.cs new file mode 100644 index 00000000..1245e812 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/Dto/UserWorkWxInput.cs @@ -0,0 +1,1008 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 创建成员输入参数 +/// +/// +///
最后更新:2025/07/25 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/create?access_token=ACCESS_TOKEN +///
权限说明:仅通讯录同步助手或第三方通讯录应用可调用 +///
限制说明:每个部门下的部门、成员总数不能超过3万个 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90195 +///
+[HttpRemoteApi(Action = "user/create", Desc = "创建成员", HttpMethod = HttpMethodEnum.Post)] +public class CreateUserWorkWxInput : AuthWorkWxInput +{ + /// + /// 成员UserID。对应管理端的账号,企业内必须唯一。长度为1~64个字节。只能由数字、字母和"_-@."四种字符组成,且第一个字符必须是数字或字母。 + /// + /// 是否必填:是 + [CustomJsonProperty("userid")] + [Required(ErrorMessage = "用户ID不能为空")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "用户ID长度必须在1-64个字节之间")] + [RegularExpression("^[a-zA-Z0-9][a-zA-Z0-9_\\-@.]*$", ErrorMessage = "用户ID只能由数字、字母和_-@.组成,且必须以数字或字母开头")] + public string UserId { get; set; } + + /// + /// 成员名称。长度为1~64个utf8字符 + /// + /// 是否必填:是 + [CustomJsonProperty("name")] + [Required(ErrorMessage = "成员名称不能为空")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "成员名称长度必须在1-64个字符之间")] + public string Name { get; set; } + + /// + /// 成员别名。长度1~64个utf8字符 + /// + /// 是否必填:否 + [CustomJsonProperty("alias")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "别名长度必须在1-64个字符之间")] + public string Alias { get; set; } + + /// + /// 手机号码。企业内必须唯一,mobile/email二者不能同时为空 + /// + /// 是否必填:否 + [CustomJsonProperty("mobile")] + public string Mobile { get; set; } + + /// + /// 邮箱。企业内必须唯一,mobile/email二者不能同时为空 + /// + /// 是否必填:否 + [CustomJsonProperty("email")] + [EmailAddress(ErrorMessage = "邮箱格式不正确")] + [StringLength(64, MinimumLength = 6, ErrorMessage = "邮箱长度必须在6-64个字节之间")] + public string Email { get; set; } + + /// + /// 成员所属部门id列表,不超过100个 + /// + /// 是否必填:否 + [CustomJsonProperty("department")] + [MaxLength(100, ErrorMessage = "部门数量不能超过100个")] + public List Department { get; set; } + + /// + /// 部门内的排序值,个数必须和参数department的个数一致 + /// + /// 是否必填:否 + [CustomJsonProperty("order")] + public List Order { get; set; } + + /// + /// 职务信息。长度为0~128个字符 + /// + /// 是否必填:否 + [CustomJsonProperty("position")] + [StringLength(128, ErrorMessage = "职务信息长度不能超过128个字符")] + public string Position { get; set; } + + /// + /// 性别。1表示男性,2表示女性 + /// + /// 是否必填:否 + [CustomJsonProperty("gender")] + [Range(1, 2, ErrorMessage = "性别只能是1(男)或2(女)")] + public int? Gender { get; set; } + + /// + /// 企业邮箱账号 + /// + /// 是否必填:否 + [CustomJsonProperty("biz_mail")] + [EmailAddress(ErrorMessage = "企业邮箱格式不正确")] + [StringLength(64, MinimumLength = 6, ErrorMessage = "企业邮箱长度必须在6-64个字节之间")] + public string BizMail { get; set; } + + /// + /// 座机。32字节以内,由纯数字、"-"、"+"或","组成 + /// + /// 是否必填:否 + [CustomJsonProperty("telephone")] + [StringLength(32, ErrorMessage = "座机号码长度不能超过32个字节")] + [RegularExpression("^[0-9\\-\\+,]*$", ErrorMessage = "座机号码只能包含数字、-、+或,")] + public string Telephone { get; set; } + + /// + /// 在所在的部门内是否为部门负责人。1表示为部门负责人,0表示非部门负责人 + /// + /// 是否必填:否 + [CustomJsonProperty("is_leader_in_dept")] + public List IsLeaderInDept { get; set; } + + /// + /// 直属上级UserID,设置范围为企业内成员,可以设置最多1个上级 + /// + /// 是否必填:否 + [CustomJsonProperty("direct_leader")] + [MaxLength(1, ErrorMessage = "直属上级最多只能设置1个")] + public List DirectLeader { get; set; } + + /// + /// 成员头像的mediaid,通过素材管理接口上传图片获得的mediaid + /// + /// 是否必填:否 + [CustomJsonProperty("avatar_mediaid")] + public string AvatarMediaId { get; set; } + + /// + /// 启用/禁用成员。1表示启用成员,0表示禁用成员 + /// + /// 是否必填:否 + [CustomJsonProperty("enable")] + [Range(0, 1, ErrorMessage = "启用状态只能是0(禁用)或1(启用)")] + public int? Enable { get; set; } + + /// + /// 扩展属性 + /// + /// 是否必填:否 + [CustomJsonProperty("extattr")] + public ExtAttrDto ExtAttr { get; set; } + + /// + /// 是否邀请该成员使用企业微信,默认值为true + /// + /// 是否必填:否 + [CustomJsonProperty("to_invite")] + public bool? ToInvite { get; set; } + + /// + /// 对外职务,长度12个汉字内 + /// + /// 是否必填:否 + [CustomJsonProperty("external_position")] + [StringLength(36, ErrorMessage = "对外职务长度不能超过12个汉字")] + public string ExternalPosition { get; set; } + + /// + /// 成员对外属性 + /// + /// 是否必填:否 + [CustomJsonProperty("external_profile")] + public ExternalProfileDto ExternalProfile { get; set; } + + /// + /// 地址。长度最大128个字符 + /// + /// 是否必填:否 + [CustomJsonProperty("address")] + [StringLength(128, ErrorMessage = "地址长度不能超过128个字符")] + public string Address { get; set; } + + /// + /// 主部门 + /// + /// 是否必填:否 + [CustomJsonProperty("main_department")] + public long? MainDepartment { get; set; } + + /// + /// 扩展属性 + /// + public class ExtAttrDto + { + /// + /// 扩展属性列表 + /// + [CustomJsonProperty("attrs")] + public List Attrs { get; set; } + } + + /// + /// 扩展属性项 + /// + public class ExtAttrItemDto + { + /// + /// 属性类型:0-文本,1-网页 + /// + [CustomJsonProperty("type")] + [Range(0, 1, ErrorMessage = "扩展属性类型只能是0(文本)或1(网页)")] + public int Type { get; set; } + + /// + /// 属性名称 + /// + [CustomJsonProperty("name")] + [Required(ErrorMessage = "扩展属性名称不能为空")] + public string Name { get; set; } + + /// + /// 文本属性值(当type=0时使用) + /// + [CustomJsonProperty("text")] + public ExtAttrTextDto Text { get; set; } + + /// + /// 网页属性值(当type=1时使用) + /// + [CustomJsonProperty("web")] + public ExtAttrWebDto Web { get; set; } + } + + /// + /// 扩展属性文本值 + /// + public class ExtAttrTextDto + { + /// + /// 文本值 + /// + [CustomJsonProperty("value")] + [Required(ErrorMessage = "文本值不能为空")] + public string Value { get; set; } + } + + /// + /// 扩展属性网页值 + /// + public class ExtAttrWebDto + { + /// + /// 网页链接 + /// + [CustomJsonProperty("url")] + [Required(ErrorMessage = "网页链接不能为空")] + [Url(ErrorMessage = "网页链接格式不正确")] + public string Url { get; set; } + + /// + /// 网页标题 + /// + [CustomJsonProperty("title")] + [Required(ErrorMessage = "网页标题不能为空")] + public string Title { get; set; } + } + + /// + /// 成员对外属性 + /// + public class ExternalProfileDto + { + /// + /// 企业简称 + /// + [CustomJsonProperty("external_corp_name")] + public string ExternalCorpName { get; set; } + + /// + /// 视频号信息 + /// + [CustomJsonProperty("wechat_channels")] + public WechatChannelsDto WechatChannels { get; set; } + + /// + /// 对外属性列表 + /// + [CustomJsonProperty("external_attr")] + public List ExternalAttr { get; set; } + } + + /// + /// 视频号信息 + /// + public class WechatChannelsDto + { + /// + /// 视频号名称 + /// + [CustomJsonProperty("nickname")] + public string Nickname { get; set; } + } + + /// + /// 对外属性项 + /// + public class ExternalAttrItemDto + { + /// + /// 属性类型:0-文本,1-网页,2-小程序 + /// + [CustomJsonProperty("type")] + [Range(0, 2, ErrorMessage = "对外属性类型只能是0(文本)、1(网页)或2(小程序)")] + public int Type { get; set; } + + /// + /// 属性名称 + /// + [CustomJsonProperty("name")] + [Required(ErrorMessage = "对外属性名称不能为空")] + public string Name { get; set; } + + /// + /// 文本属性值(当type=0时使用) + /// + [CustomJsonProperty("text")] + public ExtAttrTextDto Text { get; set; } + + /// + /// 网页属性值(当type=1时使用) + /// + [CustomJsonProperty("web")] + public ExtAttrWebDto Web { get; set; } + + /// + /// 小程序属性值(当type=2时使用) + /// + [CustomJsonProperty("miniprogram")] + public ExternalAttrMiniprogramDto Miniprogram { get; set; } + } + + /// + /// 对外属性小程序值 + /// + public class ExternalAttrMiniprogramDto + { + /// + /// 小程序appid + /// + [CustomJsonProperty("appid")] + [Required(ErrorMessage = "小程序appid不能为空")] + public string AppId { get; set; } + + /// + /// 小程序页面路径 + /// + [CustomJsonProperty("pagepath")] + [Required(ErrorMessage = "小程序页面路径不能为空")] + public string PagePath { get; set; } + + /// + /// 小程序标题 + /// + [CustomJsonProperty("title")] + [Required(ErrorMessage = "小程序标题不能为空")] + public string Title { get; set; } + } +} + +/// +/// 读取成员输入参数 +/// +/// +///
最后更新:2025/03/06 +///
请求方式:GET(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=USERID +///
权限说明:应用须拥有指定成员的查看权限 +///
限制说明:应用只能获取可见范围内的成员信息,且每种应用获取的字段有所不同 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90196 +///
+[HttpRemoteApi(Action = "user/get", Desc = "读取成员", HttpMethod = HttpMethodEnum.Get)] +public class UserWorkWxInput : AuthWorkWxInput +{ + /// + /// 成员UserID。对应管理端的账号,企业内必须唯一。不区分大小写,长度为1~64个字节 + /// + /// 是否必填:是 + [CustomJsonProperty("userid")] + [Required(ErrorMessage = "用户ID不能为空")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "用户ID长度必须在1-64个字节之间")] + public string UserId { get; set; } +} + +/// +/// 更新成员输入参数 +/// +/// +///
最后更新:2025/07/25 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/update?access_token=ACCESS_TOKEN +///
权限说明:仅通讯录同步助手或第三方通讯录应用可调用 +///
限制说明:每个部门下的部门、成员总数不能超过3万个 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90197 +///
+[HttpRemoteApi(Action = "user/update", Desc = "更新成员", HttpMethod = HttpMethodEnum.Post)] +public class UpdateUserWorkWxInput : AuthWorkWxInput +{ + /// + /// 成员UserID。对应管理端的账号,企业内必须唯一。不区分大小写,长度为1~64个字节 + /// + /// 是否必填:是 + [CustomJsonProperty("userid")] + [Required(ErrorMessage = "用户ID不能为空")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "用户ID长度必须在1-64个字节之间")] + [RegularExpression("^[a-zA-Z0-9][a-zA-Z0-9_\\-@.]*$", ErrorMessage = "用户ID只能由数字、字母和_-@.组成,且必须以数字或字母开头")] + public string UserId { get; set; } + + /// + /// 成员名称。长度为1~64个utf8字符 + /// + /// 是否必填:否 + [CustomJsonProperty("name")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "成员名称长度必须在1-64个字符之间")] + public string Name { get; set; } + + /// + /// 别名。长度为1-64个utf8字符 + /// + /// 是否必填:否 + [CustomJsonProperty("alias")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "别名长度必须在1-64个字符之间")] + public string Alias { get; set; } + + /// + /// 手机号码。企业内必须唯一 + /// + /// 是否必填:否 + [CustomJsonProperty("mobile")] + public string Mobile { get; set; } + + /// + /// 邮箱。企业内必须唯一 + /// + /// 是否必填:否 + [CustomJsonProperty("email")] + [EmailAddress(ErrorMessage = "邮箱格式不正确")] + [StringLength(64, MinimumLength = 6, ErrorMessage = "邮箱长度必须在6-64个字节之间")] + public string Email { get; set; } + + /// + /// 企业邮箱账号。长度6~63个字节,且为有效的企业邮箱格式 + /// + /// 是否必填:否 + [CustomJsonProperty("biz_mail")] + [EmailAddress(ErrorMessage = "企业邮箱格式不正确")] + [StringLength(63, MinimumLength = 6, ErrorMessage = "企业邮箱长度必须在6-63个字节之间")] + public string BizMail { get; set; } + + /// + /// 企业邮箱别名。最多可设置5个别名,更新时为覆盖式更新 + /// + /// 是否必填:否 + [CustomJsonProperty("biz_mail_alias")] + public BizMailAliasDto BizMailAlias { get; set; } + + /// + /// 成员所属部门id列表,不超过100个 + /// + /// 是否必填:否 + [CustomJsonProperty("department")] + [MaxLength(100, ErrorMessage = "部门数量不能超过100个")] + public List Department { get; set; } + + /// + /// 部门内的排序值,数量必须和department一致 + /// + /// 是否必填:否 + [CustomJsonProperty("order")] + public List Order { get; set; } + + /// + /// 职务信息。长度为0~128个utf8字符 + /// + /// 是否必填:否 + [CustomJsonProperty("position")] + [StringLength(128, ErrorMessage = "职务信息长度不能超过128个字符")] + public string Position { get; set; } + + /// + /// 性别。1表示男性,2表示女性 + /// + /// 是否必填:否 + [CustomJsonProperty("gender")] + [Range(1, 2, ErrorMessage = "性别只能是1(男)或2(女)")] + public int? Gender { get; set; } + + /// + /// 座机。由1-32位的纯数字、"-"、"+"或","组成 + /// + /// 是否必填:否 + [CustomJsonProperty("telephone")] + [StringLength(32, MinimumLength = 1, ErrorMessage = "座机号码长度必须在1-32个字节之间")] + [RegularExpression("^[0-9\\-\\+,]*$", ErrorMessage = "座机号码只能包含数字、-、+或,")] + public string Telephone { get; set; } + + /// + /// 部门负责人字段,个数必须和department一致,表示在所在的部门内是否为负责人。0-否,1-是 + /// + /// 是否必填:否 + [CustomJsonProperty("is_leader_in_dept")] + public List IsLeaderInDept { get; set; } + + /// + /// 直属上级,可以设置企业范围内成员为直属上级,最多设置1个 + /// + /// 是否必填:否 + [CustomJsonProperty("direct_leader")] + [MaxLength(1, ErrorMessage = "直属上级最多只能设置1个")] + public List DirectLeader { get; set; } + + /// + /// 成员头像的mediaid,通过素材管理接口上传图片获得的mediaid + /// + /// 是否必填:否 + [CustomJsonProperty("avatar_mediaid")] + public string AvatarMediaId { get; set; } + + /// + /// 启用/禁用成员。1表示启用成员,0表示禁用成员 + /// + /// 是否必填:否 + [CustomJsonProperty("enable")] + [Range(0, 1, ErrorMessage = "启用状态只能是0(禁用)或1(启用)")] + public int? Enable { get; set; } + + /// + /// 扩展属性 + /// + /// 是否必填:否 + [CustomJsonProperty("extattr")] + public CreateUserWorkWxInput.ExtAttrDto ExtAttr { get; set; } + + /// + /// 对外职务,不超过12个汉字 + /// + /// 是否必填:否 + [CustomJsonProperty("external_position")] + [StringLength(36, ErrorMessage = "对外职务长度不能超过12个汉字")] + public string ExternalPosition { get; set; } + + /// + /// 成员对外属性 + /// + /// 是否必填:否 + [CustomJsonProperty("external_profile")] + public CreateUserWorkWxInput.ExternalProfileDto ExternalProfile { get; set; } + + /// + /// 地址。长度最大128个字符 + /// + /// 是否必填:否 + [CustomJsonProperty("address")] + [StringLength(128, ErrorMessage = "地址长度不能超过128个字符")] + public string Address { get; set; } + + /// + /// 主部门 + /// + /// 是否必填:否 + [CustomJsonProperty("main_department")] + public long? MainDepartment { get; set; } + + /// + /// 新的用户ID(如果userid由系统自动生成,则仅允许修改一次) + /// + /// 是否必填:否 + [CustomJsonProperty("new_userid")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "新用户ID长度必须在1-64个字节之间")] + [RegularExpression("^[a-zA-Z0-9][a-zA-Z0-9_\\-@.]*$", ErrorMessage = "新用户ID只能由数字、字母和_-@.组成,且必须以数字或字母开头")] + public string NewUserId { get; set; } + + /// + /// 验证部门相关字段的一致性 + /// + public bool ValidateDepartmentFields() + { + if (Department != null && Order != null && Department.Count != Order.Count) + { + return false; + } + + if (Department != null && IsLeaderInDept != null && Department.Count != IsLeaderInDept.Count) + { + return false; + } + + return true; + } + + /// + /// 获取验证错误信息 + /// + public string GetValidationErrors() + { + if (Department != null && Order != null && Department.Count != Order.Count) + { + return "部门数量和排序值数量不一致"; + } + + if (Department != null && IsLeaderInDept != null && Department.Count != IsLeaderInDept.Count) + { + return "部门数量和负责人标识数量不一致"; + } + + return null; + } + + /// + /// 企业邮箱别名 + /// + public class BizMailAliasDto + { + /// + /// 企业邮箱别名列表,最多可设置5个别名 + /// + [CustomJsonProperty("item")] + [MaxLength(5, ErrorMessage = "企业邮箱别名最多只能设置5个")] + public List Items { get; set; } = new List(); + + /// + /// 清空所有别名 + /// + public void Clear() + { + Items?.Clear(); + } + + /// + /// 添加别名 + /// + public bool AddAlias(string alias) + { + if (Items == null) Items = new List(); + if (Items.Count >= 5) return false; + + Items.Add(alias); + return true; + } + + /// + /// 移除别名 + /// + public bool RemoveAlias(string alias) + { + return Items?.Remove(alias) ?? false; + } + } +} + +/// +/// 删除成员 +/// +/// +///
最后更新:2021/08/09 +///
请求方式:GET(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/delete?access_token=ACCESS_TOKEN&userid=USERID +///
权限说明:仅通讯录同步助手或第三方通讯录应用可调用 +///
特殊说明:若是绑定了腾讯企业邮,则会同时删除邮箱账号 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90198 +///
+[HttpRemoteApi(Action = "user/delete", Desc = "删除成员", HttpMethod = HttpMethodEnum.Get)] +public class DeleteUserWorkWxInput : AuthWorkWxInput +{ + /// + /// 成员UserID。对应管理端的账号 + /// + /// 是否必填:是 + [CustomJsonProperty("userid")] + [Required(ErrorMessage = "用户ID不能为空")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "用户ID长度必须在1-64个字节之间")] + public string UserId { get; set; } +} + +/// +/// 批量删除成员 +/// +/// +///
最后更新:2019/06/13 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete?access_token=ACCESS_TOKEN +///
权限说明:仅通讯录同步助手或第三方通讯录应用可调用 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90199 +///
+[HttpRemoteApi(Action = "user/batchdelete", Desc = "批量删除成员", HttpMethod = HttpMethodEnum.Post)] +public class BatchDeleteUserWorkWxInput : AuthWorkWxInput +{ + /// + /// 成员UserID列表。对应管理端的账号。最多支持200个。若存在无效UserID,直接返回错误 + /// + /// 是否必填:是 + [CustomJsonProperty("useridlist")] + [Required(ErrorMessage = "用户ID列表不能为空")] + [MinLength(1, ErrorMessage = "至少需要指定一个用户ID")] + [MaxLength(200, ErrorMessage = "单次最多只能删除200个用户")] + public List UserIdList { get; set; } +} + +/// +/// 获取部门成员 +/// +/// +///
最后更新:2022/08/22 +///
请求方式:GET(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID +///
权限说明:应用须拥有指定部门的查看权限 +///
限制说明:如需获取该部门及其子部门的所有成员,需逐层递归获取 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90200 +///
+[HttpRemoteApi(Action = "user/simplelist", Desc = "获取部门成员", HttpMethod = HttpMethodEnum.Get)] +public class DeptUserSimpleListWorkWxInput : AuthWorkWxInput +{ + /// + /// 获取的部门id + /// + /// 是否必填:是 + [CustomJsonProperty("department_id")] + [Required(ErrorMessage = "部门ID不能为空")] + public long DepartmentId { get; set; } + + /// + /// 是否递归获取子部门成员 + /// + /// 是否必填:否 + [CustomJsonProperty("fetch_child")] + public bool? FetchChild { get; set; } +} + +/// +/// 获取部门成员详情 +/// +/// +///
最后更新:2024/07/24 +///
请求方式:GET(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/list?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID +///
权限说明:应用须拥有指定部门的查看权限 +///
限制说明:应用只能获取可见范围内的成员信息,且每种应用获取的字段有所不同 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90201 +///
+[HttpRemoteApi(Action = "user/list", Desc = "获取部门成员详情", HttpMethod = HttpMethodEnum.Get)] +public class DeptUserDetailListWorkWxInput : AuthWorkWxInput +{ + /// + /// 获取的部门id + /// + /// 是否必填:是 + [CustomJsonProperty("department_id")] + [Required(ErrorMessage = "部门ID不能为空")] + public long DepartmentId { get; set; } + + /// + /// 是否递归获取子部门成员 + /// + /// 是否必填:否 + [CustomJsonProperty("fetch_child")] + public bool? FetchChild { get; set; } +} + +/// +/// userid转openid +/// +/// +///
最后更新:2022/01/20 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid?access_token=ACCESS_TOKEN +///
权限说明:成员必须处于应用的可见范围内 +///
使用场景:企业支付,在使用企业红包和向员工付款时使用 +///
限制说明:需要成员使用微信登录企业微信或者关注微信插件才能转成openid +///
文档地址:https://developer.work.weixin.qq.com/document/path/90202 +///
+[HttpRemoteApi(Action = "user/convert_to_openid", Desc = "userid转openid", HttpMethod = HttpMethodEnum.Post)] +public class ConvToOpenIdWorkWxInput : AuthWorkWxInput +{ + /// + /// 企业内的成员id + /// + /// 是否必填:是 + [CustomJsonProperty("userid")] + [Required(ErrorMessage = "用户ID不能为空")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "用户ID长度必须在1-64个字节之间")] + public string UserId { get; set; } +} + +/// +/// openid转userid +/// +/// +///
最后更新:2022/01/20 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_userid?access_token=ACCESS_TOKEN +///
权限说明:管理组需对openid对应的企业微信成员有查看权限 +///
使用场景:企业支付之后的结果查询 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90202 +///
+[HttpRemoteApi(Action = "user/convert_to_userid", Desc = "openid转userid", HttpMethod = HttpMethodEnum.Post)] +public class ConvToUserIdWorkWxInput : AuthWorkWxInput +{ + /// + /// 在使用企业支付之后,返回结果的openid + /// + /// 是否必填:是 + [CustomJsonProperty("openid")] + [Required(ErrorMessage = "OpenID不能为空")] + public string OpenId { get; set; } +} + +/// +/// 登录二次验证 +/// +/// +///
最后更新:2023/11/15 +///
请求方式:GET(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?access_token=ACCESS_TOKEN&userid=USERID +///
使用场景:安全性要求高的企业进行成员验证,开启二次验证后使用 +///
流程说明:成员登录→跳转企业验证页面→获取code→根据code获取userid→调用本接口完成验证 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90203 +///
+[HttpRemoteApi(Action = "user/authsucc", Desc = "登录二次验证", HttpMethod = HttpMethodEnum.Get)] +public class UserAuthSuccessWorkWxInput : AuthWorkWxInput +{ + /// + /// 成员UserID。对应管理端的账号 + /// + /// 是否必填:是 + [CustomJsonProperty("userid")] + [Required(ErrorMessage = "用户ID不能为空")] + [StringLength(64, MinimumLength = 1, ErrorMessage = "用户ID长度必须在1-64个字节之间")] + public string UserId { get; set; } +} + +/// +/// 邀请成员 +/// +/// +///
最后更新:2024/11/04 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/batch/invite?access_token=ACCESS_TOKEN +///
权限说明:须拥有指定成员、部门或标签的查看权限,第三方仅通讯录应用可调用 +///
限制说明:user, party, tag三者不能同时为空;邀请频率有限制 +///
文档地址:https://developer.work.weixin.qq.com/document/path/90213 +///
+[HttpRemoteApi(Action = "batch/invite", Desc = "邀请成员", HttpMethod = HttpMethodEnum.Post)] +public class InviteUserWorkWxInput : AuthWorkWxInput +{ + /// + /// 成员ID列表, 最多支持1000个 + /// + /// 是否必填:否 + [CustomJsonProperty("user")] + [MaxLength(1000, ErrorMessage = "成员ID列表最多支持1000个")] + public List User { get; set; } + + /// + /// 部门ID列表,最多支持100个 + /// + /// 是否必填:否 + [CustomJsonProperty("party")] + [MaxLength(100, ErrorMessage = "部门ID列表最多支持100个")] + public List Party { get; set; } + + /// + /// 标签ID列表,最多支持100个 + /// + /// 是否必填:否 + [CustomJsonProperty("tag")] + [MaxLength(100, ErrorMessage = "标签ID列表最多支持100个")] + public List Tag { get; set; } + + /// + /// 验证邀请参数 + /// + public (bool IsValid, string ErrorMessage) Validate() + { + if ((User == null || User.Count == 0) && + (Party == null || Party.Count == 0) && + (Tag == null || Tag.Count == 0)) + { + return (false, "user, party, tag三者不能同时为空"); + } + + return (true, null); + } +} + +/// +/// 获取加入企业二维码 +/// +/// +///
最后更新:2019/11/30 +///
请求方式:GET(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/corp/get_join_qrcode?access_token=ACCESS_TOKEN&size_type=SIZE_TYPE +///
权限说明:须拥有通讯录的管理权限,使用通讯录同步的Secret +///
文档地址:https://developer.work.weixin.qq.com/document/path/90214 +///
+[HttpRemoteApi(Action = "corp/get_join_qrcode", Desc = "获取加入企业二维码", HttpMethod = HttpMethodEnum.Get)] +public class JoinQrcodeWorkWxInput : AuthWorkWxInput +{ + /// + /// qrcode尺寸类型,1: 171 x 171; 2: 399 x 399; 3: 741 x 741; 4: 2052 x 2052 + /// + /// 是否必填:否 + [CustomJsonProperty("size_type")] + [Range(1, 4, ErrorMessage = "尺寸类型必须在1-4之间")] + public int? SizeType { get; set; } = 3; +} + +/// +/// 手机号获取userid +/// +/// +///
最后更新:2022/08/16 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=ACCESS_TOKEN +///
权限说明:应用须拥有指定成员的查看权限 +///
限制说明:请确保手机号的正确性,若出错的次数超出企业规模人数的20%,会导致1天不可调用 +///
文档地址:https://developer.work.weixin.qq.com/document/path/95402 +///
+[HttpRemoteApi(Action = "user/getuserid", Desc = "手机号获取userid", HttpMethod = HttpMethodEnum.Post)] +public class UserIdByMobileWorkWxInput : AuthWorkWxInput +{ + /// + /// 用户在企业微信通讯录中的手机号码。长度为5~32个字节 + /// + /// 是否必填:是 + [CustomJsonProperty("mobile")] + [Required(ErrorMessage = "手机号不能为空")] + [StringLength(32, MinimumLength = 5, ErrorMessage = "手机号长度必须在5-32个字节之间")] + public string Mobile { get; set; } +} + +/// +/// 邮箱获取userid +/// +/// +///
最后更新:2022/07/19 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/get_userid_by_email?access_token=ACCESS_TOKEN +///
权限说明:应用须拥有指定成员的查看权限 +///
限制说明:请确保邮箱的正确性,若出错的次数较多,会导致1天不可调用 +///
文档地址:https://developer.work.weixin.qq.com/document/path/95892 +///
+[HttpRemoteApi(Action = "user/get_userid_by_email", Desc = "邮箱获取userid", HttpMethod = HttpMethodEnum.Post)] +public class UserIdByEmailWorkWxInput : AuthWorkWxInput +{ + /// + /// 邮箱 + /// + /// 是否必填:是 + [CustomJsonProperty("email")] + [Required(ErrorMessage = "邮箱不能为空")] + [EmailAddress(ErrorMessage = "邮箱格式不正确")] + public string Email { get; set; } + + /// + /// 邮箱类型:1-企业邮箱(默认);2-个人邮箱 + /// + /// 是否必填:否 + [CustomJsonProperty("email_type")] + [Range(1, 2, ErrorMessage = "邮箱类型只能是1(企业邮箱)或2(个人邮箱)")] + public int? EmailType { get; set; } = 1; + + /// + /// 邮箱类型描述 + /// + public string EmailTypeDesc => EmailType switch + { + 1 => "企业邮箱", + 2 => "个人邮箱", + _ => "未知类型" + }; +} + +/// +/// 获取成员ID列表 +/// +/// +///
最后更新:2024/07/23 +///
请求方式:POST(HTTPS) +///
请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/list_id?access_token=ACCESS_TOKEN +///
权限说明:仅支持通过"通讯录同步secret"调用 +///
文档地址:https://developer.work.weixin.qq.com/document/path/96067 +///
+[HttpRemoteApi(Action = "user/list_id", Desc = "获取成员ID列表", HttpMethod = HttpMethodEnum.Post)] +public class UserIdListWorkWxInput : AuthWorkWxInput +{ + /// + /// 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用不填 + /// + /// 是否必填:否 + [CustomJsonProperty("cursor")] + public string Cursor { get; set; } + + /// + /// 分页,预期请求的数据量,取值范围 1 ~ 10000 + /// + /// 是否必填:否 + [CustomJsonProperty("limit")] + [Range(1, 10000, ErrorMessage = "分页大小必须在1-10000之间")] + public int? Limit { get; set; } = 1000; +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/Dto/UserWorkWxOutput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/Dto/UserWorkWxOutput.cs new file mode 100644 index 00000000..c2552fe2 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/Dto/UserWorkWxOutput.cs @@ -0,0 +1,899 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +using System.Text; +using System.Text.RegularExpressions; + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 创建成员输出参数 +/// +public class CreateUserWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 因填写不存在的部门,新增的部门列表 + /// + [CustomJsonProperty("created_department_list")] + public CreatedDepartmentListDto CreatedDepartmentList { get; set; } + + /// + /// 新增部门列表 + /// + public class CreatedDepartmentListDto + { + /// + /// 部门信息列表 + /// + [CustomJsonProperty("department_info")] + public List DepartmentInfo { get; set; } + } + + /// + /// 部门信息 + /// + public class DepartmentInfo + { + /// + /// 部门名称 + /// + [CustomJsonProperty("name")] + public string Name { get; set; } + + /// + /// 部门ID + /// + [CustomJsonProperty("id")] + public long Id { get; set; } + } +} + +/// +/// 读取成员输出参数 +/// +/// +///
注意:应用只能获取可见范围内的成员信息,且每种应用获取的字段有所不同 +///
从2022年6月20号开始,新创建的自建应用与代开发应用不再返回敏感字段 +///
+public class UserWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 成员UserID。对应管理端的账号,企业内必须唯一。不区分大小写,长度为1~64个字节;第三方应用返回的值为open_userid + /// + [CustomJsonProperty("userid")] + public string UserId { get; set; } + + /// + /// 成员名称;第三方不可获取,调用时返回userid以代替name;代开发自建应用需要管理员授权才返回 + /// + [CustomJsonProperty("name")] + public string Name { get; set; } + + /// + /// 成员所属部门id列表,仅返回该应用有查看权限的部门id + /// + [CustomJsonProperty("department")] + public List Department { get; set; } + + /// + /// 部门内的排序值,默认为0。数量必须和department一致,数值越大排序越前面 + /// + [CustomJsonProperty("order")] + public List Order { get; set; } + + /// + /// 职务信息;代开发自建应用需要管理员授权才返回 + /// + [CustomJsonProperty("position")] + public string Position { get; set; } + + /// + /// 手机号码,代开发自建应用需要管理员授权且成员oauth2授权获取 + /// + [CustomJsonProperty("mobile")] + public string Mobile { get; set; } + + /// + /// 性别。0表示未定义,1表示男性,2表示女性 + /// + [CustomJsonProperty("gender")] + public int? Gender { get; set; } + + /// + /// 性别描述 + /// + public string GenderDesc => Gender switch + { + 1 => "男", + 2 => "女", + 0 => "未定义", + _ => "未知" + }; + + /// + /// 邮箱,代开发自建应用需要管理员授权且成员oauth2授权获取 + /// + [CustomJsonProperty("email")] + public string Email { get; set; } + + /// + /// 企业邮箱,代开发自建应用需要管理员授权且成员oauth2授权获取 + /// + [CustomJsonProperty("biz_mail")] + public string BizMail { get; set; } + + /// + /// 表示在所在的部门内是否为部门负责人,数量与department一致 + /// + [CustomJsonProperty("is_leader_in_dept")] + public List IsLeaderInDept { get; set; } + + /// + /// 直属上级UserID,返回在应用可见范围内的直属上级列表,最多有1个直属上级 + /// + [CustomJsonProperty("direct_leader")] + public List DirectLeader { get; set; } + + /// + /// 头像url。代开发自建应用需要管理员授权且成员oauth2授权获取 + /// + [CustomJsonProperty("avatar")] + public string Avatar { get; set; } + + /// + /// 头像缩略图url + /// + [CustomJsonProperty("thumb_avatar")] + public string ThumbAvatar { get; set; } + + /// + /// 座机。代开发自建应用需要管理员授权才返回 + /// + [CustomJsonProperty("telephone")] + public string Telephone { get; set; } + + /// + /// 别名 + /// + [CustomJsonProperty("alias")] + public string Alias { get; set; } + + /// + /// 地址。代开发自建应用需要管理员授权且成员oauth2授权获取 + /// + [CustomJsonProperty("address")] + public string Address { get; set; } + + /// + /// 全局唯一ID。对于同一个服务商,不同应用获取到企业内同一个成员的open_userid是相同的 + /// + [CustomJsonProperty("open_userid")] + public string OpenUserId { get; set; } + + /// + /// 主部门,仅当应用对主部门有查看权限时返回 + /// + [CustomJsonProperty("main_department")] + public long? MainDepartment { get; set; } + + /// + /// 扩展属性 + /// + [CustomJsonProperty("extattr")] + public CreateUserWorkWxInput.ExtAttrDto ExtAttr { get; set; } + + /// + /// 激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业 + /// + [CustomJsonProperty("status")] + public int? Status { get; set; } + + /// + /// 状态描述 + /// + public string StatusDesc => Status switch + { + 1 => "已激活", + 2 => "已禁用", + 4 => "未激活", + 5 => "退出企业", + _ => "未知状态" + }; + + /// + /// 员工个人二维码URL + /// + [CustomJsonProperty("qr_code")] + public string QrCode { get; set; } + + /// + /// 对外职务 + /// + [CustomJsonProperty("external_position")] + public string ExternalPosition { get; set; } + + /// + /// 成员对外属性 + /// + [CustomJsonProperty("external_profile")] + public CreateUserWorkWxInput.ExternalProfileDto ExternalProfile { get; set; } + + /// + /// 是否为部门负责人(主部门) + /// + public bool IsLeader + { + get + { + if (IsLeaderInDept == null || Department == null || MainDepartment == null) + return false; + + var mainDeptIndex = Department.IndexOf(MainDepartment.Value); + return mainDeptIndex >= 0 && mainDeptIndex < IsLeaderInDept.Count && IsLeaderInDept[mainDeptIndex] == 1; + } + } + + /// + /// 是否已激活 + /// + public bool IsActive => Status == 1; + + /// + /// 获取主部门名称(需要外部传入部门映射) + /// + public string GetMainDepartmentName(Dictionary departmentMap) + { + if (MainDepartment.HasValue && departmentMap != null && departmentMap.TryGetValue(MainDepartment.Value, out var name)) + return name; + return null; + } + + /// + /// 视频号信息(响应) + /// + public class WechatChannelsResponse + { + /// + /// 视频号名称 + /// + [CustomJsonProperty("nickname")] + public string Nickname { get; set; } + + /// + /// 视频号状态 + /// + [CustomJsonProperty("status")] + public int? Status { get; set; } + } +} + +/// +/// 获取部门成员输出参数 +/// +public class DeptUserSimpleListWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 成员列表 + /// + [CustomJsonProperty("userlist")] + public List UserList { get; set; } = new List(); + + /// + /// 成员数量 + /// + public int Count => UserList?.Count ?? 0; + + /// + /// 是否包含成员 + /// + public bool HasUsers => Count > 0; + + /// + /// 获取用户ID列表 + /// + public List UserIds => UserList?.Select(u => u.UserId).ToList() ?? new List(); + + /// + /// 获取OpenUserID列表 + /// + public List OpenUserIds => UserList?.Where(u => !string.IsNullOrEmpty(u.OpenUserId)) + .Select(u => u.OpenUserId) + .ToList() ?? new List(); + + /// + /// 部门成员简略信息 + /// + public class DepartmentUserSimpleInfo + { + /// + /// 成员UserID。对应管理端的账号 + /// + [CustomJsonProperty("userid")] + public string UserId { get; set; } + + /// + /// 成员名称,第三方应用可能返回userid代替name + /// + [CustomJsonProperty("name")] + public string Name { get; set; } + + /// + /// 成员所属部门列表。列表项为部门ID,32位整型 + /// + [CustomJsonProperty("department")] + public List Department { get; set; } + + /// + /// 全局唯一ID。对于同一个服务商,不同应用获取到企业内同一个成员的open_userid是相同的 + /// + [CustomJsonProperty("open_userid")] + public string OpenUserId { get; set; } + + /// + /// 获取显示名称(优先显示name,如果没有则显示userid) + /// + public string DisplayName => !string.IsNullOrEmpty(Name) ? Name : UserId; + + /// + /// 是否包含指定部门 + /// + public bool ContainsDepartment(long departmentId) + { + return Department?.Contains(departmentId) ?? false; + } + + /// + /// 获取主部门(第一个部门) + /// + public long? MainDepartment => Department?.FirstOrDefault(); + } +} + +/// +/// 获取部门成员详情输出参数 +/// +public class DeptUserDetailListWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 成员列表 + /// + [CustomJsonProperty("userlist")] + public List UserList { get; set; } = new List(); + + /// + /// 成员数量 + /// + public int Count => UserList?.Count ?? 0; + + /// + /// 是否包含成员 + /// + public bool HasUsers => Count > 0; + + /// + /// 获取已激活的成员列表 + /// + public List ActiveUsers => UserList?.Where(u => u.IsActive).ToList() ?? new List(); + + /// + /// 获取部门负责人列表 + /// + public List LeaderUsers => UserList?.Where(u => u.IsLeader).ToList() ?? new List(); + + /// + /// 获取用户ID列表 + /// + public List UserIds => UserList?.Select(u => u.UserId).ToList() ?? new List(); + + /// + /// 按状态分组统计 + /// + public Dictionary GetStatusStatistics() + { + return UserList? + .GroupBy(u => u.StatusDesc) + .ToDictionary(g => g.Key, g => g.Count()) ?? new Dictionary(); + } + + /// + /// 部门成员详细信息 + /// + public class DepartmentUserDetailInfo + { + /// + /// 成员UserID。对应管理端的账号 + /// + [CustomJsonProperty("userid")] + public string UserId { get; set; } + + /// + /// 成员名称;第三方不可获取,调用时返回userid以代替name + /// + [CustomJsonProperty("name")] + public string Name { get; set; } + + /// + /// 英文名 + /// + [CustomJsonProperty("english_name")] + public string EnglishName { get; set; } + + /// + /// 成员所属部门id列表,仅返回该应用有查看权限的部门id + /// + [CustomJsonProperty("department")] + public List Department { get; set; } + + /// + /// 部门内的排序值,默认为0。数量必须和department一致 + /// + [CustomJsonProperty("order")] + public List Order { get; set; } + + /// + /// 职务信息;代开发自建应用需要管理员授权才返回 + /// + [CustomJsonProperty("position")] + public string Position { get; set; } + + /// + /// 手机号码,代开发自建应用需要管理员授权才返回 + /// + [CustomJsonProperty("mobile")] + public string Mobile { get; set; } + + /// + /// 性别。0表示未定义,1表示男性,2表示女性 + /// + [CustomJsonProperty("gender")] + public int? Gender { get; set; } + + /// + /// 性别描述 + /// + public string GenderDesc => Gender switch + { + 1 => "男", + 2 => "女", + 0 => "未定义", + _ => "未知" + }; + + /// + /// 邮箱,代开发自建应用需要管理员授权才返回 + /// + [CustomJsonProperty("email")] + public string Email { get; set; } + + /// + /// 企业邮箱,代开发自建应用不返回 + /// + [CustomJsonProperty("biz_mail")] + public string BizMail { get; set; } + + /// + /// 表示在所在的部门内是否为部门负责人。0-否;1-是 + /// + [CustomJsonProperty("is_leader_in_dept")] + public List IsLeaderInDept { get; set; } + + /// + /// 直属上级UserID,返回在应用可见范围内的直属上级列表 + /// + [CustomJsonProperty("direct_leader")] + public List DirectLeader { get; set; } + + /// + /// 头像url。第三方仅通讯录应用可获取 + /// + [CustomJsonProperty("avatar")] + public string Avatar { get; set; } + + /// + /// 头像缩略图url。第三方仅通讯录应用可获取 + /// + [CustomJsonProperty("thumb_avatar")] + public string ThumbAvatar { get; set; } + + /// + /// 座机。代开发自建应用需要管理员授权才返回 + /// + [CustomJsonProperty("telephone")] + public string Telephone { get; set; } + + /// + /// 别名;第三方仅通讯录应用可获取 + /// + [CustomJsonProperty("alias")] + public string Alias { get; set; } + + /// + /// 扩展属性 + /// + [CustomJsonProperty("extattr")] + public CreateUserWorkWxInput.ExtAttrDto ExtAttr { get; set; } + + /// + /// 激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业 + /// + [CustomJsonProperty("status")] + public int? Status { get; set; } + + /// + /// 状态描述 + /// + public string StatusDesc => Status switch + { + 1 => "已激活", + 2 => "已禁用", + 4 => "未激活", + 5 => "退出企业", + _ => "未知状态" + }; + + /// + /// 地址。代开发自建应用需要管理员授权才返回 + /// + [CustomJsonProperty("address")] + public string Address { get; set; } + + /// + /// 全局唯一ID。仅第三方应用可获取 + /// + [CustomJsonProperty("open_userid")] + public string OpenUserId { get; set; } + + /// + /// 主部门,仅当应用对主部门有查看权限时返回 + /// + [CustomJsonProperty("main_department")] + public long? MainDepartment { get; set; } + + /// + /// 员工个人二维码URL + /// + [CustomJsonProperty("qr_code")] + public string QrCode { get; set; } + + /// + /// 对外职务 + /// + [CustomJsonProperty("external_position")] + public string ExternalPosition { get; set; } + + /// + /// 成员对外属性 + /// + [CustomJsonProperty("external_profile")] + public CreateUserWorkWxInput.ExternalProfileDto ExternalProfile { get; set; } + + /// + /// 获取显示名称(优先显示name,如果没有则显示userid) + /// + public string DisplayName => !string.IsNullOrEmpty(Name) ? Name : UserId; + + /// + /// 是否已激活 + /// + public bool IsActive => Status == 1; + + /// + /// 是否为部门负责人(主部门) + /// + public bool IsLeader + { + get + { + if (IsLeaderInDept == null || Department == null || MainDepartment == null) + return false; + + var mainDeptIndex = Department.IndexOf(MainDepartment.Value); + return mainDeptIndex >= 0 && mainDeptIndex < IsLeaderInDept.Count && IsLeaderInDept[mainDeptIndex] == 1; + } + } + + /// + /// 是否包含指定部门 + /// + public bool ContainsDepartment(long departmentId) + { + return Department?.Contains(departmentId) ?? false; + } + + /// + /// 获取主部门排序值 + /// + public long? GetMainDepartmentOrder() + { + if (Order == null || Department == null || MainDepartment == null) + return null; + + var mainDeptIndex = Department.IndexOf(MainDepartment.Value); + return mainDeptIndex >= 0 && mainDeptIndex < Order.Count ? Order[mainDeptIndex] : (long?)null; + } + } +} + +/// +/// userid转openid输出参数 +/// +public class ConvToOpenIdWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 企业微信成员userid对应的openid + /// + [CustomJsonProperty("openid")] + public string OpenId { get; set; } +} + +/// +/// openid转userid输出参数 +/// +public class ConvToUserIdWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 该openid在企业微信对应的成员userid + /// + [CustomJsonProperty("userid")] + public string UserId { get; set; } +} + +/// +/// 邀请成员输出参数 +/// +public class InviteUserWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 非法成员列表 + /// + [CustomJsonProperty("invaliduser")] + public List InvalidUser { get; set; } = new List(); + + /// + /// 非法部门列表 + /// + [CustomJsonProperty("invalidparty")] + public List InvalidParty { get; set; } = new List(); + + /// + /// 非法标签列表 + /// + [CustomJsonProperty("invalidtag")] + public List InvalidTag { get; set; } = new List(); + + /// + /// 有效成员数量 + /// + public int ValidUserCount => (InputUserCount - InvalidUserCount); + + /// + /// 有效部门数量 + /// + public int ValidPartyCount => (InputPartyCount - InvalidPartyCount); + + /// + /// 有效标签数量 + /// + public int ValidTagCount => (InputTagCount - InvalidTagCount); + + /// + /// 非法成员数量 + /// + public int InvalidUserCount => InvalidUser?.Count ?? 0; + + /// + /// 非法部门数量 + /// + public int InvalidPartyCount => InvalidParty?.Count ?? 0; + + /// + /// 非法标签数量 + /// + public int InvalidTagCount => InvalidTag?.Count ?? 0; + + /// + /// 输入成员数量(需要在调用时设置) + /// + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public int InputUserCount { get; set; } + + /// + /// 输入部门数量(需要在调用时设置) + /// + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public int InputPartyCount { get; set; } + + /// + /// 输入标签数量(需要在调用时设置) + /// + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public int InputTagCount { get; set; } + + /// + /// 是否全部有效 + /// + public bool IsAllValid => InvalidUserCount == 0 && InvalidPartyCount == 0 && InvalidTagCount == 0; + + /// + /// 获取邀请结果摘要 + /// + public string GetInviteSummary() + { + var summary = new StringBuilder(); + summary.Append("邀请结果: "); + + if (InvalidUserCount > 0) + { + summary.Append($"{InvalidUserCount}个无效成员"); + } + + if (InvalidPartyCount > 0) + { + if (summary.Length > 12) summary.Append(", "); + summary.Append($"{InvalidPartyCount}个无效部门"); + } + + if (InvalidTagCount > 0) + { + if (summary.Length > 12) summary.Append(", "); + summary.Append($"{InvalidTagCount}个无效标签"); + } + + if (IsAllValid) + { + summary.Append("全部有效"); + } + + return summary.ToString(); + } +} + +/// +/// 获取加入企业二维码输出参数 +/// +public class JoinQrcodeWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 二维码链接,有效期7天 + /// + [CustomJsonProperty("join_qrcode")] + public string JoinQrcode { get; set; } + + /// + /// 二维码尺寸描述 + /// + public string QrcodeSizeDesc => SizeType switch + { + 1 => "171x171", + 2 => "399x399", + 3 => "741x741", + 4 => "2052x2052", + _ => "未知尺寸" + }; + + /// + /// 尺寸类型(从URL中解析) + /// + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public int? SizeType + { + get + { + if (string.IsNullOrEmpty(JoinQrcode))return null; + var match = Regex.Match(JoinQrcode, @"qr_size=(\d)"); + return match.Success ? int.Parse(match.Groups[1].Value) : null; + } + } +} + +/// +/// 成员Id输出参数 +/// +public class UserIdWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 用户userid + /// + [CustomJsonProperty("userid")] + public string UserId { get; set; } +} + +/// +/// 获取成员ID列表输出参数 +/// +public class UserIdListWorkWxOutput : BaseWorkWxOutput +{ + /// + /// 分页游标,下次请求时填写以获取之后分页的记录 + /// + [CustomJsonProperty("next_cursor")] + public string NextCursor { get; set; } + + /// + /// 用户-部门关系列表 + /// + [CustomJsonProperty("dept_user")] + public List DeptUser { get; set; } = new List(); + + /// + /// 用户数量 + /// + public int UserCount => DeptUser?.Count ?? 0; + + /// + /// 是否还有更多数据 + /// + public bool HasMore => !string.IsNullOrEmpty(NextCursor); + + /// + /// 获取去重后的用户ID列表 + /// + public List DistinctUserIds => DeptUser?.Select(d => d.UserId).Distinct().ToList() ?? new List(); + + /// + /// 按部门分组用户 + /// + public Dictionary> GroupUsersByDepartment() + { + var result = new Dictionary>(); + + if (DeptUser == null) return result; + + foreach (var item in DeptUser) + { + if (!result.ContainsKey(item.Department)) + { + result[item.Department] = new List(); + } + result[item.Department].Add(item.UserId); + } + + return result; + } + + /// + /// 按用户分组部门 + /// + public Dictionary> GroupDepartmentsByUser() + { + var result = new Dictionary>(); + + if (DeptUser == null) return result; + + foreach (var item in DeptUser) + { + if (!result.ContainsKey(item.UserId)) + { + result[item.UserId] = new List(); + } + result[item.UserId].Add(item.Department); + } + + return result; + } + + /// + /// 用户-部门关系信息 + /// + public class DeptUserInfo + { + /// + /// 用户userid,当用户在多个部门下时会有多条记录 + /// + [CustomJsonProperty("userid")] + public string UserId { get; set; } + + /// + /// 用户所属部门 + /// + [CustomJsonProperty("department")] + public long Department { get; set; } + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/WorkWxUserHelper.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/WorkWxUserHelper.cs new file mode 100644 index 00000000..267a41cc --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/WorkWxUserHelper.cs @@ -0,0 +1,55 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 企业微信成员工具类 +/// +public class WorkWxUserHelper +{ + /// + /// 检查是否包含敏感字段(根据应用类型判断) + /// + public static bool HasSensitiveFields(UserWorkWxOutput user, bool isNewApp) + { + if (isNewApp) + { + // 新应用无法获取的敏感字段 + return string.IsNullOrEmpty(user.Avatar) && + string.IsNullOrEmpty(user.Mobile) && + string.IsNullOrEmpty(user.Email) && + string.IsNullOrEmpty(user.BizMail) && + string.IsNullOrEmpty(user.QrCode) && + string.IsNullOrEmpty(user.Address) && + (user.Gender == null || user.Gender == 0); + } + return true; + } + + /// + /// 获取显示名称(优先显示name,如果没有则显示userid) + /// + public static string GetDisplayName(UserWorkWxOutput user) + { + return !string.IsNullOrEmpty(user.Name) ? user.Name : user.UserId; + } + + /// + /// 获取完整的部门信息 + /// + public static string GetDepartmentInfo(UserWorkWxOutput user, Dictionary departmentMap) + { + if (user.Department == null || departmentMap == null) + return string.Empty; + + var departments = user.Department + .Select(deptId => departmentMap.TryGetValue(deptId, out var name) ? $"{name}({deptId})" : deptId.ToString()) + .ToList(); + + return string.Join(", ", departments); + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/WorkWxUserService.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/WorkWxUserService.cs new file mode 100644 index 00000000..c125b3f1 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/WorkWxUserService.cs @@ -0,0 +1,166 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 企业微信成员服务 🧩 +/// +public class WorkWxUserService(WorkWxBaseService baseService) : ITransient +{ + /// + /// 创建成员 + /// + /// + /// + public async Task Create(CreateUserWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 读取成员 + /// + /// 成员UserID。对应管理端的账号,企业内必须唯一。不区分大小写,长度为1~64个字节 + /// + public async Task Get(string userId) + { + return await baseService.SendAsync(new() { UserId = userId }); + } + + /// + /// 修改成员 + /// + /// + /// + public async Task Update(UpdateUserWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 删除成员 + /// + /// 成员UserID。对应管理端的账号 + /// + public async Task Delete(string userId) + { + return await baseService.SendAsync(new() { UserId = userId }); + } + + /// + /// 批量删除成员 + /// + /// + /// + public async Task DeleteUser(BatchDeleteUserWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 获取部门成员 + /// + /// + /// + public async Task SimpleListDept(DeptUserSimpleListWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 获取部门成员详情 + /// + /// + /// + public async Task DeptUserDetail(DeptUserDetailListWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// userid转openid + /// + /// + /// + public async Task UserIdToOpenId(ConvToOpenIdWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// openid转userid + /// + /// + /// + public async Task OpenIdToUserId(ConvToUserIdWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 登录二次验证 + /// + /// 成员UserID。对应管理端的账号 + /// + public async Task AuthSucc(string userId) + { + return await baseService.SendAsync(new() { UserId = userId }); + } + + /// + /// 邀请成员 + /// + /// + /// + public async Task InviteUser(InviteUserWorkWxInput input) + { + return await baseService.SendAsync(input); + } + + /// + /// 获取加入企业二维码 + /// + /// qrcode尺寸类型,1: 171 x 171; 2: 399 x 399; 3: 741 x 741; 4: 2052 x 2052 + /// + public async Task GetJoinQrcode(int? sizeType) + { + return await baseService.SendAsync(new() { SizeType = sizeType }); + } + + /// + /// 通过手机号获取成员ID + /// + /// 用户在企业微信通讯录中的手机号码。长度为5~32个字节 + /// + public async Task GetUserIdByMobile(string mobile) + { + return await baseService.SendAsync(new() { Mobile = mobile }); + } + + /// + /// 通过邮箱获取成员ID + /// + /// 邮箱 + /// 邮箱类型:1-企业邮箱(默认);2-个人邮箱 + /// + public async Task GetUserIdByEmail(string email, int? emailType = 1) + { + return await baseService.SendAsync(new(){ Email = email, EmailType = emailType }); + } + + /// + /// 获取成员ID列表 + /// + /// 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用不填 + /// 分页,预期请求的数据量,取值范围 1 ~ 10000 + /// + public async Task GetUserIdList(string cursor = null, int? limit = 20) + { + return await baseService.SendAsync(new(){ Cursor = cursor, Limit = limit }); + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/WorkWxUserUpdateHelper.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/WorkWxUserUpdateHelper.cs new file mode 100644 index 00000000..7a0f2525 --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/User/WorkWxUserUpdateHelper.cs @@ -0,0 +1,92 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 企业微信成员更新工具类 +/// +public class WorkWxUserUpdateHelper +{ + /// + /// 从现有成员信息创建更新对象 + /// + public static UpdateUserWorkWxInput CreateFromExisting(UserWorkWxOutput existingUser) + { + return new UpdateUserWorkWxInput + { + UserId = existingUser.UserId, + Name = existingUser.Name, + Alias = existingUser.Alias, + Mobile = existingUser.Mobile, + Email = existingUser.Email, + Department = existingUser.Department, + Order = existingUser.Order, + Position = existingUser.Position, + Gender = existingUser.Gender, + Telephone = existingUser.Telephone, + IsLeaderInDept = existingUser.IsLeaderInDept, + DirectLeader = existingUser.DirectLeader, + Enable = existingUser.Status == 1 ? 1 : (existingUser.Status == 2 ? 0 : (int?)null), + ExtAttr = existingUser.ExtAttr, + ExternalPosition = existingUser.ExternalPosition, + ExternalProfile = existingUser.ExternalProfile, + Address = existingUser.Address, + MainDepartment = existingUser.MainDepartment + }; + } + + /// + /// 创建部门更新信息 + /// + public static UpdateUserWorkWxInput CreateDepartmentUpdate(string userId, List departments, List orders = null, List isLeaders = null) + { + var input = new UpdateUserWorkWxInput + { + UserId = userId, + Department = departments + }; + + if (orders != null && orders.Count == departments.Count) + { + input.Order = orders; + } + + if (isLeaders != null && isLeaders.Count == departments.Count) + { + input.IsLeaderInDept = isLeaders; + } + + return input; + } + + /// + /// 创建基础信息更新 + /// + public static UpdateUserWorkWxInput CreateBasicInfoUpdate(string userId, string name = null, string mobile = null, string email = null, string position = null) + { + return new UpdateUserWorkWxInput + { + UserId = userId, + Name = name, + Mobile = mobile, + Email = email, + Position = position + }; + } + + /// + /// 创建状态更新 + /// + public static UpdateUserWorkWxInput CreateStatusUpdate(string userId, bool enable) + { + return new UpdateUserWorkWxInput + { + UserId = userId, + Enable = enable ? 1 : 0 + }; + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/WorkWxBaseService.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/WorkWxBaseService.cs new file mode 100644 index 00000000..f2073dae --- /dev/null +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Service/WorkWxBaseService.cs @@ -0,0 +1,111 @@ +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + + +using System.Reflection; +using System.Text; +using Admin.NET.Plugin.WorkWeixin.Const; +using Furion.FriendlyException; +using Furion.JsonSerialization; +using Furion.Logging; +using Microsoft.Extensions.Options; + +namespace Admin.NET.Plugin.WorkWeixin; + +/// +/// 企业微信接口基类服务 🧩 +/// +public class WorkWxBaseService( + SysCacheService sysCacheService, + SysConfigService sysConfigService, + IHttpRemoteService httpRemoteService, + IOptions options) : ITransient +{ + /// + /// 发起请求 + /// + /// 输入类型 + /// 返回类型 + /// 输入参数 + /// 返回结果 + public async Task SendAsync(T input) where R : BaseWorkWxOutput + { + var attr = typeof(T).GetCustomAttribute(); + if (attr == null || string.IsNullOrWhiteSpace(attr.Action) || string.IsNullOrWhiteSpace(attr.Desc)) + throw Oops.Oh($"接口入参类型({typeof(T).FullName})未正确配置[HttpRemoteApi]特性"); + + // 拼接请求地址,并设置token + var url = options.Value.WorkWeixin.BaseAddress + $"/cgi-bin/{attr.Action}?"; + if (input is AuthWorkWxInput) + { + var token = sysCacheService.Get(WorkWeixinConst.KeyWorkWeixinToken) ?? await GetTokenAsync(); + url += "access_token=" + token; + } + + HttpResponseMessage response; + try + { + response = attr.HttpMethod switch + { + HttpMethodEnum.Get => await httpRemoteService.GetAsync( + url + input.ToCustomJsonPropertyQueryString(), + builder => builder.SetHttpOptions(options.Value.WorkWeixin, attr.Desc)), + HttpMethodEnum.Post => await httpRemoteService.PostAsync(url, + builder => builder.SetHttpOptions(options.Value.WorkWeixin, attr.Desc) + .SetContent(new StringContent(JSON.Serialize(input, CustomJsonPropertyConverter.Options), Encoding.UTF8, "application/json"))), + _ => throw Oops.Oh($"[企业微信] 不支持的请求方式{attr.HttpMethod.ToString()}:({typeof(T).FullName})"), + }; + } + catch (Exception ex) + { + Log.Error(ex.Message); + throw Oops.Oh("[企业微信] 服务不可用,请检查网路是否正常" + ex.Message); + } + return await HandleHttpResponseAsync(response); + } + + /// + /// 处理HTTP响应 + /// + /// + /// + /// + private async Task HandleHttpResponseAsync(HttpResponseMessage respMsg) where R : BaseWorkWxOutput + { + if (!respMsg.IsSuccessStatusCode) throw Oops.Oh("[企业微信] 请求失败"); + + var responseContent = await respMsg.Content.ReadAsStringAsync(); + if (string.IsNullOrWhiteSpace(responseContent)) throw Oops.Oh("[企业微信] 响应体为空"); + + try + { + var resp = JSON.Deserialize(responseContent, CustomJsonPropertyConverter.Options); + if (resp?.ErrCode == 0) return resp; + throw Oops.Oh("[企业微信] 请求失败:" + resp?.ErrMsg); + } + catch (Exception ex) + { + Log.Error(ex.Message); + throw Oops.Oh((ex is AppFriendlyException ? "" : "[企业微信] 序列化失败:") + ex.Message); + } + } + + /// + /// 获取企业微信接口凭证 + /// + /// + private async Task GetTokenAsync() + { + using var disposable = sysCacheService.BeginCacheLock(WorkWeixinConst.KeyLockWorkWeixin); + var result = await SendAsync(new() + { + CorpId = await sysConfigService.GetConfigValueByCode(WorkWeixinConst.WorkWeixinCorpId), + CorpSecret = await sysConfigService.GetConfigValueByCode(WorkWeixinConst.WorkWeixinCorpSecret) + }); + sysCacheService.Set(WorkWeixinConst.KeyWorkWeixinToken, result.AccessToken, TimeSpan.FromSeconds(result.ExpiresIn)); + return result.AccessToken; + } +} \ No newline at end of file diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Startup.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Startup.cs index 7b6a39a7..bf78c015 100644 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Startup.cs +++ b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Startup.cs @@ -4,7 +4,6 @@ // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! -using Admin.NET.Plugin.WorkWeixin.Option; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -16,7 +15,6 @@ public class Startup : AppStartup { public void ConfigureServices(IServiceCollection services) { - services.AddConfigurableOptions(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Utils/BaseHttpOutput.cs b/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Utils/BaseHttpOutput.cs deleted file mode 100644 index 8ab0cac4..00000000 --- a/Admin.NET/Plugins/Admin.NET.Plugin.WorkWeixin/Utils/BaseHttpOutput.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Plugin.WorkWeixin; - -/// -/// 企业微信接口输出基类 -/// -public class BaseWorkOutput -{ - /// - /// 返回码 - /// - [JsonProperty("errcode")] - [JsonPropertyName("errcode")] - public int ErrCode { get; set; } - - /// - /// 对返回码的文本描述内容 - /// - [JsonProperty("errmsg")] - [JsonPropertyName("errmsg")] - public string ErrMsg { get; set; } -} - -/// -/// 带id的输出参数 -/// -public class BaseWorkIdOutput : BaseWorkOutput -{ - /// - /// id - /// - [JsonProperty("id")] - [JsonPropertyName("id")] - public long? Id { get; set; } -} \ No newline at end of file