From d111c8c2c0cff58a264722053a39c73e3237cd35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E6=AD=A3=E6=98=93?= Date: Mon, 9 Jun 2025 09:22:59 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E5=8D=87?= =?UTF-8?q?=E7=BA=A7+=E4=BF=AE=E6=94=B9=EF=BC=88=E9=87=8D=E8=A6=81?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=EF=BC=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AutoMapperProfile/AutoMapperProfile.cs | 13 + U8Server/Controllers/BaseController.cs | 211 +++++++++++++++ .../WMentryWarehousing_productController.cs | 243 ++++++++++++++++++ U8Server/Controllers/v1/V1U8AuthController.cs | 24 ++ .../Controllers/v1/V1U8WarehouseController.cs | 24 ++ U8Server/Extensions/AppServiceExtensions.cs | 68 +++++ U8Server/Extensions/CorsExtension.cs | 27 ++ U8Server/Extensions/DataPermi.cs | 120 +++++++++ U8Server/Extensions/DbExtension.cs | 208 +++++++++++++++ U8Server/Extensions/EntityExtension.cs | 46 ++++ U8Server/Extensions/HttpContextExtension.cs | 225 ++++++++++++++++ U8Server/Extensions/SqlSugarCache.cs | 64 +++++ U8Server/Framework/JwtUtil.cs | 170 ++++++++++++ U8Server/GlobalUsing.cs | 7 + .../Middleware/GlobalExceptionMiddleware.cs | 137 ++++++++++ U8Server/Program.cs | 30 +++ U8Server/Properties/launchSettings.json | 31 +++ U8Server/U8Server.csproj | 28 ++ U8Server/Util/GetSign.cs | 19 ++ U8Server/Util/MD5Encryption.cs | 110 ++++++++ U8Server/Util/StringExtensions.cs | 20 ++ U8Server/appsettings.Development.json | 8 + U8Server/appsettings.json | 21 ++ ZR.Admin.WebApi/Program.cs | 6 +- ZR.Admin.WebApi/ZR.Admin.WebApi.csproj | 3 + ZR.Admin.WebApi/appsettings.json | 12 + ZRAdmin.sln | 6 + 27 files changed, 1880 insertions(+), 1 deletion(-) create mode 100644 U8Server/AutoMapperProfile/AutoMapperProfile.cs create mode 100644 U8Server/Controllers/BaseController.cs create mode 100644 U8Server/Controllers/WMentryWarehousing_productController.cs create mode 100644 U8Server/Controllers/v1/V1U8AuthController.cs create mode 100644 U8Server/Controllers/v1/V1U8WarehouseController.cs create mode 100644 U8Server/Extensions/AppServiceExtensions.cs create mode 100644 U8Server/Extensions/CorsExtension.cs create mode 100644 U8Server/Extensions/DataPermi.cs create mode 100644 U8Server/Extensions/DbExtension.cs create mode 100644 U8Server/Extensions/EntityExtension.cs create mode 100644 U8Server/Extensions/HttpContextExtension.cs create mode 100644 U8Server/Extensions/SqlSugarCache.cs create mode 100644 U8Server/Framework/JwtUtil.cs create mode 100644 U8Server/GlobalUsing.cs create mode 100644 U8Server/Middleware/GlobalExceptionMiddleware.cs create mode 100644 U8Server/Program.cs create mode 100644 U8Server/Properties/launchSettings.json create mode 100644 U8Server/U8Server.csproj create mode 100644 U8Server/Util/GetSign.cs create mode 100644 U8Server/Util/MD5Encryption.cs create mode 100644 U8Server/Util/StringExtensions.cs create mode 100644 U8Server/appsettings.Development.json create mode 100644 U8Server/appsettings.json diff --git a/U8Server/AutoMapperProfile/AutoMapperProfile.cs b/U8Server/AutoMapperProfile/AutoMapperProfile.cs new file mode 100644 index 00000000..8a5066f1 --- /dev/null +++ b/U8Server/AutoMapperProfile/AutoMapperProfile.cs @@ -0,0 +1,13 @@ +using AutoMapper; + +namespace ZR.Admin.WebApi.AutoMapperProfile +{ + public class AutoMapperProfile : Profile + { + public AutoMapperProfile() + { + + + } + } +} diff --git a/U8Server/Controllers/BaseController.cs b/U8Server/Controllers/BaseController.cs new file mode 100644 index 00000000..e5dc779e --- /dev/null +++ b/U8Server/Controllers/BaseController.cs @@ -0,0 +1,211 @@ +using Infrastructure; +using Infrastructure.Extensions; +using Infrastructure.Model; +using Microsoft.AspNetCore.Mvc; +using MiniExcelLibs; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System.Web; +using Io = System.IO; + +namespace WebApi.Controllers +{ + public class BaseController : ControllerBase + { + public static string TIME_FORMAT_FULL = "yyyy-MM-dd HH:mm:ss"; + + /// + /// 返回成功封装 + /// + /// + /// + /// + protected IActionResult SUCCESS(object data, string timeFormatStr = "yyyy-MM-dd HH:mm:ss") + { + string jsonStr = GetJsonStr(GetApiResult(data != null ? ResultCode.SUCCESS : ResultCode.NO_DATA, data), timeFormatStr); + return Content(jsonStr, "application/json"); + } + + /// + /// json输出带时间格式的 + /// + /// + /// + protected IActionResult ToResponse(ApiResult apiResult) + { + string jsonStr = GetJsonStr(apiResult, TIME_FORMAT_FULL); + + return Content(jsonStr, "application/json"); + } + + protected IActionResult ToResponse(long rows, string timeFormatStr = "yyyy-MM-dd HH:mm:ss") + { + string jsonStr = GetJsonStr(ToJson(rows), timeFormatStr); + + return Content(jsonStr, "application/json"); + } + + protected IActionResult ToResponse(ResultCode resultCode, string msg = "") + { + return ToResponse(new ApiResult((int)resultCode, msg)); + } + + /// + /// 导出Excel + /// + /// 完整文件路径 + /// 带扩展文件名 + /// + protected IActionResult ExportExcel(string path, string fileName) + { + //var webHostEnvironment = App.WebHostEnvironment; + if (!Path.Exists(path)) + { + throw new CustomException(fileName + "文件不存在"); + } + var stream = Io.File.OpenRead(path); //创建文件流 + + Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition"); + return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", HttpUtility.UrlEncode(fileName)); + // return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",fileName); + } + + #region 方法 + + /// + /// 响应返回结果 + /// + /// 受影响行数 + /// + /// + protected ApiResult ToJson(long rows, object? data = null) + { + return rows > 0 ? ApiResult.Success("success", data) : GetApiResult(ResultCode.FAIL); + } + + /// + /// 全局Code使用 + /// + /// + /// + /// + protected ApiResult GetApiResult(ResultCode resultCode, object? data = null) + { + var msg = resultCode.GetDescription(); + + return new ApiResult((int)resultCode, msg, data); + } + + /// + /// + /// + /// + /// + /// + private static string GetJsonStr(ApiResult apiResult, string timeFormatStr) + { + if (string.IsNullOrEmpty(timeFormatStr)) + { + timeFormatStr = TIME_FORMAT_FULL; + } + var serializerSettings = new JsonSerializerSettings + { + // 设置为驼峰命名 + ContractResolver = new CamelCasePropertyNamesContractResolver(), + DateFormatString = timeFormatStr + }; + + return JsonConvert.SerializeObject(apiResult, Formatting.Indented, serializerSettings); + } + #endregion + + /// + /// 导出Excel + /// + /// + /// + /// + /// + protected string ExportExcel(List list, string sheetName, string fileName) + { + return ExportExcelMini(list, sheetName, fileName).Item1; + } + + /// + /// + /// + /// + /// + /// + /// + /// + protected (string, string) ExportExcelMini(List list, string sheetName, string fileName) + { + IWebHostEnvironment webHostEnvironment = (IWebHostEnvironment)App.ServiceProvider.GetService(typeof(IWebHostEnvironment)); + string sFileName = $"{fileName}{DateTime.Now:MM-dd-HHmmss}.xlsx"; + string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "export", sFileName); + + Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); + + MiniExcel.SaveAs(fullPath, list, sheetName: sheetName); + return (sFileName, fullPath); + } + + /// + /// 导出多个工作表(Sheet) + /// + /// + /// + /// + protected (string, string) ExportExcelMini(Dictionary sheets, string fileName) + { + IWebHostEnvironment webHostEnvironment = (IWebHostEnvironment)App.ServiceProvider.GetService(typeof(IWebHostEnvironment)); + string sFileName = $"{fileName}{DateTime.Now:MM-dd-HHmmss}.xlsx"; + string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "export", sFileName); + + Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); + + MiniExcel.SaveAs(fullPath, sheets); + return (sFileName, fullPath); + } + + /// + /// 下载导入模板 + /// + /// 数据类型 + /// 空数据类型集合 + /// 下载文件名 + /// + protected (string, string) DownloadImportTemplate(List list, string fileName) + { + IWebHostEnvironment webHostEnvironment = App.WebHostEnvironment; + string sFileName = $"{fileName}.xlsx"; + string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "ImportTemplate", sFileName); + + //不存在模板创建模板 + if (!Directory.Exists(fullPath)) + { + Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); + MiniExcel.SaveAs(fullPath, list, overwriteFile: true); + } + return (sFileName, fullPath); + } + + /// + /// 下载指定文件模板 + /// + /// 下载文件名 + /// + protected (string, string) DownloadImportTemplate(string fileName) + { + IWebHostEnvironment webHostEnvironment = (IWebHostEnvironment)App.ServiceProvider.GetService(typeof(IWebHostEnvironment)); + string sFileName = $"{fileName}.xlsx"; + string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "ImportTemplate", sFileName); + + return (sFileName, fullPath); + } + + + + } +} diff --git a/U8Server/Controllers/WMentryWarehousing_productController.cs b/U8Server/Controllers/WMentryWarehousing_productController.cs new file mode 100644 index 00000000..c6a76049 --- /dev/null +++ b/U8Server/Controllers/WMentryWarehousing_productController.cs @@ -0,0 +1,243 @@ +using Microsoft.AspNetCore.Mvc; +using ZR.Admin.WebApi.Extensions; +using ZR.Model.MES.wms; +using ZR.Model.MES.wms.Dto; +using ZR.Service.mes.wms.IService; + +namespace WebApi.Controllers.mes.wms +{ + /// + /// 入库模块 + /// + [Route("/mes/wm/entrywarehouse")] + public class WMentryWarehousing_productController : BaseController + { + + private readonly IWMentryWarehousing_productService wm_entryWarehousing_productService; + public WMentryWarehousing_productController(IWMentryWarehousing_productService wm_entryWarehousing_productService) + { + this.wm_entryWarehousing_productService = wm_entryWarehousing_productService; + } + + + + /// + /// 1. 判断是否为库位码 + /// + /// + [HttpGet("is_production_location")] + [Log(Title = "判断是否为库位码")] + public IActionResult IsProductionLocation(string production_location_code = "") + { + if (string.IsNullOrEmpty(production_location_code)) + { + return ToResponse(new ApiResult(200, "传入为空", false)); + + } + // 查询 wm_info 表,根据库位码,查询在表中是否存在,true false + bool state = this.wm_entryWarehousing_productService.IsProductionLoacation(production_location_code); + + return ToResponse(new ApiResult(200, "success", state)); + } + + /// + /// 2. 判断是否为成品库箱子码 + /// + /// + [HttpGet("is_production_package")] + [Log(Title = "判断是否为成品库箱子码")] + public IActionResult IsProductionPackage(string package_code = "") + { + if (string.IsNullOrEmpty(package_code)) + { + return ToResponse(new ApiResult(200, "传入为空", false)); + + } + int state = this.wm_entryWarehousing_productService.isProductionPackage(package_code); + string msg = null; + if (state == 0) + msg = "外箱标签码不存在"; + else if (state == 1) + msg = "success"; + + else if (state == 2) + msg = "误扫码,不是外箱标签码"; + + + return ToResponse(new ApiResult(200, msg, state)); + + } + + /// + /// 3.判断是否为满箱 + /// + /// + [HttpGet("is_full_package")] + [Log(Title = "判断是否为满箱")] + public IActionResult IsFullPackage(string package_code = "") + { + + if (string.IsNullOrEmpty(package_code)) + { + return ToResponse(new ApiResult(200, "传入为空", false)); + + } + bool state = this.wm_entryWarehousing_productService.isFullPackage(package_code); + string msg = null; + if (state) + { + msg = "满箱"; + } + else + { + msg = "零头箱"; + } + + return ToResponse(new ApiResult(200, msg, state)); + } + /// + /// 4.入库 + /// + /// + /// + [HttpPost("into_product_warehouse")] + [Log(Title = "入库")] + public IActionResult IntoProductwarehouse([FromBody] WmgoodsDto wmgoodsDto) + { + try + { + if (wmgoodsDto == null) + { + return ToResponse(new ApiResult(200, "传入参数为空", false)); + } + string msg = ""; + + string createName = HttpContext.GetName(); + int num = this.wm_entryWarehousing_productService.IntoProductwarehouse(wmgoodsDto, createName); + if (num == 0) + { + msg = "入库数为0!"; + + + } + else if (num >= 1) + { + msg = "成功入库" + num + "箱"; + + + } + return ToResponse(new ApiResult(200, msg, num)); + } + catch (Exception e) + { + return ToResponse(new ApiResult(500, e.Message, e.Message)); + } + + } + + + /// + /// 获取库位已经存在箱子 + /// + /// + /// + [HttpGet("packagelist")] + [Log(Title = "获取库位已经存在箱子")] + public IActionResult Getpackagelist(string locationcode) + { + if (string.IsNullOrEmpty(locationcode)) + { + return ToResponse(new ApiResult(200, "传入为空", false)); + } + string msg = null; + + List productionList = this.wm_entryWarehousing_productService.Getpackagelist(locationcode); + + return ToResponse(new ApiResult(200, msg, productionList)); + } + + + + /// + /// 解析外标签码 + /// + /// + /// + [HttpGet("resolution_package")] + public IActionResult ResolutionPackage(string code = "") + { + try + { + if (string.IsNullOrEmpty(code)) + { + + return ToResponse(new ApiResult(200, "传入为空", false)); + } + ResultionPackageCodeDto data = this.wm_entryWarehousing_productService.ResolutionPackage(code); + if (data == null) + { + return ToResponse(new ApiResult(500, "外标签解析异常", data)); + } + return ToResponse(new ApiResult(200, "success", data)); + } + catch (Exception ex) + { + return ToResponse(new ApiResult(500, ex.Message, "外标签解析异常")); + } + ; + + } + /// + /// 7 判断箱子是否存在成品库仓库里 + /// + /// + /// + [HttpGet("is_existed_warehouse")] + public IActionResult IsExistedWarehouse(string originalCode = "") + { + if (string.IsNullOrEmpty(originalCode)) + { + + return ToResponse(new ApiResult(200, "传入为空", false)); + } + string msg = null; + bool data = this.wm_entryWarehousing_productService.IsExistedWarehouse(originalCode); + if (data) + { + msg = "存在"; + + } + else + { + msg = "不存在"; + } + + return ToResponse(new ApiResult(200, msg, data)); + + } + + /// + /// all.判断标签扫描结果是否可入库(综合结果判断) + /// + /// + [HttpGet("checkWarehousing")] + [Log(Title = "判断标签扫描结果是否可入库")] + public IActionResult CheckWarehousing(string production_packcode = "", string location = "", + bool isStrict = false) + { + string msg = this.wm_entryWarehousing_productService.checkWarehousing(production_packcode, location, isStrict); + if ("ok".Equals(msg)) + { + // 可入库 + return ToResponse(new ApiResult(200, msg, true)); + } + else + { + // 不可入库 + return ToResponse(new ApiResult(200, msg, false)); + } + + } + } + +} diff --git a/U8Server/Controllers/v1/V1U8AuthController.cs b/U8Server/Controllers/v1/V1U8AuthController.cs new file mode 100644 index 00000000..3c64a096 --- /dev/null +++ b/U8Server/Controllers/v1/V1U8AuthController.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Mvc; +using U8Server.Util; +using WebApi.Controllers; +namespace U8Server.Controllers.v1 +{ + [ApiController] + [Route("/v1/u8/auth")] + public class V1U8AuthController : BaseController + { + private readonly ILogger _logger; + + public V1U8AuthController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "getMd5")] + [Log(Title = "ȡmd5Կ")] + public string GetMd5() + { + return GetSign.GetBy16Md5(); + } + } +} diff --git a/U8Server/Controllers/v1/V1U8WarehouseController.cs b/U8Server/Controllers/v1/V1U8WarehouseController.cs new file mode 100644 index 00000000..9c7078df --- /dev/null +++ b/U8Server/Controllers/v1/V1U8WarehouseController.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Mvc; +using U8Server.Util; +using WebApi.Controllers; +namespace U8Server.Controllers.v1 +{ + [ApiController] + [Route("/v1/u8/warehouse")] + public class V1U8WarehouseController : BaseController + { + private readonly ILogger _logger; + + public V1U8WarehouseController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "getMd52")] + [Log(Title = "ȡmd5Կ")] + public string GetMd5() + { + return GetSign.GetBy16Md5(); + } + } +} diff --git a/U8Server/Extensions/AppServiceExtensions.cs b/U8Server/Extensions/AppServiceExtensions.cs new file mode 100644 index 00000000..9ee35c3e --- /dev/null +++ b/U8Server/Extensions/AppServiceExtensions.cs @@ -0,0 +1,68 @@ +using System.Reflection; + +namespace ZR.Admin.WebApi.Extensions +{ + /// + /// App服务注册 + /// + public static class AppServiceExtensions + { + /// + /// 注册引用程序域中所有有AppService标记的类的服务 + /// + /// + public static void AddAppService(this IServiceCollection services) + { + var cls = AppSettings.Get("InjectClass"); + if (cls == null || cls.Length <= 0) + { + throw new Exception("请更新appsettings类"); + } + foreach (var item in cls) + { + Register(services, item); + } + } + + private static void Register(IServiceCollection services, string item) + { + Assembly assembly = Assembly.Load(item); + foreach (var type in assembly.GetTypes()) + { + var serviceAttribute = type.GetCustomAttribute(); + + if (serviceAttribute != null) + { + var serviceType = serviceAttribute.ServiceType; + //情况1 适用于依赖抽象编程,注意这里只获取第一个 + if (serviceType == null && serviceAttribute.InterfaceServiceType) + { + serviceType = type.GetInterfaces().FirstOrDefault(); + } + //情况2 不常见特殊情况下才会指定ServiceType,写起来麻烦 + if (serviceType == null) + { + serviceType = type; + } + + switch (serviceAttribute.ServiceLifetime) + { + case LifeTime.Singleton: + services.AddSingleton(serviceType, type); + break; + case LifeTime.Scoped: + services.AddScoped(serviceType, type); + break; + case LifeTime.Transient: + services.AddTransient(serviceType, type); + break; + default: + services.AddTransient(serviceType, type); + break; + } + //System.Console.WriteLine($"注册:{serviceType}"); + } + } + } + } +} diff --git a/U8Server/Extensions/CorsExtension.cs b/U8Server/Extensions/CorsExtension.cs new file mode 100644 index 00000000..cb3b1c49 --- /dev/null +++ b/U8Server/Extensions/CorsExtension.cs @@ -0,0 +1,27 @@ +namespace ZR.Admin.WebApi.Extensions +{ + public static class CorsExtension + { + /// + /// 跨域配置 + /// + /// + /// + public static void AddCors(this IServiceCollection services, IConfiguration configuration) + { + var corsUrls = configuration.GetSection("corsUrls").Get(); + + //配置跨域 + services.AddCors(c => + { + c.AddPolicy("Policy", policy => + { + policy.WithOrigins(corsUrls ?? Array.Empty()) + .AllowAnyHeader()//允许任意头 + .AllowCredentials()//允许cookie + .AllowAnyMethod();//允许任意方法 + }); + }); + } + } +} diff --git a/U8Server/Extensions/DataPermi.cs b/U8Server/Extensions/DataPermi.cs new file mode 100644 index 00000000..efbcdf5e --- /dev/null +++ b/U8Server/Extensions/DataPermi.cs @@ -0,0 +1,120 @@ +using SqlSugar; +using SqlSugar.IOC; +using ZR.Admin.WebApi.Framework; +using ZR.Model.System; + +namespace ZR.Admin.WebApi.Extensions +{ + public enum DataPermiEnum + { + None = 0, + /// + /// 全部数据权限 + /// + All = 1, + /// + /// 仅本人数据权限 + /// + SELF = 5, + /// + /// 部门数据权限 + /// + DEPT = 3, + /// + /// 自定数据权限 + /// + CUSTOM = 2, + /// + /// 部门及以下数据权限 + /// + DEPT_CHILD = 4 + } + /// + /// 数据权限 + /// + public class DataPermi + { + /// + /// 数据过滤 + /// + /// 多库id + public static void FilterData(int configId) + { + //获取当前用户的信息 + var user = JwtUtil.GetLoginUser(App.HttpContext); + if (user == null) return; + //管理员不过滤 + if (user.RoleIds.Any(f => f.Equals(GlobalConstant.AdminRole))) return; + var db = DbScoped.SugarScope.GetConnectionScope(configId); + var expUser = Expressionable.Create(); + var expRole = Expressionable.Create(); + var expLoginlog = Expressionable.Create(); + + foreach (var role in user.Roles.OrderBy(f => f.DataScope)) + { + var dataScope = (DataPermiEnum)role.DataScope; + if (DataPermiEnum.All.Equals(dataScope))//所有权限 + { + break; + } + else if (DataPermiEnum.CUSTOM.Equals(dataScope))//自定数据权限 + { + //" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId())); + + expUser.Or(it => SqlFunc.Subqueryable().Where(f => f.DeptId == it.DeptId && f.RoleId == role.RoleId).Any()); + } + else if (DataPermiEnum.DEPT.Equals(dataScope))//本部门数据 + { + expUser.Or(it => it.DeptId == user.DeptId); + } + else if (DataPermiEnum.DEPT_CHILD.Equals(dataScope))//本部门及以下数据 + { + //SQl OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) ) + var allChildDepts = db.Queryable().ToChildList(it => it.ParentId, user.DeptId); + + expUser.Or(it => allChildDepts.Select(f => f.DeptId).ToList().Contains(it.DeptId)); + } + else if (DataPermiEnum.SELF.Equals(dataScope))//仅本人数据 + { + expUser.Or(it => it.UserId == user.UserId); + expRole.Or(it => user.RoleIds.Contains(it.RoleKey)); + expLoginlog.And(it => it.UserName == user.UserName); + } + } + + db.QueryFilter.AddTableFilter(expUser.ToExpression()); + db.QueryFilter.AddTableFilter(expRole.ToExpression()); + db.QueryFilter.AddTableFilter(expLoginlog.ToExpression()); + } + + public static void FilterData1(int configId) + { + //获取当前用户的信息 + var user = JwtUtil.GetLoginUser(App.HttpContext); + if (user == null) return; + var db = DbScoped.SugarScope.GetConnectionScope(configId); + + foreach (var role in user.Roles.OrderBy(f => f.DataScope)) + { + var dataScope = (DataPermiEnum)role.DataScope; + if (DataPermiEnum.All.Equals(dataScope))//所有权限 + { + break; + } + else if (DataPermiEnum.CUSTOM.Equals(dataScope))//自定数据权限 + { + } + else if (DataPermiEnum.DEPT.Equals(dataScope))//本部门数据 + { + } + else if (DataPermiEnum.DEPT_CHILD.Equals(dataScope))//本部门及以下数据 + { + + } + else if (DataPermiEnum.SELF.Equals(dataScope))//仅本人数据 + { + } + } + } + } +} diff --git a/U8Server/Extensions/DbExtension.cs b/U8Server/Extensions/DbExtension.cs new file mode 100644 index 00000000..d8c36c3c --- /dev/null +++ b/U8Server/Extensions/DbExtension.cs @@ -0,0 +1,208 @@ +using Infrastructure.Extensions; +using SqlSugar; +using SqlSugar.IOC; +using ZR.Model; +using ZR.Model.System; + +namespace ZR.Admin.WebApi.Extensions +{ + /// + /// sqlsugar 数据处理 + /// + public static class DbExtension + { + private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + /// + /// 初始化db + /// + /// + /// + /// + public static void AddDb(this IServiceCollection services, IConfiguration Configuration, IWebHostEnvironment environment) + { + List dbConfigs = Configuration.GetSection("DbConfigs").Get>(); + + var iocList = new List(); + foreach (var item in dbConfigs) + { + iocList.Add(new IocConfig() + { + ConfigId = item.ConfigId, + ConnectionString = item.Conn, + DbType = (IocDbType)item.DbType, + IsAutoCloseConnection = item.IsAutoCloseConnection + }); + } + SugarIocServices.AddSqlSugar(iocList); + ICacheService cache = new SqlSugarCache(); + SugarIocServices.ConfigurationSugar(db => + { + var u = App.User; + if (u != null) + { + DataPermi.FilterData(0); + //ConfigId = 1的数据权限过滤 + //DataPermi.FilterData1(1); + } + + iocList.ForEach(iocConfig => + { + SetSugarAop(db, iocConfig, cache); + }); + }); + } + + /// + /// 数据库Aop设置 + /// + /// + /// + /// + private static void SetSugarAop(SqlSugarClient db, IocConfig iocConfig, ICacheService cache) + { + var config = db.GetConnectionScope(iocConfig.ConfigId).CurrentConnectionConfig; + var showDbLog = AppSettings.Get("ShowDbLog"); + string configId = config.ConfigId; + db.GetConnectionScope(configId).Aop.OnLogExecuting = (sql, pars) => + { + if (showDbLog) + { + string log = $"【db{configId} SQL语句】{UtilMethods.GetSqlString(config.DbType, sql, pars)}\n"; + if (sql.TrimStart().StartsWith("SELECT", StringComparison.OrdinalIgnoreCase)) + { + logger.Info(log); + } + else if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase)) + { + logger.Warn(log); + } + else if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("TRUNCATE", StringComparison.OrdinalIgnoreCase)) + { + logger.Error(log); + } + else + { + log = $"【db{configId} SQL语句】dbo.{sql} {string.Join(", ", pars.Select(x => x.ParameterName + " = " + GetParsValue(x)))};\n"; + logger.Info(log); + } + } + }; + db.GetConnectionScope(configId).Aop.OnError = (ex) => + { + //var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value)); + + string sql = "【错误SQL】" + UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n"; + logger.Error(ex, $"{sql}\r\n{ex.Message}\r\n{ex.StackTrace}"); + }; + db.GetConnectionScope(configId).Aop.DataExecuting = (oldValue, entiyInfo) => + { + }; + //差异日志功能 + db.GetConnectionScope(configId).Aop.OnDiffLogEvent = it => + { + //操作前记录 包含: 字段描述 列名 值 表名 表描述 + var editBeforeData = it.BeforeData;//插入Before为null,之前还没进库 + //操作后记录 包含: 字段描述 列名 值 表名 表描述 + var editAfterData = it.AfterData; + var sql = it.Sql; + var parameter = it.Parameters; + var data = it.BusinessData;//这边会显示你传进来的对象 + var time = it.Time; + var diffType = it.DiffType;//enum insert 、update and delete + + if (diffType == DiffType.delete) + { + string name = App.UserName; + + foreach (var item in editBeforeData) + { + var pars = db.Utilities.SerializeObject(item.Columns.ToDictionary(it => it.ColumnName, it => it.Value)); + + SqlDiffLog log = new() + { + BeforeData = pars, + BusinessData = data?.ToString(), + DiffType = diffType.ToString(), + Sql = sql, + TableName = item.TableName, + UserName = name, + AddTime = DateTime.Now, + ConfigId = configId + }; + //logger.WithProperty("title", data).Info(pars); + db.GetConnectionScope(0).Insertable(log).ExecuteReturnSnowflakeId(); + } + } + }; + db.GetConnectionScope(configId).CurrentConnectionConfig.MoreSettings = new ConnMoreSettings() + { + IsAutoRemoveDataCache = true + }; + db.GetConnectionScope(configId).CurrentConnectionConfig.ConfigureExternalServices = new ConfigureExternalServices() + { + DataInfoCacheService = cache, + EntityService = (c, p) => + { + if (p.IsPrimarykey == true)//主键不能为null + { + p.IsNullable = false; + } + else if (p.ExtendedAttribute?.ToString() == ProteryConstant.NOTNULL.ToString()) + { + p.IsNullable = false; + } + else//则否默认为null + { + p.IsNullable = true; + } + + if (config.DbType == DbType.PostgreSQL) + { + if (c.Name == nameof(SysMenu.IsCache) || c.Name == nameof(SysMenu.IsFrame)) + { + p.DataType = "char(1)"; + } + } + #region 兼容Oracle + if (config.DbType == DbType.Oracle) + { + if (p.IsIdentity == true) + { + if (p.EntityName == nameof(SysUser)) + { + p.OracleSequenceName = "SEQ_SYS_USER_USERID"; + } + else if (p.EntityName == nameof(SysRole)) + { + p.OracleSequenceName = "SEQ_SYS_ROLE_ROLEID"; + } + else if (p.EntityName == nameof(SysDept)) + { + p.OracleSequenceName = "SEQ_SYS_DEPT_DEPTID"; + } + else if (p.EntityName == nameof(SysMenu)) + { + p.OracleSequenceName = "SEQ_SYS_MENU_MENUID"; + } + else + { + p.OracleSequenceName = "SEQ_ID"; + } + } + } + #endregion + } + }; + } + + private static object GetParsValue(SugarParameter x) + { + if (x.DbType == System.Data.DbType.String || x.DbType == System.Data.DbType.DateTime || x.DbType == System.Data.DbType.String) + { + return "'" + x.Value + "'"; + } + return x.Value; + } + } +} diff --git a/U8Server/Extensions/EntityExtension.cs b/U8Server/Extensions/EntityExtension.cs new file mode 100644 index 00000000..fc06210d --- /dev/null +++ b/U8Server/Extensions/EntityExtension.cs @@ -0,0 +1,46 @@ + +using System.Reflection; + +namespace ZR.Admin.WebApi.Extensions +{ + public static class EntityExtension + { + public static TSource ToCreate(this TSource source, HttpContext? context = null) + { + var types = source?.GetType(); + if (types == null) return source; + BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance; + + types.GetProperty("CreateTime", flag)?.SetValue(source, DateTime.Now, null); + types.GetProperty("CreatedTime", flag)?.SetValue(source, DateTime.Now, null); + types.GetProperty("AddTime", flag)?.SetValue(source, DateTime.Now, null); + if(context != null) + { + types.GetProperty("CreateBy", flag)?.SetValue(source, context.GetName(), null); + types.GetProperty("Create_by", flag)?.SetValue(source, context.GetName(), null); + types.GetProperty("CreatedBy", flag)?.SetValue(source, context.GetName(), null); + types.GetProperty("UserId", flag)?.SetValue(source, context.GetUId(), null); + } + return source; + } + + public static TSource ToUpdate(this TSource source, HttpContext? context = null) + { + var types = source?.GetType(); + if (types == null) return source; + BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance; + + types.GetProperty("UpdateTime", flag)?.SetValue(source, DateTime.Now, null); + types.GetProperty("Update_time", flag)?.SetValue(source, DateTime.Now, null); + types.GetProperty("UpdatedTime", flag)?.SetValue(source, DateTime.Now, null); + if(context != null) + { + types.GetProperty("UpdateBy", flag)?.SetValue(source, context.GetName(), null); + types.GetProperty("Update_by", flag)?.SetValue(source, context.GetName(), null); + types.GetProperty("UpdatedBy", flag)?.SetValue(source, context.GetName(), null); + } + return source; + } + + } +} diff --git a/U8Server/Extensions/HttpContextExtension.cs b/U8Server/Extensions/HttpContextExtension.cs new file mode 100644 index 00000000..33ca0558 --- /dev/null +++ b/U8Server/Extensions/HttpContextExtension.cs @@ -0,0 +1,225 @@ +using Infrastructure.Extensions; +using System.Security.Claims; +using System.Text; +using System.Text.RegularExpressions; +using UAParser; +using ZR.Model.System; + +namespace ZR.Admin.WebApi.Extensions +{ + /// + /// HttpContext扩展类 + /// + public static partial class HttpContextExtension + { + /// + /// 是否是ajax请求 + /// + /// + /// + public static bool IsAjaxRequest(this HttpRequest request) + { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + + //return request.Headers.ContainsKey("X-Requested-With") && + // request.Headers["X-Requested-With"].Equals("XMLHttpRequest"); + + return request.Headers["X-Requested-With"] == "XMLHttpRequest" || request.Headers != null && request.Headers["X-Requested-With"] == "XMLHttpRequest"; + } + + /// + /// 获取客户端IP + /// + /// + /// + public static string GetClientUserIp(this HttpContext context) + { + if (context == null) return ""; + var result = context.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (string.IsNullOrEmpty(result)) + { + result = context.Connection.RemoteIpAddress?.ToString(); + } + if (string.IsNullOrEmpty(result)) + throw new Exception("获取IP失败"); + + if (result.Contains("::1")) + result = "127.0.0.1"; + + result = result.Replace("::ffff:", "127.0.0.1"); + result = result.Split(':')?.FirstOrDefault() ?? "127.0.0.1"; + result = IsIP(result) ? result : "127.0.0.1"; + return result; + } + + /// + /// 判断是否IP + /// + /// + /// + public static bool IsIP(string ip) + { + return Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); + } + + /// + /// 获取登录用户id + /// + /// + /// + public static long GetUId(this HttpContext context) + { + var uid = context.User.FindFirstValue(ClaimTypes.PrimarySid); + return !string.IsNullOrEmpty(uid) ? long.Parse(uid) : 0; + } + + /// + /// 获取登录用户名 + /// + /// + /// + public static string? GetName(this HttpContext context) + { + var uid = context.User?.Identity?.Name; + + return uid; + } + + /// + /// 判断是否是管理员 + /// + /// + /// + public static bool IsAdmin(this HttpContext context) + { + List roles = new(); + roles.Add($"{GlobalConstant.AdminRole}"); + roles.Add("杨琳磊"); + var userName = context.GetName(); + return roles.Contains(userName); + } + + /// + /// ClaimsIdentity + /// + /// + /// + public static IEnumerable? GetClaims(this HttpContext context) + { + return context.User?.Identities; + } + //public static int GetRole(this HttpContext context) + //{ + // var roleid = context.User.FindFirstValue(ClaimTypes.Role) ?? "0"; + + // return int.Parse(roleid); + //} + + public static string GetUserAgent(this HttpContext context) + { + return context.Request.Headers["User-Agent"]; + } + + /// + /// 获取请求令牌 + /// + /// + /// + public static string GetToken(this HttpContext context) + { + return context.Request.Headers["Authorization"]; + } + + /// + /// 获取浏览器信息 + /// + /// + /// + public static ClientInfo GetClientInfo(this HttpContext context) + { + var str = context.GetUserAgent(); + var uaParser = Parser.GetDefault(); + ClientInfo c = uaParser.Parse(str); + + return c; + } + + /// + /// 获取请求Url + /// + /// + /// + public static string? GetRequestUrl(this HttpContext context) + { + return context != null ? context.Request.Path.Value : ""; + } + + /// + /// 获取请求参数 + /// + /// + /// + public static string GetQueryString(this HttpContext context) + { + return context != null ? context.Request.QueryString.Value : ""; + } + + /// + /// 获取body请求参数 + /// + /// + /// + public static string GetBody(this HttpContext context) + { + context.Request.EnableBuffering(); + //context.Request.Body.Seek(0, SeekOrigin.Begin); + //using var reader = new StreamReader(context.Request.Body, Encoding.UTF8); + ////需要使用异步方式才能获取 + //return reader.ReadToEndAsync().Result; + string body = string.Empty; + var buffer = new MemoryStream(); + context.Request.Body.Seek(0, SeekOrigin.Begin); + context.Request.Body.CopyToAsync(buffer); + buffer.Position = 0; + try + { + using StreamReader streamReader = new(buffer, Encoding.UTF8); + body = streamReader.ReadToEndAsync().Result; + } + finally + { + buffer?.Dispose(); + } + return body; + } + + /// + /// 设置请求参数 + /// + /// + /// + public static void GetRequestValue(this HttpContext context, SysOperLog operLog) + { + string reqMethod = operLog.RequestMethod; + string param = string.Empty; + + if (HttpMethods.IsPost(reqMethod) || HttpMethods.IsPut(reqMethod) || HttpMethods.IsDelete(reqMethod)) + { + param = context.GetBody(); + param = PwdRep().Replace(param, "***"); + } + if (param.IsEmpty()) + { + param = context.GetQueryString(); + } + operLog.OperParam = param; + } + + [GeneratedRegex("(?<=\"password\":\")[^\",]*")] + private static partial Regex PwdRep(); + } + +} diff --git a/U8Server/Extensions/SqlSugarCache.cs b/U8Server/Extensions/SqlSugarCache.cs new file mode 100644 index 00000000..8f0ec30d --- /dev/null +++ b/U8Server/Extensions/SqlSugarCache.cs @@ -0,0 +1,64 @@ +namespace ZR.Admin.WebApi.Extensions +{ + public class SqlSugarCache : SqlSugar.ICacheService + { + public void Add(string key, V value) + { + //RedisServer.Cache.Set(key, value, 3600 + RedisHelper.RandomExpired(5, 30)); + CacheHelper.SetCache(key, value); + } + + public void Add(string key, V value, int cacheDurationInSeconds) + { + //RedisServer.Cache.Set(key, value, cacheDurationInSeconds); + CacheHelper.SetCaches(key, value, cacheDurationInSeconds); + } + + public bool ContainsKey(string key) + { + //return RedisServer.Cache.Exists(key); + return CacheHelper.Exists(key); + } + + public V Get(string key) + { + //return RedisServer.Cache.Get(key); + return (V)CacheHelper.Get(key); + } + + public IEnumerable GetAllKey() + { + //return RedisServer.Cache.Keys("*"); + return CacheHelper.GetCacheKeys(); + } + + public V GetOrCreate(string cacheKey, Func create, int cacheDurationInSeconds = int.MaxValue) + { + if (ContainsKey(cacheKey)) + { + var result = Get(cacheKey); + if (result == null) + { + return create(); + } + else + { + return result; + } + } + else + { + var restul = create(); + + Add(cacheKey, restul, cacheDurationInSeconds); + return restul; + } + } + + public void Remove(string key) + { + //RedisServer.Cache.Del(key); + CacheHelper.Remove(key); + } + } +} \ No newline at end of file diff --git a/U8Server/Framework/JwtUtil.cs b/U8Server/Framework/JwtUtil.cs new file mode 100644 index 00000000..584a93ec --- /dev/null +++ b/U8Server/Framework/JwtUtil.cs @@ -0,0 +1,170 @@ +using Infrastructure.Extensions; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using ZR.Admin.WebApi.Extensions; +using ZR.Model.System.Dto; +using ZR.Service.System; + +namespace ZR.Admin.WebApi.Framework +{ + /// + /// 2020-11-20 + /// + public class JwtUtil + { + /// + /// 获取用户身份信息 + /// + /// + /// + public static LoginUser GetLoginUser(HttpContext httpContext) + { + string token = httpContext.GetToken(); + + if (!string.IsNullOrEmpty(token)) + { + return ValidateJwtToken(ParseToken(token)); + } + return null; + } + + /// + /// 生成token + /// + /// + /// + /// + public static string GenerateJwtToken(List claims, JwtSettings jwtSettings) + { + var authTime = DateTime.Now; + var expiresAt = authTime.AddMinutes(jwtSettings.Expire); + var tokenHandler = new JwtSecurityTokenHandler(); + var key = Encoding.ASCII.GetBytes(jwtSettings.SecretKey); + claims.Add(new Claim("Audience", jwtSettings.Audience)); + claims.Add(new Claim("Issuer", jwtSettings.Issuer)); + + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(claims), + Issuer = jwtSettings.Issuer, + Audience = jwtSettings.Audience, + IssuedAt = authTime,//token生成时间 + Expires = expiresAt, + //NotBefore = authTime, + TokenType = "Bearer", + //对称秘钥,签名证书 + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + }; + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); + } + /// + /// 检查客户端和服务器的Token是否一样 + /// + /// + public static TokenValidationParameters ValidParameters() + { + JwtSettings jwtSettings = new(); + AppSettings.Bind("JwtSettings", jwtSettings); + + if (jwtSettings == null || jwtSettings.SecretKey.IsEmpty()) + { + throw new Exception("JwtSettings获取失败"); + } + var key = Encoding.ASCII.GetBytes(jwtSettings.SecretKey); + + var tokenDescriptor = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + ValidateIssuer = true, + ValidateAudience = true, + ValidIssuer = jwtSettings.Issuer, + ValidAudience = jwtSettings.Audience, + IssuerSigningKey = new SymmetricSecurityKey(key), + ValidateLifetime = true,//是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比 + ClockSkew = TimeSpan.FromSeconds(30) + //RequireExpirationTime = true,//过期时间 + }; + return tokenDescriptor; + } + /// + /// 从令牌中获取数据声明 + /// + /// 令牌 + /// + public static IEnumerable? ParseToken(string token) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var validateParameter = ValidParameters(); + token = token.Replace("Bearer ", ""); + try + { + tokenHandler.ValidateToken(token, validateParameter, out SecurityToken validatedToken); + + var jwtToken = tokenHandler.ReadJwtToken(token); + return jwtToken.Claims; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + // return null if validation fails + return null; + } + } + + /// + /// jwt token校验 合法用户和其权限 + /// + /// + /// + public static LoginUser? ValidateJwtToken(IEnumerable jwtToken) + { + try + { + LoginUser loginUser = null; + //todo: ClaimTypes.UserData ??? 从何而来 + var userData = jwtToken.FirstOrDefault(x => x.Type == ClaimTypes.UserData)?.Value; + if (userData != null) + { + loginUser = JsonConvert.DeserializeObject(userData); + //todo 从缓存拿到权限,如果拿不到权限说明非法用户 + var permissions = CacheService.GetUserPerms(GlobalConstant.UserPermKEY + loginUser?.UserId); + + if (loginUser?.UserName == GlobalConstant.AdminRole) + { + permissions = new List() { GlobalConstant.AdminPerm }; + } + if (permissions == null) return null; + loginUser.Permissions = permissions; + } + return loginUser; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return null; + } + } + + /// + ///组装Claims + /// + /// + /// + public static List AddClaims(LoginUser user) + { + var claims = new List() + { + new Claim(ClaimTypes.PrimarySid, user.UserId.ToString()), + new Claim(ClaimTypes.Name, user.UserName), + new Claim(ClaimTypes.UserData, JsonConvert.SerializeObject(user)) + }; + + return claims; + } + + } +} diff --git a/U8Server/GlobalUsing.cs b/U8Server/GlobalUsing.cs new file mode 100644 index 00000000..f455cbac --- /dev/null +++ b/U8Server/GlobalUsing.cs @@ -0,0 +1,7 @@ +global using Infrastructure; +global using Infrastructure.Attribute; +global using Infrastructure.Enums; +global using Infrastructure.Model; +global using Mapster; +global using Microsoft.AspNetCore.Authorization; +global using ZR.Common; diff --git a/U8Server/Middleware/GlobalExceptionMiddleware.cs b/U8Server/Middleware/GlobalExceptionMiddleware.cs new file mode 100644 index 00000000..a25ad5d1 --- /dev/null +++ b/U8Server/Middleware/GlobalExceptionMiddleware.cs @@ -0,0 +1,137 @@ +using IPTools.Core; +using Microsoft.AspNetCore.Http.Features; +using NLog; +using System.Text.Encodings.Web; +using System.Text.Json; +using ZR.Admin.WebApi.Extensions; +using ZR.Model.System; +using ZR.Service.System.IService; + +namespace ZR.Admin.WebApi.Middleware +{ + /// + /// 全局异常处理中间件 + /// 调用 app.UseMiddlewareGlobalExceptionMiddleware>(); + /// + public class GlobalExceptionMiddleware + { + private readonly RequestDelegate next; + private readonly ISysOperLogService SysOperLogService; + + static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + public GlobalExceptionMiddleware(RequestDelegate next, ISysOperLogService sysOperLog) + { + this.next = next; + this.SysOperLogService = sysOperLog; + } + + public async Task Invoke(HttpContext context) + { + try + { + await next(context); + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex); + } + } + + private async Task HandleExceptionAsync(HttpContext context, Exception ex) + { + NLog.LogLevel logLevel = NLog.LogLevel.Info; + int code = (int)ResultCode.GLOBAL_ERROR; + string msg; + string error = string.Empty; + //自定义异常 + if (ex is CustomException customException) + { + code = customException.Code; + msg = customException.Message; + error = customException.LogMsg; + } + else if (ex is ArgumentException)//参数异常 + { + code = (int)ResultCode.PARAM_ERROR; + msg = ex.Message; + } + else + { + msg = "服务器好像出了点问题,请联系系统管理员..."; + error = $"{ex.Message}"; + logLevel = NLog.LogLevel.Error; + context.Response.StatusCode = 500; + } + var options = new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true + }; + + ApiResult apiResult = new(code, msg); + string responseResult = JsonSerializer.Serialize(apiResult, options).ToLower(); + string ip = HttpContextExtension.GetClientUserIp(context); + var ip_info = IpTool.Search(ip); + + SysOperLog sysOperLog = new() + { + Status = 1, + OperIp = ip, + OperUrl = HttpContextExtension.GetRequestUrl(context), + RequestMethod = context.Request.Method, + JsonResult = responseResult, + ErrorMsg = string.IsNullOrEmpty(error) ? msg : error, + OperName = HttpContextExtension.GetName(context), + OperLocation = ip_info.Province + " " + ip_info.City, + OperTime = DateTime.Now + }; + HttpContextExtension.GetRequestValue(context, sysOperLog); + var endpoint = GetEndpoint(context); + if (endpoint != null) + { + var logAttribute = endpoint.Metadata.GetMetadata(); + if (logAttribute != null) + { + sysOperLog.BusinessType = (int)logAttribute.BusinessType; + sysOperLog.Title = logAttribute?.Title; + sysOperLog.OperParam = logAttribute.IsSaveRequestData ? sysOperLog.OperParam : ""; + sysOperLog.JsonResult = logAttribute.IsSaveResponseData ? sysOperLog.JsonResult : ""; + } + } + LogEventInfo ei = new(logLevel, "GlobalExceptionMiddleware", error) + { + Exception = ex, + Message = error + }; + ei.Properties["status"] = 1;//走正常返回都是通过走GlobalExceptionFilter不通过 + ei.Properties["jsonResult"] = responseResult; + ei.Properties["requestParam"] = sysOperLog.OperParam; + ei.Properties["user"] = sysOperLog.OperName; + + Logger.Log(ei); + context.Response.ContentType = "text/json;charset=utf-8"; + await context.Response.WriteAsync(responseResult, System.Text.Encoding.UTF8); + + string errorMsg = $"> 操作人:{sysOperLog.OperName}" + + $"\n> 操作地区:{sysOperLog.OperIp}({sysOperLog.OperLocation})" + + $"\n> 操作模块:{sysOperLog.Title}" + + $"\n> 操作地址:{sysOperLog.OperUrl}" + + $"\n> 错误信息:{msg}\n\n> {error}"; + + WxNoticeHelper.SendMsg("系统出错", errorMsg, "", WxNoticeHelper.MsgType.markdown); + SysOperLogService.InsertOperlog(sysOperLog); + } + + public static Endpoint GetEndpoint(HttpContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + return context.Features.Get()?.Endpoint; + } + } +} diff --git a/U8Server/Program.cs b/U8Server/Program.cs new file mode 100644 index 00000000..138924fe --- /dev/null +++ b/U8Server/Program.cs @@ -0,0 +1,30 @@ +using Nacos.AspNetCore.V2; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +// +builder.Configuration.AddNacosV2Configuration(builder.Configuration.GetSection("NacosConfig")); +//ע +builder.Services.AddNacosAspNet(builder.Configuration); + + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/U8Server/Properties/launchSettings.json b/U8Server/Properties/launchSettings.json new file mode 100644 index 00000000..736e4682 --- /dev/null +++ b/U8Server/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:62659", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5222", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/U8Server/U8Server.csproj b/U8Server/U8Server.csproj new file mode 100644 index 00000000..ff34cefd --- /dev/null +++ b/U8Server/U8Server.csproj @@ -0,0 +1,28 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + diff --git a/U8Server/Util/GetSign.cs b/U8Server/Util/GetSign.cs new file mode 100644 index 00000000..210e9d0c --- /dev/null +++ b/U8Server/Util/GetSign.cs @@ -0,0 +1,19 @@ +using System.Globalization; +using U8Server.Extensions; + +namespace U8Server.Util +{ + public class GetSign + { + public static string GetBy16Md5() + { + string appId = "gN9yId!!lfwaRoi3"; + string appSecret = "xr35$IQAutBRX1UYhgOUY#CqChI#Y3b$"; + string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture); + string key = $"{appSecret}{appId}{timestamp}{appSecret}"; + string sign = key.ToMD5Encrypt(uppercase: false, is16bit: true) + ?? throw new InvalidOperationException("MD5加密失败"); + return sign; + } + } +} diff --git a/U8Server/Util/MD5Encryption.cs b/U8Server/Util/MD5Encryption.cs new file mode 100644 index 00000000..3237061b --- /dev/null +++ b/U8Server/Util/MD5Encryption.cs @@ -0,0 +1,110 @@ +using System.Security.Cryptography; +using System.Text; + +namespace U8Server.Util +{ + // + // 摘要: + // MD5 加密 + public static class MD5Encryption + { + // + // 摘要: + // MD5 比较 + // + // 参数: + // text: + // 加密文本 + // + // hash: + // MD5 字符串 + // + // uppercase: + // 是否输出大写加密,默认 false + // + // is16: + // 是否输出 16 位 + // + // 返回结果: + // bool + public static bool Compare(string text, string hash, bool uppercase = false, bool is16 = false) + { + return Compare(Encoding.UTF8.GetBytes(text), hash, uppercase, is16); + } + + // + // 摘要: + // MD5 加密 + // + // 参数: + // text: + // 加密文本 + // + // uppercase: + // 是否输出大写加密,默认 false + // + // is16: + // 是否输出 16 位 + public static string Encrypt(string text, bool uppercase = false, bool is16 = false) + { + return Encrypt(Encoding.UTF8.GetBytes(text), uppercase, is16); + } + + // + // 摘要: + // MD5 加密 + // + // 参数: + // bytes: + // 字节数组 + // + // uppercase: + // 是否输出大写加密,默认 false + // + // is16: + // 是否输出 16 位 + public static string Encrypt(byte[] bytes, bool uppercase = false, bool is16 = false) + { + byte[] array = MD5.HashData(bytes); + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < array.Length; i++) + { + stringBuilder.Append(array[i].ToString("x2")); + } + + string text = stringBuilder.ToString(); + string text2 = ((!is16) ? text : text.Substring(8, 16)); + if (uppercase) + { + return text2.ToUpper(); + } + + return text2; + } + + // + // 摘要: + // MD5 比较 + // + // 参数: + // bytes: + // 字节数组 + // + // hash: + // MD5 字符串 + // + // uppercase: + // 是否输出大写加密,默认 false + // + // is16: + // 是否输出 16 位 + // + // 返回结果: + // bool + public static bool Compare(byte[] bytes, string hash, bool uppercase = false, bool is16 = false) + { + string value = Encrypt(bytes, uppercase, is16); + return hash.Equals(value, StringComparison.OrdinalIgnoreCase); + } + } +} diff --git a/U8Server/Util/StringExtensions.cs b/U8Server/Util/StringExtensions.cs new file mode 100644 index 00000000..b35545a5 --- /dev/null +++ b/U8Server/Util/StringExtensions.cs @@ -0,0 +1,20 @@ +using U8Server.Util; + +namespace U8Server.Extensions +{ + public static class StringExtensions + { + /// + /// 对字符串进行 MD5 加密(扩展方法) + /// + /// 待加密的字符串 + /// 是否返回大写形式 + /// 是否返回 16 位 MD5(默认 32 位) + /// 加密后的字符串 + public static string ToMD5Encrypt(this string input, bool uppercase = false, bool is16bit = false) + { + // 直接调用现有的 MD5Encryption.Encrypt 方法 + return MD5Encryption.Encrypt(input, uppercase, is16bit); + } + } +} \ No newline at end of file diff --git a/U8Server/appsettings.Development.json b/U8Server/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/U8Server/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/U8Server/appsettings.json b/U8Server/appsettings.json new file mode 100644 index 00000000..98ff6e90 --- /dev/null +++ b/U8Server/appsettings.json @@ -0,0 +1,21 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "NacosConfig": { + "Listeners": [ + { + "Optional": false, + "DataId": "U8ServerConfig", + "Group": "DEFAULT_GROUP" + } + ], + // 服务注册必填参数 + "ServerAddresses": [ "http://127.0.0.1:8848" ], // Nacos 服务器地址 + "Namespace": ""// 命名空间(默认 public) + }, + "AllowedHosts": "*" +} diff --git a/ZR.Admin.WebApi/Program.cs b/ZR.Admin.WebApi/Program.cs index 7c485961..09b63f7b 100644 --- a/ZR.Admin.WebApi/Program.cs +++ b/ZR.Admin.WebApi/Program.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; +using Nacos.AspNetCore.V2; using System.Text.Json.Serialization; using ZR.Admin.WebApi.AutoMapperProfile; using ZR.Admin.WebApi.Extensions; @@ -14,9 +15,12 @@ using ZR.Common.Cache; using ZR.Common.MqttHelper; var builder = WebApplication.CreateBuilder(args); - // Add services to the container. builder.Services.AddControllers(); +//配置中心 +//builder.Configuration.AddNacosV2Configuration(builder.Configuration.GetSection("NacosConfig")); +//服务注册 +//builder.Services.AddNacosAspNet(builder.Configuration); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); diff --git a/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj b/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj index 749a6506..6a0efb6a 100644 --- a/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj +++ b/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj @@ -36,6 +36,9 @@ + + + diff --git a/ZR.Admin.WebApi/appsettings.json b/ZR.Admin.WebApi/appsettings.json index d1643bae..26daa0a4 100644 --- a/ZR.Admin.WebApi/appsettings.json +++ b/ZR.Admin.WebApi/appsettings.json @@ -16,5 +16,17 @@ "QualityOfServiceLevel": "AtLeastOnce" } ] + }, + "NacosConfig": { + "Listeners": [ + { + "Optional": false, + "DataId": "MesServerConfig", + "Group": "DEFAULT_GROUP" + } + ], + // 服务注册必填参数 + "ServerAddresses": [ "http://127.0.0.1:8848" ], // Nacos 服务器地址 + "Namespace": "" // 命名空间(默认 public) } } \ No newline at end of file diff --git a/ZRAdmin.sln b/ZRAdmin.sln index 633d2abc..2538cc51 100644 --- a/ZRAdmin.sln +++ b/ZRAdmin.sln @@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZR.Repository", "ZR.Reposit EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZR.CodeGenerator", "ZR.CodeGenerator\ZR.CodeGenerator.csproj", "{B353DE0B-12C6-4C15-909A-DB68F71D5AE9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "U8Server", "U8Server\U8Server.csproj", "{B1F5C2C3-8E09-4140-A573-C19FD2E33ADE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -57,6 +59,10 @@ Global {B353DE0B-12C6-4C15-909A-DB68F71D5AE9}.Debug|Any CPU.Build.0 = Debug|Any CPU {B353DE0B-12C6-4C15-909A-DB68F71D5AE9}.Release|Any CPU.ActiveCfg = Release|Any CPU {B353DE0B-12C6-4C15-909A-DB68F71D5AE9}.Release|Any CPU.Build.0 = Release|Any CPU + {B1F5C2C3-8E09-4140-A573-C19FD2E33ADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1F5C2C3-8E09-4140-A573-C19FD2E33ADE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1F5C2C3-8E09-4140-A573-C19FD2E33ADE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1F5C2C3-8E09-4140-A573-C19FD2E33ADE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE