feat(work): 升级公共组件

This commit is contained in:
Fu Diwei 2022-01-21 12:32:03 +08:00
parent be90a93012
commit b4add56432
30 changed files with 333 additions and 415 deletions

View File

@ -1,12 +1,9 @@
using System; using System;
using System.Linq;
namespace Newtonsoft.Json.Converters namespace Newtonsoft.Json.Converters
{ {
internal class SeparatedByVBarInt32ArrayConverter : JsonConverter<int[]?> internal class TextualIntegerArrayWithPipeSplitConverter : JsonConverter<int[]?>
{ {
private const string SEPARATOR = "|";
public override bool CanRead public override bool CanRead
{ {
get { return true; } get { return true; }
@ -27,28 +24,29 @@ namespace Newtonsoft.Json.Converters
{ {
string? value = serializer.Deserialize<string>(reader); string? value = serializer.Deserialize<string>(reader);
if (value == null) if (value == null)
return existingValue; return null;
if (string.IsNullOrEmpty(value))
return Array.Empty<int>();
try string[] strArr = value.Split('|');
int[] intArr = new int[strArr.Length];
for (int i = 0; i < strArr.Length; i++)
{ {
return value if (!int.TryParse(strArr[i], out int j))
.Split(new string[] { SEPARATOR }, StringSplitOptions.RemoveEmptyEntries) throw new JsonSerializationException("Unexpected token when parsing string to integer.");
.Select(e => int.Parse(e))
.ToArray(); intArr[i] = j;
}
catch (FormatException ex)
{
throw new JsonReaderException(ex.Message, ex);
} }
return intArr;
} }
throw new JsonReaderException(); throw new JsonSerializationException();
} }
public override void WriteJson(JsonWriter writer, int[]? value, JsonSerializer serializer) public override void WriteJson(JsonWriter writer, int[]? value, JsonSerializer serializer)
{ {
if (value != null) if (value != null)
writer.WriteValue(string.Join(SEPARATOR, value)); writer.WriteValue(string.Join("|", value));
else else
writer.WriteNull(); writer.WriteNull();
} }

View File

@ -2,10 +2,8 @@
namespace Newtonsoft.Json.Converters namespace Newtonsoft.Json.Converters
{ {
internal class SeparatedByVBarStringArrayConverter : JsonConverter<string[]?> internal class TextualStringArrayWithPipeSplitConverter : JsonConverter<string[]?>
{ {
private const string SEPARATOR = "|";
public override bool CanRead public override bool CanRead
{ {
get { return true; } get { return true; }
@ -26,18 +24,20 @@ namespace Newtonsoft.Json.Converters
{ {
string? value = serializer.Deserialize<string>(reader); string? value = serializer.Deserialize<string>(reader);
if (value == null) if (value == null)
return existingValue; return null;
if (string.IsNullOrEmpty(value))
return Array.Empty<string>();
return value.Split(new string[] { SEPARATOR }, StringSplitOptions.RemoveEmptyEntries); return value.Split('|');
} }
throw new JsonReaderException(); throw new JsonSerializationException();
} }
public override void WriteJson(JsonWriter writer, string[]? value, JsonSerializer serializer) public override void WriteJson(JsonWriter writer, string[]? value, JsonSerializer serializer)
{ {
if (value != null) if (value != null)
writer.WriteValue(string.Join(SEPARATOR, value)); writer.WriteValue(string.Join("|", value));
else else
writer.WriteNull(); writer.WriteNull();
} }

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Newtonsoft.Json.Converters
{
internal class TextualIntegerListWithPipeSplitConverter : JsonConverter
{
private readonly JsonConverter<int[]?> _converter = new TextualIntegerArrayWithPipeSplitConverter();
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType &&
typeof(IList<>).IsAssignableFrom(objectType.GetGenericTypeDefinition()) &&
typeof(int) == objectType.GetGenericArguments()[0];
}
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
int[]? array = _converter.ReadJson(reader, objectType, null, false, serializer);
return array?.ToList();
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
_converter.WriteJson(writer, ((IList<int>?)value)?.ToArray(), serializer);
}
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Newtonsoft.Json.Converters
{
internal class TextualStringListWithPipeSplitConverter : JsonConverter
{
private readonly JsonConverter<string[]?> _converter = new TextualStringArrayWithPipeSplitConverter();
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType &&
typeof(IList<>).IsAssignableFrom(objectType.GetGenericTypeDefinition()) &&
typeof(string) == objectType.GetGenericArguments()[0];
}
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
string[]? array = _converter.ReadJson(reader, objectType, null, false, serializer);
return array?.ToList();
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
_converter.WriteJson(writer, ((IList<string>?)value)?.ToArray(), serializer);
}
}
}

View File

@ -3,7 +3,7 @@ using System.Text.Json.Serialization;
namespace System.Text.Json.Converters namespace System.Text.Json.Converters
{ {
internal class SeparatedByVBarInt32ArrayConverter : JsonConverter<int[]?> internal class TextualIntegerArrayWithPipeSplitConverter : JsonConverter<int[]?>
{ {
private const string SEPARATOR = "|"; private const string SEPARATOR = "|";
@ -18,18 +18,19 @@ namespace System.Text.Json.Converters
string? value = reader.GetString(); string? value = reader.GetString();
if (value == null) if (value == null)
return null; return null;
if (string.IsNullOrEmpty(value))
return Array.Empty<int>();
try string[] strArr = value.Split('|');
int[] intArr = new int[strArr.Length];
for (int i = 0; i < strArr.Length; i++)
{ {
return value if (!int.TryParse(strArr[i], out int j))
.Split(new string[] { SEPARATOR }, StringSplitOptions.RemoveEmptyEntries) throw new JsonException("Unexpected token when parsing string to integer.");
.Select(e => int.Parse(e))
.ToArray(); intArr[i] = j;
}
catch (FormatException ex)
{
throw new JsonException(ex.Message, ex);
} }
return intArr;
} }
throw new JsonException(); throw new JsonException();
@ -38,7 +39,7 @@ namespace System.Text.Json.Converters
public override void Write(Utf8JsonWriter writer, int[]? value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, int[]? value, JsonSerializerOptions options)
{ {
if (value != null) if (value != null)
writer.WriteStringValue(string.Join(SEPARATOR, value)); writer.WriteStringValue(string.Join("|", value));
else else
writer.WriteNullValue(); writer.WriteNullValue();
} }

View File

@ -2,10 +2,8 @@
namespace System.Text.Json.Converters namespace System.Text.Json.Converters
{ {
internal class SeparatedByVBarStringArrayConverter : JsonConverter<string[]?> internal class TextualStringArrayWithPipeSplitConverter : JsonConverter<string[]?>
{ {
private const string SEPARATOR = "|";
public override string[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override string[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType == JsonTokenType.Null) if (reader.TokenType == JsonTokenType.Null)
@ -17,8 +15,10 @@ namespace System.Text.Json.Converters
string? value = reader.GetString(); string? value = reader.GetString();
if (value == null) if (value == null)
return null; return null;
if (string.IsNullOrEmpty(value))
return Array.Empty<string>();
return value.Split(new string[] { SEPARATOR }, StringSplitOptions.RemoveEmptyEntries); return value.Split('|');
} }
throw new JsonException(); throw new JsonException();
@ -27,7 +27,7 @@ namespace System.Text.Json.Converters
public override void Write(Utf8JsonWriter writer, string[]? value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, string[]? value, JsonSerializerOptions options)
{ {
if (value != null) if (value != null)
writer.WriteStringValue(string.Join(SEPARATOR, value)); writer.WriteStringValue(string.Join("|", value));
else else
writer.WriteNullValue(); writer.WriteNullValue();
} }

View File

@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
namespace System.Text.Json.Converters
{
internal class TextualIntegerListWithPipeSplitConverter : JsonConverterFactory
{
private sealed class InnerTextualIntegerListWithVBarSplitConverter : JsonConverter<IList<int>?>
{
private readonly JsonConverter<int[]?> _converter = new TextualIntegerArrayWithPipeSplitConverter();
public override IList<int>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
int[]? array = _converter.Read(ref reader, typeToConvert, options);
IList<int>? list = array?.ToList();
return list;
}
public override void Write(Utf8JsonWriter writer, IList<int>? value, JsonSerializerOptions options)
{
_converter.Write(writer, value?.ToArray(), options);
}
}
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert.IsGenericType &&
typeof(IList<>).IsAssignableFrom(typeToConvert.GetGenericTypeDefinition()) &&
typeof(int) == typeToConvert.GetGenericArguments()[0];
}
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
return new InnerTextualIntegerListWithVBarSplitConverter();
}
}
}

View File

@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
namespace System.Text.Json.Converters
{
internal class TextualStringListWithPipeSplitConverter : JsonConverterFactory
{
private sealed class InnerTextualStringListWithVBarSplitConverter : JsonConverter<IList<string>?>
{
private readonly JsonConverter<string[]?> _converter = new TextualStringArrayWithPipeSplitConverter();
public override IList<string>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string[]? array = _converter.Read(ref reader, typeToConvert, options);
IList<string>? list = array?.ToList();
return list;
}
public override void Write(Utf8JsonWriter writer, IList<string>? value, JsonSerializerOptions options)
{
_converter.Write(writer, value?.ToArray(), options);
}
}
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert.IsGenericType &&
typeof(IList<>).IsAssignableFrom(typeToConvert.GetGenericTypeDefinition()) &&
typeof(string) == typeToConvert.GetGenericArguments()[0];
}
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
return new InnerTextualStringListWithVBarSplitConverter();
}
}
}

View File

@ -1,42 +0,0 @@
using System;
using System.Collections.Generic;
namespace Newtonsoft.Json.Converters
{
internal class SeparatedByVBarInt32IListConverter : JsonConverter<IList<int>?>
{
private readonly JsonConverter<List<int>?> _converter = new SeparatedByVBarInt32ListConverter();
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override IList<int>? ReadJson(JsonReader reader, Type objectType, IList<int>? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return _converter.ReadJson(reader, objectType, ConvertIListToList(existingValue), hasExistingValue, serializer);
}
public override void WriteJson(JsonWriter writer, IList<int>? value, JsonSerializer serializer)
{
_converter.WriteJson(writer, ConvertIListToList(value), serializer);
}
private List<int>? ConvertIListToList(IList<int>? src)
{
if (src == null)
return null;
List<int>? dest = src as List<int>;
if (dest != null)
return dest;
return new List<int>(src);
}
}
}

View File

@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Newtonsoft.Json.Converters
{
internal class SeparatedByVBarInt32ListConverter : JsonConverter<List<int>?>
{
private readonly JsonConverter<int[]?> _converter = new SeparatedByVBarInt32ArrayConverter();
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override List<int>? ReadJson(JsonReader reader, Type objectType, List<int>? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return _converter.ReadJson(reader, objectType, existingValue?.ToArray(), hasExistingValue, serializer)?.ToList();
}
public override void WriteJson(JsonWriter writer, List<int>? value, JsonSerializer serializer)
{
_converter.WriteJson(writer, value?.ToArray(), serializer);
}
}
}

View File

@ -1,42 +0,0 @@
using System;
using System.Collections.Generic;
namespace Newtonsoft.Json.Converters
{
internal class SeparatedByVBarStringIListConverter : JsonConverter<IList<string>?>
{
private readonly JsonConverter<List<string>?> _converter = new SeparatedByVBarStringListConverter();
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override IList<string>? ReadJson(JsonReader reader, Type objectType, IList<string>? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return _converter.ReadJson(reader, objectType, ConvertIListToList(existingValue), hasExistingValue, serializer);
}
public override void WriteJson(JsonWriter writer, IList<string>? value, JsonSerializer serializer)
{
_converter.WriteJson(writer, ConvertIListToList(value), serializer);
}
private List<string>? ConvertIListToList(IList<string>? src)
{
if (src == null)
return null;
List<string>? dest = src as List<string>;
if (dest != null)
return dest;
return new List<string>(src);
}
}
}

View File

@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Newtonsoft.Json.Converters
{
internal class SeparatedByVBarStringListConverter : JsonConverter<List<string>?>
{
private readonly JsonConverter<string[]?> _converter = new SeparatedByVBarStringArrayConverter();
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override List<string>? ReadJson(JsonReader reader, Type objectType, List<string>? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return _converter.ReadJson(reader, objectType, existingValue?.ToArray(), hasExistingValue, serializer)?.ToList();
}
public override void WriteJson(JsonWriter writer, List<string>? value, JsonSerializer serializer)
{
_converter.WriteJson(writer, value?.ToArray(), serializer);
}
}
}

View File

@ -1,32 +0,0 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace System.Text.Json.Converters
{
internal class SeparatedByVBarInt32IListConverter : JsonConverter<IList<int>?>
{
private readonly JsonConverter<List<int>?> _converter = new SeparatedByVBarInt32ListConverter();
public override IList<int>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return _converter.Read(ref reader, typeToConvert, options);
}
public override void Write(Utf8JsonWriter writer, IList<int>? value, JsonSerializerOptions options)
{
_converter.Write(writer, ConvertIListToList(value), options);
}
private List<int>? ConvertIListToList(IList<int>? src)
{
if (src == null)
return null;
List<int>? dest = src as List<int>;
if (dest != null)
return dest;
return new List<int>(src);
}
}
}

View File

@ -1,21 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
namespace System.Text.Json.Converters
{
internal class SeparatedByVBarInt32ListConverter : JsonConverter<List<int>?>
{
private readonly JsonConverter<int[]?> _converter = new SeparatedByVBarInt32ArrayConverter();
public override List<int>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return _converter.Read(ref reader, typeToConvert, options)?.ToList();
}
public override void Write(Utf8JsonWriter writer, List<int>? value, JsonSerializerOptions options)
{
_converter.Write(writer, value?.ToArray(), options);
}
}
}

View File

@ -1,32 +0,0 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace System.Text.Json.Converters
{
internal class SeparatedByVBarStringIListConverter : JsonConverter<IList<string>?>
{
private readonly JsonConverter<List<string>?> _converter = new SeparatedByVBarStringListConverter();
public override IList<string>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return _converter.Read(ref reader, typeToConvert, options);
}
public override void Write(Utf8JsonWriter writer, IList<string>? value, JsonSerializerOptions options)
{
_converter.Write(writer, ConvertIListToList(value), options);
}
private List<string>? ConvertIListToList(IList<string>? src)
{
if (src == null)
return null;
List<string>? dest = src as List<string>;
if (dest != null)
return dest;
return new List<string>(src);
}
}
}

View File

@ -1,21 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
namespace System.Text.Json.Converters
{
internal class SeparatedByVBarStringListConverter : JsonConverter<List<string>?>
{
private readonly JsonConverter<string[]?> _converter = new SeparatedByVBarStringArrayConverter();
public override List<string>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return _converter.Read(ref reader, typeToConvert, options)?.ToList();
}
public override void Write(Utf8JsonWriter writer, List<string>? value, JsonSerializerOptions options)
{
_converter.Write(writer, value?.ToArray(), options);
}
}
}

View File

@ -964,27 +964,27 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.Models
/// 获取或设置接收消息的成员账号列表。 /// 获取或设置接收消息的成员账号列表。
/// </summary> /// </summary>
[Newtonsoft.Json.JsonProperty("touser")] [Newtonsoft.Json.JsonProperty("touser")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.SeparatedByVBarStringIListConverter))] [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.TextualStringListWithPipeSplitConverter))]
[System.Text.Json.Serialization.JsonPropertyName("touser")] [System.Text.Json.Serialization.JsonPropertyName("touser")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.SeparatedByVBarStringIListConverter))] [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.TextualStringListWithPipeSplitConverter))]
public IList<string>? ToUserIdList { get; set; } public IList<string>? ToUserIdList { get; set; }
/// <summary> /// <summary>
/// 获取或设置接收消息的部门 ID 列表。 /// 获取或设置接收消息的部门 ID 列表。
/// </summary> /// </summary>
[Newtonsoft.Json.JsonProperty("toparty")] [Newtonsoft.Json.JsonProperty("toparty")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.SeparatedByVBarInt32IListConverter))] [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.TextualIntegerListWithPipeSplitConverter))]
[System.Text.Json.Serialization.JsonPropertyName("toparty")] [System.Text.Json.Serialization.JsonPropertyName("toparty")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.SeparatedByVBarInt32IListConverter))] [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.TextualIntegerListWithPipeSplitConverter))]
public IList<int>? ToDepartmentIdList { get; set; } public IList<int>? ToDepartmentIdList { get; set; }
/// <summary> /// <summary>
/// 获取或设置接收消息的标签 ID 列表。 /// 获取或设置接收消息的标签 ID 列表。
/// </summary> /// </summary>
[Newtonsoft.Json.JsonProperty("totag")] [Newtonsoft.Json.JsonProperty("totag")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.SeparatedByVBarInt32IListConverter))] [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.TextualIntegerListWithPipeSplitConverter))]
[System.Text.Json.Serialization.JsonPropertyName("totag")] [System.Text.Json.Serialization.JsonPropertyName("totag")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.SeparatedByVBarInt32IListConverter))] [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.TextualIntegerListWithPipeSplitConverter))]
public IList<int>? ToTagIdList { get; set; } public IList<int>? ToTagIdList { get; set; }
/// <summary> /// <summary>

View File

@ -9,27 +9,27 @@
/// 获取或设置无效的成员账号列表。 /// 获取或设置无效的成员账号列表。
/// </summary> /// </summary>
[Newtonsoft.Json.JsonProperty("invaliduser")] [Newtonsoft.Json.JsonProperty("invaliduser")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.SeparatedByVBarStringArrayConverter))] [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.TextualStringArrayWithPipeSplitConverter))]
[System.Text.Json.Serialization.JsonPropertyName("invaliduser")] [System.Text.Json.Serialization.JsonPropertyName("invaliduser")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.SeparatedByVBarStringArrayConverter))] [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.TextualStringArrayWithPipeSplitConverter))]
public string[]? InvalidUserIdList { get; set; } public string[]? InvalidUserIdList { get; set; }
/// <summary> /// <summary>
/// 获取或设置无效的部门 ID 列表。 /// 获取或设置无效的部门 ID 列表。
/// </summary> /// </summary>
[Newtonsoft.Json.JsonProperty("invalidparty")] [Newtonsoft.Json.JsonProperty("invalidparty")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.SeparatedByVBarInt32ArrayConverter))] [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.TextualIntegerArrayWithPipeSplitConverter))]
[System.Text.Json.Serialization.JsonPropertyName("invalidparty")] [System.Text.Json.Serialization.JsonPropertyName("invalidparty")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.SeparatedByVBarInt32ArrayConverter))] [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.TextualIntegerArrayWithPipeSplitConverter))]
public int[]? InvalidDepartmentIdList { get; set; } public int[]? InvalidDepartmentIdList { get; set; }
/// <summary> /// <summary>
/// 获取或设置无效的标签 ID 列表。 /// 获取或设置无效的标签 ID 列表。
/// </summary> /// </summary>
[Newtonsoft.Json.JsonProperty("invalidtag")] [Newtonsoft.Json.JsonProperty("invalidtag")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.SeparatedByVBarInt32ArrayConverter))] [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.TextualIntegerArrayWithPipeSplitConverter))]
[System.Text.Json.Serialization.JsonPropertyName("invalidtag")] [System.Text.Json.Serialization.JsonPropertyName("invalidtag")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.SeparatedByVBarInt32ArrayConverter))] [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.TextualIntegerArrayWithPipeSplitConverter))]
public int[]? InvalidTagIdList { get; set; } public int[]? InvalidTagIdList { get; set; }
/// <summary> /// <summary>

View File

@ -9,9 +9,9 @@
/// 获取或设置无效的成员账号列表。 /// 获取或设置无效的成员账号列表。
/// </summary> /// </summary>
[Newtonsoft.Json.JsonProperty("invalidlist")] [Newtonsoft.Json.JsonProperty("invalidlist")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.SeparatedByVBarStringArrayConverter))] [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.TextualStringArrayWithPipeSplitConverter))]
[System.Text.Json.Serialization.JsonPropertyName("invalidlist")] [System.Text.Json.Serialization.JsonPropertyName("invalidlist")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.SeparatedByVBarStringArrayConverter))] [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.TextualStringArrayWithPipeSplitConverter))]
public string[]? InvalidUserIdList { get; set; } public string[]? InvalidUserIdList { get; set; }
/// <summary> /// <summary>

View File

@ -0,0 +1,26 @@
## SKIT.FlurlHttpClient.Wechat.Work
[![GitHub Stars](https://img.shields.io/github/stars/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat?logo=github&label=Stars)](https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat) [![GitHub Forks](https://img.shields.io/github/forks/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat?logo=github&label=Forks)](https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat) [![NuGet Download](https://img.shields.io/nuget/dt/SKIT.FlurlHttpClient.Wechat.Work.svg?sanitize=true&label=Downloads)](https://www.nuget.org/packages/SKIT.FlurlHttpClient.Wechat.Work) [![License](https://img.shields.io/github/license/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat?label=License)](https://mit-license.org/)
基于 `Flurl.Http` 的企业微信 API 客户端。
---
### 【功能特性】
- 基于企业微信 API 封装。
- 支持企业内部开发、第三方应用开发(即服务商)、智慧硬件开发(即硬件接入)三种模式。
- 提供了企业微信 API 所需的 SHA-1、AES 等算法工具类。
- 提供了 JS-SDK 签名、解析回调通知事件等扩展方法。
---
### 【开发文档】
[点此查看](https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat)。
---
### 【更新日志】
[点此查看](https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat/blob/main/CHANGELOG.md)。

View File

@ -9,6 +9,8 @@
<PropertyGroup> <PropertyGroup>
<PackageId>SKIT.FlurlHttpClient.Wechat.Work</PackageId> <PackageId>SKIT.FlurlHttpClient.Wechat.Work</PackageId>
<PackageIcon>LOGO.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat</PackageProjectUrl> <PackageProjectUrl>https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat</PackageProjectUrl>
<PackageTags>Flurl.Http Wechat Weixin MicroMessage WechatWork WeixinWork Wxwork 微信 企业微信 企业号 微信企业号</PackageTags> <PackageTags>Flurl.Http Wechat Weixin MicroMessage WechatWork WeixinWork Wxwork 微信 企业微信 企业号 微信企业号</PackageTags>
@ -17,17 +19,28 @@
<Authors>Fu Diwei</Authors> <Authors>Fu Diwei</Authors>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git</RepositoryUrl> <RepositoryUrl>https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git</RepositoryUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<Deterministic>true</Deterministic>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<None Include="../../LOGO.png" Pack="true" PackagePath="/" />
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net461'" /> <Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net461'" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="2.1.1" /> <PackageReference Include="SKIT.FlurlHttpClient.Common" Version="2.2.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -10,7 +10,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.Utilities
public static class SHA1Utility public static class SHA1Utility
{ {
/// <summary> /// <summary>
/// 获取信息摘要。 /// 获取 SHA-1 信息摘要。
/// </summary> /// </summary>
/// <param name="bytes">信息字节数组。</param> /// <param name="bytes">信息字节数组。</param>
/// <returns>信息摘要。</returns> /// <returns>信息摘要。</returns>
@ -24,7 +24,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.Utilities
} }
/// <summary> /// <summary>
/// 获取信息摘要。 /// 获取 SHA-1 信息摘要。
/// </summary> /// </summary>
/// <param name="message">文本信息。</param> /// <param name="message">文本信息。</param>
/// <returns>信息摘要。</returns> /// <returns>信息摘要。</returns>

View File

@ -1,8 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netcoreapp3.1; net6.0</TargetFrameworks> <TargetFrameworks>net472; netcoreapp3.1; net6.0</TargetFrameworks>
<LangVersion>9.0</LangVersion> <LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<NullableReferenceTypes>true</NullableReferenceTypes>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -7,10 +7,10 @@ using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
{ {
public class WechatWorkExecuteCgibinTests public class TestCase_ApiExecuteCgibinTests
{ {
[Fact(DisplayName = "[GET] /cgi-bin/gettoken")] [Fact(DisplayName = "测试用例:调用 API [GET] /cgi-bin/gettoken")]
public async Task CgibinTokenTest() public async Task TestExecuteCgibinToken()
{ {
var request = new Models.CgibinGetTokenRequest(); var request = new Models.CgibinGetTokenRequest();
var response = await TestClients.Instance.ExecuteCgibinGetTokenAsync(request); var response = await TestClients.Instance.ExecuteCgibinGetTokenAsync(request);

View File

@ -9,25 +9,25 @@ using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
{ {
public class WechatWorkDeclarationTests public class TestCase_CodeReviewAnalyzer
{ {
private static readonly Assembly _assembly = Assembly.Load("SKIT.FlurlHttpClient.Wechat.Work"); private Assembly SourceAssembly { get; } = Assembly.Load("SKIT.FlurlHttpClient.Wechat.Work");
[Fact(DisplayName = "验证 API 模型命名")] [Fact(DisplayName = "代码评审:分析 API 模型命名")]
public void ApiModelsNamingTest() public void TestApiModelsNaming()
{ {
CodeStyleUtil.VerifyApiModelsNaming(_assembly, out var ex); CodeStyleUtil.VerifyApiModelsNaming(SourceAssembly, out var ex);
if (ex != null) if (ex != null)
throw ex; throw ex;
Assert.Null(ex); Assert.Null(ex);
} }
[Fact(DisplayName = "验证 API 模型定义")] [Fact(DisplayName = "代码评审:分析 API 模型定义")]
public void ApiModelsDefinitionTest() public void TestApiModelsDefinition()
{ {
string workdir = Path.Combine(TestConfigs.ProjectTestDirectory, "ModelSamples"); string workdir = Path.Combine(TestConfigs.ProjectTestDirectory, "ModelSamples");
CodeStyleUtil.VerifyApiModelsDefinition(_assembly, workdir, out var ex); CodeStyleUtil.VerifyApiModelsDefinition(SourceAssembly, workdir, out var ex);
if (ex != null) if (ex != null)
throw ex; throw ex;
@ -35,11 +35,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
Assert.Null(ex); Assert.Null(ex);
} }
[Fact(DisplayName = "验证 API 事件定义")] [Fact(DisplayName = "代码评审:分析 API 事件定义")]
public void ApiEventsDefinitionTest() public void TestApiEventsDefinition()
{ {
string workdir = Path.Combine(TestConfigs.ProjectTestDirectory, "EventSamples"); string workdir = Path.Combine(TestConfigs.ProjectTestDirectory, "EventSamples");
CodeStyleUtil.VerifyApiEventsDefinition(_assembly, workdir, out var ex); CodeStyleUtil.VerifyApiEventsDefinition(SourceAssembly, workdir, out var ex);
if (ex != null) if (ex != null)
throw ex; throw ex;
@ -47,10 +47,10 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
Assert.Null(ex); Assert.Null(ex);
} }
[Fact(DisplayName = "验证 API 接口命名")] [Fact(DisplayName = "代码评审:分析 API 接口命名")]
public void ApiExtensionsNamingTest() public void TestApiExtensionsNaming()
{ {
CodeStyleUtil.VerifyApiExtensionsNaming(_assembly, out var ex); CodeStyleUtil.VerifyApiExtensionsNaming(SourceAssembly, out var ex);
if (ex != null) if (ex != null)
throw ex; throw ex;
@ -58,8 +58,8 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
Assert.Null(ex); Assert.Null(ex);
} }
[Fact(DisplayName = "验证代码规范")] [Fact(DisplayName = "代码评审:分析代码规范")]
public void CodeStyleTest() public void TestCodeStyle()
{ {
string workdir = Path.Combine(TestConfigs.ProjectSourceDirectory); string workdir = Path.Combine(TestConfigs.ProjectSourceDirectory);
CodeStyleUtil.VerifySourceCodeStyle(workdir, out var ex); CodeStyleUtil.VerifySourceCodeStyle(workdir, out var ex);

View File

@ -8,10 +8,10 @@ using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
{ {
public class WechatWorkEventDeserializationTests public class TestCase_EventTests
{ {
[Fact(DisplayName = "验签并解密回调数据")] [Fact(DisplayName = "测试用例:验签并解密回调数据")]
public void GetEventMessageTypeTest() public void TestDeserializeEventFromXml()
{ {
string callbacMsgSig = "477715d11cdb4164915debcba66cb864d751f3e6"; string callbacMsgSig = "477715d11cdb4164915debcba66cb864d751f3e6";
string callbacTimeStamp = "1409659813"; string callbacTimeStamp = "1409659813";
@ -33,5 +33,25 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
Assert.Equal("wx5823bf96d3bd56c7", eventModel.ToUserName); Assert.Equal("wx5823bf96d3bd56c7", eventModel.ToUserName);
Assert.Equal("mycreate", eventModel.FromUserName); Assert.Equal("mycreate", eventModel.FromUserName);
} }
[Fact(DisplayName = "测试用例:验证微信服务器")]
public void TestVerifyEventSignatureForEcho()
{
string callbacMsgSig = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3";
string callbacTimeStamp = "1409659589";
string callbacNonce = "263014780";
string callbackEcho = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==";
var options = new WechatWorkClientOptions()
{
CorpId = "wx5823bf96d3bd56c7",
PushEncodingAESKey = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C",
PushToken = "QDG6eK"
};
var client = new WechatWorkClient(options);
Assert.True(client.VerifyEventSignatureForEcho(callbacTimeStamp, callbacNonce, callbackEcho, callbacMsgSig, out string replyEcho));
Assert.Equal("1616140317555161061", replyEcho);
}
} }
} }

View File

@ -8,10 +8,10 @@ using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
{ {
public class WechatWorkUtilityTests public class TestCase_WxBizMsgCryptorUtilityTests
{ {
[Fact(DisplayName = "回调信息解析")] [Fact(DisplayName = "测试用例:回调信息解析")]
public void WxBizMsgCryptorParsingTest() public void TestWxBizMsgCryptorParsing()
{ {
string xml = "<xml><ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName><Encrypt><![CDATA[RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==]]></Encrypt><AgentID><![CDATA[218]]></AgentID></xml>"; string xml = "<xml><ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName><Encrypt><![CDATA[RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==]]></Encrypt><AgentID><![CDATA[218]]></AgentID></xml>";
@ -22,8 +22,22 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
Assert.Equal("RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==", encryptedMsg); Assert.Equal("RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==", encryptedMsg);
} }
[Fact(DisplayName = "回调信息验签")] [Fact(DisplayName = "测试用例:回调信息解密")]
public void WxBizMsgCryptorSignatureTest() public void TestWxBizMsgCryptorDecrypt()
{
string corpId = "wx5823bf96d3bd56c7";
string encodingAESKey = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C";
string cipherText = "RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==";
string actualCipherText = Utilities.WxBizMsgCryptor.AESDecrypt(cipherText, encodingAESKey, out string actualCorpId);
string expectedCipherText = "<xml><ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName>\n<FromUserName><![CDATA[mycreate]]></FromUserName>\n<CreateTime>1409659813</CreateTime>\n<MsgType><![CDATA[text]]></MsgType>\n<Content><![CDATA[hello]]></Content>\n<MsgId>4561255354251345929</MsgId>\n<AgentID>218</AgentID>\n</xml>";
Assert.Equal(corpId, actualCorpId);
Assert.Equal(expectedCipherText, actualCipherText, ignoreCase: true);
}
[Fact(DisplayName = "测试用例:回调信息生成签名")]
public void TestWxBizMsgCryptorGenerateSignature()
{ {
string token = "QDG6eK"; string token = "QDG6eK";
string timestamp = "1409659813"; string timestamp = "1409659813";
@ -37,18 +51,16 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
Assert.True(Utilities.WxBizMsgCryptor.VerifySignature(sToken: token, sTimestamp: timestamp, sNonce: nonce, sMsgEncrypt: cipherText, sMsgSign: expectedSignText)); Assert.True(Utilities.WxBizMsgCryptor.VerifySignature(sToken: token, sTimestamp: timestamp, sNonce: nonce, sMsgEncrypt: cipherText, sMsgSign: expectedSignText));
} }
[Fact(DisplayName = "回调信息解密")] [Fact(DisplayName = "测试用例:回调信息验证签名")]
public void WxBizMsgCryptorDecryptTest() public void TestWxBizMsgCryptorVerifySignature()
{ {
string corpId = "wx5823bf96d3bd56c7"; string token = "ovAkP0Tb";
string encodingAESKey = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C"; string reqMsgSig = "8128e41a38892d814e4aa67753425da9dc8ec2b3";
string cipherText = "RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q=="; string reqTimeStamp = "1628737717";
string reqNonce = "1811081856";
string reqCipherText = "Q/jUxIL3/jRaFeTKesIr1QSq2SOEApDqlzcRrRM6Jlk4EbMBns3plPOR/W3gThOEq+zYI42fNSoIUb3cQwt9zyD1aLU/7D3WNLute7LQ9LSHjZEfVmx5zcIR9zvrUWGjhe1whTPH4e1WR6vbOYs8o/bDRF0vX/NcE4XK7P83Y6CzQiJoKbjVCne84s0zcw5eh+ZUDB55eaDHPSoS7kAC8kB00pfBoDF0jyfc8CUKLW97e72vJGyUWjZ0BvYN+R+tFjMgEzg/EN1imuuFnf40DMAcvB6y+C97TuaWjpgfRdowGWzn10JAFNukRfQqjdA0e2bfczJ7+t9w/t8/XSMADJOt1xbnP+I5cRX/r7ueBGmG/6ejP3myO9yTXHdujGvwrXHuWw+J7qD4VoUVjbm2vQ1qQKbrweKssr6O+3XSbanZ5R3n26EpN/gfgX+r6rcGViqsFop9Ai9xMnfJUubB6Q==";
string actualCipherText = Utilities.WxBizMsgCryptor.AESDecrypt(cipherText, encodingAESKey, out string actualCorpId); Assert.True(Utilities.WxBizMsgCryptor.VerifySignature(token, reqTimeStamp, reqNonce, reqCipherText, reqMsgSig));
string expectedCipherText = "<xml><ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName>\n<FromUserName><![CDATA[mycreate]]></FromUserName>\n<CreateTime>1409659813</CreateTime>\n<MsgType><![CDATA[text]]></MsgType>\n<Content><![CDATA[hello]]></Content>\n<MsgId>4561255354251345929</MsgId>\n<AgentID>218</AgentID>\n</xml>";
Assert.Equal(corpId, actualCorpId);
Assert.Equal(expectedCipherText, actualCipherText, ignoreCase: true);
} }
} }
} }

View File

@ -8,8 +8,9 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
{ {
static TestConfigs() static TestConfigs()
{ {
// NOTICE: 请在项目根目录下按照 appsettings.json 的格式新建 appsettings.local.json 填入测试参数。 // NOTICE: 请在项目根目录下按照 appsettings.json 的格式新建 appsettings.local.json 填入测试参数。
// WARN: 敏感信息请不要提交到 git // WARNING: 请在 DEBUG 模式下运行测试用例。
// WARNING: 敏感信息请不要提交到 git
try try
{ {
@ -17,12 +18,12 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
using var jdoc = JsonDocument.Parse(stream); using var jdoc = JsonDocument.Parse(stream);
var config = jdoc.RootElement.GetProperty("TestConfig"); var config = jdoc.RootElement.GetProperty("TestConfig");
WechatCorpId = config.GetProperty("CorpId").GetString(); WechatCorpId = config.GetProperty("CorpId").GetString()!;
WechatAgentId = int.Parse(config.GetProperty("AgentId").GetString()); WechatAgentId = int.Parse(config.GetProperty("AgentId").GetString())!;
WechatAgentSecret = config.GetProperty("AgentSecret").GetString(); WechatAgentSecret = config.GetProperty("AgentSecret").GetString()!;
ProjectSourceDirectory = jdoc.RootElement.GetProperty("ProjectSourceDirectory").GetString(); ProjectSourceDirectory = jdoc.RootElement.GetProperty("ProjectSourceDirectory").GetString()!;
ProjectTestDirectory = jdoc.RootElement.GetProperty("ProjectTestDirectory").GetString(); ProjectTestDirectory = jdoc.RootElement.GetProperty("ProjectTestDirectory").GetString()!;
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
{
public class WechatWorkEventVerificationTests
{
[Fact(DisplayName = "验证微信服务器")]
public void GetEventMessageTypeTest()
{
string callbacMsgSig = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3";
string callbacTimeStamp = "1409659589";
string callbacNonce = "263014780";
string callbackEcho = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==";
var options = new WechatWorkClientOptions()
{
CorpId = "wx5823bf96d3bd56c7",
PushEncodingAESKey = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C",
PushToken = "QDG6eK"
};
var client = new WechatWorkClient(options);
Assert.True(client.VerifyEventSignatureForEcho(callbacTimeStamp, callbacNonce, callbackEcho, callbacMsgSig, out string replyEcho));
Assert.Equal("1616140317555161061", replyEcho);
}
}
}

View File

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
{
public class WechatWorkWxBizMsgCryptorTests
{
[Fact(DisplayName = "验签回调数据")]
public void VerifyEventTest()
{
string token = "ovAkP0Tb";
string reqMsgSig = "8128e41a38892d814e4aa67753425da9dc8ec2b3";
string reqTimeStamp = "1628737717";
string reqNonce = "1811081856";
string reqCipherText = "Q/jUxIL3/jRaFeTKesIr1QSq2SOEApDqlzcRrRM6Jlk4EbMBns3plPOR/W3gThOEq+zYI42fNSoIUb3cQwt9zyD1aLU/7D3WNLute7LQ9LSHjZEfVmx5zcIR9zvrUWGjhe1whTPH4e1WR6vbOYs8o/bDRF0vX/NcE4XK7P83Y6CzQiJoKbjVCne84s0zcw5eh+ZUDB55eaDHPSoS7kAC8kB00pfBoDF0jyfc8CUKLW97e72vJGyUWjZ0BvYN+R+tFjMgEzg/EN1imuuFnf40DMAcvB6y+C97TuaWjpgfRdowGWzn10JAFNukRfQqjdA0e2bfczJ7+t9w/t8/XSMADJOt1xbnP+I5cRX/r7ueBGmG/6ejP3myO9yTXHdujGvwrXHuWw+J7qD4VoUVjbm2vQ1qQKbrweKssr6O+3XSbanZ5R3n26EpN/gfgX+r6rcGViqsFop9Ai9xMnfJUubB6Q==";
Assert.True(Utilities.WxBizMsgCryptor.VerifySignature(token, reqTimeStamp, reqNonce, reqCipherText, reqMsgSig));
}
}
}