first commit

This commit is contained in:
qianhao.xu
2024-09-23 09:14:22 +08:00
commit fdbe20be5a
407 changed files with 35117 additions and 0 deletions

View 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;
}
}
}

View 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>

View 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; // 如果没有找到前缀,返回原始名称
}
}

View 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;
}
}

View 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;
}
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,6 @@
namespace DOAN.Common.DynamicApiSimple
{
public interface IDynamicApi
{
}
}

View 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
View 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
View 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";
}
}
}
}

View 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
View 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
View 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();
}
}
}

View 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;
}
}
}