first commit
This commit is contained in:
67
DOAN.Common/AliyunOssHelper.cs
Normal file
67
DOAN.Common/AliyunOssHelper.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Aliyun.OSS;
|
||||
using Aliyun.OSS.Common;
|
||||
using Infrastructure;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace DOAN.Common
|
||||
{
|
||||
public class AliyunOssHelper
|
||||
{
|
||||
static string accessKeyId = AppSettings.GetConfig("ALIYUN_OSS:KEY");
|
||||
static string accessKeySecret = AppSettings.GetConfig("ALIYUN_OSS:SECRET");
|
||||
static string endpoint = AppSettings.GetConfig("ALIYUN_OSS:REGIONID");
|
||||
static string bucketName1 = AppSettings.GetConfig("ALIYUN_OSS:bucketName");
|
||||
|
||||
/// <summary>
|
||||
/// 上传到阿里云
|
||||
/// </summary>
|
||||
/// <param name="filestreams"></param>
|
||||
/// <param name="dirPath">存储路径 eg: upload/2020/01/01/xxx.png</param>
|
||||
/// <param name="bucketName">存储桶 如果为空默认取配置文件</param>
|
||||
public static System.Net.HttpStatusCode PutObjectFromFile(Stream filestreams, string dirPath, string bucketName = "")
|
||||
{
|
||||
OssClient client = new(endpoint, accessKeyId, accessKeySecret);
|
||||
if (string.IsNullOrEmpty(bucketName)) { bucketName = bucketName1; }
|
||||
try
|
||||
{
|
||||
dirPath = dirPath.Replace("\\", "/");
|
||||
PutObjectResult putObjectResult = client.PutObject(bucketName, dirPath, filestreams);
|
||||
// Console.WriteLine("Put object:{0} succeeded", directory);
|
||||
|
||||
return putObjectResult.HttpStatusCode;
|
||||
}
|
||||
catch (OssException ex)
|
||||
{
|
||||
Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID:{2}\tHostID:{3}",
|
||||
ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Failed with error info: {0}", ex.Message);
|
||||
}
|
||||
return System.Net.HttpStatusCode.BadRequest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除资源
|
||||
/// </summary>
|
||||
/// <param name="dirPath"></param>
|
||||
/// <param name="bucketName"></param>
|
||||
/// <returns></returns>
|
||||
public static System.Net.HttpStatusCode DeleteFile(string dirPath, string bucketName = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(bucketName)) { bucketName = bucketName1; }
|
||||
try
|
||||
{
|
||||
OssClient client = new(endpoint, accessKeyId, accessKeySecret);
|
||||
DeleteObjectResult putObjectResult = client.DeleteObject(bucketName, dirPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
return System.Net.HttpStatusCode.BadRequest;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
DOAN.Common/DOAN.Common.csproj
Normal file
16
DOAN.Common/DOAN.Common.csproj
Normal file
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
|
||||
<PackageReference Include="MailKit" Version="4.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Infrastructure\DOAN.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
225
DOAN.Common/DynamicApiSimple/ApiConvention.cs
Normal file
225
DOAN.Common/DynamicApiSimple/ApiConvention.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
using Infrastructure.Helper;
|
||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DOAN.Common.DynamicApiSimple;
|
||||
|
||||
class ApiConvention : IApplicationModelConvention
|
||||
{
|
||||
public void Apply(ApplicationModel application)
|
||||
{
|
||||
foreach (var controller in application.Controllers)
|
||||
{
|
||||
var type = controller.ControllerType;
|
||||
if (typeof(IDynamicApi).IsAssignableFrom(type) || type.IsDefined(typeof(DynamicApiAttribute), true))
|
||||
{
|
||||
ClearAction(controller);
|
||||
ConfigureApiExplorer(controller);
|
||||
ConfigureSelector(controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearAction(ControllerModel controller)
|
||||
{
|
||||
Type genericBaseType = AssemblyUtils.GetGenericTypeByName("BaseService`1");
|
||||
var needRemoveAction = controller.Actions
|
||||
.Where(action => !action.ActionMethod.DeclaringType.IsDerivedFromGenericBaseRepository(genericBaseType))
|
||||
.ToList();
|
||||
|
||||
foreach (var actionModel in needRemoveAction)
|
||||
{
|
||||
controller.Actions.Remove(actionModel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void ConfigureApiExplorer(ControllerModel controller)
|
||||
{
|
||||
if (!controller.ApiExplorer.IsVisible.HasValue)
|
||||
controller.ApiExplorer.IsVisible = true;
|
||||
|
||||
foreach (var action in controller.Actions)
|
||||
{
|
||||
if (!action.ApiExplorer.IsVisible.HasValue)
|
||||
{
|
||||
action.ApiExplorer.IsVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureSelector(ControllerModel controller)
|
||||
{
|
||||
RemoveEmptySelectors(controller.Selectors);
|
||||
|
||||
if (controller.Selectors.Any(selector => selector.AttributeRouteModel != null))
|
||||
return;
|
||||
|
||||
foreach (var action in controller.Actions)
|
||||
{
|
||||
ConfigureSelector(action);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveEmptySelectors(IList<SelectorModel> selectors)
|
||||
{
|
||||
for (var i = selectors.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var selector = selectors[i];
|
||||
if (selector.AttributeRouteModel == null &&
|
||||
(selector.ActionConstraints == null || selector.ActionConstraints.Count <= 0) &&
|
||||
(selector.EndpointMetadata == null || selector.EndpointMetadata.Count <= 0))
|
||||
{
|
||||
selectors.Remove(selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureSelector(ActionModel action)
|
||||
{
|
||||
RemoveEmptySelectors(action.Selectors);
|
||||
|
||||
if (action.Selectors.Count <= 0)
|
||||
AddServiceSelector(action);
|
||||
else
|
||||
NormalizeSelectorRoutes(action);
|
||||
}
|
||||
|
||||
private void AddServiceSelector(ActionModel action)
|
||||
{
|
||||
var template = new Microsoft.AspNetCore.Mvc.RouteAttribute(GetRouteTemplate(action));
|
||||
var selector = new SelectorModel
|
||||
{
|
||||
AttributeRouteModel = new AttributeRouteModel(template)
|
||||
};
|
||||
selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) }));
|
||||
action.Selectors.Add(selector);
|
||||
}
|
||||
|
||||
private void NormalizeSelectorRoutes(ActionModel action)
|
||||
{
|
||||
foreach (var selector in action.Selectors)
|
||||
{
|
||||
var template = new Microsoft.AspNetCore.Mvc.RouteAttribute(GetRouteTemplate(action,selector));
|
||||
selector.AttributeRouteModel = new AttributeRouteModel(template);
|
||||
if (selector.ActionConstraints.OfType<HttpMethodActionConstraint>().FirstOrDefault()?.HttpMethods?.FirstOrDefault() == null)
|
||||
selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) }));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private string GetRouteTemplate(ActionModel action,SelectorModel selectorModel=null)
|
||||
{
|
||||
var routeTemplate = new StringBuilder();
|
||||
var names = action.Controller.ControllerType.Namespace.Split('.');
|
||||
if (names.Length > 2)
|
||||
{
|
||||
routeTemplate.Append(names[^2]);
|
||||
}
|
||||
|
||||
// Controller
|
||||
var controllerName = action.Controller.ControllerName;
|
||||
if (controllerName.EndsWith("Service"))
|
||||
controllerName = controllerName[0..^7];
|
||||
|
||||
if (selectorModel is { AttributeRouteModel: not null })
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(selectorModel.AttributeRouteModel?.Template))
|
||||
{
|
||||
if (selectorModel.AttributeRouteModel.Template.StartsWith("/"))
|
||||
{
|
||||
routeTemplate.Append(selectorModel.AttributeRouteModel.Template);
|
||||
}
|
||||
else
|
||||
{
|
||||
routeTemplate.Append($"{BaseRoute}/{controllerName}/{selectorModel.AttributeRouteModel.Template}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
routeTemplate.Append($"{BaseRoute}/{controllerName}");
|
||||
|
||||
// Action
|
||||
var actionName = action.ActionName;
|
||||
if (actionName.EndsWith("Async") || actionName.EndsWith("async"))
|
||||
actionName = actionName[..^"Async".Length];
|
||||
|
||||
if (!string.IsNullOrEmpty(actionName))
|
||||
{
|
||||
routeTemplate.Append($"/{RemoveHttpMethodPrefix(actionName)}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return routeTemplate.ToString();
|
||||
}
|
||||
|
||||
private static string GetHttpMethod(ActionModel action)
|
||||
{
|
||||
var actionName = action.ActionName.ToLower();
|
||||
string Method = string.Empty;
|
||||
if (!string.IsNullOrEmpty(actionName))
|
||||
{
|
||||
Method = GetName(actionName);
|
||||
}
|
||||
return Method;
|
||||
}
|
||||
|
||||
private static string GetName(string actionName)
|
||||
{
|
||||
string result = "POST";
|
||||
foreach (string key in Methods.Keys)
|
||||
{
|
||||
if (actionName.Contains(key))
|
||||
{
|
||||
result = Methods[key];
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
internal static Dictionary<string, string> Methods { get; private set; }
|
||||
internal static string BaseRoute { get; private set; } = "api";
|
||||
static ApiConvention()
|
||||
{
|
||||
Methods = new Dictionary<string, string>()
|
||||
{
|
||||
|
||||
["get"] = "GET",
|
||||
["find"] = "GET",
|
||||
["fetch"] = "GET",
|
||||
["query"] = "GET",
|
||||
["post"] = "POST",
|
||||
["add"] = "POST",
|
||||
["create"] = "POST",
|
||||
["insert"] = "POST",
|
||||
["submit"] = "POST",
|
||||
["put"] = "POST",
|
||||
["update"] = "POST",
|
||||
["delete"] = "DELETE",
|
||||
["remove"] = "DELETE",
|
||||
["clear"] = "DELETE",
|
||||
["patch"] = "PATCH"
|
||||
};
|
||||
|
||||
}
|
||||
private static string RemoveHttpMethodPrefix(string actionName)
|
||||
{
|
||||
foreach (var method in Methods.Keys)
|
||||
{
|
||||
if (actionName.StartsWith(method, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 移除前缀并返回结果
|
||||
return actionName.Substring(method.Length);
|
||||
}
|
||||
}
|
||||
|
||||
return actionName; // 如果没有找到前缀,返回原始名称
|
||||
}
|
||||
}
|
||||
28
DOAN.Common/DynamicApiSimple/ApiFeatureProvider.cs
Normal file
28
DOAN.Common/DynamicApiSimple/ApiFeatureProvider.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
|
||||
namespace DOAN.Common.DynamicApiSimple;
|
||||
|
||||
class ApiFeatureProvider : ControllerFeatureProvider
|
||||
{
|
||||
protected override bool IsController(TypeInfo typeInfo)
|
||||
{
|
||||
Type type = typeInfo.AsType();
|
||||
// 不能是非公开的、值类型、抽象类、泛型类或基元类型
|
||||
if (!type.IsPublic || type.IsValueType || type.IsAbstract || type.IsGenericType || type.IsPrimitive || string.IsNullOrWhiteSpace(type.Namespace)) return false;
|
||||
|
||||
// 原生层或者实现IDynamicApiController(类),[DynamicApi](接口)
|
||||
if ((!typeof(Controller).IsAssignableFrom(type) && typeof(ControllerBase).IsAssignableFrom(type)) || type.IsDefined(typeof(DynamicApiAttribute), true) || typeof(IDynamicApi).IsAssignableFrom(type))
|
||||
{
|
||||
// 如果是忽略的则跳过自定义的接口在前面会报错,所以必须在后面
|
||||
if (type.IsDefined(typeof(ApiExplorerSettingsAttribute), true) && type.GetCustomAttribute<ApiExplorerSettingsAttribute>(true).IgnoreApi)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
24
DOAN.Common/DynamicApiSimple/DynamicApiAttribute.cs
Normal file
24
DOAN.Common/DynamicApiSimple/DynamicApiAttribute.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace DOAN.Common.DynamicApiSimple
|
||||
{
|
||||
/// <summary>
|
||||
/// 动态api属性
|
||||
/// </summary>
|
||||
public class DynamicApiAttribute : Attribute
|
||||
{
|
||||
public string Name;
|
||||
public string Order;
|
||||
public string Description;
|
||||
public DynamicApiAttribute()
|
||||
{
|
||||
|
||||
}
|
||||
public DynamicApiAttribute(string _name, string _order, string _description)
|
||||
{
|
||||
Name = _name;
|
||||
Order = _order;
|
||||
Description = _description;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
DOAN.Common/DynamicApiSimple/Extens/DynamicApiExtens.cs
Normal file
52
DOAN.Common/DynamicApiSimple/Extens/DynamicApiExtens.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Infrastructure.Helper;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace DOAN.Common.DynamicApiSimple.Extens
|
||||
{
|
||||
public static class DynamicApiExtens
|
||||
{
|
||||
public static string TIME_FORMAT_FULL = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
/// <summary>
|
||||
/// 注入动态api
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddDynamicApi(this IServiceCollection services)
|
||||
{
|
||||
services.AddMvc()
|
||||
.ConfigureApplicationPartManager(m =>
|
||||
{
|
||||
foreach (Assembly assembly in AssemblyUtils.GetAssemblies())
|
||||
{
|
||||
|
||||
if (m.ApplicationParts.Any(it => it.Name.Equals(assembly.FullName.Split(',')[0]))) continue;
|
||||
|
||||
m.ApplicationParts.Add(new AssemblyPart(assembly));
|
||||
}
|
||||
m.FeatureProviders.Add(new ApiFeatureProvider());
|
||||
}).AddNewtonsoftJson(options =>
|
||||
{
|
||||
options.SerializerSettings.DateFormatString = TIME_FORMAT_FULL;
|
||||
options.SerializerSettings.Converters.Add(new IsoDateTimeConverter
|
||||
{
|
||||
DateTimeFormat = TIME_FORMAT_FULL,
|
||||
});
|
||||
// 设置为驼峰命名
|
||||
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
||||
});
|
||||
|
||||
services.Configure<MvcOptions>(o =>
|
||||
{
|
||||
o.Conventions.Add(new ApiConvention());
|
||||
});
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
DOAN.Common/DynamicApiSimple/IDynamicApi.cs
Normal file
6
DOAN.Common/DynamicApiSimple/IDynamicApi.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DOAN.Common.DynamicApiSimple
|
||||
{
|
||||
public interface IDynamicApi
|
||||
{
|
||||
}
|
||||
}
|
||||
49
DOAN.Common/DynamicApiSimple/JsonModelBinder.cs
Normal file
49
DOAN.Common/DynamicApiSimple/JsonModelBinder.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace DOAN.Common.DynamicApiSimple;
|
||||
|
||||
public class JsonModelBinder : IModelBinder
|
||||
{
|
||||
private readonly IModelBinder _fallbackBinder;
|
||||
|
||||
public JsonModelBinder(IModelBinder fallbackBinder)
|
||||
{
|
||||
_fallbackBinder = fallbackBinder;
|
||||
}
|
||||
|
||||
public async Task BindModelAsync(ModelBindingContext bindingContext)
|
||||
{
|
||||
if (bindingContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bindingContext));
|
||||
}
|
||||
|
||||
var request = bindingContext.HttpContext.Request;
|
||||
if ((request.Method == "POST" || request.Method == "PUT") && request.ContentType != null && request.ContentType.Contains("application/json"))
|
||||
{
|
||||
using (var reader = new StreamReader(request.Body))
|
||||
{
|
||||
var body = await reader.ReadToEndAsync();
|
||||
if (!string.IsNullOrEmpty(body))
|
||||
{
|
||||
var result = JsonConvert.DeserializeObject(body, bindingContext.ModelType);
|
||||
bindingContext.Result = ModelBindingResult.Success(result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_fallbackBinder != null)
|
||||
{
|
||||
await _fallbackBinder.BindModelAsync(bindingContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
bindingContext.Result = ModelBindingResult.Failed();
|
||||
}
|
||||
}
|
||||
}
|
||||
156
DOAN.Common/ExcelHelper.cs
Normal file
156
DOAN.Common/ExcelHelper.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace DOAN.Common
|
||||
{
|
||||
public class ExcelHelper<T> where T : new()
|
||||
{
|
||||
/// <summary>
|
||||
/// 导入数据
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <returns></returns>
|
||||
//public static IEnumerable<T> ImportData(Stream stream)
|
||||
//{
|
||||
// using ExcelPackage package = new(stream);
|
||||
// //ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||
// ExcelWorksheet worksheet = package.Workbook.Worksheets[0];//读取第1个sheet
|
||||
// //获取表格的列数和行数
|
||||
|
||||
// int colStart = worksheet.Dimension.Start.Column;
|
||||
// int colEnd = worksheet.Dimension.End.Column;
|
||||
// int rowStart = worksheet.Dimension.Start.Row;
|
||||
// int rowEnd = worksheet.Dimension.End.Row;
|
||||
// //int rowCount = worksheet.Dimension.Rows;
|
||||
// //int ColCount = worksheet.Dimension.Columns;
|
||||
|
||||
// List<T> resultList = new();
|
||||
// List<PropertyInfo> propertyInfos = new();// new(typeof(T).GetProperties());
|
||||
// Dictionary<string, int> dictHeader = new();
|
||||
// for (int i = colStart; i < colEnd; i++)
|
||||
// {
|
||||
// var name = worksheet.Cells[rowStart, i].Value?.ToString();
|
||||
// dictHeader[name] = i;
|
||||
|
||||
// PropertyInfo propertyInfo = MapPropertyInfo(name);
|
||||
// if (propertyInfo != null)
|
||||
// {
|
||||
// propertyInfos.Add(propertyInfo);
|
||||
// }
|
||||
// }
|
||||
// for (int row = rowStart + 1; row <= rowEnd; row++)
|
||||
// {
|
||||
// T result = new();
|
||||
|
||||
// foreach (PropertyInfo p in propertyInfos)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// ExcelRange cell = worksheet.Cells[row, dictHeader[p.Name]];
|
||||
// if (cell.Value == null)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// switch (p.PropertyType.Name.ToLower())
|
||||
// {
|
||||
// case "string":
|
||||
// p.SetValue(result, cell.GetValue<string>());
|
||||
// break;
|
||||
// case "int16":
|
||||
// p.SetValue(result, cell.GetValue<short>()); break;
|
||||
// case "int32":
|
||||
// p.SetValue(result, cell.GetValue<int>()); break;
|
||||
// case "int64":
|
||||
// p.SetValue(result, cell.GetValue<long>()); break;
|
||||
// case "decimal":
|
||||
// p.SetValue(result, cell.GetValue<decimal>());
|
||||
// break;
|
||||
// case "double":
|
||||
// p.SetValue(result, cell.GetValue<double>()); break;
|
||||
// case "datetime":
|
||||
// p.SetValue(result, cell.GetValue<DateTime>()); break;
|
||||
// case "boolean":
|
||||
// p.SetValue(result, cell.GetValue<bool>()); break;
|
||||
// case "char":
|
||||
// p.SetValue(result, cell.GetValue<string>()); break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// catch (KeyNotFoundException ex)
|
||||
// {
|
||||
// Console.WriteLine("未找到该列将继续循环," + ex.Message);
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
// resultList.Add(result);
|
||||
// }
|
||||
|
||||
// return resultList;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 查找Excel列名对应的实体属性
|
||||
/// </summary>
|
||||
/// <param name="columnName"></param>
|
||||
/// <returns></returns>
|
||||
public static PropertyInfo MapPropertyInfo(string columnName)
|
||||
{
|
||||
PropertyInfo[] propertyList = GetProperties(typeof(T));
|
||||
PropertyInfo propertyInfo = propertyList.Where(p => p.Name == columnName).FirstOrDefault();
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
return propertyInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (PropertyInfo tempPropertyInfo in propertyList)
|
||||
{
|
||||
System.ComponentModel.DescriptionAttribute[] attributes = (System.ComponentModel.DescriptionAttribute[])tempPropertyInfo.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
if (attributes[0].Description == columnName)
|
||||
{
|
||||
return tempPropertyInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 得到类里面的属性集合
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="columns"></param>
|
||||
/// <returns></returns>
|
||||
public static PropertyInfo[] GetProperties(Type type, string[] columns = null)
|
||||
{
|
||||
PropertyInfo[] properties = null;
|
||||
properties = type.GetProperties();
|
||||
|
||||
if (columns != null && columns.Length > 0)
|
||||
{
|
||||
// 按columns顺序返回属性
|
||||
var columnPropertyList = new List<PropertyInfo>();
|
||||
foreach (var column in columns)
|
||||
{
|
||||
var columnProperty = properties.Where(p => p.Name == column).FirstOrDefault();
|
||||
if (columnProperty != null)
|
||||
{
|
||||
columnPropertyList.Add(columnProperty);
|
||||
}
|
||||
}
|
||||
return columnPropertyList.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
153
DOAN.Common/MailHelper.cs
Normal file
153
DOAN.Common/MailHelper.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using Infrastructure;
|
||||
using Infrastructure.Model;
|
||||
using MailKit.Net.Smtp;
|
||||
using MimeKit;
|
||||
using MimeKit.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace DOAN.Common
|
||||
{
|
||||
public class MailHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 发送人邮箱
|
||||
/// </summary>
|
||||
private readonly MailOptions mailOptions = new();
|
||||
|
||||
public MailHelper()
|
||||
{
|
||||
List<MailOptions> options = new();
|
||||
|
||||
AppSettings.Bind("MailOptions", options);
|
||||
mailOptions = options.First();
|
||||
}
|
||||
public MailHelper(MailOptions _mailOptions)
|
||||
{
|
||||
mailOptions = _mailOptions;
|
||||
}
|
||||
/// <summary>
|
||||
/// 发送一个
|
||||
/// </summary>
|
||||
/// <param name="toAddress"></param>
|
||||
/// <param name="subject"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="path"></param>
|
||||
public string SendMail(string toAddress, string subject, string text, string path = "", string html = "")
|
||||
{
|
||||
IEnumerable<MailboxAddress> mailboxes = new List<MailboxAddress>() {
|
||||
new MailboxAddress(toAddress, toAddress)
|
||||
};
|
||||
|
||||
return SendMail(mailboxes, subject, text, path, html);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送多个邮箱
|
||||
/// </summary>
|
||||
/// <param name="toAddress"></param>
|
||||
/// <param name="subject"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="path"></param>
|
||||
public string SendMail(string[] toAddress, string subject, string text, string path = "", string html = "")
|
||||
{
|
||||
IList<MailboxAddress> mailboxes = new List<MailboxAddress>() { };
|
||||
foreach (var item in toAddress)
|
||||
{
|
||||
mailboxes.Add(new MailboxAddress(item, item));
|
||||
}
|
||||
|
||||
return SendMail(mailboxes, subject, text, path, html);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送邮件
|
||||
/// </summary>
|
||||
/// <param name="toAddress"></param>
|
||||
/// <param name="subject">主题</param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="path">附件url地址</param>
|
||||
/// <param name="html">网页HTML内容</param>
|
||||
private string SendMail(IEnumerable<MailboxAddress> toAddress, string subject, string text, string path = "", string html = "")
|
||||
{
|
||||
MimeMessage message = new MimeMessage();
|
||||
//发件人
|
||||
message.From.Add(new MailboxAddress(mailOptions.FromName, mailOptions.FromEmail));
|
||||
//收件人
|
||||
message.To.AddRange(toAddress);
|
||||
message.Subject = subject;
|
||||
message.Date = DateTime.Now;
|
||||
|
||||
//创建附件Multipart
|
||||
Multipart multipart = new Multipart("mixed");
|
||||
var alternative = new MultipartAlternative();
|
||||
//html内容
|
||||
if (!string.IsNullOrEmpty(html))
|
||||
{
|
||||
var Html = new TextPart(TextFormat.Html)
|
||||
{
|
||||
Text = html
|
||||
};
|
||||
alternative.Add(Html);
|
||||
}
|
||||
//文本内容
|
||||
//if (!string.IsNullOrEmpty(text))
|
||||
//{
|
||||
var plain = new TextPart(TextFormat.Plain)
|
||||
{
|
||||
Text = text + "\r\n\n\n" + mailOptions.Signature
|
||||
};
|
||||
alternative.Add(plain);
|
||||
//}
|
||||
|
||||
//附件
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
string[] files = path.Split(",");
|
||||
foreach (var file in files)
|
||||
{
|
||||
MimePart attachment = new()
|
||||
{
|
||||
Content = new MimeContent(File.OpenRead(file), ContentEncoding.Default),
|
||||
//读取文件,只能用绝对路径
|
||||
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
|
||||
ContentTransferEncoding = ContentEncoding.Base64,
|
||||
//文件名字
|
||||
FileName = Path.GetFileName(path)
|
||||
};
|
||||
alternative.Add(attachment);
|
||||
}
|
||||
}
|
||||
multipart.Add(alternative);
|
||||
//赋值邮件内容
|
||||
message.Body = multipart;
|
||||
|
||||
//开始发送
|
||||
using var client = new SmtpClient();
|
||||
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
|
||||
//client.CheckCertificateRevocation = false;
|
||||
client.Connect(mailOptions.Smtp, mailOptions.Port, mailOptions.UseSsl);
|
||||
|
||||
//登录,发送
|
||||
//特别说明,对于服务器端的中文相应,Exception中有编码问题,显示乱码了
|
||||
client.Authenticate(System.Text.Encoding.UTF8, mailOptions.FromEmail, mailOptions.Password);
|
||||
|
||||
try
|
||||
{
|
||||
var result = client.Send(message);
|
||||
//断开
|
||||
client.Disconnect(true);
|
||||
Console.WriteLine($"【{DateTime.Now}】发送邮件结果{result}");
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
client.Disconnect(true);
|
||||
Log.WriteLine(ConsoleColor.Red, "发送邮件失败" + ex.Message);
|
||||
return "fail";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
DOAN.Common/Model/WxTokenResult.cs
Normal file
13
DOAN.Common/Model/WxTokenResult.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace DOAN.Common.Model
|
||||
{
|
||||
public class WxTokenResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 0、正常
|
||||
/// </summary>
|
||||
public int errcode { get; set; }
|
||||
public string errmsg { get; set; }
|
||||
public string access_token { get; set; }
|
||||
public string ticket { get; set; }
|
||||
}
|
||||
}
|
||||
169
DOAN.Common/Tools.cs
Normal file
169
DOAN.Common/Tools.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using Infrastructure;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace DOAN.Common
|
||||
{
|
||||
public class Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// 要分割的字符串 eg: 1,3,10,00
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="split">分割的字符串</param>
|
||||
/// <returns></returns>
|
||||
public static long[] SpitLongArrary(string str, char split = ',')
|
||||
{
|
||||
if (string.IsNullOrEmpty(str)) { return Array.Empty<long>(); }
|
||||
str = str.TrimStart(split).TrimEnd(split);
|
||||
string[] strIds = str.Split(split, (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
long[] infoIdss = Array.ConvertAll(strIds, s => long.Parse(s));
|
||||
return infoIdss;
|
||||
}
|
||||
|
||||
public static int[] SpitIntArrary(string str, char split = ',')
|
||||
{
|
||||
if (string.IsNullOrEmpty(str)) { return Array.Empty<int>(); }
|
||||
string[] strIds = str.Split(split, (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
int[] infoIdss = Array.ConvertAll(strIds, s => int.Parse(s));
|
||||
return infoIdss;
|
||||
}
|
||||
public static T[] SplitAndConvert<T>(string input, char split = ',')
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) { return Array.Empty<T>(); }
|
||||
string[] parts = input.Split(split, (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
T[] result = Array.ConvertAll(parts, s => (T)Convert.ChangeType(s, typeof(T)));
|
||||
//for (int i = 0; i < parts.Length; i++)
|
||||
//{
|
||||
// result[i] = (T)Convert.ChangeType(parts[i], typeof(T));
|
||||
//}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据日期获取星期几
|
||||
/// </summary>
|
||||
public static string GetWeekByDate(DateTime dt)
|
||||
{
|
||||
var day = new[] { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
|
||||
return day[Convert.ToInt32(dt.DayOfWeek.ToString("d"))];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 得到这个月的第几周
|
||||
/// </summary>
|
||||
/// <param name="daytime">年月日</param>
|
||||
/// <returns>传递过来的时间是第几周</returns>
|
||||
public static int GetWeekNumInMonth(DateTime daytime)
|
||||
{
|
||||
int dayInMonth = daytime.Day;
|
||||
//本月第一天
|
||||
DateTime firstDay = daytime.AddDays(1 - daytime.Day);
|
||||
//本月第一天是周几
|
||||
int weekday = (int)firstDay.DayOfWeek == 0 ? 7 : (int)firstDay.DayOfWeek;
|
||||
//本月第一周有几天
|
||||
int firstWeekEndDay = 7 - (weekday - 1);
|
||||
//当前日期和第一周之差
|
||||
int diffday = dayInMonth - firstWeekEndDay;
|
||||
diffday = diffday > 0 ? diffday : 1;
|
||||
//当前是第几周,如果整除7就减一天
|
||||
int weekNumInMonth = ((diffday % 7) == 0
|
||||
? (diffday / 7 - 1)
|
||||
: (diffday / 7)) + 1 + (dayInMonth > firstWeekEndDay ? 1 : 0);
|
||||
return weekNumInMonth;
|
||||
}
|
||||
/// <summary>
|
||||
/// 判断一个字符串是否为url
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsUrl(string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
string Url = @"^http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?$";
|
||||
return Regex.IsMatch(str, Url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static bool CheckUserName(string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
string rg = @"^[a-z][a-z0-9-_]*$";
|
||||
return Regex.IsMatch(str, rg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算密码强度
|
||||
/// </summary>
|
||||
/// <param name="password">密码字符串</param>
|
||||
/// <returns></returns>
|
||||
public static bool PasswordStrength(string password)
|
||||
{
|
||||
//空字符串强度值为0
|
||||
if (string.IsNullOrEmpty(password)) return false;
|
||||
|
||||
//字符统计
|
||||
int iNum = 0, iLtt = 0, iSym = 0;
|
||||
foreach (char c in password)
|
||||
{
|
||||
if (c >= '0' && c <= '9') iNum++;
|
||||
else if (c >= 'a' && c <= 'z') iLtt++;
|
||||
else if (c >= 'A' && c <= 'Z') iLtt++;
|
||||
else iSym++;
|
||||
}
|
||||
|
||||
if (iLtt == 0 && iSym == 0) return false; //纯数字密码
|
||||
if (iNum == 0 && iLtt == 0) return false; //纯符号密码
|
||||
if (iNum == 0 && iSym == 0) return false; //纯字母密码
|
||||
|
||||
if (password.Length >= 6 && password.Length < 16) return true;//长度不大于6的密码
|
||||
|
||||
if (iLtt == 0) return true; //数字和符号构成的密码
|
||||
if (iSym == 0) return true; //数字和字母构成的密码
|
||||
if (iNum == 0) return true; //字母和符号构成的密码
|
||||
|
||||
return true; //由数字、字母、符号构成的密码
|
||||
}
|
||||
///<summary>
|
||||
///生成随机字符串
|
||||
///</summary>
|
||||
///<param name="length">目标字符串的长度</param>
|
||||
///<param name="useNum">是否包含数字,1=包含,默认为包含</param>
|
||||
///<param name="useLow">是否包含小写字母,1=包含,默认为包含</param>
|
||||
///<param name="useUpp">是否包含大写字母,1=包含,默认为包含</param>
|
||||
///<param name="useSpe">是否包含特殊字符,1=包含,默认为不包含</param>
|
||||
///<param name="custom">要包含的自定义字符,直接输入要包含的字符列表</param>
|
||||
///<returns>指定长度的随机字符串</returns>
|
||||
public static string GetRandomString(int length, bool useNum, bool useLow, bool useUpp, bool useSpe, string custom)
|
||||
{
|
||||
byte[] b = new byte[4];
|
||||
System.Security.Cryptography.RandomNumberGenerator.Create().GetBytes(b);
|
||||
Random r = new(BitConverter.ToInt32(b, 0));
|
||||
string s = null, str = custom;
|
||||
if (useNum == true) { str += "0123456789"; }
|
||||
if (useLow == true) { str += "abcdefghijklmnopqrstuvwxyz"; }
|
||||
if (useUpp == true) { str += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
|
||||
if (useSpe == true) { str += "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
s += str.Substring(r.Next(0, str.Length - 1), 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
DOAN.Common/WxHelper.cs
Normal file
119
DOAN.Common/WxHelper.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using Infrastructure;
|
||||
using Infrastructure.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using DOAN.Common.Model;
|
||||
|
||||
namespace DOAN.Common
|
||||
{
|
||||
public class WxHelper
|
||||
{
|
||||
private static readonly string GetTokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
|
||||
private static readonly string GetTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
|
||||
private static readonly string AppID = AppSettings.App(new string[] { "WxOpen", "AppID" });
|
||||
private static readonly string AppSECRET = AppSettings.App(new string[] { "WxOpen", "AppSecret" });
|
||||
|
||||
/// <summary>
|
||||
/// 获取访问token
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// {"errcode":0,"errmsg":"ok","access_token":"iCbcfE1OjfRhV0_io-CzqTNC0lnrudeW3oF5rhJKfmINaxLClLa1FoqAY_wEXtodYh_DTnrtAwZfzeb-NRXvwiOoqUTHx3i6QKLYcfBtF8y-xd5mvaeaf3e9mvTAPhmX0lkm1cLTwRLmoa1IwzgQ-QZEZcuIcntWdEMGseVYok3BwCGpC87bt6nNdgnekZdFVRp1uuaxoctDGlXpoQlQsA","expires_in":7200}
|
||||
/// </returns>
|
||||
private static WxTokenResult GetAccessToken()
|
||||
{
|
||||
if (AppID.IsEmpty() || AppSECRET.IsEmpty())
|
||||
{
|
||||
Console.WriteLine("公众号配置错误");
|
||||
throw new ArgumentException("公众号配置错误");
|
||||
};
|
||||
var Ck = "wx_token";
|
||||
string getTokenUrl = $"{GetTokenUrl}?grant_type=client_credential&appid={AppID}&secret={AppSECRET}";
|
||||
if (CacheHelper.Get(Ck) is WxTokenResult tokenResult)
|
||||
{
|
||||
return tokenResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
string result = HttpHelper.HttpGet(getTokenUrl);
|
||||
|
||||
tokenResult = JsonConvert.DeserializeObject<WxTokenResult>(result);
|
||||
|
||||
if (tokenResult?.errcode == 0)
|
||||
{
|
||||
CacheHelper.SetCache(Ck, tokenResult, 110);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("GetAccessToken失败,结果=" + result);
|
||||
throw new Exception("获取AccessToken失败");
|
||||
}
|
||||
}
|
||||
return tokenResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取ticket
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static WxTokenResult GetTicket()
|
||||
{
|
||||
WxTokenResult token = GetAccessToken();
|
||||
string ticket = token?.access_token;
|
||||
var Ck = "wx_ticket";
|
||||
string getTokenUrl = $"{GetTicketUrl}?access_token={ticket}&type=jsapi";
|
||||
if (CacheHelper.Get(Ck) is WxTokenResult tokenResult)
|
||||
{
|
||||
return tokenResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
string result = HttpHelper.HttpGet(getTokenUrl);
|
||||
tokenResult = JsonConvert.DeserializeObject<WxTokenResult>(result);
|
||||
|
||||
if (tokenResult?.errcode == 0)
|
||||
{
|
||||
CacheHelper.SetCache(Ck, tokenResult, 110);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("GetTicket,结果=" + result);
|
||||
throw new Exception("获取ticket失败");
|
||||
}
|
||||
}
|
||||
return tokenResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回正确的签名
|
||||
/// </summary>
|
||||
/// <param name="jsapi_ticket"></param>
|
||||
/// <param name="timestamp"></param>
|
||||
/// <param name="noncestr"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetSignature(string jsapi_ticket, string timestamp, string noncestr, string url = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsapi_ticket) || string.IsNullOrEmpty(noncestr) || string.IsNullOrEmpty(timestamp) || string.IsNullOrEmpty(url))
|
||||
return null;
|
||||
|
||||
//将字段添加到列表中。
|
||||
string[] arr = new[]
|
||||
{
|
||||
string.Format("jsapi_ticket={0}",jsapi_ticket),
|
||||
string.Format("noncestr={0}",noncestr),
|
||||
string.Format("timestamp={0}",timestamp),
|
||||
string.Format("url={0}",url)
|
||||
};
|
||||
//字典排序
|
||||
Array.Sort(arr);
|
||||
//使用URL键值对的格式拼接成字符串
|
||||
var temp = string.Join("&", arr);
|
||||
|
||||
var sha1Arr = SHA1.HashData(Encoding.UTF8.GetBytes(temp));
|
||||
|
||||
return BitConverter.ToString(sha1Arr).Replace("-", "").ToLower();
|
||||
}
|
||||
}
|
||||
}
|
||||
151
DOAN.Common/WxNoticeHelper.cs
Normal file
151
DOAN.Common/WxNoticeHelper.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using Infrastructure;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using DOAN.Common.Model;
|
||||
|
||||
namespace DOAN.Common
|
||||
{
|
||||
public class WxNoticeHelper
|
||||
{
|
||||
//CorpID 企业ID
|
||||
//AGENTID 应用的ID
|
||||
//Secret 应用的ID对应的密钥
|
||||
private static readonly string AGENTID = AppSettings.App(new string[] { "WxCorp", "AgentID" });
|
||||
private static readonly string CORPID = AppSettings.App(new string[] { "WxCorp", "CorpID" });
|
||||
private static readonly string CORPSECRET = AppSettings.App(new string[] { "WxCorp", "CorpSecret" });
|
||||
private static readonly string SEND_USER = AppSettings.App(new string[] { "WxCorp", "SendUser" });
|
||||
private static readonly string SendUrl = "https://qyapi.weixin.qq.com/cgi-bin/message/send";
|
||||
private static readonly string GetTokenUrl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
|
||||
|
||||
/// <summary>
|
||||
/// 消息类型
|
||||
/// </summary>
|
||||
public enum MsgType { markdown, text, textcard, interactive_taskcard }
|
||||
|
||||
/// <summary>
|
||||
/// 发送消息公共模板方法
|
||||
/// </summary>
|
||||
/// <param name="toUser">微信微信好友id,默认@all发给所有关注该应用的用户</param>
|
||||
/// <param name="title">标题</param>
|
||||
/// <param name="content">内容</param>
|
||||
/// <param name="msgType">消息类型</param>
|
||||
/// <returns></returns>
|
||||
public static (int, string) SendMsg(string title, string content, string toUser = "", MsgType msgType = MsgType.text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(toUser))
|
||||
{
|
||||
toUser = SEND_USER;
|
||||
}
|
||||
if (string.IsNullOrEmpty(title))
|
||||
{
|
||||
return (0, "title不能为空");
|
||||
}
|
||||
if (string.IsNullOrEmpty(CORPID))
|
||||
{
|
||||
System.Console.WriteLine("如需微信接收异常消息,请完成企业微信配置");
|
||||
return (0, "请完成企业微信通知配置");
|
||||
}
|
||||
WxTokenResult tokenResult = GetAccessToken();
|
||||
|
||||
if (tokenResult == null || tokenResult.errcode != 0)
|
||||
{
|
||||
return (0, tokenResult?.errmsg);
|
||||
}
|
||||
|
||||
Dictionary<string, object> dic = null;
|
||||
switch (msgType)
|
||||
{
|
||||
case MsgType.markdown:
|
||||
dic = GetMarkdown(title, content, toUser);
|
||||
break;
|
||||
case MsgType.text:
|
||||
dic = GetText(title, content, toUser);
|
||||
break;
|
||||
case MsgType.textcard:
|
||||
break;
|
||||
case MsgType.interactive_taskcard:
|
||||
break;
|
||||
default:
|
||||
dic = GetText(title, content, toUser);
|
||||
break;
|
||||
}
|
||||
string postData = JsonSerializer.Serialize(dic);
|
||||
string msgUrl = $"{SendUrl}?access_token={tokenResult.access_token}";
|
||||
|
||||
//返回结果
|
||||
//{"errcode":0,"errmsg":"ok","invaliduser":""}
|
||||
string msgResult = HttpHelper.HttpPost(msgUrl, postData, "contentType/json");
|
||||
WxTokenResult getTokenResult = JsonSerializer.Deserialize<WxTokenResult>(msgResult);
|
||||
System.Console.WriteLine(msgResult);
|
||||
return (getTokenResult?.errcode == 0 ? 100 : 0, getTokenResult?.errmsg);
|
||||
}
|
||||
public static (int, string) SendMsg(string title, string content, string toUser)
|
||||
{
|
||||
return SendMsg(title, content, toUser, MsgType.markdown);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取访问token
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// {"errcode":0,"errmsg":"ok","access_token":"iCbcfE1OjfRhV0_io-CzqTNC0lnrudeW3oF5rhJKfmINaxLClLa1FoqAY_wEXtodYh_DTnrtAwZfzeb-NRXvwiOoqUTHx3i6QKLYcfBtF8y-xd5mvaeaf3e9mvTAPhmX0lkm1cLTwRLmoa1IwzgQ-QZEZcuIcntWdEMGseVYok3BwCGpC87bt6nNdgnekZdFVRp1uuaxoctDGlXpoQlQsA","expires_in":7200}
|
||||
/// </returns>
|
||||
private static WxTokenResult GetAccessToken()
|
||||
{
|
||||
string getTokenUrl = $"{GetTokenUrl}?corpid={CORPID}&corpsecret={CORPSECRET}";
|
||||
string getTokenResult = HttpHelper.HttpGet(getTokenUrl);
|
||||
System.Console.WriteLine(getTokenResult);
|
||||
WxTokenResult tokenResult = JsonSerializer.Deserialize<WxTokenResult>(getTokenResult);
|
||||
return tokenResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送text
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="toUser"></param>
|
||||
/// <returns></returns>
|
||||
private static Dictionary<string, object> GetText(string title, string content, string toUser = "")
|
||||
{
|
||||
Dictionary<string, object> dic = new()
|
||||
{
|
||||
{ "msgtype", "text" },
|
||||
{ "touser", toUser },
|
||||
{ "agentid", AGENTID },
|
||||
{ "text", new Dictionary<string, string>
|
||||
{
|
||||
{ "content",$"{title}\n\n{content}"
|
||||
}
|
||||
}}
|
||||
};
|
||||
return dic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送markdown
|
||||
/// </summary>
|
||||
/// <param name="title">要发送的标题</param>
|
||||
/// <param name="content">发送的内容</param>
|
||||
/// <param name="toUser">指定接收消息的成员,成员ID列表(多个接收者用‘|’分隔,最多支持1000个)。 特殊情况:指定为”@all”,则向该企业应用的全部成员发送</param>
|
||||
/// <returns></returns>
|
||||
private static Dictionary<string, object> GetMarkdown(string title, string content, string toUser = "")
|
||||
{
|
||||
Dictionary<string, object> dic = new()
|
||||
{
|
||||
{ "touser", toUser },
|
||||
{ "msgtype", "markdown" },
|
||||
{ "agentid", AGENTID },
|
||||
{ "enable_duplicate_check", 1 },
|
||||
{
|
||||
"markdown",
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ "content", $"**{title}**\n\n{content}" }
|
||||
}
|
||||
}
|
||||
};
|
||||
return dic;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user