.gitignore
@@ -12,3 +12,10 @@ cylsg/cylsg.Web.Core/obj/ cylsg/cylsg.Web.Entry/bin/ cylsg/cylsg.Web.Entry/obj/ cylsg/cylsg.Authorization/bin/ cylsg/cylsg.Authorization/obj/ cylsg/cylsg.redis/obj/ cylsg/cylsg.services/obj/ cylsg/EzUpFiles/obj/ cylsg/EzWechat/obj/ cylsg/cylsg.redis/bin/ cylsg/EzUpFiles/EzFileUploadService.cs
New file @@ -0,0 +1,709 @@ using Aliyun.OSS; using Aliyun.OSS.Util; using COSXML; using COSXML.Auth; using EzCoreNet.Utility; using EzCoreNet.Utility.Extend; using EzCoreNetFurion.IServices; using EzCoreNetFurion.IServices.SysSet; using EzCoreNetFurion.Model.Emun; using Furion; using Furion.DependencyInjection; using Furion.FriendlyException; using Microsoft.AspNetCore.Http; using Qiniu.Storage; using Qiniu.Util; using SqlSugar; using System.Globalization; namespace EzUpFiles { /// <summary> /// 附件服务程序 /// </summary> public class EzFileUploadService : IEzFileUploadService, IScoped { private readonly ISYSSettingService _SYSSetting; private readonly HttpRequest? _request; private readonly ISqlSugarClient _sqlSugarClient; public EzFileUploadService(ISYSSettingService sYSSetting, IHttpContextAccessor httpContext, ISqlSugarClient sqlSugarClient) { _SYSSetting = sYSSetting; _request = httpContext.HttpContext?.Request ?? null; _sqlSugarClient = sqlSugarClient; } /// <summary> /// 上传附件 /// </summary> /// <returns></returns> public async Task<string> UploadFiles() { var op = _SYSSetting.GetAttachmentSetting(); if (op == null) throw Oops.Oh("你没有对附件保存进行设置,请设置附件保存"); var maxSize = 1024 * 1024 * (op.AttachmentSaveFileSize?.toInt() ?? 0); //上传大小5M var file = _request?.Form?.Files["file"]; if (file == null) { throw Oops.Oh("你没有选择文件"); } var fileName = file.FileName; var fileExt = Path.GetExtension(fileName).ToLowerInvariant(); //检查大小 if (file.Length > maxSize) { throw Oops.Oh("文件大于设置文件"); } //检查文件扩展名 if (string.IsNullOrEmpty(fileExt) || Array.IndexOf(op.AttachmentSaveFileExtName?.Split(',') ?? new string[] { "" }, fileExt.Substring(1).ToLower()) == -1) { throw Oops.Oh("上传文件扩展名是不允许的扩展名,请上传后缀名为:" + op.AttachmentSaveFileExtName); } string url = string.Empty; switch (op.AttachmentSaveMode) { case AttachmentSaveMode.AliOSS: url = await UpLoadFileForAliYunOSS(op, fileExt, file); break; case AttachmentSaveMode.TencentCOS: url = await UpLoadFileForQCloudOSS(op, fileExt, file); break; case AttachmentSaveMode.QiNiuKodo: url = await UpLoadFileForQiNiuKoDo(op, fileExt, file); break; case AttachmentSaveMode.Logical: url = await UpLoadFileForLocalStorage(op, fileExt, file); break; case AttachmentSaveMode.DataBase: throw Oops.Oh("不支持"); break; default: break; } return url; } /// <summary> /// 上传base64 /// </summary> /// <param name="base64"></param> /// <returns></returns> public async Task<string> UploadFilesFByBase64(string base64) { var op = _SYSSetting.GetAttachmentSetting(); if (op == null) throw Oops.Oh("你没有对附件保存进行设置,请设置附件保存"); if (string.IsNullOrEmpty(base64)) { throw Oops.Oh("没有内容"); } //检查上传大小 if (!CommonHelper.CheckBase64Size(base64, op.AttachmentSaveFileSize?.toInt() ?? 5)) { throw Oops.Oh("上传文件大小超过限制,最大允许上传" + op.AttachmentSaveFileSize + "M"); } base64 = base64.Replace("data:image/png;base64,", "").Replace("data:image/jgp;base64,", "").Replace("data:image/jpg;base64,", "").Replace("data:image/jpeg;base64,", "");//将base64头部信息替换 byte[] bytes = Convert.FromBase64String(base64); MemoryStream memStream = new MemoryStream(bytes); string url = string.Empty; switch (op.AttachmentSaveMode) { case AttachmentSaveMode.AliOSS: url = await UpLoadBase64ForAliYunOSS(op, memStream); break; case AttachmentSaveMode.TencentCOS: url = await UpLoadBase64ForQCloudOSS(op, bytes); break; case AttachmentSaveMode.QiNiuKodo: url = await UpLoadBase64ForQiNiuKoDo(op, bytes); break; case AttachmentSaveMode.Logical: url = await UpLoadBase64ForLocalStorage(op, memStream); break; case AttachmentSaveMode.DataBase: throw Oops.Oh("不支持"); break; default: break; } return url; } /// <summary> /// 删除文件 /// </summary> /// <param name="Path"></param> /// <returns></returns> public async Task<bool> DelFile(string Path) { var op = _SYSSetting.GetAttachmentSetting(); if (op == null) throw Oops.Oh("你没有对附件保存进行设置,请设置附件保存"); bool ret = false; switch (op.AttachmentSaveMode) { case AttachmentSaveMode.AliOSS: ret = await DelFileForAliYunOSS(op, Path); break; case AttachmentSaveMode.TencentCOS: ret = await DelFileForQCloudOSS(op, Path); break; case AttachmentSaveMode.QiNiuKodo: ret = await DelFileForQiNiuKoDo(op, Path); break; case AttachmentSaveMode.Logical: ret = await DelFileForLocalStorage(op, Path); break; case AttachmentSaveMode.DataBase: throw Oops.Oh("不支持"); default: break; } return ret; } #region 本地上传方法(File) /// <summary> /// 本地上传方法(File) /// </summary> /// <param name="options"></param> /// <param name="fileExt"></param> /// <param name="file"></param> /// <returns></returns> public async Task<string> UpLoadFileForLocalStorage(SYSDictionaryAttachmentSetting options, string fileExt, IFormFile file) { var newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", DateTimeFormatInfo.InvariantInfo) + fileExt; var today = DateTime.Now.ToString("yyyyMMdd"); var saveUrl = options.AttachmentSavePath + today + "/"; var dirPath = App.WebHostEnvironment.WebRootPath + saveUrl; if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); var filePath = dirPath + newFileName; var fileUrl = saveUrl + newFileName; await using (var fs = File.Create(filePath)) { await file.CopyToAsync(fs); fs.Flush(); } return options.AttachmentSave_LogicalSaveBaseUrl + fileUrl; } #endregion #region 阿里云上传方法(File) /// <summary> /// 阿里云上传方法(File) /// </summary> /// <param name="options"></param> /// <param name="fileExt"></param> /// <param name="file"></param> /// <returns></returns> public async Task<string> UpLoadFileForAliYunOSS(SYSDictionaryAttachmentSetting options, string fileExt, IFormFile file) { var newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", DateTimeFormatInfo.InvariantInfo) + fileExt; var today = DateTime.Now.ToString("yyyyMMdd"); //上传到阿里云 await using var fileStream = file.OpenReadStream(); var md5 = OssUtils.ComputeContentMd5(fileStream, file.Length); var filePath = options.AttachmentSavePath + today + "/" + newFileName; //云文件保存路径 //初始化阿里云配置--外网Endpoint、访问ID、访问password var aliYun = new OssClient(options.AttachmentSave_AliOSSEndpoint, options.AttachmentSave_AliOSSAccessKeyID, options.AttachmentSave_AliOSSAccessKeySecret); //将文件md5值赋值给meat头信息,服务器验证文件MD5 var objectMeta = new ObjectMetadata { ContentMd5 = md5 }; var task = Task.Run(() => //文件上传--空间名、文件保存路径、文件流、meta头信息(文件md5) //返回meta头信息(文件md5) aliYun.PutObject(options.AttachmentSave_AliOSSBucketName, filePath, fileStream, objectMeta) ); //等待完成 try { task.Wait(); } catch (AggregateException ex) { throw Oops.Oh(ex.Message); } //返回给UEditor的插入编辑器的图片的src return options.AttachmentSave_AliOSSSaveBaseUrl + filePath; } #endregion #region 腾讯云存储上传方法(File) /// <summary> /// 腾讯云存储上传方法(File) /// </summary> /// <param name="options"></param> /// <param name="fileExt"></param> /// <param name="file"></param> /// <returns></returns> public async Task<string> UpLoadFileForQCloudOSS(SYSDictionaryAttachmentSetting options, string fileExt, IFormFile file) { var newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", DateTimeFormatInfo.InvariantInfo) + fileExt; var today = DateTime.Now.ToString("yyyyMMdd"); var filePath = options.AttachmentSavePath + today + "/" + newFileName; //云文件保存路径 //上传到腾讯云OSS //初始化 CosXmlConfig string appid = options.AttachmentSave_TencentAPPID;//设置腾讯云账户的账户标识 APPID string region = options.AttachmentSave_TencentCosRegion; //设置一个默认的存储桶地域 CosXmlConfig config = new CosXmlConfig.Builder() //.SetAppid(appid) .IsHttps(true) //设置默认 HTTPS 请求 .SetRegion(region) //设置一个默认的存储桶地域 .SetDebugLog(true) //显示日志 .Build(); //创建 CosXmlConfig 对象 long durationSecond = 600; //每次请求签名有效时长,单位为秒 QCloudCredentialProvider qCloudCredentialProvider = new DefaultQCloudCredentialProvider(options.AttachmentSave_TencentSecretId, options.AttachmentSave_TencentSecretKey, durationSecond); byte[] bytes; await using (var ms = new MemoryStream()) { await file.CopyToAsync(ms); bytes = ms.ToArray(); } try { var cosXml = new CosXmlServer(config, qCloudCredentialProvider); COSXML.Model.Object.PutObjectRequest putObjectRequest = new COSXML.Model.Object.PutObjectRequest(options.AttachmentSave_TencentBucketName, filePath, bytes); cosXml.PutObject(putObjectRequest); } catch (COSXML.CosException.CosClientException clientEx) { throw Oops.Oh(clientEx.Message); } catch (COSXML.CosException.CosServerException serverEx) { throw Oops.Oh(serverEx.GetInfo); } return options.AttachmentSave_TencentSaveBaseUrl + filePath; } #endregion #region 七牛云存储上传(File) /// <summary> /// 七牛云存储上传(File) /// </summary> /// <param name="options"></param> /// <param name="fileExt"></param> /// <param name="file"></param> /// <returns></returns> public async Task<string> UpLoadFileForQiNiuKoDo(SYSDictionaryAttachmentSetting options, string fileExt, IFormFile file) { var newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_fffff", DateTimeFormatInfo.InvariantInfo) + fileExt; Mac mac = new Mac(options.AttachmentSave_QiNiuKodoAK, options.AttachmentSave_QiNiuKodoSK); byte[] bytes; await using (var ms = new MemoryStream()) { await file.CopyToAsync(ms); bytes = ms.ToArray(); } // 设置上传策略 PutPolicy putPolicy = new PutPolicy(); // 设置要上传的目标空间 putPolicy.Scope = options.AttachmentSave_QiNiuKodoBucketName; // 上传策略的过期时间(单位:秒) putPolicy.SetExpires(3600); // 文件上传完毕后,在多少天后自动被删除 //putPolicy.DeleteAfterDays = 1; // 生成上传token string token = Qiniu.Util.Auth.CreateUploadToken(mac, putPolicy.ToJsonString()); Config config = new Config(); // 设置 http 或者 https 上传 config.UseHttps = true; config.UseCdnDomains = true; config.ChunkSize = ChunkUnit.U512K; UploadManager um = new UploadManager(config); var fileTemp = (options?.AttachmentSavePath?.Substring(1) ?? "") + newFileName; var task = Task.Run(() => um.UploadData(bytes, fileTemp, token, null)); task.Wait(); var outData = task.Result; if (outData?.Code != 200) throw Oops.Oh(outData?.Text); return options.AttachmentSave_QiNiuKodoSaveBaseUrl + options.AttachmentSavePath + newFileName; } #endregion #region 本地上传方法(Base64) /// <summary> /// 本地上传方法(Base64) /// </summary> /// <param name="options"></param> /// <param name="memStream"></param> /// <returns></returns> public async Task<string> UpLoadBase64ForLocalStorage(SYSDictionaryAttachmentSetting options, MemoryStream memStream) { var newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", DateTimeFormatInfo.InvariantInfo) + ".jpg"; var today = DateTime.Now.ToString("yyyyMMdd"); byte[] data = new byte[memStream.Length]; memStream.Seek(0, SeekOrigin.Begin); memStream.Read(data, 0, Convert.ToInt32(memStream.Length)); SixLabors.ImageSharp.Image image = SixLabors.ImageSharp.Image.Load(new MemoryStream(data)); var saveUrl = options.AttachmentSavePath + today + "/"; var dirPath = App.WebHostEnvironment.WebRootPath + saveUrl; if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); var filePath = dirPath + newFileName; var fileUrl = saveUrl + newFileName; //保存到图片 await image.SaveAsync(filePath); return options.AttachmentSave_LogicalSaveBaseUrl + fileUrl; } #endregion #region 阿里云上传方法(Base64) /// <summary> /// 阿里云上传方法(Base64) /// </summary> /// <param name="options"></param> /// <param name="memStream"></param> /// <returns></returns> public async Task<string> UpLoadBase64ForAliYunOSS(SYSDictionaryAttachmentSetting options, MemoryStream memStream) { var newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", DateTimeFormatInfo.InvariantInfo) + ".jpg"; var today = DateTime.Now.ToString("yyyyMMdd"); // 设置当前流的位置为流的开始 memStream.Seek(0, SeekOrigin.Begin); await using var fileStream = memStream; var md5 = OssUtils.ComputeContentMd5(fileStream, memStream.Length); var filePath = options.AttachmentSavePath + today + "/" + newFileName; //云文件保存路径 //初始化阿里云配置--外网Endpoint、访问ID、访问password var aliyun = new OssClient(options.AttachmentSave_AliOSSEndpoint, options.AttachmentSave_AliOSSAccessKeyID, options.AttachmentSave_AliOSSAccessKeySecret); //将文件md5值赋值给meat头信息,服务器验证文件MD5 var objectMeta = new ObjectMetadata { ContentMd5 = md5 }; try { //文件上传--空间名、文件保存路径、文件流、meta头信息(文件md5) //返回meta头信息(文件md5) aliyun.PutObject(options.AttachmentSave_AliOSSBucketName, filePath, fileStream, objectMeta); } catch (AggregateException ex) { throw Oops.Oh(ex.Message); } //返回给UEditor的插入编辑器的图片的src return options.AttachmentSave_AliOSSSaveBaseUrl + filePath; } #endregion #region 腾讯云存储上传方法(Base64) /// <summary> /// 腾讯云存储上传方法(Base64) /// </summary> /// <param name="options"></param> /// <param name="bytes"></param> /// <returns></returns> public async Task<string> UpLoadBase64ForQCloudOSS(SYSDictionaryAttachmentSetting options, byte[] bytes) { var newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", DateTimeFormatInfo.InvariantInfo) + ".jpg"; var today = DateTime.Now.ToString("yyyyMMdd"); //初始化 CosXmlConfig string appid = options.AttachmentSave_TencentAPPID;//设置腾讯云账户的账户标识 APPID string region = options.AttachmentSave_TencentCosRegion; //设置一个默认的存储桶地域 CosXmlConfig config = new CosXmlConfig.Builder() //.SetAppid(appid) .IsHttps(true) //设置默认 HTTPS 请求 .SetRegion(region) //设置一个默认的存储桶地域 .SetDebugLog(true) //显示日志 .Build(); //创建 CosXmlConfig 对象 long durationSecond = 600; //每次请求签名有效时长,单位为秒 QCloudCredentialProvider qCloudCredentialProvider = new DefaultQCloudCredentialProvider(options.AttachmentSave_TencentSecretId, options.AttachmentSave_TencentSecretKey, durationSecond); var cosXml = new CosXmlServer(config, qCloudCredentialProvider); var filePath = options.AttachmentSavePath + today + "/" + newFileName; //云文件保存路径 try { COSXML.Model.Object.PutObjectRequest putObjectRequest = new COSXML.Model.Object.PutObjectRequest(options.AttachmentSave_TencentBucketName, filePath, bytes); var task = Task.Run(() => cosXml.PutObject(putObjectRequest)); task.Wait(); } catch (COSXML.CosException.CosClientException clientEx) { throw Oops.Oh(clientEx.Message); } catch (COSXML.CosException.CosServerException serverEx) { throw Oops.Oh(serverEx.GetInfo()); } return options.AttachmentSave_TencentSaveBaseUrl + filePath; } #endregion #region 牛云上传方法(Base64) /// <summary> /// 七牛云上传方法(Base64) /// </summary> /// <param name="options"></param> /// <param name="bytes"></param> /// <returns></returns> public async Task<string> UpLoadBase64ForQiNiuKoDo(SYSDictionaryAttachmentSetting options, byte[] bytes) { var newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_fffff", DateTimeFormatInfo.InvariantInfo) + ".jpg"; Mac mac = new Mac(options.AttachmentSave_QiNiuKodoAK, options.AttachmentSave_QiNiuKodoSK); // 设置上传策略 PutPolicy putPolicy = new PutPolicy(); // 设置要上传的目标空间 putPolicy.Scope = options.AttachmentSave_QiNiuKodoBucketName; // 上传策略的过期时间(单位:秒) putPolicy.SetExpires(3600); // 文件上传完毕后,在多少天后自动被删除 //putPolicy.DeleteAfterDays = 1; // 生成上传token string token = Qiniu.Util.Auth.CreateUploadToken(mac, putPolicy.ToJsonString()); Config config = new Config(); // 设置 http 或者 https 上传 config.UseHttps = true; config.UseCdnDomains = true; config.ChunkSize = ChunkUnit.U512K; var fileTemp = (options?.AttachmentSavePath?.Substring(1) ?? "") + newFileName; UploadManager um = new UploadManager(config); var task = Task.Run(() => um.UploadData(bytes, fileTemp, token, null)); // var outData = um.UploadData(bytes, newFileName, token, null); task.Wait(); var outdata = task.Result; if (outdata?.Code != 200) throw Oops.Oh(outdata?.Text); return options.AttachmentSave_QiNiuKodoSaveBaseUrl + options.AttachmentSavePath + newFileName; } #endregion #region 删除文件 /// <summary> /// 删除本地文件 /// </summary> /// <param name="options"></param> /// <param name="fileUrl"></param> /// <returns></returns> public async Task<bool> DelFileForLocalStorage(SYSDictionaryAttachmentSetting options, string fileUrl) { string key = App.WebHostEnvironment.WebRootPath + (options.AttachmentSavePath?.RemoveStartWithStr("/") ?? "") + fileUrl.GetFileName(); File.Delete(key); return true; } /// <summary> /// 删除七牛文件夹 /// </summary> /// <param name="options"></param> /// <param name="fileUrl"></param> /// <returns></returns> public async Task<bool> DelFileForQiNiuKoDo(SYSDictionaryAttachmentSetting options, string fileUrl) { Config config = new Config(); var a = fileUrl.GetFileName(); Mac mac = new Mac(options.AttachmentSave_QiNiuKodoAK, options.AttachmentSave_QiNiuKodoSK); BucketManager bucketManager = new BucketManager(mac, config); string key = (options.AttachmentSavePath?.RemoveStartWithStr("/") ?? "") + fileUrl.GetFileName(); var task = Task.Run(() => bucketManager.Delete(options.AttachmentSave_QiNiuKodoBucketName, key)); // var outData = um.UploadData(bytes, newFileName, token, null); task.Wait(); var outdata = task.Result; if (outdata?.Code != 200) throw Oops.Oh(outdata?.Text); return true; } /// <summary> /// 腾讯云删除 /// </summary> /// <param name="options"></param> /// <param name="fileUrl">带xxx.xx的链接地址</param> /// <returns></returns> public async Task<bool> DelFileForQCloudOSS(SYSDictionaryAttachmentSetting options, string fileUrl) { //初始化 CosXmlConfig string appid = options.AttachmentSave_TencentAPPID;//设置腾讯云账户的账户标识 APPID string region = options.AttachmentSave_TencentCosRegion; //设置一个默认的存储桶地域 CosXmlConfig config = new CosXmlConfig.Builder() //.SetAppid(appid) .IsHttps(true) //设置默认 HTTPS 请求 .SetRegion(region) //设置一个默认的存储桶地域 .SetDebugLog(true) //显示日志 .Build(); //创建 CosXmlConfig 对象 long durationSecond = 600; //每次请求签名有效时长,单位为秒 QCloudCredentialProvider qCloudCredentialProvider = new DefaultQCloudCredentialProvider(options.AttachmentSave_TencentSecretId, options.AttachmentSave_TencentSecretKey, durationSecond); var cosXml = new CosXmlServer(config, qCloudCredentialProvider); string key = (options.AttachmentSavePath?.RemoveStartWithStr("/") ?? "") + fileUrl.GetFileName(); try { COSXML.Model.Object.DeleteObjectRequest DelObjectRequest = new COSXML.Model.Object.DeleteObjectRequest(options.AttachmentSave_TencentBucketName, key); var task = Task.Run(() => cosXml.DeleteObject(DelObjectRequest)); task.Wait(); } catch (COSXML.CosException.CosClientException clientEx) { throw Oops.Oh(clientEx.Message); } catch (COSXML.CosException.CosServerException serverEx) { throw Oops.Oh(serverEx.GetInfo()); } return true; } /// <summary> /// 腾讯云删除 /// </summary> /// <param name="options"></param> /// <param name="fileUrl">带xxx.xx的链接地址</param> /// <returns></returns> public async Task<bool> DelFileForAliYunOSS(SYSDictionaryAttachmentSetting options, string fileUrl) { //初始化阿里云配置--外网Endpoint、访问ID、访问password var aliyun = new OssClient(options.AttachmentSave_AliOSSEndpoint, options.AttachmentSave_AliOSSAccessKeyID, options.AttachmentSave_AliOSSAccessKeySecret); try { var task = Task.Run(() => aliyun.DeleteObject(options.AttachmentSave_AliOSSBucketName, (options.AttachmentSavePath?.RemoveStartWithStr("/") ?? "") + fileUrl.GetFileName())); task.Wait(); } catch (Exception ex) { throw Oops.Oh(ex.Message); } //返回给UEditor的插入编辑器的图片的src return true; } #endregion } } cylsg/EzUpFiles/EzUpFiles.csproj
New file @@ -0,0 +1,9 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project> cylsg/EzUpFiles/IEzFileUploadService.cs
New file @@ -0,0 +1,30 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EzUpFiles { public interface IEzFileUploadService { /// <summary> /// 上传base64 /// </summary> /// <param name="base64"></param> /// <returns></returns> Task<string> UploadFilesFByBase64(string base64); /// <summary> /// 上传文件 /// </summary> /// <returns></returns> Task<string> UploadFiles(); /// <summary> /// 删除文件 /// </summary> /// <param name="Path"></param> /// <returns></returns> Task<bool> DelFile(string Path); } } cylsg/EzWechat/EzWechat.csproj
New file @@ -0,0 +1,17 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.4.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\cylsg.Core\cylsg.Core.csproj" /> </ItemGroup> </Project> cylsg/EzWechat/IWechatService.cs
New file @@ -0,0 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EzWechat { public interface IWechatService { } } cylsg/EzWechat/Startup.cs
New file @@ -0,0 +1,22 @@ using Furion; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using SqlSugar; namespace cylsg.Authorization { public class Startup : AppStartup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } } } cylsg/EzWechat/WechatConfig.json
New file @@ -0,0 +1,7 @@ { "WechatAPP" :{ "AppId": "微信 AppId", "AppSecret": "微信 AppSecret" } } cylsg/EzWechat/WechatService.cs
New file @@ -0,0 +1,36 @@ using Furion; using SKIT.FlurlHttpClient.Wechat.Api; using SKIT.FlurlHttpClient.Wechat.Api.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EzWechat { public class WechatService:IWechatService { public WechatService() { /* 以公众号获取用户信息接口为例 */ var request = new WechatApiClientOptions() { AppId = App.Configuration["WechatAPP:AppId"], AppSecret = App.Configuration["WechatAPP:AppSecret"], //ImmeDeliveryAppKey = "即时配送相关服务 AppKey,不用则不填", //ImmeDeliveryAppSecret = "即时配送相关服务 AppSecret,不用则不填", //VirtualPaymentAppKey = "虚拟支付相关服务 AppKey,不用则不填", //MidasOfferId = "米大师 1.0 相关服务 OfferId,不用则不填", //MidasAppKey = "米大师 1.0 相关服务 AppKey,不用则不填", //MidasOfferIdV2 = "米大师 2.0 相关服务 OfferId,不用则不填", //MidasAppKeyV2 = "米大师 2.0 相关服务 AppKey,不用则不填" }; } } } cylsg/cylsg.Application/System/Services/ISystemService.cs
@@ -2,5 +2,5 @@ public interface ISystemService { string GetDescription(); string GetDescription(); } cylsg/cylsg.Application/System/Services/SystemService.cs
@@ -4,6 +4,6 @@ { public string GetDescription() { return "让 .NET 开发更简单,更通用,更流行。"; return "川印招聘"; } } cylsg/cylsg.Authorization/EzAuthorizationService.cs
New file @@ -0,0 +1,154 @@ using Furion.Authorization; using Furion.DataEncryption; using Furion; using Furion.DependencyInjection; using Microsoft.AspNetCore.Http; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using EzCoreNet.Redis; using SqlSugar.Extensions; using Furion.FriendlyException; namespace cylsg.Authorization { /// <summary> /// /// </summary> public class EzAuthorizationService : IEzAuthorizationService, IScoped { private IEzCoreNetRedisService _redisCacheSc; private IHttpContextAccessor _Context; public EzAuthorizationService(IEzCoreNetRedisService redisCacheSc, IHttpContextAccessor httpContext) { _redisCacheSc = redisCacheSc; _Context = httpContext; } /// <summary> /// Token /// </summary> /// <returns></returns> public TokenInfo CreateToken<T>(T jwt) where T : EzJwtModel { IDictionary<string, object> propertyDictionary = new Dictionary<string, object>(); PropertyInfo[] properties = jwt.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo property in properties) { string propertyName = property.Name; object propertyValue = property.GetValue(jwt); propertyDictionary.Add(propertyName.ToLower(), propertyValue); } var expires = DateTime.Now.AddSeconds(Convert.ToInt32(App.GetConfig<JWTSettingsOptions>("JWTSettings").ExpiredTime * 60 ?? 3600)); var token = JWTEncryption.Encrypt(propertyDictionary, App.GetConfig<JWTSettingsOptions>("JWTSettings").ExpiredTime ?? 3600); DateTimeOffset dto = new DateTimeOffset(DateTime.Now); var Expires = dto.ToUnixTimeSeconds(); IDictionary<string, object> REfpropertyDictionary = new Dictionary<string, object>(); REfpropertyDictionary.Add(new( "RefTokenID", $"{jwt.ITCode}:{Expires}" )); var RefExpires = App.Configuration["JWTSettings:RefreshTokenExpires"].ObjToInt(); var refreshToken = JWTEncryption.Encrypt(REfpropertyDictionary, RefExpires); //写入刷新可Token时间 _redisCacheSc.Add($"{jwt.ITCode}:{Expires}", jwt, RefExpires * 60); return new TokenInfo { accessToken = token, expires = expires, refreshToken = refreshToken }; } /// <summary> /// 刷新TOKEN /// </summary> /// <typeparam name="T"></typeparam> /// <param name="refToken"></param> /// <returns></returns> /// <exception cref="TAxiosException"></exception> public async Task<TokenInfo?> RefreshToken<T>(string refToken) where T : EzJwtModel { // IDictionary<string, object> REfpropertyDictionary = new Dictionary<string, object>(); // var tokenInfo = JWTEncryption.ReadJwtToken("token"); var (isValid, tokenData, validationResult) = JWTEncryption.Validate(refToken); if (!isValid) new EZCoreException(" 系统错误,请重新登录", System.Net.HttpStatusCode.Unauthorized); var user = tokenData.Claims; if (user == null) throw new EZCoreException("参数丢失,请重新登录", System.Net.HttpStatusCode.Unauthorized); var key = user.Where(x => x.Type == "RefTokenID").Select(x => x.Value).FirstOrDefault(); //if (key == null) // throw Oops.Oh("token已过期,请重新登录", System.Net.HttpStatusCode.Unauthorized); var jwtConfig = App.GetConfig<JWTSettingsOptions>("JWTSettings"); if (jwtConfig == null) throw new EZCoreException(" 系统错误,请重新登录", System.Net.HttpStatusCode.Unauthorized); var LoinData = _redisCacheSc.Get<T>(key); if (LoinData == null) throw new EZCoreException("token已过期,请重新登录", System.Net.HttpStatusCode.Unauthorized); //Oops.Oh("token已过期,请重新登录", System.Net.HttpStatusCode.Unauthorized); var refreshTokenouttimes = _redisCacheSc.GetTtl(key); if (refreshTokenouttimes <= 0) { throw new EZCoreException("token已过期,请重新登录", System.Net.HttpStatusCode.Unauthorized); } return await Task.Run<TokenInfo?>(() => { IDictionary<string, object> propertyDictionary = new Dictionary<string, object>(); PropertyInfo[] properties = LoinData.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo property in properties) { string propertyName = property.Name; object propertyValue = property.GetValue(LoinData); propertyDictionary.Add(propertyName.ToLower(), propertyValue); } var expires = DateTime.Now.AddSeconds(Convert.ToInt32(jwtConfig.ExpiredTime * 60)); var token = JWTEncryption.Encrypt(propertyDictionary, jwtConfig.ExpiredTime); IDictionary<string, object> refreshTokenClaims = new Dictionary<string, object>(); refreshTokenClaims.Add(new( "RefTokenID", key )); var refreshToken = JWTEncryption.Encrypt(refreshTokenClaims, refreshTokenouttimes); return new TokenInfo { accessToken = token, expires = expires, refreshToken = refreshToken }; }); } } } cylsg/cylsg.Authorization/EzJwtModel.cs
New file @@ -0,0 +1,38 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace cylsg.Authorization { public class EzJwtModel { /// <summary> /// 姓名ID /// </summary> public int? UserID { get; set; } /// <summary> /// 用户姓名 /// </summary> public string? NickName { get; set; } /// <summary> /// 用户登录名称 /// </summary> public string? ITCode { get; set; } public UserLogInTypeKey UserTypeKey { get; set; } } /// <summary> /// 用户登录类型 /// </summary> public enum UserLogInTypeKey { /// <summary> /// 员工管理员 /// </summary> Employees } } cylsg/cylsg.Authorization/IEzAuthorizationService.cs
New file @@ -0,0 +1,40 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace cylsg.Authorization { /// <summary> /// Ez鉴权服务 /// </summary> public interface IEzAuthorizationService { TokenInfo CreateToken<T>(T jwt) where T : EzJwtModel; public Task<TokenInfo?> RefreshToken<T>(string refToken) where T : EzJwtModel; } /// <summary> /// toke详情 /// </summary> public class TokenInfo { /// <summary> /// 当前token /// </summary> public string accessToken { get; set; } /// <summary> /// 刷新Token /// </summary> public string refreshToken { get; set; } /// <summary> /// 当前token到期时间 /// </summary> public DateTime? expires { get; set; } } } cylsg/cylsg.Authorization/Startup.cs
New file @@ -0,0 +1,21 @@ using Furion; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using SqlSugar; namespace cylsg.Authorization { public class Startup : AppStartup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } } } cylsg/cylsg.Authorization/cylsg.Authorization.csproj
New file @@ -0,0 +1,15 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\cylsg.Core\cylsg.Core.csproj" /> <ProjectReference Include="..\cylsg.Model\cylsg.Model.csproj" /> <ProjectReference Include="..\cylsg.redis\cylsg.redis.csproj" /> </ItemGroup> </Project> cylsg/cylsg.Authorization/cylsgException.cs
New file @@ -0,0 +1,34 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; namespace cylsg.Authorization { /// <summary> /// 作为ezcore 的扩充 /// </summary> public class EZCoreException : Exception { public EZCoreException(string message, HttpStatusCode? sCode = null) : base(message) { if (sCode != null) StatusCode = sCode; } public EZCoreException(HttpStatusCode? sCode = null) { if (sCode != null) StatusCode = sCode; } public EZCoreException(string message, Exception innerException, HttpStatusCode? sCode = null) : base(message, innerException) { if (sCode != null) StatusCode = sCode; } public HttpStatusCode? StatusCode { get; set; } = HttpStatusCode.OK; } } cylsg/cylsg.Web.Core/Handlers/EzCoreRESTFulResultProvider.cs
New file @@ -0,0 +1,174 @@ using Furion.FriendlyException; using Furion.UnifyResult; using Furion; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc; using System; using System.Threading.Tasks; using Furion.DataValidation; using cylsg.Authorization; namespace cylsg.Web.Core.Handlers { /// <summary> /// 统一返回结果 /// </summary> [UnifyModel(typeof(EzCoreResultMode<>))] internal class EzCoreRESTFulResultProvider : IUnifyResultProvider { /// <summary> /// 异常返回值 /// </summary> /// <param name="context"></param> /// <param name="metadata"></param> /// <returns></returns> public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata) { //if (context is TAxiosException) // return new JsonResult(new JsonResult(TAxiosFulResult((int)((TAxios.Utility.TAxiosException)context.Exception).StatusCode, Code: 0, data: metadata.Data, errors: metadata.Errors) // , UnifyContext.GetSerializerSettings(context))); var ex = context.Exception as EZCoreException; if (ex != null) { //如果是异常特殊抛出的异常,这里需要处理状态码 context.HttpContext.Response.StatusCode = (int)ex.StatusCode; } return new JsonResult(EzCoreFulResult(metadata.StatusCode, Code: 0, data: metadata.Data, errors: metadata.Errors) , UnifyContext.GetSerializerSettings(context)); // 当前行仅限 Furion 4.6.6+ 使用 } /// <summary> /// 成功返回值 /// </summary> /// <param name="context"></param> /// <param name="data"></param> /// <returns></returns> public IActionResult OnSucceeded(ActionExecutedContext context, object data) { return new JsonResult(EzCoreFulResult(StatusCodes.Status200OK, 1, data) , UnifyContext.GetSerializerSettings(context)); // 当前行仅限 Furion 4.6.6+ 使用 } /// <summary> /// 验证失败返回值 /// </summary> /// <param name="context"></param> /// <param name="metadata"></param> /// <returns></returns> public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata) { //设置返回状态码 return new JsonResult(EzCoreFulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, Code: 0, data: metadata.Data, errors: metadata.ValidationResult) , UnifyContext.GetSerializerSettings(context)); // 当前行仅限 Furion 4.6.6+ 使用 } /// <summary> /// 特定状态码返回值 OnResponseStatusCodes /// </summary> /// <param name="context"></param> /// <param name="statusCode"></param> /// <param name="unifyResultSettings"></param> /// <returns></returns> public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings) { // 设置响应状态码 UnifyContext.SetResponseStatusCodes(context, statusCode, unifyResultSettings); switch (statusCode) { // 处理 401 状态码 case StatusCodes.Status401Unauthorized: context.Response.StatusCode = statusCode; await context.Response.WriteAsJsonAsync(EzCoreFulResult(statusCode, Code: 0, errors: "401 Unauthorized") , App.GetOptions<JsonOptions>()?.JsonSerializerOptions); break; // 处理 403 状态码 转换为401状态码 case StatusCodes.Status403Forbidden: context.Response.StatusCode = StatusCodes.Status401Unauthorized; await context.Response.WriteAsJsonAsync(EzCoreFulResult(statusCode, Code: 0, errors: "403 Forbidden") , App.GetOptions<JsonOptions>()?.JsonSerializerOptions); break; default: break; } } /// <summary> /// 返回 RESTful 风格结果集 /// </summary> /// <param name="statusCode"></param> /// <param name="Code">成功状态码</param> /// <param name="data"></param> /// <param name="errors"></param> /// <returns></returns> private static EzCoreResultMode<object> EzCoreFulResult(int statusCode, int Code = 1, object data = default, object errors = default) { return new EzCoreResultMode<object> { Code = Code, StatusCode = statusCode, Data = data, Error = errors, Extras = UnifyContext.Take(), Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }; } public IActionResult OnAuthorizeException(DefaultHttpContext context, ExceptionMetadata metadata) { throw new NotImplementedException(); } } /// <summary> /// 统一返回类型模型定义 /// </summary> /// <typeparam name="T"></typeparam> public class EzCoreResultMode<T> { /// <summary> /// 标准状态码 /// </summary> public int? StatusCode { get; set; } /// <summary> /// 状态码 /// </summary> public int? Code { get; set; } /// <summary> /// 数据 /// </summary> public T? Data { get; set; } /// <summary> /// 错误信息 /// </summary> public object Error { get; set; } /// <summary> /// 附加数据 /// </summary> public object Extras { get; set; } /// <summary> /// 时间戳 /// </summary> public long? Timestamp { get; set; } /// <summary> /// 其他消息 /// </summary> public string Message { get; set; } } } cylsg/cylsg.Web.Core/Handlers/JwtHandler.cs
@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Http; using System.Threading.Tasks; namespace cylsg.Web.Core; namespace cylsg.Web.Core.Handlers; public class JwtHandler : AppAuthorizeHandler { cylsg/cylsg.Web.Core/Startup.cs
@@ -1,4 +1,5 @@ using cylsg.Core; using cylsg.Web.Core.Handlers; using Furion; using Furion.VirtualFileServer; using Microsoft.AspNetCore.Builder; @@ -24,9 +25,9 @@ return DbContext.Instance; } ); services.AddControllersWithViews() .AddInjectWithUnifyResult(); services.AddControllersWithViews() .AddInjectWithUnifyResult<EzCoreRESTFulResultProvider>(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) cylsg/cylsg.Web.Core/cylsg.Web.Core.csproj
@@ -15,6 +15,7 @@ <ItemGroup> <ProjectReference Include="..\cylsg.Application\cylsg.Application.csproj" /> <ProjectReference Include="..\cylsg.Authorization\cylsg.Authorization.csproj" /> </ItemGroup> </Project> cylsg/cylsg.Web.Core/cylsg.Web.Core.xml
@@ -4,5 +4,94 @@ <name>cylsg.Web.Core</name> </assembly> <members> <member name="T:cylsg.Web.Core.Handlers.EzCoreRESTFulResultProvider"> <summary> 统一返回结果 </summary> </member> <member name="M:cylsg.Web.Core.Handlers.EzCoreRESTFulResultProvider.OnException(Microsoft.AspNetCore.Mvc.Filters.ExceptionContext,Furion.FriendlyException.ExceptionMetadata)"> <summary> 异常返回值 </summary> <param name="context"></param> <param name="metadata"></param> <returns></returns> </member> <member name="M:cylsg.Web.Core.Handlers.EzCoreRESTFulResultProvider.OnSucceeded(Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext,System.Object)"> <summary> 成功返回值 </summary> <param name="context"></param> <param name="data"></param> <returns></returns> </member> <member name="M:cylsg.Web.Core.Handlers.EzCoreRESTFulResultProvider.OnValidateFailed(Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext,Furion.DataValidation.ValidationMetadata)"> <summary> 验证失败返回值 </summary> <param name="context"></param> <param name="metadata"></param> <returns></returns> </member> <member name="M:cylsg.Web.Core.Handlers.EzCoreRESTFulResultProvider.OnResponseStatusCodes(Microsoft.AspNetCore.Http.HttpContext,System.Int32,Furion.UnifyResult.UnifyResultSettingsOptions)"> <summary> 特定状态码返回值 OnResponseStatusCodes </summary> <param name="context"></param> <param name="statusCode"></param> <param name="unifyResultSettings"></param> <returns></returns> </member> <member name="M:cylsg.Web.Core.Handlers.EzCoreRESTFulResultProvider.EzCoreFulResult(System.Int32,System.Int32,System.Object,System.Object)"> <summary> 返回 RESTful 风格结果集 </summary> <param name="statusCode"></param> <param name="Code">成功状态码</param> <param name="data"></param> <param name="errors"></param> <returns></returns> </member> <member name="T:cylsg.Web.Core.Handlers.EzCoreResultMode`1"> <summary> 统一返回类型模型定义 </summary> <typeparam name="T"></typeparam> </member> <member name="P:cylsg.Web.Core.Handlers.EzCoreResultMode`1.StatusCode"> <summary> 标准状态码 </summary> </member> <member name="P:cylsg.Web.Core.Handlers.EzCoreResultMode`1.Code"> <summary> 状态码 </summary> </member> <member name="P:cylsg.Web.Core.Handlers.EzCoreResultMode`1.Data"> <summary> 数据 </summary> </member> <member name="P:cylsg.Web.Core.Handlers.EzCoreResultMode`1.Error"> <summary> 错误信息 </summary> </member> <member name="P:cylsg.Web.Core.Handlers.EzCoreResultMode`1.Extras"> <summary> 附加数据 </summary> </member> <member name="P:cylsg.Web.Core.Handlers.EzCoreResultMode`1.Timestamp"> <summary> 时间戳 </summary> </member> <member name="P:cylsg.Web.Core.Handlers.EzCoreResultMode`1.Message"> <summary> 其他消息 </summary> </member> </members> </doc> cylsg/cylsg.Web.Entry/appsettings.json
@@ -14,10 +14,27 @@ //"DbType": "Sqlite", //"IsAutoCloseConnection": true "ConnectionString": "Server=MS-FSEUTNLCXFDB\\SQLEXPRESS;Database=CyLsgDb; MultipleActiveResultSets=true;pooling=true;min pool size=5;max pool size=32767;connect timeout=20;Encrypt=True;TrustServerCertificate=True;integrated security=True;", "DbType": "SqlServer", // "SqlServer" ,mysql, "IsAutoCloseConnection": true "ConnectionString": "Server=MS-FSEUTNLCXFDB\\SQLEXPRESS;Database=CyLsgDb; MultipleActiveResultSets=true;pooling=true;min pool size=5;max pool size=32767;connect timeout=20;Encrypt=True;TrustServerCertificate=True;integrated security=True;", "DbType": "SqlServer", // "SqlServer" ,mysql, "IsAutoCloseConnection": true } ] ], "JWTSettings": { "ValidateIssuerSigningKey": true, // 是否验证密钥,bool 类型,默认true "IssuerSigningKey": "129(*dasd09213)*(*jKDl65656656532jiohi", // 密钥,string 类型,必须是复杂密钥,长度大于16 "ValidateIssuer": true, // 是否验证签发方,bool 类型,默认true "ValidIssuer": "www.51zhengcai.com", // 签发方,string 类型 "ValidateAudience": true, // 是否验证签收方,bool 类型,默认true "ValidAudience": "www.51zhengcai.com", // 签收方,string 类型 "ValidateLifetime": true, // 是否验证过期时间,bool 类型,默认true,建议true "ExpiredTime": 20, // 过期时间,long 类型,单位分钟,默认20分钟 "ClockSkew": 5, // 过期时间容错值,long 类型,单位秒,默认 5秒 "Algorithm": "HS256", // 加密算法,string 类型,默认 HS256 "RefreshTokenExpires": 1440 //分钟 1天 1440分钟 } } cylsg/cylsg.redis/EzCoreNetRedisService.cs
New file @@ -0,0 +1,104 @@ using Furion; using Furion.DependencyInjection; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EzCoreNet.Redis { public class EzCoreNetRedisService : IEzCoreNetRedisService,IScoped { public bool Add(string key, object value, int expireSeconds) { return RedisHelper.Set(key, value, expireSeconds); } public bool DelKey(string key) { RedisHelper.Del(key); return true; } public T? Get<T>(string key) { return RedisHelper.Get<T>(key); } public long GetTtl(string key) { return RedisHelper.Ttl(key); } public bool SetTtl(string key, int ttl) { return RedisHelper.Expire(key, ttl); } /// <summary> /// 设置一个键值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public bool Add<T>(string key, T value, int expireSeconds = -1) { return RedisHelper.Set(key, value, expireSeconds); } public async Task<long> delegateAllKeyWith(string Prefix) { // 使用Scan模式匹配所有符合前缀的key var keys = GetAllKey(Prefix); // 批量删除找到的keys if (keys != null && keys.Count() > 0) { return await RedisHelper.DelAsync(keys.ToArray()); } return 0; // 如果没有找到匹配的key,返回0 } public IEnumerable<string> GetAllKey(string Prefix) { return RedisHelper.Keys(Prefix + "*"); } /// <summary> /// 后去一个缓存,如果没有,则从数据库中获取, /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="DataFormSql">读取方法</param> /// <param name="dc"> 读取的数据库服务</param> /// <param name="expireSeconds"></param> /// <returns></returns> public T? GetAndRefCacheKey<T>(string key, Func<ISqlSugarClient, T> DataFormSql, ISqlSugarClient? dc = null, int? expireSeconds = 3600) { var Data = RedisHelper.Get<T>(key); if (Data == null && DataFormSql != null) { if (dc == null) dc = App.GetService<ISqlSugarClient>(); Data = DataFormSql.Invoke(dc); // Data = DataFormSql.BeginInvoke(dc,ar=> DataFormSql.EndInvoke(ar),null); if (expireSeconds == null) expireSeconds = -1; if (Data != null) RedisHelper.Set(key, Data, (int)expireSeconds); } return Data; } } } cylsg/cylsg.redis/IEzCoreNetRedisService.cs
New file @@ -0,0 +1,80 @@ using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EzCoreNet.Redis { /// <summary> /// ezcore 对redis 封装 /// </summary> public interface IEzCoreNetRedisService { /// <summary> /// 设置一个键值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expireSeconds"></param> /// <returns></returns> bool Add(string key, object value, int expireSeconds); /// <summary> /// 获取一个键值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> T? Get<T>(string key); /// <summary> /// 获取键值剩余过时间 秒级 /// </summary> /// <param name="key"></param> /// <returns></returns> long GetTtl(string key); /// <summary> /// 删除键值 /// </summary> /// <param name="key"></param> /// <returns></returns> bool DelKey(string key); /// <summary> /// 给key设置 有效时间 /// </summary> /// <param name="key"></param> /// <param name="ttl"></param> /// <returns></returns> bool SetTtl(string key, int ttl); /// <summary> /// 设置一个键值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expireSeconds"></param> /// <returns></returns> bool Add<T>(string key, T value, int expireSeconds); /// <summary> /// 获取一个缓存,如果不存在,者根据DataFormSql 在数据库中获取缓存,并保存expireSeconds 时间, /// </summary> /// <typeparam name="T"> 类型</typeparam> /// <param name="key">Key</param> /// <param name="DataFormSql">读取数据函数</param> /// <param name="expireSeconds">过期时间,为空表示永不过期,默认为3600</param> /// <returns></returns> T? GetAndRefCacheKey<T>(string key, Func<ISqlSugarClient, T> DataFormSql, ISqlSugarClient? dc = null, int? expireSeconds = 3600); /// <summary> /// 删除以开头的所有组件,主要用于操作目录 /// </summary> /// <param name="Prefix"></param> /// <returns></returns> Task<long> delegateAllKeyWith(string Prefix); } } cylsg/cylsg.redis/RedisConfig.json
New file @@ -0,0 +1,8 @@ { //请保持redis为正常可用 "RedisConfig": { // 如果采用容器化部署Service 要写成redis的服务名,否则写地址 "ConnectionString": "127.0.0.1:6379,password=,connectTimeout=30000,responseTimeout=30000,abortConnect=false,connectRetry=1,syncTimeout=10000,DefaultDatabase=1" //redis数据库连接字符串 } } cylsg/cylsg.redis/Startup.cs
New file @@ -0,0 +1,53 @@ using cylsg.Core; using Furion; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EzCoreNet.Redis { /// <summary> /// 封装注册函数和配置函数 /// </summary> public static class startup { public static void AddEzCoreNetRedisService(this IServiceCollection services, Action<opentions>? redisOptions = null) { opentions op = new opentions(); if (redisOptions != null) { redisOptions.Invoke(op); } //只使用普通模式,其他后续开发 var cs = new CSRedis.CSRedisClient(op.Configuration); //注入链接 RedisHelper.Initialization(cs); services.AddScoped<IEzCoreNetRedisService, EzCoreNetRedisService>(); } } public class RedisStartup : AppStartup { public void ConfigureServices(IServiceCollection services) { services.AddEzCoreNetRedisService(op=> { op.Configuration = App.Configuration["RedisConfig:ConnectionString"]; }); } } } cylsg/cylsg.redis/cylsg.redis.csproj
New file @@ -0,0 +1,17 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="CSRedisCore" Version="3.8.803" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\cylsg.Core\cylsg.Core.csproj" /> </ItemGroup> </Project> cylsg/cylsg.redis/opentions.cs
New file @@ -0,0 +1,23 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EzCoreNet.Redis { public class opentions { /// <summary> /// 默认本机链接 数据库为0 /// </summary> public opentions() { Configuration = "127.0.0.1:6379,password=,connectTimeout=30000,responseTimeout=30000,abortConnect=false,connectRetry=1,syncTimeout=10000,DefaultDatabase=1"; } /// <summary> /// 链接字符串 /// </summary> public string Configuration { get; set; } } } cylsg/cylsg.services/cylsg.services.csproj
New file @@ -0,0 +1,14 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\cylsg.Core\cylsg.Core.csproj" /> <ProjectReference Include="..\cylsg.Model\cylsg.Model.csproj" /> </ItemGroup> </Project> cylsg/cylsg.sln
@@ -15,6 +15,14 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cylsg.utility", "cylsg.utility\cylsg.utility.csproj", "{5ED2C481-2D74-43BC-ABAF-1455A052FAA4}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EzWechat", "EzWechat\EzWechat.csproj", "{709CC927-07D3-4400-BD57-838A44B4FBD4}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cylsg.services", "cylsg.services\cylsg.services.csproj", "{28CCF771-A760-4191-B299-4E53B5B23D91}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cylsg.redis", "cylsg.redis\cylsg.redis.csproj", "{15042E77-32D5-46DE-9CE5-24D093C63422}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cylsg.Authorization", "cylsg.Authorization\cylsg.Authorization.csproj", "{1197C9BB-C73D-42FB-A114-AFB0A7466615}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +53,22 @@ {5ED2C481-2D74-43BC-ABAF-1455A052FAA4}.Debug|Any CPU.Build.0 = Debug|Any CPU {5ED2C481-2D74-43BC-ABAF-1455A052FAA4}.Release|Any CPU.ActiveCfg = Release|Any CPU {5ED2C481-2D74-43BC-ABAF-1455A052FAA4}.Release|Any CPU.Build.0 = Release|Any CPU {709CC927-07D3-4400-BD57-838A44B4FBD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {709CC927-07D3-4400-BD57-838A44B4FBD4}.Debug|Any CPU.Build.0 = Debug|Any CPU {709CC927-07D3-4400-BD57-838A44B4FBD4}.Release|Any CPU.ActiveCfg = Release|Any CPU {709CC927-07D3-4400-BD57-838A44B4FBD4}.Release|Any CPU.Build.0 = Release|Any CPU {28CCF771-A760-4191-B299-4E53B5B23D91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {28CCF771-A760-4191-B299-4E53B5B23D91}.Debug|Any CPU.Build.0 = Debug|Any CPU {28CCF771-A760-4191-B299-4E53B5B23D91}.Release|Any CPU.ActiveCfg = Release|Any CPU {28CCF771-A760-4191-B299-4E53B5B23D91}.Release|Any CPU.Build.0 = Release|Any CPU {15042E77-32D5-46DE-9CE5-24D093C63422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {15042E77-32D5-46DE-9CE5-24D093C63422}.Debug|Any CPU.Build.0 = Debug|Any CPU {15042E77-32D5-46DE-9CE5-24D093C63422}.Release|Any CPU.ActiveCfg = Release|Any CPU {15042E77-32D5-46DE-9CE5-24D093C63422}.Release|Any CPU.Build.0 = Release|Any CPU {1197C9BB-C73D-42FB-A114-AFB0A7466615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1197C9BB-C73D-42FB-A114-AFB0A7466615}.Debug|Any CPU.Build.0 = Debug|Any CPU {1197C9BB-C73D-42FB-A114-AFB0A7466615}.Release|Any CPU.ActiveCfg = Release|Any CPU {1197C9BB-C73D-42FB-A114-AFB0A7466615}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE