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