移动系统liao
2024-08-14 04956fce30bded82267844718fa5e4e04148e487
no message
9个文件已修改
22个文件已添加
1779 ■■■■■ 已修改文件
.gitignore 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/EzUpFiles/EzFileUploadService.cs 709 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/EzUpFiles/EzUpFiles.csproj 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/EzUpFiles/IEzFileUploadService.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/EzWechat/EzWechat.csproj 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/EzWechat/IWechatService.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/EzWechat/Startup.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/EzWechat/WechatConfig.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/EzWechat/WechatService.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Application/System/Services/ISystemService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Application/System/Services/SystemService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Authorization/EzAuthorizationService.cs 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Authorization/EzJwtModel.cs 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Authorization/IEzAuthorizationService.cs 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Authorization/Startup.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Authorization/cylsg.Authorization.csproj 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Authorization/cylsgException.cs 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Web.Core/Handlers/EzCoreRESTFulResultProvider.cs 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Web.Core/Handlers/JwtHandler.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Web.Core/Startup.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Web.Core/cylsg.Web.Core.csproj 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Web.Core/cylsg.Web.Core.xml 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.Web.Entry/appsettings.json 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.redis/EzCoreNetRedisService.cs 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.redis/IEzCoreNetRedisService.cs 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.redis/RedisConfig.json 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.redis/Startup.cs 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.redis/cylsg.redis.csproj 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.redis/opentions.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.services/cylsg.services.csproj 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cylsg/cylsg.sln 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.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