仓储-build
This commit is contained in:
225
src/WMS.Web.Repositories/Configuration/DbContextExtensions.cs
Normal file
225
src/WMS.Web.Repositories/Configuration/DbContextExtensions.cs
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace WMS.Web.Repositories.Configuration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// db上下文扩展类
|
||||||
|
/// </summary>
|
||||||
|
public static class DbContextExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<T> SqlQuery<T>(this DatabaseFacade facade, string sql, bool isAlias = false, params object[] parameters) where T : class, new()
|
||||||
|
{
|
||||||
|
var dt = SqlQuery(facade, sql, parameters);
|
||||||
|
if (isAlias)
|
||||||
|
return dt.ToEnumerableAlias<T>();
|
||||||
|
return dt.ToEnumerable<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<IEnumerable<T>> SqlQueryAsync<T>(this DatabaseFacade facade, string sql, bool isAlias = false, params object[] parameters) where T : class, new()
|
||||||
|
{
|
||||||
|
return await Task.Run(() =>
|
||||||
|
{
|
||||||
|
var dt = SqlQuery(facade, sql, parameters);
|
||||||
|
if (isAlias)
|
||||||
|
return dt.ToEnumerableAlias<T>();
|
||||||
|
return dt.ToEnumerable<T>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<T> ToEnumerable<T>(this DataTable dt) where T : class, new()
|
||||||
|
{
|
||||||
|
var propertyInfos = typeof(T).GetProperties();
|
||||||
|
var ts = new T[dt.Rows.Count];
|
||||||
|
var i = 0;
|
||||||
|
foreach (DataRow row in dt.Rows)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var t = new T();
|
||||||
|
foreach (var p in propertyInfos)
|
||||||
|
if (dt.Columns.IndexOf(p.Name) != -1 && row[p.Name] != DBNull.Value)
|
||||||
|
p.SetValue(t, row[p.Name], null);
|
||||||
|
|
||||||
|
ts[i] = t;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对象属性别名的映射关系
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="dt"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IEnumerable<T> ToEnumerableAlias<T>(this DataTable dt) where T : class, new()
|
||||||
|
{
|
||||||
|
var propertyInfos = typeof(T).GetProperties();
|
||||||
|
var ts = new T[dt.Rows.Count];
|
||||||
|
var i = 0;
|
||||||
|
foreach (DataRow row in dt.Rows)
|
||||||
|
{
|
||||||
|
var t = new T();
|
||||||
|
foreach (var p in propertyInfos)
|
||||||
|
{
|
||||||
|
var attrs = p.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.Schema.ColumnAttribute), true);
|
||||||
|
if (attrs.Length > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var name = ((System.ComponentModel.DataAnnotations.Schema.ColumnAttribute)attrs[0]).Name;
|
||||||
|
if (dt.Columns.IndexOf(name) != -1 && row[name] != DBNull.Value)
|
||||||
|
p.SetValue(t, row[name], null);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ts[i] = t;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DataTable SqlQuery(this DatabaseFacade facade, string sql, params object[] parameters)
|
||||||
|
{
|
||||||
|
var cmd = CreateCommand(facade, sql, out var conn, parameters);
|
||||||
|
var reader = cmd.ExecuteReader();
|
||||||
|
var dt = new DataTable();
|
||||||
|
dt.Load(reader);
|
||||||
|
reader.Close();
|
||||||
|
conn.Close();
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DbCommand CreateCommand(DatabaseFacade facade, string sql, out DbConnection dbConn, params object[] parameters)
|
||||||
|
{
|
||||||
|
var conn = facade.GetDbConnection();
|
||||||
|
dbConn = conn;
|
||||||
|
conn.Open();
|
||||||
|
var cmd = conn.CreateCommand();
|
||||||
|
if (facade.IsMySql())
|
||||||
|
{
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
CombineParamsMySql(ref cmd, parameters);
|
||||||
|
}
|
||||||
|
if (facade.IsSqlServer())
|
||||||
|
{
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
CombineParams(ref cmd, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CombineParams(ref DbCommand command, params object[] parameters)
|
||||||
|
{
|
||||||
|
if (parameters != null)
|
||||||
|
foreach (SqlParameter parameter in parameters)
|
||||||
|
{
|
||||||
|
if (!parameter.ParameterName.Contains("@"))
|
||||||
|
parameter.ParameterName = $"@{parameter.ParameterName}";
|
||||||
|
command.Parameters.Add(parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CombineParamsMySql(ref DbCommand command, params object[] parameters)
|
||||||
|
{
|
||||||
|
if (parameters != null)
|
||||||
|
foreach (MySqlConnector.MySqlParameter parameter in parameters)
|
||||||
|
{
|
||||||
|
if (!parameter.ParameterName.Contains("@"))
|
||||||
|
parameter.ParameterName = $"@{parameter.ParameterName}";
|
||||||
|
command.Parameters.Add(parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ExecuteSqlCommand(this DatabaseFacade facade, string sql, params object[] parameters)
|
||||||
|
{
|
||||||
|
var cmd = CreateCommand(facade, sql, out var conn, parameters);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var msg = ex.Message;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static IQueryable<T> SortBy<T>(this IQueryable<T> source, string sortExpression)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("source");
|
||||||
|
}
|
||||||
|
|
||||||
|
string sortDirection = String.Empty;
|
||||||
|
string propertyName = String.Empty;
|
||||||
|
|
||||||
|
sortExpression = sortExpression.Trim();
|
||||||
|
int spaceIndex = sortExpression.Trim().IndexOf(" ");
|
||||||
|
if (spaceIndex < 0)
|
||||||
|
{
|
||||||
|
propertyName = sortExpression;
|
||||||
|
sortDirection = "ASC";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
propertyName = sortExpression.Substring(0, spaceIndex);
|
||||||
|
sortDirection = sortExpression.Substring(spaceIndex + 1).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String.IsNullOrEmpty(propertyName))
|
||||||
|
{
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty);
|
||||||
|
MemberExpression property = Expression.Property(parameter, propertyName);
|
||||||
|
LambdaExpression lambda = Expression.Lambda(property, parameter);
|
||||||
|
|
||||||
|
string methodName = (sortDirection == "ASC") ? "OrderBy" : "OrderByDescending";
|
||||||
|
|
||||||
|
Expression methodCallExpression = Expression.Call(typeof(Queryable), methodName,
|
||||||
|
new Type[] { source.ElementType, property.Type },
|
||||||
|
source.Expression, Expression.Quote(lambda));
|
||||||
|
|
||||||
|
return source.Provider.CreateQuery<T>(methodCallExpression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/WMS.Web.Repositories/Configuration/Log/EFCoreLogger.cs
Normal file
39
src/WMS.Web.Repositories/Configuration/Log/EFCoreLogger.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace WMS.Web.Repositories.Configuration.Log
|
||||||
|
{
|
||||||
|
public class EFCoreLogger : ILogger
|
||||||
|
{
|
||||||
|
private readonly string categoryName;
|
||||||
|
|
||||||
|
public EFCoreLogger(string categoryName) => this.categoryName = categoryName;
|
||||||
|
|
||||||
|
public bool IsEnabled(LogLevel logLevel) => true;
|
||||||
|
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||||
|
{
|
||||||
|
if (categoryName == "Microsoft.EntityFrameworkCore.Database.Command" && logLevel == LogLevel.Information)
|
||||||
|
{
|
||||||
|
var logContent = formatter(state, exception);
|
||||||
|
var str = logContent.ToLower();
|
||||||
|
if ((str.Contains("update") || str.Contains("delete") || str.Contains("insert")) &&
|
||||||
|
(str.Contains("t_sub_ppbom") || str.Contains("t_sub_ppbomentry") || str.Contains("t_sub_ppbomentry_l")
|
||||||
|
|| str.Contains("t_prd_ppbom") || str.Contains("t_prd_ppbomentry") || str.Contains("t_prd_ppbomentry_l")
|
||||||
|
|| str.Contains("t_eng_bom") || str.Contains("t_eng_bomchild")))
|
||||||
|
{
|
||||||
|
if (!Directory.Exists("D:/Logs"))
|
||||||
|
Directory.CreateDirectory("D:/Logs");
|
||||||
|
|
||||||
|
logContent = "\r\n\r\n" + DateTime.Now + "--" + logContent;
|
||||||
|
//没有文件会自动创建,有就追加
|
||||||
|
System.IO.File.AppendAllText("D:/Logs/增删改记录.txt", logContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public IDisposable BeginScope<TState>(TState state) => null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace WMS.Web.Repositories.Configuration.Log
|
||||||
|
{
|
||||||
|
public class EFCoreLoggerProvider : ILoggerProvider
|
||||||
|
{
|
||||||
|
public ILogger CreateLogger(string categoryName) => new EFCoreLogger(categoryName);
|
||||||
|
public void Dispose() { }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Debug;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using WMS.Web.Repositories.Configuration.Log;
|
||||||
|
|
||||||
|
namespace WMS.Web.Repositories.Configuration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// db上下文
|
||||||
|
/// </summary>
|
||||||
|
public class RepositoryDbContext : DbContext
|
||||||
|
{
|
||||||
|
[Obsolete]
|
||||||
|
public readonly LoggerFactory LoggerFactory = new LoggerFactory(new[] { new DebugLoggerProvider() });
|
||||||
|
public RepositoryDbContext(DbContextOptions<RepositoryDbContext> options) : base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
LoggerFactory.AddProvider(new EFCoreLoggerProvider());
|
||||||
|
base.OnConfiguring(optionsBuilder);
|
||||||
|
optionsBuilder.UseLoggerFactory(LoggerFactory).EnableSensitiveDataLogging();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
187
src/WMS.Web.Repositories/DependencyInjection/AppBuilder.cs
Normal file
187
src/WMS.Web.Repositories/DependencyInjection/AppBuilder.cs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using WMS.Web.Core.Help;
|
||||||
|
using WMS.Web.Domain.IService.Public;
|
||||||
|
using WMS.Web.Domain.Mappers;
|
||||||
|
using WMS.Web.Domain.Options;
|
||||||
|
using WMS.Web.Domain.Services.Public;
|
||||||
|
|
||||||
|
namespace WMS.Web.Repositories.DependencyInjection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// App服务
|
||||||
|
/// </summary>
|
||||||
|
public class AppBuilder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 服务集合
|
||||||
|
/// </summary>
|
||||||
|
public IServiceCollection Services { get; }
|
||||||
|
|
||||||
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
public IWebHostEnvironment WebHostEnvironment { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// asf 服务
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
public AppBuilder(IServiceCollection services, IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
|
||||||
|
{
|
||||||
|
Services = services;
|
||||||
|
Configuration = configuration;
|
||||||
|
WebHostEnvironment = webHostEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编译服务
|
||||||
|
/// </summary>
|
||||||
|
public void Build()
|
||||||
|
{
|
||||||
|
this.AddCors();
|
||||||
|
this.InitRedis();
|
||||||
|
this.AddSwagger();
|
||||||
|
this.AddOther();
|
||||||
|
this.AddConfigOptions();
|
||||||
|
this.AddServiceRepositories();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 其它功能注入:AutoMapper等其它
|
||||||
|
/// </summary>
|
||||||
|
private void AddOther()
|
||||||
|
{
|
||||||
|
Services.AddMvc().AddNewtonsoftJson(opt =>
|
||||||
|
{
|
||||||
|
//// 忽略循环引用
|
||||||
|
//opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||||
|
opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
||||||
|
//// 不使用驼峰
|
||||||
|
//opt.SerializerSettings.ContractResolver = new DefaultContractResolver();
|
||||||
|
});
|
||||||
|
|
||||||
|
//AutoMapper映射关系
|
||||||
|
Services.AddAutoMapper(typeof(AppMapper).Assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 跨域注入
|
||||||
|
/// </summary>
|
||||||
|
private void AddCors()
|
||||||
|
{
|
||||||
|
// 全局跨域注入
|
||||||
|
Services.AddCors(opt =>
|
||||||
|
{
|
||||||
|
string[] urls = Configuration.GetSection("AllowedCores").Value.Split(',');
|
||||||
|
opt.AddPolicy("AllowAllOrigin", builder =>
|
||||||
|
{
|
||||||
|
|
||||||
|
builder.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowCredentials()
|
||||||
|
.WithOrigins(urls);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化redis
|
||||||
|
/// </summary>
|
||||||
|
private void InitRedis()
|
||||||
|
{
|
||||||
|
var option = Configuration.GetSection("AppOptions").Get<AppOptions>();
|
||||||
|
var option_soa = Configuration.GetSection("SoaOptions").Get<SoaOptions>();
|
||||||
|
//初始化redis
|
||||||
|
RedisClient.redisClient.InitConnect(option.RedisConnectionString);
|
||||||
|
Services.AddHttpClient("ops_client", c =>
|
||||||
|
{
|
||||||
|
c.BaseAddress = new Uri(option_soa.Url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册swagger
|
||||||
|
/// </summary>
|
||||||
|
private void AddSwagger()
|
||||||
|
{
|
||||||
|
if (WebHostEnvironment.IsDevelopment())
|
||||||
|
{
|
||||||
|
// 注册Swagger服务
|
||||||
|
Services.AddSwaggerGen(c =>
|
||||||
|
{
|
||||||
|
// 添加文档信息
|
||||||
|
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
|
||||||
|
{
|
||||||
|
Title = "WMS",
|
||||||
|
Version = "v1",
|
||||||
|
Description = "WMS-Api"
|
||||||
|
});
|
||||||
|
#region 读取xml信息
|
||||||
|
// 使用反射获取xml文件。并构造出文件的路径
|
||||||
|
//// 启用xml注释. 该方法第二个参数启用控制器的注释,默认为false.
|
||||||
|
foreach (var file in Directory.GetFiles(AppContext.BaseDirectory, "WMS.*.xml")) c.IncludeXmlComments(file, true);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 启用swagger验证功能
|
||||||
|
//添加一个必须的全局安全信息,和AddSecurityDefinition方法指定的方案名称一致即可,CoreAPI。
|
||||||
|
var securit = new OpenApiSecurityRequirement()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference=new OpenApiReference { Type=ReferenceType.SecurityScheme,Id= "WMSAPI" }
|
||||||
|
},
|
||||||
|
new string[] { }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
c.AddSecurityRequirement(securit);
|
||||||
|
c.AddSecurityDefinition("WMSAPI", new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可",
|
||||||
|
Name = "Authorization",//jwt默认的参数名称
|
||||||
|
In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
|
||||||
|
Type = SecuritySchemeType.ApiKey
|
||||||
|
});
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 系统配置
|
||||||
|
/// </summary>
|
||||||
|
private void AddConfigOptions()
|
||||||
|
{
|
||||||
|
//系统配置注册
|
||||||
|
Services.AddOptions<AppOptions>();
|
||||||
|
Services.Configure<AppOptions>(Configuration.GetSection("AppOptions"));
|
||||||
|
Services.AddOptions<SoaOptions>();
|
||||||
|
Services.Configure<SoaOptions>(Configuration.GetSection("SoaOptions"));
|
||||||
|
Services.AddOptions<HttpOptions>();
|
||||||
|
Services.Configure<HttpOptions>(Configuration.GetSection("HttpOptions"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注入服务层
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
private void AddServiceRepositories()
|
||||||
|
{
|
||||||
|
Services.AddSingleton<RedisClientService>();
|
||||||
|
Services.AddTransient<HttpClientHelp>();
|
||||||
|
//注入服务
|
||||||
|
Services.AddTransient<ILoginService, LoginService>();
|
||||||
|
Services.AddTransient<IHttpClientService, HttpClientService>();
|
||||||
|
Services.AddTransient<ISingleDataService, SingleDataService>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using WMS.Web.Repositories.Configuration;
|
||||||
|
using WMS.Web.Repositories.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
{
|
||||||
|
public static class AppBuilderExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
/// <param name="configureDbContext"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static AppBuilder AddDbContext(this AppBuilder builder, Action<DbContextOptionsBuilder> configureDbContext)
|
||||||
|
{
|
||||||
|
builder.Services.AddDbContext<RepositoryDbContext>(configureDbContext);
|
||||||
|
builder.Services.AddRepositories();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注入仓储层
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
private static void AddRepositories(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WMS.Web.Core.Dto.Login;
|
||||||
|
using WMS.Web.Domain.IService.Public;
|
||||||
|
using WMS.Web.Domain.Services.Public;
|
||||||
|
|
||||||
|
namespace WMS.Web.Repositories.DependencyInjection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// token 黑名单中间件
|
||||||
|
/// </summary>
|
||||||
|
public class AuthorizationTokenSecurityPolicy
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly IMemoryCache _cache;
|
||||||
|
private DateTime _refreshTime = DateTime.Now.AddYears(-5);
|
||||||
|
private readonly RedisClientService _redisClientService;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// token黑名单中间件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <param name="cache"></param>
|
||||||
|
public AuthorizationTokenSecurityPolicy(RequestDelegate next, IMemoryCache cache, RedisClientService redisClientService)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_cache = cache;
|
||||||
|
_redisClientService = redisClientService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task Invoke(HttpContext context, ILoginService loginService)
|
||||||
|
{
|
||||||
|
string authorization = context.Request.Headers["Authorization"];
|
||||||
|
string path = context.Request.Path.Value.ToLower();
|
||||||
|
string[] pathlist = path.Split('/');
|
||||||
|
bool isLogin = pathlist.Where(x => x == "login" || x.ToLower() == "heart" || x.ToLower() == "test").Any();
|
||||||
|
if (isLogin)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 200;
|
||||||
|
await _next(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(authorization))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 401;
|
||||||
|
var result = JsonConvert.SerializeObject(new { status = 401, data = string.Empty, message = "授权失败,请重新登录" });
|
||||||
|
context.Response.ContentType = "application/json;charset=utf-8";
|
||||||
|
await context.Response.WriteAsync(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string token = string.Empty;
|
||||||
|
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
token = authorization.Substring("Bearer ".Length).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断前端过来的token
|
||||||
|
if (string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 401;
|
||||||
|
var result = JsonConvert.SerializeObject(new { status = 401, data = string.Empty, message = "授权失败,请重新登录" });
|
||||||
|
context.Response.ContentType = "application/json;charset=utf-8";
|
||||||
|
await context.Response.WriteAsync(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//1.验证是否有登录数据缓存
|
||||||
|
var logininfo = _redisClientService.GetStringKey<LoginInDto>($"ops_login_{token}");
|
||||||
|
if (logininfo == null)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 401;
|
||||||
|
var result = JsonConvert.SerializeObject(new { status = 401, data = string.Empty, message = "授权失败,请重新登录" });
|
||||||
|
context.Response.ContentType = "application/json;charset=utf-8";
|
||||||
|
await context.Response.WriteAsync(result);
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//2.判断缓存的token 过期时间
|
||||||
|
if (!string.IsNullOrEmpty(logininfo.TokenInfo.Token) && logininfo.TokenInfo.Expired.AddMinutes(-10) <= DateTime.Now)
|
||||||
|
{
|
||||||
|
var refreshtokeninfo = RefreshToken(logininfo.TokenInfo.Token, logininfo.TokenInfo.RefreshToken, loginService);
|
||||||
|
|
||||||
|
if (!refreshtokeninfo)
|
||||||
|
{
|
||||||
|
RemoveRedisLoginInfo(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//3.验证检查是否有黑名单的token缓存
|
||||||
|
string blacktoken = _redisClientService.GetStringKey($"ops_black_token_{logininfo.UserInfo.UcId}");
|
||||||
|
if (string.IsNullOrEmpty(blacktoken))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 200;
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RemoveRedisLoginInfo(token);
|
||||||
|
context.Response.StatusCode = 403;
|
||||||
|
var result = JsonConvert.SerializeObject(new { status = 403, data = string.Empty, message = "拒绝用户证书试图访问此web站点,请与站点管理员联系以建立用户证书权限" });
|
||||||
|
context.Response.ContentType = "application/json;charset=utf-8";
|
||||||
|
await context.Response.WriteAsync(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (context.Response.StatusCode == 401 || context.Response.StatusCode == 403)
|
||||||
|
{
|
||||||
|
//调用单点的退出接口
|
||||||
|
var dto = new LoginOutDto()
|
||||||
|
{
|
||||||
|
UcId = logininfo.UserInfo.UcId.ToString(),
|
||||||
|
SessionId = logininfo.UserInfo.SeesionId,
|
||||||
|
Token = logininfo.TokenInfo.Token,
|
||||||
|
AccessToken = logininfo.TokenInfo.PhpToken,
|
||||||
|
ExpiresIn = logininfo.TokenInfo.Expired
|
||||||
|
|
||||||
|
};
|
||||||
|
var res = await loginService.LoginOutAsync(dto);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 过期,或者失效,移出token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
private void RemoveRedisLoginInfo(string token)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
var info = _redisClientService.GetStringKey<LoginInDto>($"ops_login_{token}");
|
||||||
|
if (info != null)
|
||||||
|
{
|
||||||
|
_redisClientService.RemoveStringKey($"ops_login_{token}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="RefreshToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private bool RefreshToken(string OldToken, string RefreshToken, ILoginService _loginService)
|
||||||
|
{
|
||||||
|
var res = _loginService.RefreshToken(OldToken, RefreshToken);
|
||||||
|
if (!res.Result.Success)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace WMS.Web.Repositories.DependencyInjection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 统一全局错误拦截
|
||||||
|
/// </summary>
|
||||||
|
public class ErrorHandlingMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate next;
|
||||||
|
private readonly ILogger<ErrorHandlingMiddleware> _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 统一错误处理中间件
|
||||||
|
/// </summary>
|
||||||
|
public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
|
||||||
|
{
|
||||||
|
this.next = next;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
private string bodyStr = "";
|
||||||
|
/// <summary>
|
||||||
|
/// 激活
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//默认 请求body只能读取一次 所以在这里需要手动把他设置成读取多次 并且 不能使用using 释放
|
||||||
|
context.Request.EnableBuffering();
|
||||||
|
StreamReader requestReader = new StreamReader(context.Request.Body, Encoding.UTF8);
|
||||||
|
bodyStr = await requestReader.ReadToEndAsync();
|
||||||
|
context.Request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
await next(context);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// var statusCode = context.Response.StatusCode;
|
||||||
|
var statusCode = 500;
|
||||||
|
if (ex is ArgumentException) statusCode = 200;
|
||||||
|
|
||||||
|
_logger.LogError($"统一拦截异常处理: {ex.Message}, StackTrace:{ex.StackTrace},Path:{context.Request.Path},Parame:{bodyStr}");
|
||||||
|
|
||||||
|
await HandleExceptionAsync(context, statusCode, "服务器错误,不能执行此请求,请稍后重试,若问题一直存在,请与站点管理员联系");
|
||||||
|
// await HandleExceptionAsync(context, statusCode, "服务器错误");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var statusCode = context.Response.StatusCode;
|
||||||
|
var msg = "";
|
||||||
|
// if (statusCode == 401)
|
||||||
|
// {
|
||||||
|
// msg = "未授权";
|
||||||
|
// }
|
||||||
|
if (statusCode == 404)
|
||||||
|
msg = "服务器暂无响应,请稍后重试,若问题一直存在,请与站点管理员联系";
|
||||||
|
else if (statusCode == 502) msg = "网关出错,请与站点管理员联系";
|
||||||
|
// else if (statusCode != 200)
|
||||||
|
// {
|
||||||
|
// msg = "未知错误";
|
||||||
|
// }
|
||||||
|
if (!string.IsNullOrWhiteSpace(msg)) await HandleExceptionAsync(context, statusCode, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task HandleExceptionAsync(HttpContext context, int statusCode, string msg)
|
||||||
|
{
|
||||||
|
string data = null;
|
||||||
|
var result = JsonConvert.SerializeObject(new { status = statusCode, result = data, message = msg });
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
|
context.Response.ContentType = "application/json;charset=utf-8";
|
||||||
|
return context.Response.WriteAsync(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using WMS.Web.Repositories.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 添加app框架
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="startupAction"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddApp(this IServiceCollection services, IConfiguration configuration, IWebHostEnvironment webHostEnvironment, Action<AppBuilder> startupAction)
|
||||||
|
{
|
||||||
|
services.AddAppCore(configuration, webHostEnvironment, startupAction);
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加app核心服务
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="startupAction">ASF启动配置函数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static IServiceCollection AddAppCore(this IServiceCollection services, IConfiguration configuration, IWebHostEnvironment webHostEnvironment, Action<AppBuilder> startupAction)
|
||||||
|
{
|
||||||
|
var builder = new AppBuilder(services, configuration, webHostEnvironment);
|
||||||
|
startupAction?.Invoke(builder);
|
||||||
|
builder.Build();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,11 +4,6 @@
|
|||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Configuration\" />
|
|
||||||
<Folder Include="DependencyInjection\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
|
||||||
<PackageReference Include="BarcodeLib" Version="2.4.0" />
|
<PackageReference Include="BarcodeLib" Version="2.4.0" />
|
||||||
|
|||||||
Reference in New Issue
Block a user