From 5e753f6b9dd3e7ccaed71ac4ee5619f33f1c44cc Mon Sep 17 00:00:00 2001 From: colin Date: Tue, 14 Oct 2025 17:18:36 +0800 Subject: [PATCH] feat(wechat): wecom tag management integration --- ...=> IWeChatWorkApprovalTemplateProvider.cs} | 0 ... => WeChatWorkApprovalTemplateProvider.cs} | 4 +- .../Security/IWeChatWorkServerProvider.cs | 17 +++ .../Work/Security/Models/WeChatDomainModel.cs | 44 ++++++ .../Work/Security/Models/WeChatIpModel.cs | 38 +++++ .../Models/WeChatServerDomainModel.cs | 25 ++++ .../Models/WeChatServerDomainResponse.cs | 22 +++ .../Work/Security/WeChatWorkServerProvider.cs | 32 +++++ .../Work/Tags/IWeChatWorkTagProvider.cs | 93 ++++++++++++ .../Abp/WeChat/Work/Tags/Models/TagInfo.cs | 25 ++++ .../WeChat/Work/Tags/Models/TagUserInfo.cs | 25 ++++ .../Tags/Request/WeChatWorkGetTagRequest.cs | 23 +++ .../WeChatWorkTagChangeMemberRequest.cs | 57 ++++++++ .../Request/WeChatWorkTagCreateRequest.cs | 33 +++++ .../Request/WeChatWorkTagUpdateRequest.cs | 33 +++++ .../WeChatWorkTagChangeMemberResponse.cs | 26 ++++ .../Response/WeChatWorkTagCreateResponse.cs | 21 +++ .../Response/WeChatWorkTagListResponse.cs | 23 +++ .../WeChatWorkTagMemberInfoResponse.cs | 37 +++++ .../WeChat/Work/Tags/WeChatWorkTagProvider.cs | 89 ++++++++++++ ...tpClientWeChatWorkRequestExtensions.Tag.cs | 134 ++++++++++++++++++ 21 files changed, 799 insertions(+), 2 deletions(-) rename aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/{IApprovalTemplateProvider.cs => IWeChatWorkApprovalTemplateProvider.cs} (100%) rename aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/{ApprovalTemplateProvider.cs => WeChatWorkApprovalTemplateProvider.cs} (96%) create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/IWeChatWorkServerProvider.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatDomainModel.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatIpModel.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatServerDomainModel.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatServerDomainResponse.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkServerProvider.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/IWeChatWorkTagProvider.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Models/TagInfo.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Models/TagUserInfo.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkGetTagRequest.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagChangeMemberRequest.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagCreateRequest.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagUpdateRequest.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagChangeMemberResponse.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagCreateResponse.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagListResponse.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagMemberInfoResponse.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/WeChatWorkTagProvider.cs create mode 100644 aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/System/Net/Http/HttpClientWeChatWorkRequestExtensions.Tag.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/IApprovalTemplateProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/IWeChatWorkApprovalTemplateProvider.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/IApprovalTemplateProvider.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/IWeChatWorkApprovalTemplateProvider.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/ApprovalTemplateProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/WeChatWorkApprovalTemplateProvider.cs similarity index 96% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/ApprovalTemplateProvider.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/WeChatWorkApprovalTemplateProvider.cs index aa7436c10..9d074bffe 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/ApprovalTemplateProvider.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Approvals/WeChatWorkApprovalTemplateProvider.cs @@ -11,12 +11,12 @@ using Volo.Abp.Features; namespace LINGYUN.Abp.WeChat.Work.Approvals; [RequiresFeature(WeChatWorkFeatureNames.Enable)] -public class ApprovalTemplateProvider : IApprovalTemplateProvider, ISingletonDependency +public class WeChatWorkApprovalTemplateProvider : IApprovalTemplateProvider, ISingletonDependency { protected IHttpClientFactory HttpClientFactory { get; } protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } - public ApprovalTemplateProvider( + public WeChatWorkApprovalTemplateProvider( IHttpClientFactory httpClientFactory, IWeChatWorkTokenProvider weChatWorkTokenProvider) { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/IWeChatWorkServerProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/IWeChatWorkServerProvider.cs new file mode 100644 index 000000000..8f527f4d0 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/IWeChatWorkServerProvider.cs @@ -0,0 +1,17 @@ +using LINGYUN.Abp.WeChat.Work.Security.Models; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Work.Security; +public interface IWeChatWorkServerProvider +{ + /// + /// 获取企业微信域名IP信息 + /// + /// + /// 参考:https://developer.work.weixin.qq.com/document/path/100079 + /// + /// + /// + Task GetWeChatServerAsync(CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatDomainModel.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatDomainModel.cs new file mode 100644 index 000000000..0105cb67d --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatDomainModel.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Security.Models; +public class WeChatDomainModel +{ + /// + /// 域名 + /// + [JsonProperty("domain")] + [JsonPropertyName("domain")] + public string Domain { get; set; } = default!; + /// + /// 泛域名 + /// + [JsonProperty("universal_domian")] + [JsonPropertyName("universal_domian")] + public string UniversalDomian { get; set; } + /// + /// 协议 如TCP UDP + /// + [JsonProperty("protocol")] + [JsonPropertyName("protocol")] + public string Protocol { get; set; } = default!; + /// + /// 端口号列表 + /// + [JsonProperty("port")] + [JsonPropertyName("port")] + public List Port { get; set; } = new List(); + /// + /// 是否必要,0-否 1-是, 如果必要的域名或IP被拦截,将导致企业微信的功能出现异常 + /// + [JsonProperty("is_necessary")] + [JsonPropertyName("is_necessary")] + public int IsNecessary { get; set; } = default!; + /// + /// IP涉及到的功能的描述信息 + /// + [JsonProperty("description")] + [JsonPropertyName("description")] + public string Description { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatIpModel.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatIpModel.cs new file mode 100644 index 000000000..ab8e0e4bc --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatIpModel.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Security.Models; +public class WeChatIpModel +{ + /// + /// ip地址 + /// + [JsonProperty("ip")] + [JsonPropertyName("ip")] + public string Ip { get; set; } = default!; + /// + /// 协议 如TCP UDP + /// + [JsonProperty("protocol")] + [JsonPropertyName("protocol")] + public string Protocol { get; set; } = default!; + /// + /// 端口号列表 + /// + [JsonProperty("port")] + [JsonPropertyName("port")] + public List Port { get; set; } = new List(); + /// + /// 是否必要,0-否 1-是, 如果必要的域名或IP被拦截,将导致企业微信的功能出现异常 + /// + [JsonProperty("is_necessary")] + [JsonPropertyName("is_necessary")] + public int IsNecessary { get; set; } = default!; + /// + /// IP涉及到的功能的描述信息 + /// + [JsonProperty("description")] + [JsonPropertyName("description")] + public string Description { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatServerDomainModel.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatServerDomainModel.cs new file mode 100644 index 000000000..dd84447fa --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatServerDomainModel.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Security.Models; +public class WeChatServerDomainModel +{ + /// + /// 域名列表 + /// + [JsonProperty("domain_list")] + [JsonPropertyName("domain_list")] + public List Domains { get; } + /// + /// Ip列表 + /// + [JsonProperty("ip_list")] + [JsonPropertyName("ip_list")] + public List Ips { get; } + public WeChatServerDomainModel(List domains, List ips) + { + Domains = domains; + Ips = ips; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatServerDomainResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatServerDomainResponse.cs new file mode 100644 index 000000000..3d65b35ba --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatServerDomainResponse.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace LINGYUN.Abp.WeChat.Work.Security.Models; +public class WeChatServerDomainResponse : WeChatWorkResponse +{ + /// + /// 域名列表 + /// + [JsonProperty("domain_list")] + public List Domains { get; set; } = new List(); + /// + /// Ip列表 + /// + [JsonProperty("ip_list")] + public List Ips { get; set; } = new List(); + public WeChatServerDomainModel ToServerDomain() + { + ThrowIfNotSuccess(); + return new WeChatServerDomainModel(Domains, Ips); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkServerProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkServerProvider.cs new file mode 100644 index 000000000..fcca18361 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkServerProvider.cs @@ -0,0 +1,32 @@ +using LINGYUN.Abp.WeChat.Work.Security.Models; +using LINGYUN.Abp.WeChat.Work.Token; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.WeChat.Work.Security; +public class WeChatWorkServerProvider : IWeChatWorkServerProvider, ISingletonDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } + + public WeChatWorkServerProvider( + IHttpClientFactory httpClientFactory, + IWeChatWorkTokenProvider weChatWorkTokenProvider) + { + HttpClientFactory = httpClientFactory; + WeChatWorkTokenProvider = weChatWorkTokenProvider; + } + + public async virtual Task GetWeChatServerAsync(CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); + + using var response = await client.GetServerDomainIpAsync(token.AccessToken, cancellationToken); + var serverDomainResponse = await response.DeserializeObjectAsync(); + + return serverDomainResponse.ToServerDomain(); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/IWeChatWorkTagProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/IWeChatWorkTagProvider.cs new file mode 100644 index 000000000..df2aa3dc6 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/IWeChatWorkTagProvider.cs @@ -0,0 +1,93 @@ +using LINGYUN.Abp.WeChat.Work.Tags.Request; +using LINGYUN.Abp.WeChat.Work.Tags.Response; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Work.Tags; +/// +/// 标签管理接口 +/// +public interface IWeChatWorkTagProvider +{ + /// + /// 创建标签 + /// + /// + /// 详情见:https://developer.work.weixin.qq.com/document/path/90210 + /// + /// 创建标签请求参数 + /// + /// 创建标签响应参数 + Task CreateAsync( + WeChatWorkTagCreateRequest request, + CancellationToken cancellationToken = default); + /// + /// 更新标签名字 + /// + /// + /// 详情见:https://developer.work.weixin.qq.com/document/path/90211 + /// + /// 更新标签名字请求参数 + /// + /// 更新标签名字响应参数 + Task UpdateAsync( + WeChatWorkTagUpdateRequest request, + CancellationToken cancellationToken = default); + /// + /// 删除标签 + /// + /// + /// 详情见:https://developer.work.weixin.qq.com/document/path/90212 + /// + /// 删除标签请求参数 + /// + /// 删除标签响应参数 + Task DeleteAsync( + WeChatWorkGetTagRequest request, + CancellationToken cancellationToken = default); + /// + /// 获取标签成员 + /// + /// + /// 详情见:https://developer.work.weixin.qq.com/document/path/90213 + /// + /// 获取标签成员请求参数 + /// + /// 获取标签成员响应参数 + Task GetMemberAsync( + WeChatWorkGetTagRequest request, + CancellationToken cancellationToken = default); + /// + /// 增加标签成员 + /// + /// + /// 详情见:https://developer.work.weixin.qq.com/document/path/90214 + /// + /// 增加标签成员请求参数 + /// + /// 增加标签成员响应参数 + Task AddMemberAsync( + WeChatWorkTagChangeMemberRequest request, + CancellationToken cancellationToken = default); + /// + /// 增加标签成员 + /// + /// + /// 详情见:https://developer.work.weixin.qq.com/document/path/90214 + /// + /// 增加标签成员请求参数 + /// + /// 增加标签成员响应参数 + Task DeleteMemberAsync( + WeChatWorkTagChangeMemberRequest request, + CancellationToken cancellationToken = default); + /// + /// 获取标签列表 + /// + /// + /// 详情见:https://developer.work.weixin.qq.com/document/path/90214 + /// + /// + /// 获取标签列表响应参数 + Task GetListAsync(CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Models/TagInfo.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Models/TagInfo.cs new file mode 100644 index 000000000..24cbeffb8 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Models/TagInfo.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Tags.Models; +/// +/// 标签 +/// +public class TagInfo +{ + /// + /// 标签id + /// + [NotNull] + [JsonProperty("tagid")] + [JsonPropertyName("tagid")] + public int TagId { get; set; } + /// + /// 标签名 + /// + [NotNull] + [JsonProperty("tagname")] + [JsonPropertyName("tagname")] + public string TagName { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Models/TagUserInfo.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Models/TagUserInfo.cs new file mode 100644 index 000000000..720ba0b1b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Models/TagUserInfo.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Tags.Models; +/// +/// 标签中包含的成员 +/// +public class TagUserInfo +{ + /// + /// 成员账号 + /// + [NotNull] + [JsonProperty("userid")] + [JsonPropertyName("userid")] + public string UserId { get; set; } + /// + /// 成员名称 + /// + [NotNull] + [JsonProperty("name")] + [JsonPropertyName("name")] + public string Name { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkGetTagRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkGetTagRequest.cs new file mode 100644 index 000000000..f1c4fb22a --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkGetTagRequest.cs @@ -0,0 +1,23 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.Tags.Request; +/// +/// 获取标签请求参数 +/// +public class WeChatWorkGetTagRequest +{ + /// + /// 标签id + /// + [NotNull] + [JsonProperty("tagid")] + [JsonPropertyName("tagid")] + public int TagId { get; set; } + public WeChatWorkGetTagRequest(int tagId) + { + TagId = Check.Positive(tagId, nameof(tagId)); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagChangeMemberRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagChangeMemberRequest.cs new file mode 100644 index 000000000..22aba51f1 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagChangeMemberRequest.cs @@ -0,0 +1,57 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.Tags.Request; +/// +/// 标签成员变更请求参数 +/// +public class WeChatWorkTagChangeMemberRequest +{ + /// + /// 标签id,非负整型,指定此参数时新增的标签会生成对应的标签id,不指定时则以目前最大的id自增。 + /// + [NotNull] + [JsonProperty("tagid")] + [JsonPropertyName("tagid")] + public int TagId { get; set; } + /// + /// 企业成员ID列表,注意:userlist、partylist不能同时为空,单次请求个数不超过1000 + /// + [CanBeNull] + [JsonProperty("userlist")] + [JsonPropertyName("userlist")] + public List Users { get; set; } + /// + /// 企业部门ID列表,注意:userlist、partylist不能同时为空,单次请求个数不超过100 + /// + [CanBeNull] + [JsonProperty("partylist")] + [JsonPropertyName("partylist")] + public List Parts { get; set; } + public WeChatWorkTagChangeMemberRequest( + int tagId, + List users = null, + List parts = null) + { + TagId = Check.Positive(tagId, nameof(tagId)); + Users = users; + Parts = parts; + + if (users == null && parts == null) + { + throw new ArgumentNullException("users/parts", "userlist、partylist不能同时为空!"); + } + if (users.Count > 1000) + { + throw new ArgumentOutOfRangeException(nameof(users), "企业成员ID列表单次请求个数不超过1000!"); + } + if (parts.Count > 100) + { + throw new ArgumentOutOfRangeException(nameof(users), "企业部门ID列表单次请求个数不超过100!"); + } + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagCreateRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagCreateRequest.cs new file mode 100644 index 000000000..27a6d6dcd --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagCreateRequest.cs @@ -0,0 +1,33 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.Tags.Request; +/// +/// 创建标签请求参数 +/// +public class WeChatWorkTagCreateRequest +{ + /// + /// 标签id,非负整型,指定此参数时新增的标签会生成对应的标签id,不指定时则以目前最大的id自增。 + /// + [NotNull] + [JsonProperty("tagid")] + [JsonPropertyName("tagid")] + public int? TagId { get; set; } + /// + /// 标签名称,长度限制为32个字以内(汉字或英文字母),标签名不可与其他标签重名。 + /// + [NotNull] + [StringLength(32)] + [JsonProperty("tagname")] + [JsonPropertyName("tagname")] + public string TagName { get; set; } + public WeChatWorkTagCreateRequest(string tagName, int? tagId = null) + { + TagName = Check.NotNullOrWhiteSpace(tagName, nameof(tagName), 32); + TagId = tagId; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagUpdateRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagUpdateRequest.cs new file mode 100644 index 000000000..902d70e4b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Request/WeChatWorkTagUpdateRequest.cs @@ -0,0 +1,33 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Work.Tags.Request; +/// +/// 更新标签请求参数 +/// +public class WeChatWorkTagUpdateRequest +{ + /// + /// 标签id,非负整型,指定此参数时新增的标签会生成对应的标签id,不指定时则以目前最大的id自增。 + /// + [NotNull] + [JsonProperty("tagid")] + [JsonPropertyName("tagid")] + public int TagId { get; set; } + /// + /// 标签名称,长度限制为32个字以内(汉字或英文字母),标签名不可与其他标签重名。 + /// + [NotNull] + [StringLength(32)] + [JsonProperty("tagname")] + [JsonPropertyName("tagname")] + public string TagName { get; set; } + public WeChatWorkTagUpdateRequest(int tagId, string tagName) + { + TagId = Check.Positive(tagId, nameof(tagId)); + TagName = Check.NotNullOrWhiteSpace(tagName, nameof(tagName), 32); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagChangeMemberResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagChangeMemberResponse.cs new file mode 100644 index 000000000..d0e6cd002 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagChangeMemberResponse.cs @@ -0,0 +1,26 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Tags.Response; +/// +/// 标签成员变更响应参数 +/// +public class WeChatWorkTagChangeMemberResponse : WeChatWorkResponse +{ + /// + /// 若部分userid非法,则返回 + /// + [CanBeNull] + [JsonProperty("invalidlist")] + [JsonPropertyName("invalidlist")] + public string InvalidList { get; set; } + /// + /// 若部分partylist非法,则返回 + /// + [CanBeNull] + [JsonProperty("invalidparty")] + [JsonPropertyName("invalidparty")] + public List InvalidPart { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagCreateResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagCreateResponse.cs new file mode 100644 index 000000000..8e0a1f084 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagCreateResponse.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Tags.Response; +/// +/// 创建标签响应参数 +/// +/// +/// 详情见: https://developer.work.weixin.qq.com/document/path/90210 +/// +public class WeChatWorkTagCreateResponse : WeChatWorkResponse +{ + /// + /// 标签id + /// + [NotNull] + [JsonProperty("tagid")] + [JsonPropertyName("tagid")] + public string TagId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagListResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagListResponse.cs new file mode 100644 index 000000000..ed905495d --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagListResponse.cs @@ -0,0 +1,23 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.Tags.Models; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Tags.Response; +/// +/// 获取标签列表响应参数 +/// +/// +/// 详情见: https://developer.work.weixin.qq.com/document/path/90216 +/// +public class WeChatWorkTagListResponse : WeChatWorkResponse +{ + /// + /// 标签列表 + /// + [NotNull] + [JsonProperty("taglist")] + [JsonPropertyName("taglist")] + public List Tags { get; set; } +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagMemberInfoResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagMemberInfoResponse.cs new file mode 100644 index 000000000..fce599a69 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/Response/WeChatWorkTagMemberInfoResponse.cs @@ -0,0 +1,37 @@ +using JetBrains.Annotations; +using LINGYUN.Abp.WeChat.Work.Tags.Models; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Tags.Response; +/// +/// 标签成员响应参数 +/// +/// +/// 详情见: https://developer.work.weixin.qq.com/document/path/90213 +/// +public class WeChatWorkTagMemberInfoResponse : WeChatWorkResponse +{ + /// + /// 标签名 + /// + [NotNull] + [JsonProperty("tagname")] + [JsonPropertyName("tagname")] + public string TagName { get; set; } + /// + /// 标签中包含的成员列表 + /// + [NotNull] + [JsonProperty("userlist")] + [JsonPropertyName("userlist")] + public List Users { get; set; } + /// + /// 标签中包含的部门id列表 + /// + [NotNull] + [JsonProperty("partylist")] + [JsonPropertyName("partylist")] + public List Parts { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/WeChatWorkTagProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/WeChatWorkTagProvider.cs new file mode 100644 index 000000000..3eb40560a --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Tags/WeChatWorkTagProvider.cs @@ -0,0 +1,89 @@ +using LINGYUN.Abp.WeChat.Work.Features; +using LINGYUN.Abp.WeChat.Work.Tags.Request; +using LINGYUN.Abp.WeChat.Work.Tags.Response; +using LINGYUN.Abp.WeChat.Work.Token; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace LINGYUN.Abp.WeChat.Work.Tags; + +[RequiresFeature(WeChatWorkFeatureNames.Enable)] +public class WeChatWorkTagProvider : IWeChatWorkTagProvider, ISingletonDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected IWeChatWorkTokenProvider WeChatWorkTokenProvider { get; } + + public WeChatWorkTagProvider( + IHttpClientFactory httpClientFactory, + IWeChatWorkTokenProvider weChatWorkTokenProvider) + { + HttpClientFactory = httpClientFactory; + WeChatWorkTokenProvider = weChatWorkTokenProvider; + } + + public async virtual Task AddMemberAsync(WeChatWorkTagChangeMemberRequest request, CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); + + using var response = await client.AddTagMemberAsync(token.AccessToken, request, cancellationToken); + return await response.DeserializeObjectAsync(); + } + + public async virtual Task CreateAsync(WeChatWorkTagCreateRequest request, CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); + + using var response = await client.CreateTagAsync(token.AccessToken, request, cancellationToken); + return await response.DeserializeObjectAsync(); + } + + public async virtual Task DeleteAsync(WeChatWorkGetTagRequest request, CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); + + using var response = await client.DeleteTagAsync(token.AccessToken, request, cancellationToken); + return await response.DeserializeObjectAsync(); + } + + public async virtual Task DeleteMemberAsync(WeChatWorkTagChangeMemberRequest request, CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); + + using var response = await client.DeleteTagMemberAsync(token.AccessToken, request, cancellationToken); + return await response.DeserializeObjectAsync(); + } + + public async virtual Task GetListAsync(CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); + + using var response = await client.GetTagListAsync(token.AccessToken, cancellationToken); + return await response.DeserializeObjectAsync(); + } + + public async virtual Task GetMemberAsync(WeChatWorkGetTagRequest request, CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); + + using var response = await client.GetTagMemberAsync(token.AccessToken, request, cancellationToken); + return await response.DeserializeObjectAsync(); + } + + public async virtual Task UpdateAsync(WeChatWorkTagUpdateRequest request, CancellationToken cancellationToken = default) + { + var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken); + var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); + + using var response = await client.UpdateTagAsync(token.AccessToken, request, cancellationToken); + return await response.DeserializeObjectAsync(); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/System/Net/Http/HttpClientWeChatWorkRequestExtensions.Tag.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/System/Net/Http/HttpClientWeChatWorkRequestExtensions.Tag.cs new file mode 100644 index 000000000..e9162de12 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/System/Net/Http/HttpClientWeChatWorkRequestExtensions.Tag.cs @@ -0,0 +1,134 @@ +using LINGYUN.Abp.WeChat.Work.Tags.Request; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http; +internal static partial class HttpClientWeChatWorkRequestExtensions +{ + public async static Task AddTagMemberAsync( + this HttpMessageInvoker client, + string accessToken, + WeChatWorkTagChangeMemberRequest request, + CancellationToken cancellationToken = default) + { + var urlBuilder = new StringBuilder(); + urlBuilder.Append("/cgi-bin/tag/addtagusers"); + urlBuilder.AppendFormat("?access_token={0}", accessToken); + + var httpRequest = new HttpRequestMessage( + HttpMethod.Post, + urlBuilder.ToString()) + { + Content = new StringContent(request.SerializeToJson()) + }; + + return await client.SendAsync(httpRequest, cancellationToken); + } + public async static Task CreateTagAsync( + this HttpMessageInvoker client, + string accessToken, + WeChatWorkTagCreateRequest request, + CancellationToken cancellationToken = default) + { + var urlBuilder = new StringBuilder(); + urlBuilder.Append("/cgi-bin/tag/create"); + urlBuilder.AppendFormat("?access_token={0}", accessToken); + + var httpRequest = new HttpRequestMessage( + HttpMethod.Post, + urlBuilder.ToString()) + { + Content = new StringContent(request.SerializeToJson()) + }; + + return await client.SendAsync(httpRequest, cancellationToken); + } + public async static Task DeleteTagAsync( + this HttpMessageInvoker client, + string accessToken, + WeChatWorkGetTagRequest request, + CancellationToken cancellationToken = default) + { + var urlBuilder = new StringBuilder(); + urlBuilder.Append("/cgi-bin/tag/delete"); + urlBuilder.AppendFormat("?access_token={0}", accessToken); + urlBuilder.AppendFormat("&tagid={0}", request.TagId); + + var httpRequest = new HttpRequestMessage( + HttpMethod.Get, + urlBuilder.ToString()); + + return await client.SendAsync(httpRequest, cancellationToken); + } + public async static Task DeleteTagMemberAsync( + this HttpMessageInvoker client, + string accessToken, + WeChatWorkTagChangeMemberRequest request, + CancellationToken cancellationToken = default) + { + var urlBuilder = new StringBuilder(); + urlBuilder.Append("/cgi-bin/tag/deltagusers"); + urlBuilder.AppendFormat("?access_token={0}", accessToken); + + var httpRequest = new HttpRequestMessage( + HttpMethod.Post, + urlBuilder.ToString()) + { + Content = new StringContent(request.SerializeToJson()) + }; + + return await client.SendAsync(httpRequest, cancellationToken); + } + public async static Task GetTagListAsync( + this HttpMessageInvoker client, + string accessToken, + CancellationToken cancellationToken = default) + { + var urlBuilder = new StringBuilder(); + urlBuilder.Append("/cgi-bin/tag/list"); + urlBuilder.AppendFormat("?access_token={0}", accessToken); + + var httpRequest = new HttpRequestMessage( + HttpMethod.Get, + urlBuilder.ToString()); + + return await client.SendAsync(httpRequest, cancellationToken); + } + public async static Task GetTagMemberAsync( + this HttpMessageInvoker client, + string accessToken, + WeChatWorkGetTagRequest request, + CancellationToken cancellationToken = default) + { + var urlBuilder = new StringBuilder(); + urlBuilder.Append("/cgi-bin/tag/get"); + urlBuilder.AppendFormat("?access_token={0}", accessToken); + urlBuilder.AppendFormat("&tagid={0}", request.TagId); + + var httpRequest = new HttpRequestMessage( + HttpMethod.Get, + urlBuilder.ToString()); + + return await client.SendAsync(httpRequest, cancellationToken); + } + public async static Task UpdateTagAsync( + this HttpMessageInvoker client, + string accessToken, + WeChatWorkTagUpdateRequest request, + CancellationToken cancellationToken = default) + { + var urlBuilder = new StringBuilder(); + urlBuilder.Append("/cgi-bin/tag/update"); + urlBuilder.AppendFormat("?access_token={0}", accessToken); + + var httpRequest = new HttpRequestMessage( + HttpMethod.Post, + urlBuilder.ToString()) + { + Content = new StringContent(request.SerializeToJson()) + }; + + return await client.SendAsync(httpRequest, cancellationToken); + } +}