diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/BatchTaggingInput.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/BatchTaggingInput.cs new file mode 100644 index 0000000000000000000000000000000000000000..5950f051731ae00b8117ac0a4156b0cb3187f845 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/BatchTaggingInput.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class BatchTaggingInput + { + /// + /// 粉丝列表 + /// + [JsonProperty("openid_list")] + public string[] OpenIdList { get; set; } + + [JsonProperty("tagid")] + public int TagId { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/BatchUnTaggingInput.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/BatchUnTaggingInput.cs new file mode 100644 index 0000000000000000000000000000000000000000..793578f8a25e7274321a78fc10cc69c082a92ec6 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/BatchUnTaggingInput.cs @@ -0,0 +1,6 @@ +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class BatchUnTaggingInput : BatchTaggingInput + { + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/CreateTagApiResult.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/CreateTagApiResult.cs new file mode 100644 index 0000000000000000000000000000000000000000..493ca1a8b40a0955d384942ab4e53b5f806e6750 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/CreateTagApiResult.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class CreateTagApiResult : ApiResultBase + { + [JsonProperty("tag")] + public CreateTagResultInfo Tag { get; set; } + } + + public class CreateTagResultInfo + { + /// + /// 标签id,由微信分配 + /// + [JsonProperty("id")] + public int Id { get; set; } + + /// + /// 标签名,UTF8编码 + /// + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/CreateTagInput.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/CreateTagInput.cs new file mode 100644 index 0000000000000000000000000000000000000000..6fb4154d280d58500fb1209dd1b0ed6206e54e58 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/CreateTagInput.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class CreateTagInput + { + [JsonProperty("tag")] + public CreateTagModel Tag { get; set; } + } + + public class CreateTagModel + { + /// + /// 标签名(30个字符以内) + /// + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/DeleteTagInput.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/DeleteTagInput.cs new file mode 100644 index 0000000000000000000000000000000000000000..6c45b2fe47a80b36195d3c641951732bf4b8fbf8 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/DeleteTagInput.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class DeleteTagInput + { + [JsonProperty("tag")] + public DeleteTagInfo Tag { get; set; } + } + + public class DeleteTagInfo + { + [JsonProperty("id")] + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetIdListApiResult.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetIdListApiResult.cs new file mode 100644 index 0000000000000000000000000000000000000000..6010e74c18f2697472ad728c323d6272e94665e6 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetIdListApiResult.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class GetIdListApiResult : ApiResultBase + { + /// + /// 被置上的标签列表 + /// + [JsonProperty("tagid_list")] + public int[] TagIdList { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetIdListInput.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetIdListInput.cs new file mode 100644 index 0000000000000000000000000000000000000000..c6e052464e0923628a5ee45b72882e5cf3dc4649 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetIdListInput.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class GetIdListInput + { + [JsonProperty("openid")] + public string OpenId { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetTagsApiResult.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetTagsApiResult.cs new file mode 100644 index 0000000000000000000000000000000000000000..5afff806aa677c2487726e2a725fa6a286f3d6f1 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetTagsApiResult.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class GetTagsApiResult : ApiResultBase + { + [JsonProperty("tags")] + public IEnumerable Tags { get; set; } + } + + public class GetTagInfo + { + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("count")] + public int Count { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetUserApiResult.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetUserApiResult.cs new file mode 100644 index 0000000000000000000000000000000000000000..fbcd80c1a79edd80739801bbde1011368c55aac7 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetUserApiResult.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class GetUserApiResult : ApiResultBase + { + [JsonProperty("total")] + public int Total { get; set; } + + [JsonProperty("count")] + public int Count { get; set; } + + [JsonProperty("data")] + public DataModel Data { get; set; } + + [JsonProperty("next_openid")] + public string NextOpenId { get; set; } + + public class DataModel + { + [JsonProperty("openid")] + public string[] OpenIds { get; set; } + } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetUserByTagInput.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetUserByTagInput.cs new file mode 100644 index 0000000000000000000000000000000000000000..b0a24698bde2505c4dd7ab008cc29b41122034e9 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/GetUserByTagInput.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class GetUserByTagInput + { + [JsonProperty("tagid")] + public int TagId { get; set; } + + /// + /// 第一个拉取的OPENID,不填默认从头开始拉取 + /// + [JsonProperty("next_openid")] + public string NextOpenId { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/UpdateRemarkInput.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/UpdateRemarkInput.cs new file mode 100644 index 0000000000000000000000000000000000000000..26d077b3d3290d4a575c92eef28d953f29fd8e63 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/UpdateRemarkInput.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class UpdateRemarkInput + { + /// + /// 用户标识 + /// + [JsonProperty("openid")] + public string OpenId { get; set; } + + /// + /// 新的备注名,长度必须小于30字符 + /// + [JsonProperty("remark")] + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/UpdateTagInput.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/UpdateTagInput.cs new file mode 100644 index 0000000000000000000000000000000000000000..10f35ef45b34d6a3e153885c72c150d36ad616cb --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/UpdateTagInput.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + public class UpdateTagInput + { + [JsonProperty("tag")] + public UpdateTagInfo Tag { get; set; } + } + + public class UpdateTagInfo + { + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/UserInfoApiResult.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/UserInfoApiResult.cs new file mode 100644 index 0000000000000000000000000000000000000000..ffc8adc9c6834b481dce7aedf5aca76454dad28f --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/Dtos/UserInfoApiResult.cs @@ -0,0 +1,61 @@ +using Newtonsoft.Json; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos +{ + /// + /// https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId + /// + public class UserInfoApiResult : ApiResultBase + { + [JsonProperty("subscribe")] + public int Subscribe { get; set; } + + [JsonProperty("openid")] + public string OpenId { get; set; } + + [JsonProperty("nickname")] + public string Nickname { get; set; } + + [JsonProperty("sex")] + public int Sex { get; set; } + + [JsonProperty("language")] + public string Language { get; set; } + + [JsonProperty("city")] + public string City { get; set; } + + [JsonProperty("province")] + public string Province { get; set; } + + [JsonProperty("country")] + public string Country { get; set; } + + [JsonProperty("headimgurl")] + public string Headimgurl { get; set; } + + [JsonProperty("subscribe_time")] + public int SubscribeTime { get; set; } + + [JsonProperty("unionid")] + public string UnionId { get; set; } + + [JsonProperty("remark")] + public string Remark { get; set; } + + [JsonProperty("groupid")] + public int GroupId { get; set; } + + [JsonProperty("tagid_list")] + public int[] TagIdList { get; set; } + + [JsonProperty("subscribe_scene")] + public string SubscribeScene { get; set; } + + [JsonProperty("qr_scene")] + public int QrScene { get; set; } + + [JsonProperty("qr_scene_str")] + public string QrSceneStr { get; set; } + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/ITagsApi.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/ITagsApi.cs new file mode 100644 index 0000000000000000000000000000000000000000..846d60c38bfda90494dcbf5a62406a43af749154 --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/ITagsApi.cs @@ -0,0 +1,66 @@ +using Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos; +using System.Threading.Tasks; +using WebApiClientCore.Attributes; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User +{ + /// + /// 用户标签管理 + /// + [HttpHost("https://api.weixin.qq.com/cgi-bin/tags/")] + public interface ITagsApi : IWxApiWithAccessTokenFilter + { + /// + /// 1.创建标签 + /// + /// + [HttpPost("create")] + Task CreateAsync([JsonNetContent(CharSet = "utf-8")] CreateTagInput input); + + /// + /// 2.获取公众号已创建的标签 + /// + /// + [HttpGet("get")] + Task GetAsync(); + + /// + /// 3.编辑标签 + /// + /// + [HttpPost("update")] + Task UpdateAsync([JsonNetContent(CharSet = "utf-8")] UpdateTagInput input); + + /// + /// 4.删除标签 + /// + /// + /// + [HttpPost("delete")] + Task DeleteAsync([JsonNetContent(CharSet = "utf-8")] DeleteTagInput input); + + /// + /// 批量为用户打标签 + /// + /// + /// + [HttpPost("members/batchtagging")] + Task BatchTaggingAsync([JsonNetContent(CharSet = "utf-8")] BatchTaggingInput input); + + /// + /// 批量为用户取消标签 + /// + /// + /// + [HttpPost("members/batchuntagging")] + Task BatchUnTaggingAsync([JsonNetContent(CharSet = "utf-8")] BatchUnTaggingInput input); + + /// + /// 获取用户身上的标签列表 + /// + /// + /// + [HttpPost("getidlist")] + Task GetIdListAsync([JsonNetContent(CharSet = "utf-8")] GetIdListInput input); + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/IUserApi.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/IUserApi.cs new file mode 100644 index 0000000000000000000000000000000000000000..f1cfab575aa5634b4627242f4a7f033ff40eec9d --- /dev/null +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Apis/User/IUserApi.cs @@ -0,0 +1,43 @@ +using Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos; +using Newtonsoft.Json; +using System.Threading.Tasks; +using WebApiClientCore.Attributes; + +namespace Magicodes.Wx.PublicAccount.Sdk.Apis.User +{ + /// + /// 用户管理 + /// + [HttpHost("https://api.weixin.qq.com/cgi-bin/user/")] + public interface IUserApi : IWxApiWithAccessTokenFilter + { + /// + /// 获取用户基本信息(UnionID机制) + /// + /// + [HttpGet("info")] + Task InfoAsync(string openId, string lang = "zh_CN"); + + /// + /// 设置用户备注名 + /// + /// + [HttpPost("info/updateremark")] + Task UpdateRemarkAsync([JsonNetContent(CharSet = "utf-8")] UpdateRemarkInput input); + + /// + /// 获取用户列表 + /// + /// 第一个拉取的OPENID,不填默认从头开始拉取 + /// + [HttpGet("get")] + Task GetAsync([JsonProperty("next_openid")] string nextOpenId = null); + + /// + /// 获取标签下粉丝列表 + /// + /// + [HttpPost("tag/get")] + Task GetUserByTagAsync([JsonProperty("next_openid")] GetUserByTagInput input); + } +} \ No newline at end of file diff --git a/src/Magicodes.Wx.PublicAccount.Sdk/Extentions.cs b/src/Magicodes.Wx.PublicAccount.Sdk/Extentions.cs index acf64add311f8b2b8abe3771aea0964bfd9fb9ec..0c8ead0802649da2d2ea3d198e9fcf29d8b289e2 100644 --- a/src/Magicodes.Wx.PublicAccount.Sdk/Extentions.cs +++ b/src/Magicodes.Wx.PublicAccount.Sdk/Extentions.cs @@ -5,6 +5,7 @@ using Magicodes.Wx.PublicAccount.Sdk.Apis.Menu; using Magicodes.Wx.PublicAccount.Sdk.Apis.Message; using Magicodes.Wx.PublicAccount.Sdk.Apis.Sns; using Magicodes.Wx.PublicAccount.Sdk.Apis.Token; +using Magicodes.Wx.PublicAccount.Sdk.Apis.User; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -34,6 +35,8 @@ namespace Magicodes.Wx.PublicAccount.Sdk services.AddSingleton(); services.AddHttpApi(); services.AddHttpApi(); + services.AddHttpApi(); + services.AddHttpApi(); return services; } diff --git a/tests/Magicodes.Wx.PublicAccount.Sdk.Test/ApiTests/TagsApiTest.cs b/tests/Magicodes.Wx.PublicAccount.Sdk.Test/ApiTests/TagsApiTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..45fa9f3595ee00d8f21cff0644768066ad902cf7 --- /dev/null +++ b/tests/Magicodes.Wx.PublicAccount.Sdk.Test/ApiTests/TagsApiTest.cs @@ -0,0 +1,98 @@ +using Magicodes.Wx.PublicAccount.Sdk.Apis.User; +using Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace Magicodes.Wx.PublicAccount.Sdk.Test.ApiTests +{ + public class TagsApiTest : TestBase, IClassFixture + { + private readonly ITagsApi tagsApi; + + public TagsApiTest(TestWebApplicationFactory webApplicationFactory, ITestOutputHelper output) : base( + webApplicationFactory, output) + { + tagsApi = GetRequiredService(); + } + + [Fact] + public async Task CreateAsync_Test() + { + var result = await tagsApi.CreateAsync(new CreateTagInput + { + Tag = new CreateTagModel + { + Name = "Magicodes good" + } + }); + result.EnsureSuccess(); + } + + [Fact] + public async Task GetAsync_Test() + { + var result = await tagsApi.GetAsync(); + result.EnsureSuccess(); + } + + [Fact] + public async Task UpdateAsync_Test() + { + var result = await tagsApi.UpdateAsync(new UpdateTagInput + { + Tag = new UpdateTagInfo + { + Id = 103, + Name = "Magicodes good 1" + } + }); + result.EnsureSuccess(); + } + + [Fact] + public async Task DeleteAsync_Test() + { + var result = await tagsApi.DeleteAsync(new DeleteTagInput + { + Tag = new DeleteTagInfo + { + Id = 103 + } + }); + result.EnsureSuccess(); + } + + [Fact] + public async Task BatchTaggingAsync_Test() + { + var result = await tagsApi.BatchTaggingAsync(new BatchTaggingInput + { + OpenIdList = new[] { "o6l4Nv5rjCXOCkbaCDOAmH1Eoxl4" }, + TagId = 102 + }); + result.EnsureSuccess(); + } + + [Fact] + public async Task BatchUnTaggingAsync_Test() + { + var result = await tagsApi.BatchUnTaggingAsync(new BatchUnTaggingInput + { + OpenIdList = new[] { "o6l4Nv5rjCXOCkbaCDOAmH1Eoxl4" }, + TagId = 102 + }); + result.EnsureSuccess(); + } + + [Fact] + public async Task GetIdListAsync_Test() + { + var result = await tagsApi.GetIdListAsync(new GetIdListInput + { + OpenId = "o6l4Nv5rjCXOCkbaCDOAmH1Eoxl4" + }); + result.EnsureSuccess(); + } + } +} \ No newline at end of file diff --git a/tests/Magicodes.Wx.PublicAccount.Sdk.Test/ApiTests/UserApiTest.cs b/tests/Magicodes.Wx.PublicAccount.Sdk.Test/ApiTests/UserApiTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..37918592bb147ae1b2c2c9a413aeac63865fe44b --- /dev/null +++ b/tests/Magicodes.Wx.PublicAccount.Sdk.Test/ApiTests/UserApiTest.cs @@ -0,0 +1,54 @@ +using Magicodes.Wx.PublicAccount.Sdk.Apis.User; +using Magicodes.Wx.PublicAccount.Sdk.Apis.User.Dtos; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace Magicodes.Wx.PublicAccount.Sdk.Test.ApiTests +{ + public class UserApiTest : TestBase, IClassFixture + { + private readonly IUserApi userApi; + + public UserApiTest(TestWebApplicationFactory webApplicationFactory, ITestOutputHelper output) : base(webApplicationFactory, output) + { + userApi = GetRequiredService(); + } + + [Fact] + public async Task InfoAsync_Test() + { + var result = await userApi.InfoAsync("007"); + result.EnsureSuccess(); + } + + [Fact] + public async Task UpdateRemarkAsync_Test() + { + var result = await userApi.UpdateRemarkAsync(new UpdateRemarkInput + { + OpenId = "oDF3iY9ffA-hqb2vVvbr7qxf6A0Q", + Remark = "Magicodes" + }); + result.EnsureSuccess(); + } + + [Fact] + public async Task GetAsync_Test() + { + var result = await userApi.GetAsync(); + result.EnsureSuccess(); + } + + [Fact] + public async Task GetUserByTagAsync_Test() + { + var result = await userApi.GetUserByTagAsync(new GetUserByTagInput + { + TagId = 103, + NextOpenId = string.Empty + }); + result.EnsureSuccess(); + } + } +} \ No newline at end of file