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