🍒 refactor(HttpLog): 优化Http日志记录配置,增加响应体明文记录功能
This commit is contained in:
parent
fc748272a5
commit
3f37395e15
@ -37,9 +37,4 @@ public class CommonConst
|
||||
/// 事件-发送异常邮件
|
||||
/// </summary>
|
||||
public const string SendErrorMail = "Send:ErrorMail";
|
||||
|
||||
/// <summary>
|
||||
/// 远程请求请求头参数键值前缀
|
||||
/// </summary>
|
||||
public const string HttpRemoteHeaderKeyPrefix = "__HTTP_CLIENT_";
|
||||
}
|
||||
@ -10,6 +10,11 @@ namespace Admin.NET.Core;
|
||||
/// Http远程服务扩展
|
||||
/// </summary>
|
||||
public static class HttpRemotesExtension {
|
||||
private static readonly HttpRequestOptionsKey<string> HttpNameKey = new("__HTTP_CLIENT_NAME__");
|
||||
private static readonly HttpRequestOptionsKey<HttpRemoteApiAttribute> AttrKey = new(nameof(HttpRemoteApiAttribute));
|
||||
private static readonly HttpRequestOptionsKey<string> ReqPlaintextKey = new(nameof(SysLogHttp.RequestBodyPlaintext));
|
||||
private static readonly HttpRequestOptionsKey<Func<HttpResponseMessage, Task<string>>> RespPlaintextFunc = new(nameof(SysLogHttp.ResponseBodyPlaintext) + "Func");
|
||||
|
||||
/// <summary>
|
||||
/// 添加Http远程服务
|
||||
/// </summary>
|
||||
@ -36,14 +41,73 @@ public static class HttpRemotesExtension {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 携带接口描述相关属性
|
||||
/// 设置请求接口相关属性
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <param name="attr"></param>
|
||||
/// <param name="reqPlaintext"></param>
|
||||
/// <param name="plaintext">请求明文</param>
|
||||
/// <param name="decryption">获取响应体明文的代理方法(解密)</param>
|
||||
/// <returns></returns>
|
||||
public static HttpRequestBuilder SetRemoteApiAttr(this HttpRequestBuilder builder, HttpRemoteApiAttribute attr, string reqPlaintext = null)
|
||||
public static HttpRequestBuilder SetReqRemoteApiAttr(this HttpRequestBuilder builder, HttpRemoteApiAttribute attr, string plaintext = null, Func<HttpResponseMessage, Task<string>> decryption = null)
|
||||
{
|
||||
return HttpLoggingHandler.SetRemoteApiAttr(builder, attr, reqPlaintext);
|
||||
builder.SetOnPreSendRequest(conf =>
|
||||
{
|
||||
conf.Options.Set(AttrKey, attr);
|
||||
conf.Options.Set(ReqPlaintextKey, plaintext);
|
||||
conf.Options.Set(RespPlaintextFunc, decryption);
|
||||
});
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取客户端名称
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetHttpClientName(this HttpRequestMessage request)
|
||||
{
|
||||
return request.Options.TryGetValue(HttpNameKey, out var name) ? name : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Http远程接口属性
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public static HttpRemoteApiAttribute GetHttpRemoteApiAttr(this HttpRequestMessage request)
|
||||
{
|
||||
return request.Options.TryGetValue(AttrKey, out var attr) ? attr : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取请求明文
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetRequestBodyPlaintext(this HttpRequestMessage request)
|
||||
{
|
||||
return request.Options.TryGetValue(ReqPlaintextKey, out var plaintext) ? plaintext : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取响应明文
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetResponseBodyPlaintext(this HttpResponseMessage response)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (response.RequestMessage != null && response.RequestMessage.Options.TryGetValue(RespPlaintextFunc, out var decryptionFunc))
|
||||
{
|
||||
return await decryptionFunc.Invoke(response);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Http远程服务响应体解密失败", e);
|
||||
throw;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -14,12 +14,8 @@ namespace Admin.NET.Core;
|
||||
/// </summary>
|
||||
public class HttpLoggingHandler : DelegatingHandler, ITransient
|
||||
{
|
||||
private static readonly string RespPlaintextKey = CommonConst.HttpRemoteHeaderKeyPrefix + "RESP_PLAINTEXT__";
|
||||
private static readonly string ReqPlaintextKey = CommonConst.HttpRemoteHeaderKeyPrefix + "REQ_PLAINTEXT__";
|
||||
private static readonly string IgnoreLogKey = CommonConst.HttpRemoteHeaderKeyPrefix + "IGNORE_LOG__";
|
||||
private static readonly string ApiDescKey = CommonConst.HttpRemoteHeaderKeyPrefix + "API_DESC__";
|
||||
private static readonly string HttpNameKey = CommonConst.HttpRemoteHeaderKeyPrefix + "NAME__";
|
||||
private static readonly Lazy<UserManager> _userManager = new(() => App.GetService<UserManager>());
|
||||
private static readonly Lazy<UserManager> UserManager = new(() => App.GetService<UserManager>());
|
||||
private static readonly string HttpNameKey = "__HTTP_CLIENT_NAME__";
|
||||
|
||||
private readonly Dictionary<string, bool> _enabledLogMap;
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
@ -45,27 +41,26 @@ public class HttpLoggingHandler : DelegatingHandler, ITransient
|
||||
if (!enabledLog) return await base.SendAsync(request, cancellationToken);
|
||||
|
||||
// 判断当前配置日志开关
|
||||
(string apiDesc, bool ignoreLog, string reqPlaintext) = GetRemoteApiAttrAndRemove(request.Headers);
|
||||
_ = request.Options.TryGetValue<string>(HttpNameKey, out var httpName);
|
||||
if (!string.IsNullOrWhiteSpace(httpName)) enabledLog = _enabledLogMap.GetOrDefault(httpName);
|
||||
if (!enabledLog || ignoreLog) return await base.SendAsync(request, cancellationToken);
|
||||
var httpClientName = request.GetHttpClientName();
|
||||
var attr = request.GetHttpRemoteApiAttr();
|
||||
if (!string.IsNullOrWhiteSpace(httpClientName)) enabledLog = _enabledLogMap.GetOrDefault(httpClientName);
|
||||
if (!enabledLog || attr?.IgnoreLog == true) return await base.SendAsync(request, cancellationToken);
|
||||
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
var urlList = request.RequestUri?.LocalPath.Split("/") ?? [];
|
||||
var sysLogHttp = new SysLogHttp
|
||||
{
|
||||
HttpClientName = httpName,
|
||||
HttpApiDesc = apiDesc,
|
||||
HttpApiDesc = attr?.Desc,
|
||||
HttpClientName = httpClientName,
|
||||
HttpMethod = request.Method.Method,
|
||||
ActionName = urlList.Length >= 2 ? $"{urlList[^2]}/{urlList[^1]}" : urlList.Length >= 1 ? urlList[^1] : null,
|
||||
RequestUrl = request.RequestUri?.ToString(),
|
||||
RequestHeaders = request.Headers.ToDictionary(u => u.Key, u => u.Value.Join(";")).ToJson(),
|
||||
RequestBodyPlaintext = reqPlaintext,
|
||||
TenantId = _userManager.Value?.TenantId,
|
||||
CreateUserId = _userManager.Value?.UserId,
|
||||
CreateUserName = _userManager.Value?.RealName,
|
||||
RequestBodyPlaintext = request.GetRequestBodyPlaintext(),
|
||||
TenantId = UserManager.Value?.TenantId,
|
||||
CreateUserId = UserManager.Value?.UserId,
|
||||
CreateUserName = UserManager.Value?.RealName,
|
||||
};
|
||||
// _userManager
|
||||
if (request.Content != null) sysLogHttp.RequestBody = await request.Content.ReadAsStringAsync(cancellationToken);
|
||||
|
||||
sysLogHttp.StartTime = DateTime.Now;
|
||||
@ -75,6 +70,7 @@ public class HttpLoggingHandler : DelegatingHandler, ITransient
|
||||
stopWatch.Stop();
|
||||
sysLogHttp.EndTime = DateTime.Now;
|
||||
sysLogHttp.StatusCode = response.StatusCode;
|
||||
sysLogHttp.ResponseBodyPlaintext = await response.GetResponseBodyPlaintext();
|
||||
sysLogHttp.ResponseHeaders = response.Headers.ToDictionary(u => u.Key, u => u.Value.Join(";")).ToJson();
|
||||
sysLogHttp.IsSuccessStatusCode = response.IsSuccessStatusCode ? YesNoEnum.Y : YesNoEnum.N;
|
||||
sysLogHttp.ResponseBody = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
@ -91,54 +87,7 @@ public class HttpLoggingHandler : DelegatingHandler, ITransient
|
||||
finally
|
||||
{
|
||||
sysLogHttp.Elapsed = stopWatch.ElapsedMilliseconds;
|
||||
await _eventPublisher.PublishAsync(nameof(AppEventSubscriber.CreateHttpLog), sysLogHttp, cancellationToken);
|
||||
await _eventPublisher.PublishAsync(nameof(AppEventSubscriber.CreateHttpLog), sysLogHttp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取接口描述相关属性
|
||||
/// </summary>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
private static (string apiDesc, bool ignoreLog, string reqPlaintext) GetRemoteApiAttrAndRemove(HttpRequestHeaders headers)
|
||||
{
|
||||
var result = new
|
||||
{
|
||||
ReqPlaintext = headers?.FirstOrDefault(u => u.Key == ReqPlaintextKey).Value?.FirstOrDefault()?.ToString(),
|
||||
ApiDesc = headers?.FirstOrDefault(u => u.Key == ApiDescKey).Value?.FirstOrDefault()?.ToString(),
|
||||
IgnoreLog = headers?.FirstOrDefault(u => u.Key == IgnoreLogKey).Value?.ToBoolean() ?? false,
|
||||
};
|
||||
RemoveRemoteApiAttr(headers);
|
||||
return (result.ApiDesc, result.IgnoreLog, result.ReqPlaintext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除接口描述相关属性
|
||||
/// </summary>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
private static void RemoveRemoteApiAttr(HttpRequestHeaders headers)
|
||||
{
|
||||
if (headers == null) return;
|
||||
var keys = headers
|
||||
.Where(kv => kv.Key.StartsWith(CommonConst.HttpRemoteHeaderKeyPrefix))
|
||||
.Select(u => u.Key)
|
||||
.ToList();
|
||||
foreach (var key in keys) headers.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置接口描述相关属性
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <param name="attr"></param>
|
||||
/// <param name="reqPlaintext">请求文明</param>
|
||||
/// <returns></returns>
|
||||
public static HttpRequestBuilder SetRemoteApiAttr(HttpRequestBuilder builder, HttpRemoteApiAttribute attr, string reqPlaintext = null)
|
||||
{
|
||||
builder.WithHeader(ReqPlaintextKey, reqPlaintext, replace:true);
|
||||
builder.WithHeader(IgnoreLogKey, attr.IgnoreLog, replace:true);
|
||||
builder.WithHeader(ApiDescKey, attr.Desc, replace:true);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
@ -12,9 +13,6 @@ namespace Admin.NET.Core;
|
||||
/// <summary>
|
||||
/// 自定义JSON转换器
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 自定义JSON转换器工厂类
|
||||
/// </summary>
|
||||
public class CustomJsonConverter : JsonConverterFactory
|
||||
{
|
||||
/// <summary>
|
||||
@ -113,10 +111,10 @@ public class CustomJsonConverter : JsonConverterFactory
|
||||
if (reader.TokenType == JsonTokenType.PropertyName)
|
||||
{
|
||||
var propertyName = reader.GetString();
|
||||
if (_jsonToPropertyMap.TryGetValue(propertyName, out var prop))
|
||||
if (_jsonToPropertyMap.TryGetValue(propertyName!, out var prop))
|
||||
{
|
||||
reader.Read();
|
||||
var value = JsonSerializer.Deserialize(ref reader, prop.PropertyType, _baseOptions);
|
||||
var value = DeserializeWithCustomFormat(ref reader, prop, _baseOptions);
|
||||
prop.SetValue(instance, value);
|
||||
}
|
||||
else
|
||||
@ -146,13 +144,79 @@ public class CustomJsonConverter : JsonConverterFactory
|
||||
continue;
|
||||
|
||||
var customAttr = prop.GetCustomAttribute<CustomJsonPropertyAttribute>();
|
||||
var jsonName = customAttr?.Name ?? _baseOptions.PropertyNamingPolicy?.ConvertName(prop.Name) ?? prop.Name;
|
||||
var jsonName = customAttr?.Name ?? ToCamelCase(prop.Name);
|
||||
|
||||
writer.WritePropertyName(jsonName);
|
||||
JsonSerializer.Serialize(writer, propValue, prop.PropertyType, _baseOptions);
|
||||
|
||||
// 处理日期格式化
|
||||
if (!string.IsNullOrEmpty(customAttr?.DateFormat))
|
||||
{
|
||||
if (propValue is DateTime dateTime)
|
||||
{
|
||||
writer.WriteStringValue(dateTime.ToString(customAttr.DateFormat));
|
||||
}
|
||||
else if (prop.PropertyType == typeof(DateTime?) && propValue != null)
|
||||
{
|
||||
var nullableDateTime = (DateTime?)propValue;
|
||||
writer.WriteStringValue(nullableDateTime.Value.ToString(customAttr.DateFormat));
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonSerializer.Serialize(writer, propValue, prop.PropertyType, _baseOptions);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonSerializer.Serialize(writer, propValue, prop.PropertyType, _baseOptions);
|
||||
}
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换属性名
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
private string ToCamelCase(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name)) return name;
|
||||
|
||||
return char.ToLowerInvariant(name[0]) + name.Substring(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化JSON为对象,处理自定义格式
|
||||
/// </summary>
|
||||
private object DeserializeWithCustomFormat(ref Utf8JsonReader reader, PropertyInfo prop, JsonSerializerOptions options)
|
||||
{
|
||||
var customAttr = prop.GetCustomAttribute<CustomJsonPropertyAttribute>();
|
||||
|
||||
// 特殊处理日期类型
|
||||
if (!string.IsNullOrEmpty(customAttr?.DateFormat))
|
||||
{
|
||||
if (prop.PropertyType == typeof(DateTime) && reader.TokenType == JsonTokenType.String)
|
||||
{
|
||||
var dateString = reader.GetString();
|
||||
if (DateTime.TryParseExact(dateString, customAttr.DateFormat, null, DateTimeStyles.None, out var dateTime))
|
||||
{
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
else if (prop.PropertyType == typeof(DateTime?) && reader.TokenType == JsonTokenType.String)
|
||||
{
|
||||
var dateString = reader.GetString();
|
||||
if (DateTime.TryParseExact(dateString, customAttr.DateFormat, null, DateTimeStyles.None, out var dateTime))
|
||||
{
|
||||
return (DateTime?)dateTime;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认处理
|
||||
return JsonSerializer.Deserialize(ref reader, prop.PropertyType, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,8 +37,9 @@ public static class CustomJsonHelper
|
||||
/// </summary>
|
||||
private static JsonSerializerOptions CreateOptions(JsonSerializerOptions globalOptions)
|
||||
{
|
||||
var options = globalOptions != null ? new JsonSerializerOptions(globalOptions) : new JsonSerializerOptions();
|
||||
JsonSerializerOptions options = globalOptions != null ? new(globalOptions) : new();
|
||||
options.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
|
||||
options.ReferenceHandler = ReferenceHandler.IgnoreCycles;
|
||||
options.Converters.Add(new JsonStringEnumConverter());
|
||||
options.Converters.Add(new CustomJsonConverter());
|
||||
return options;
|
||||
|
||||
@ -1,425 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy;
|
||||
|
||||
/// <summary>
|
||||
/// 创建群聊会话输入参数
|
||||
/// </summary>
|
||||
public class CreatAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 群名称
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
[JsonPropertyName("name")]
|
||||
[Required(ErrorMessage = "群名称不能为空"), MaxLength(50, ErrorMessage = "群名称最多不能超过50个字符")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 群主Id
|
||||
/// </summary>
|
||||
[JsonProperty("owner")]
|
||||
[JsonPropertyName("owner")]
|
||||
[Required(ErrorMessage = "群主Id不能为空")]
|
||||
public string Owner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 群成员Id列表
|
||||
/// </summary>
|
||||
[JsonProperty("userlist")]
|
||||
[JsonPropertyName("userlist")]
|
||||
[NotEmpty(ErrorMessage = "群成员列表不能为空")]
|
||||
public List<string> UserList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 群Id
|
||||
/// </summary>
|
||||
[JsonProperty("chatid")]
|
||||
[JsonPropertyName("chatid")]
|
||||
[Required(ErrorMessage = "群Id不能为空"), MaxLength(32, ErrorMessage = "群Id最多不能超过32个字符")]
|
||||
public string ChatId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改群聊会话输入参数
|
||||
/// </summary>
|
||||
public class UpdateAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 群Id
|
||||
/// </summary>
|
||||
[JsonProperty("chatid")]
|
||||
[JsonPropertyName("chatid")]
|
||||
[Required(ErrorMessage = "群Id不能为空"), MaxLength(32, ErrorMessage = "群Id最多不能超过32个字符")]
|
||||
public string ChatId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 群名称
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
[JsonPropertyName("name")]
|
||||
[Required(ErrorMessage = "群名称不能为空"), MaxLength(50, ErrorMessage = "群名称最多不能超过50个字符")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 群主Id
|
||||
/// </summary>
|
||||
[JsonProperty("owner")]
|
||||
[JsonPropertyName("owner")]
|
||||
[Required(ErrorMessage = "群主Id不能为空")]
|
||||
public string Owner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 添加成员的id列表
|
||||
/// </summary>
|
||||
[JsonProperty("add_user_list")]
|
||||
[JsonPropertyName("add_user_list")]
|
||||
public List<string> AddUserList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 踢出成员的id列表
|
||||
/// </summary>
|
||||
[JsonProperty("del_user_list")]
|
||||
[JsonPropertyName("del_user_list")]
|
||||
public List<string> DelUserList { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用消息推送输入基类参数
|
||||
/// </summary>
|
||||
public class SendBaseAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 群Id
|
||||
/// </summary>
|
||||
[JsonProperty("chatid")]
|
||||
[JsonPropertyName("chatid")]
|
||||
[Required(ErrorMessage = "群Id不能为空"), MaxLength(32, ErrorMessage = "群Id最多不能超过32个字符")]
|
||||
public string ChatId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息类型
|
||||
/// </summary>
|
||||
/// <example>text:文本消息</example>
|
||||
/// <example>image:图片消息</example>
|
||||
/// <example>voice:图片消息</example>
|
||||
/// <example>video:视频消息</example>
|
||||
/// <example>file:文件消息</example>
|
||||
/// <example>textcard:文本卡片</example>
|
||||
/// <example>news:图文消息</example>
|
||||
/// <example>mpnews:图文消息(存储在企业微信)</example>
|
||||
/// <example>markdown:markdown消息</example>
|
||||
[JsonProperty("msgtype")]
|
||||
[JsonPropertyName("msgtype")]
|
||||
[Required(ErrorMessage = "消息类型不能为空")]
|
||||
protected string MsgType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否是保密消息
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 推送文本消息输入参数
|
||||
/// </summary>
|
||||
public class SendTextAppChatInput : SendBaseAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
[JsonProperty("text")]
|
||||
[JsonPropertyName("text")]
|
||||
public object Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文本消息
|
||||
/// </summary>
|
||||
/// <param name="chatId"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="safe"></param>
|
||||
public SendTextAppChatInput(string chatId, string content, bool safe = false) : base(chatId, "text", safe)
|
||||
{
|
||||
Text = new { content };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 推送图片消息输入参数
|
||||
/// </summary>
|
||||
public class SendImageAppChatInput : SendBaseAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
[JsonProperty("image")]
|
||||
[JsonPropertyName("image")]
|
||||
public object Image { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图片消息
|
||||
/// </summary>
|
||||
/// <param name="chatId"></param>
|
||||
/// <param name="mediaId"></param>
|
||||
/// <param name="safe"></param>
|
||||
public SendImageAppChatInput(string chatId, string mediaId, bool safe = false) : base(chatId, "image", safe)
|
||||
{
|
||||
Image = new { media_id = mediaId };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 推送语音消息输入参数
|
||||
/// </summary>
|
||||
public class SendVoiceAppChatInput : SendBaseAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
[JsonProperty("voice")]
|
||||
[JsonPropertyName("voice")]
|
||||
public object Voice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 语音消息
|
||||
/// </summary>
|
||||
/// <param name="chatId"></param>
|
||||
/// <param name="mediaId"></param>
|
||||
/// <param name="safe"></param>
|
||||
public SendVoiceAppChatInput(string chatId, string mediaId, bool safe = false) : base(chatId, "voice", safe)
|
||||
{
|
||||
Voice = new { media_id = mediaId };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 推送视频消息输入参数
|
||||
/// </summary>
|
||||
public class SendVideoAppChatInput : SendBaseAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
[JsonProperty("video")]
|
||||
[JsonPropertyName("video")]
|
||||
public object Video { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 视频消息
|
||||
/// </summary>
|
||||
/// <param name="chatId"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="description"></param>
|
||||
/// <param name="mediaId"></param>
|
||||
/// <param name="safe"></param>
|
||||
public SendVideoAppChatInput(string chatId, string title, string description, string mediaId, bool safe = false) : base(chatId, "video", safe)
|
||||
{
|
||||
Video = new
|
||||
{
|
||||
media_id = mediaId,
|
||||
description,
|
||||
title
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 推送视频消息输入参数
|
||||
/// </summary>
|
||||
public class SendFileAppChatInput : SendBaseAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
[JsonProperty("file")]
|
||||
[JsonPropertyName("file")]
|
||||
public object File { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件消息
|
||||
/// </summary>
|
||||
/// <param name="chatId"></param>
|
||||
/// <param name="mediaId"></param>
|
||||
/// <param name="safe"></param>
|
||||
public SendFileAppChatInput(string chatId, string mediaId, bool safe = false) : base(chatId, "video", safe)
|
||||
{
|
||||
File = new { media_id = mediaId };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 推送文本卡片消息输入参数
|
||||
/// </summary>
|
||||
public class SendTextCardAppChatInput : SendBaseAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
[JsonProperty("textcard")]
|
||||
[JsonPropertyName("textcard")]
|
||||
public object TextCard { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文本卡片消息
|
||||
/// </summary>
|
||||
/// <param name="chatId"></param>
|
||||
/// <param name="title">标题</param>
|
||||
/// <param name="description">描述</param>
|
||||
/// <param name="url">点击后跳转的链接</param>
|
||||
/// <param name="btnTxt">按钮文字</param>
|
||||
/// <param name="safe"></param>
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图文消息项
|
||||
/// </summary>
|
||||
public class SendNewsItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 标题
|
||||
/// </summary>
|
||||
[JsonProperty("title")]
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
[JsonProperty("description")]
|
||||
[JsonPropertyName("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
[JsonProperty("url")]
|
||||
[JsonPropertyName("url")]
|
||||
public string Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图文消息的图片链接(推荐大图1068 * 455,小图150 * 150)
|
||||
/// </summary>
|
||||
[JsonProperty("picurl")]
|
||||
[JsonPropertyName("picurl")]
|
||||
public string PicUrl { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 推送图文消息输入参数
|
||||
/// </summary>
|
||||
public class SendNewsAppChatInput : SendBaseAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
[JsonProperty("news")]
|
||||
[JsonPropertyName("news")]
|
||||
public object News { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图文消息
|
||||
/// </summary>
|
||||
/// <param name="chatId"></param>
|
||||
/// <param name="newsList">图文消息列表</param>
|
||||
/// <param name="safe"></param>
|
||||
public SendNewsAppChatInput(string chatId, List<SendNewsItem> newsList, bool safe = false) : base(chatId, "news", safe)
|
||||
{
|
||||
News = new { articles = newsList };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图文消息项
|
||||
/// </summary>
|
||||
public class SendMpNewsItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 标题
|
||||
/// </summary>
|
||||
[JsonProperty("title")]
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 缩略图media_id
|
||||
/// </summary>
|
||||
[JsonProperty("thumb_media_id")]
|
||||
[JsonPropertyName("thumb_media_id")]
|
||||
public string ThumbMediaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 作者
|
||||
/// </summary>
|
||||
[JsonProperty("author")]
|
||||
[JsonPropertyName("author")]
|
||||
public string Author { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 点击“阅读原文”之后的页面链接
|
||||
/// </summary>
|
||||
[JsonProperty("content_source_url")]
|
||||
[JsonPropertyName("content_source_url")]
|
||||
public string ContentSourceUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图文消息的内容
|
||||
/// </summary>
|
||||
[JsonProperty("content")]
|
||||
[JsonPropertyName("content")]
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图文消息的描述
|
||||
/// </summary>
|
||||
[JsonProperty("digest")]
|
||||
[JsonPropertyName("digest")]
|
||||
public string Digest { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 推送图文消息(存储在企业微信)输入参数
|
||||
/// </summary>
|
||||
public class SendMpNewsAppChatInput : SendBaseAppChatInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
[JsonProperty("mpnews")]
|
||||
[JsonPropertyName("mpnews")]
|
||||
public object MpNews { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图文消息
|
||||
/// </summary>
|
||||
/// <param name="chatId"></param>
|
||||
/// <param name="mpNewsList">图文消息列表</param>
|
||||
/// <param name="safe"></param>
|
||||
public SendMpNewsAppChatInput(string chatId, List<SendMpNewsItem> mpNewsList, bool safe = false) : base(chatId, "mpnews", safe)
|
||||
{
|
||||
MpNews = new { articles = mpNewsList };
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy;
|
||||
|
||||
public class CreatAppChatOutput : BaseWorkOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 群聊的唯一标志
|
||||
/// </summary>
|
||||
[JsonProperty("chatid")]
|
||||
[JsonPropertyName("chatid")]
|
||||
public string ChatId { get; set; }
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy.AppChat;
|
||||
|
||||
/// <summary>
|
||||
/// 群聊会话远程调用服务
|
||||
/// </summary>
|
||||
public interface IWorkWeixinAppChatHttp : IHttpDeclarative
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建群聊会话
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <returns></returns>
|
||||
/// <inheritdoc cref="https://developer.work.weixin.qq.com/document/path/90245"/>
|
||||
[Post("https://qyapi.weixin.qq.com/cgi-bin/appchat/create")]
|
||||
Task<CreatAppChatOutput> Create([Query("access_token")] string accessToken, [Body] CreatAppChatInput body);
|
||||
|
||||
/// <summary>
|
||||
/// 修改群聊会话
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <returns></returns>
|
||||
/// <inheritdoc cref="https://developer.work.weixin.qq.com/document/path/98913"/>
|
||||
[Post("https://qyapi.weixin.qq.com/cgi-bin/appchat/update")]
|
||||
Task<CreatAppChatOutput> Update([Query("access_token")] string accessToken, [Body] UpdateAppChatInput body);
|
||||
|
||||
/// <summary>
|
||||
/// 获取群聊会话
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="chatId"></param>
|
||||
/// <returns></returns>
|
||||
/// <inheritdoc cref="https://developer.work.weixin.qq.com/document/path/98914"/>
|
||||
[Get("https://qyapi.weixin.qq.com/cgi-bin/appchat/get")]
|
||||
Task<CreatAppChatOutput> Get([Query("access_token")] string accessToken, [Query("chatid")] string chatId);
|
||||
|
||||
/// <summary>
|
||||
/// 应用推送消息
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <returns></returns>
|
||||
/// <inheritdoc cref="https://developer.work.weixin.qq.com/document/path/90248"/>
|
||||
[Post("https://qyapi.weixin.qq.com/cgi-bin/appchat/send")]
|
||||
Task<BaseWorkOutput> Send([Query("access_token")] string accessToken, [Body] SendBaseAppChatInput body);
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy;
|
||||
|
||||
public class AuthAccessTokenHttpOutput : BaseWorkOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取到的凭证
|
||||
/// </summary>
|
||||
[JsonProperty("access_token")]
|
||||
[JsonPropertyName("access_token")]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 凭证的有效时间(秒)
|
||||
/// </summary>
|
||||
[JsonProperty("expires_in")]
|
||||
[JsonPropertyName("expires_in")]
|
||||
public int ExpiresIn { get; set; }
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy.AppChat;
|
||||
|
||||
/// <summary>
|
||||
/// 授权会话远程服务
|
||||
/// </summary>
|
||||
public interface IWorkWeixinAuthHttp : IHttpDeclarative
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取接口凭证
|
||||
/// </summary>
|
||||
/// <param name="corpId">企业ID</param>
|
||||
/// <param name="corpSecret">应用的凭证密钥</param>
|
||||
/// <returns></returns>
|
||||
/// <inheritdoc cref="https://developer.work.weixin.qq.com/document/path/91039"/>
|
||||
[Post("https://qyapi.weixin.qq.com/cgi-bin/gettoken")]
|
||||
Task<AuthAccessTokenHttpOutput> GetToken([Query("corpid")] string corpId, [Query("corpsecret")] string corpSecret);
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy;
|
||||
|
||||
/// <summary>
|
||||
/// 创建部门输入参数
|
||||
/// </summary>
|
||||
public class DepartmentHttpInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 部门名称
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public long? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 父部门id
|
||||
/// </summary>
|
||||
[JsonProperty("parentid")]
|
||||
[JsonPropertyName("parentid")]
|
||||
public long? ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门名称
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 英文名称
|
||||
/// </summary>
|
||||
[JsonProperty("name_en")]
|
||||
[JsonPropertyName("name_en")]
|
||||
public string NameEn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 序号
|
||||
/// </summary>
|
||||
[JsonProperty("order")]
|
||||
[JsonPropertyName("order")]
|
||||
public int? Order { get; set; }
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy;
|
||||
|
||||
/// <summary>
|
||||
/// 部门Id列表输出参数
|
||||
/// </summary>
|
||||
public class DepartmentIdOutput : BaseWorkOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// id
|
||||
/// </summary>
|
||||
[JsonProperty("department_id")]
|
||||
[JsonPropertyName("department_id")]
|
||||
public List<DepartmentItemOutput> DepartmentList { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 部门Id输出参数
|
||||
/// </summary>
|
||||
public class DepartmentItemOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 部门名称
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public long? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 父部门id
|
||||
/// </summary>
|
||||
[JsonProperty("parentid")]
|
||||
[JsonPropertyName("parentid")]
|
||||
public long? ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 序号
|
||||
/// </summary>
|
||||
[JsonProperty("order")]
|
||||
[JsonPropertyName("order")]
|
||||
public int? Order { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 部门输出参数
|
||||
/// </summary>
|
||||
public class DepartmentOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 部门名称
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public long? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 父部门id
|
||||
/// </summary>
|
||||
[JsonProperty("parentid")]
|
||||
[JsonPropertyName("parentid")]
|
||||
public long? ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门名称
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 英文名称
|
||||
/// </summary>
|
||||
[JsonProperty("name_en")]
|
||||
[JsonPropertyName("name_en")]
|
||||
public string NameEn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门负责人列表
|
||||
/// </summary>
|
||||
[JsonProperty("department_leader")]
|
||||
[JsonPropertyName("department_leader")]
|
||||
public List<string> Leaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 序号
|
||||
/// </summary>
|
||||
[JsonProperty("order")]
|
||||
[JsonPropertyName("order")]
|
||||
public int? Order { get; set; }
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy.AppChat;
|
||||
|
||||
/// <summary>
|
||||
/// 部门远程调用服务
|
||||
/// </summary>
|
||||
public interface IDepartmentHttp : IHttpDeclarative
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建部门
|
||||
/// https://developer.work.weixin.qq.com/document/path/90205
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <returns></returns>
|
||||
[Post("https://qyapi.weixin.qq.com/cgi-bin/department/create")]
|
||||
Task<BaseWorkIdOutput> Create([Query("access_token")] string accessToken, [Body] DepartmentHttpInput body);
|
||||
|
||||
/// <summary>
|
||||
/// 修改部门
|
||||
/// https://developer.work.weixin.qq.com/document/path/90206
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <returns></returns>
|
||||
[Post("https://qyapi.weixin.qq.com/cgi-bin/department/update")]
|
||||
Task<BaseWorkOutput> Update([Query("access_token")] string accessToken, [Body] DepartmentHttpInput body);
|
||||
|
||||
/// <summary>
|
||||
/// 删除部门
|
||||
/// https://developer.work.weixin.qq.com/document/path/90207
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[Get("https://qyapi.weixin.qq.com/cgi-bin/department/delete")]
|
||||
Task<BaseWorkOutput> Delete([Query("access_token")] string accessToken, [Query] long id);
|
||||
|
||||
/// <summary>
|
||||
/// 获取部门Id列表
|
||||
/// https://developer.work.weixin.qq.com/document/path/90208
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[Get("https://qyapi.weixin.qq.com/cgi-bin/department/simplelist")]
|
||||
Task<DepartmentIdOutput> SimpleList([Query("access_token")] string accessToken, [Query] long id);
|
||||
|
||||
/// <summary>
|
||||
/// 获取部门详情
|
||||
/// https://developer.work.weixin.qq.com/document/path/90208
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[Get("https://qyapi.weixin.qq.com/cgi-bin/department/get")]
|
||||
Task<DepartmentOutput> Get([Query("access_token")] string accessToken, [Query] long id);
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy;
|
||||
|
||||
/// <summary>
|
||||
/// 标签输入参数
|
||||
/// </summary>
|
||||
public class TagHttpInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 标签id
|
||||
/// </summary>
|
||||
[JsonProperty("tagid")]
|
||||
[JsonPropertyName("tagid")]
|
||||
public long? TagId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标签名称
|
||||
/// </summary>
|
||||
[JsonProperty("tagname")]
|
||||
[JsonPropertyName("tagname")]
|
||||
public string TagName { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 增加标签成员输入参数
|
||||
/// </summary>
|
||||
public class TagUsersTagInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 标签id
|
||||
/// </summary>
|
||||
[JsonProperty("tagid")]
|
||||
[JsonPropertyName("tagid")]
|
||||
public long TagId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 企业成员ID列表
|
||||
/// </summary>
|
||||
[JsonProperty("userlist")]
|
||||
[JsonPropertyName("userlist")]
|
||||
public List<string> UserList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 企业部门ID列表
|
||||
/// </summary>
|
||||
[JsonProperty("partylist")]
|
||||
[JsonPropertyName("partylist")]
|
||||
public List<long> PartyList { get; set; }
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy;
|
||||
|
||||
/// <summary>
|
||||
/// 新增标签输出参数
|
||||
/// </summary>
|
||||
public class TagIdHttpOutput : BaseWorkOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 标签Id
|
||||
/// </summary>
|
||||
[JsonProperty("tagid")]
|
||||
[JsonPropertyName("tagid")]
|
||||
public long? TagId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标签列表输出参数
|
||||
/// </summary>
|
||||
public class TagListHttpOutput : BaseWorkOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 标签Id
|
||||
/// </summary>
|
||||
[JsonProperty("taglist")]
|
||||
[JsonPropertyName("taglist")]
|
||||
public List<TagHttpInput> TagList { get; set; }
|
||||
}
|
||||
@ -1,82 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin.Proxy;
|
||||
|
||||
/// <summary>
|
||||
/// 标签远程调用服务
|
||||
/// </summary>
|
||||
public interface ITagHttp : IHttpDeclarative
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建标签
|
||||
/// https://developer.work.weixin.qq.com/document/path/90210
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <returns></returns>
|
||||
[Post("https://qyapi.weixin.qq.com/cgi-bin/tag/create")]
|
||||
Task<BaseWorkIdOutput> Create([Query("access_token")] string accessToken, [Body] TagHttpInput body);
|
||||
|
||||
/// <summary>
|
||||
/// 更新标签名字
|
||||
/// https://developer.work.weixin.qq.com/document/path/90211
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <returns></returns>
|
||||
[Post("https://qyapi.weixin.qq.com/cgi-bin/tag/update")]
|
||||
Task<TagIdHttpOutput> Update([Query("access_token")] string accessToken, [Body] TagHttpInput body);
|
||||
|
||||
/// <summary>
|
||||
/// 删除标签
|
||||
/// https://developer.work.weixin.qq.com/document/path/90212
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="tagId"></param>
|
||||
/// <returns></returns>
|
||||
[Get("https://qyapi.weixin.qq.com/cgi-bin/tag/delete")]
|
||||
Task<BaseWorkOutput> Delete([Query("access_token")] string accessToken, [Query("tagid")] long tagId);
|
||||
|
||||
/// <summary>
|
||||
/// 获取标签详情
|
||||
/// https://developer.work.weixin.qq.com/document/path/90213
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="tagId"></param>
|
||||
/// <returns></returns>
|
||||
[Get("https://qyapi.weixin.qq.com/cgi-bin/tag/get")]
|
||||
Task<DepartmentOutput> Get([Query("access_token")] string accessToken, [Query("tagid")] long tagId);
|
||||
|
||||
/// <summary>
|
||||
/// 增加标签成员
|
||||
/// https://developer.work.weixin.qq.com/document/path/90214
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <returns></returns>
|
||||
[Post("https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers")]
|
||||
Task<DepartmentOutput> AddTagUsers([Query("access_token")] string accessToken, [Body] TagUsersTagInput body);
|
||||
|
||||
/// <summary>
|
||||
/// 删除标签成员
|
||||
/// https://developer.work.weixin.qq.com/document/path/90215
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <returns></returns>
|
||||
[Post("https://qyapi.weixin.qq.com/cgi-bin/tag/deltagusers")]
|
||||
Task<DepartmentOutput> DelTagUsers([Query("access_token")] string accessToken, [Body] TagUsersTagInput body);
|
||||
|
||||
/// <summary>
|
||||
/// 获取标签列表
|
||||
/// https://developer.work.weixin.qq.com/document/path/90216
|
||||
/// </summary>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <returns></returns>
|
||||
[Get("https://qyapi.weixin.qq.com/cgi-bin/tag/list")]
|
||||
Task<TagListHttpOutput> List([Query("access_token")] string accessToken);
|
||||
}
|
||||
@ -21,7 +21,7 @@ public class WorkWxBaseService(
|
||||
SysConfigService sysConfigService,
|
||||
IHttpRemoteService httpRemoteService) : ITransient
|
||||
{
|
||||
private static readonly Lazy<HttpRemoteItem> _options = new(() => App.GetOptions<HttpRemotesOptions>().WorkWeixin);
|
||||
private static readonly Lazy<HttpRemoteItem> Options = new(() => App.GetOptions<HttpRemotesOptions>().WorkWeixin);
|
||||
|
||||
/// <summary>
|
||||
/// 获取企业微信接口凭证
|
||||
@ -53,14 +53,14 @@ public class WorkWxBaseService(
|
||||
/// <returns>返回结果</returns>
|
||||
public async Task<R> SendAsync<T, R>(T input) where R : BaseWorkWxOutput
|
||||
{
|
||||
if (_options.Value == null || string.IsNullOrWhiteSpace(_options.Value.BaseAddress)) throw Oops.Oh("[企业微信] 服务配置缺失");
|
||||
if (Options.Value == null || string.IsNullOrWhiteSpace(Options.Value.BaseAddress)) throw Oops.Oh("[企业微信] 服务配置缺失");
|
||||
|
||||
var attr = typeof(T).GetCustomAttribute<HttpRemoteApiAttribute>();
|
||||
if (attr == null || string.IsNullOrWhiteSpace(attr.Action as string))
|
||||
throw Oops.Oh("[企业微信] 接口入参未正确配置[HttpRemoteApi]特性");
|
||||
|
||||
// 拼接请求地址,并设置token
|
||||
var url = _options.Value.BaseAddress + $"/cgi-bin/{attr.Action}?";
|
||||
var url = Options.Value.BaseAddress + $"/cgi-bin/{attr.Action}?";
|
||||
if (input is AuthWorkWxInput)
|
||||
{
|
||||
// 重试3次
|
||||
@ -81,9 +81,9 @@ public class WorkWxBaseService(
|
||||
{
|
||||
HttpMethodEnum.Get => await httpRemoteService.GetAsync(
|
||||
url + input.ToCustomJsonPropertyQueryString(),
|
||||
builder => builder.SetHttpClientName(_options.Value.HttpName).SetRemoteApiAttr(attr)),
|
||||
builder => builder.SetHttpClientName(Options.Value.HttpName).SetReqRemoteApiAttr(attr)),
|
||||
HttpMethodEnum.Post => await httpRemoteService.PostAsync(url,
|
||||
builder => builder.SetHttpClientName(_options.Value.HttpName).SetRemoteApiAttr(attr)
|
||||
builder => builder.SetHttpClientName(Options.Value.HttpName).SetReqRemoteApiAttr(attr)
|
||||
.SetContent(new StringContent(CustomJsonHelper.Serialize(input), Encoding.UTF8, "application/json"))),
|
||||
_ => throw Oops.Oh($"[企业微信] 不支持的请求方式:{attr.HttpMethod.ToString()}:({typeof(T).FullName})"),
|
||||
};
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Plugin.WorkWeixin;
|
||||
|
||||
/// <summary>
|
||||
/// 企业微信接口输出基类
|
||||
/// </summary>
|
||||
public class BaseWorkOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回码
|
||||
/// </summary>
|
||||
[JsonProperty("errcode")]
|
||||
[JsonPropertyName("errcode")]
|
||||
public int ErrCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 对返回码的文本描述内容
|
||||
/// </summary>
|
||||
[JsonProperty("errmsg")]
|
||||
[JsonPropertyName("errmsg")]
|
||||
public string ErrMsg { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 带id的输出参数
|
||||
/// </summary>
|
||||
public class BaseWorkIdOutput : BaseWorkOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// id
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public long? Id { get; set; }
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user