You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
14 KiB
387 lines
14 KiB
using System;
|
|
using System.Data;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Cors;
|
|
using Microsoft.AspNetCore.DataProtection;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Http.Features;
|
|
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
using Microsoft.EntityFrameworkCore.Storage;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Options;
|
|
using Microsoft.OpenApi.Models;
|
|
using NUglify.Helpers;
|
|
using StackExchange.Redis;
|
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
using Volo.Abp;
|
|
using Volo.Abp.Application.Dtos;
|
|
using Volo.Abp.AspNetCore.ExceptionHandling;
|
|
using Volo.Abp.AspNetCore.Mvc;
|
|
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
|
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
|
|
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling;
|
|
using Volo.Abp.AutoMapper;
|
|
using Volo.Abp.Caching;
|
|
using Volo.Abp.Content;
|
|
using Volo.Abp.Data;
|
|
using Volo.Abp.EntityFrameworkCore;
|
|
using Volo.Abp.Guids;
|
|
using Volo.Abp.Localization;
|
|
using Volo.Abp.Modularity;
|
|
using Volo.Abp.Uow;
|
|
using Win_in.Sfs.Shared.Application;
|
|
using Win_in.Sfs.Shared.Application.Contracts.ExportAndImport;
|
|
using Win_in.Sfs.Shared.Domain.Shared;
|
|
|
|
namespace Win_in.Sfs.Shared.Host;
|
|
|
|
public abstract class ModuleBase<T> : AbpModule where T : AbpModule
|
|
{
|
|
public override void ConfigureServices(ServiceConfigurationContext context)
|
|
{
|
|
ServiceConfigurationContext.SetConsoleTitleOfWebApp(Assembly.GetEntryAssembly().GetName().Name);
|
|
LimitedResultRequestDto.MaxMaxResultCount = 100000;
|
|
context.Services.Configure<AbpSequentialGuidGeneratorOptions>(o => o.DefaultSequentialGuidType = SequentialGuidType.SequentialAsString);
|
|
PreConfigureServices(context);
|
|
ConfigureAntiForgery();
|
|
ConfigureLocalizationServices();
|
|
ConfigureAuthentication();
|
|
ConfigureDistributedCache();
|
|
ConfigureDbContext();
|
|
ConfigureHttpClientProxies();
|
|
ConfigureAutoMapper();
|
|
ConfigureLocalizationServices();
|
|
ConfigureAutoApiControllers();
|
|
ConfigureSwaggerServices();
|
|
ConfigureAddCors();
|
|
SetFormLimit();
|
|
ConfigureExceptionHanding();
|
|
context.Services.AddAgileConfig();
|
|
}
|
|
|
|
public override async Task ConfigureServicesAsync(ServiceConfigurationContext context)
|
|
{
|
|
await base.ConfigureServicesAsync(context).ConfigureAwait(false);
|
|
}
|
|
|
|
public virtual void CreateDatabase<TEfCoreDbContext>(ApplicationInitializationContext context) where TEfCoreDbContext : IEfCoreDbContext
|
|
{
|
|
//if (!context.GetEnvironment().IsDevelopment())
|
|
//{
|
|
// Console.WriteLine($"生产模式不执行数据库初始化");
|
|
// return;
|
|
//}
|
|
var contextName = typeof(TEfCoreDbContext).Name;
|
|
using var scope = context.ServiceProvider.CreateScope();
|
|
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
|
|
using var uow = uowManager.Begin();
|
|
var provider = scope.ServiceProvider.GetRequiredService<IDbContextProvider<TEfCoreDbContext>>();
|
|
using var dbContext = provider.GetDbContextAsync().Result;
|
|
var dbCreator = dbContext.GetService<IRelationalDatabaseCreator>() as RelationalDatabaseCreator;
|
|
var sql = dbCreator.GenerateCreateScript();
|
|
var md5 = sql.Md5();
|
|
var path = Path.Combine(Directory.GetCurrentDirectory(), "scripts");
|
|
Directory.CreateDirectory(path);
|
|
using var sw = File.CreateText(Path.Combine(path, $"db.{contextName}.sql"));
|
|
sw.Write(sql);
|
|
|
|
Console.WriteLine($"{contextName} 初始化开始");
|
|
//创建数据库
|
|
if (!dbCreator.Exists())
|
|
{
|
|
dbCreator.Create();
|
|
var createSql = "CREATE TABLE `EFDbContext`(`Id` varchar(255) NOT NULL,`Hash` varchar(255) NOT NULL,PRIMARY KEY (`Id`));";
|
|
dbContext.Database.ExecuteSqlRaw(createSql);
|
|
}
|
|
// 查询当前DbContext是否已经初始化
|
|
using var conn = dbContext.Database.GetDbConnection();
|
|
var cmd = conn.CreateCommand();
|
|
conn.Open();
|
|
cmd.CommandText = $"SELECT `Hash` FROM EFDbContext where `Id`='{contextName}'";
|
|
var hash = cmd.ExecuteScalar();
|
|
conn.Close();
|
|
|
|
if (hash == null)
|
|
{
|
|
try
|
|
{
|
|
dbContext.Database.BeginTransaction();
|
|
dbContext.Database.ExecuteSqlRaw(sql);
|
|
dbContext.Database.ExecuteSqlRaw($"INSERT INTO `EFDbContext` VALUES ('{contextName}', '{md5}');");
|
|
dbContext.Database.CommitTransaction();
|
|
context.ServiceProvider
|
|
.GetRequiredService<IDataSeeder>()
|
|
.SeedAsync()
|
|
.Wait();
|
|
Console.WriteLine($"{contextName} 初始化成功");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
dbContext.Database.RollbackTransaction();
|
|
throw new Exception($"{contextName} 初始化失败:{ex.Message}", ex);
|
|
}
|
|
finally
|
|
{
|
|
Console.WriteLine($"{contextName} 初始化结束");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"{contextName} 数据库结构{(hash.ToString() == md5 ? "正常" : "已过时")}");
|
|
}
|
|
uow.CompleteAsync();
|
|
}
|
|
|
|
public override void OnApplicationInitialization(ApplicationInitializationContext context)
|
|
{
|
|
//
|
|
base.OnApplicationInitialization(context);
|
|
var app = context.GetApplicationBuilder();
|
|
app.UseDeveloperExceptionPage();
|
|
app.UseAbpRequestLocalization();
|
|
app.UseCorrelationId();
|
|
app.UseStaticFiles();
|
|
app.UseRouting();
|
|
app.UseCors();
|
|
app.UseAuthentication();
|
|
app.UseJwtTokenMiddleware();
|
|
app.UseUnitOfWork();
|
|
app.UseAuthorization();
|
|
app.UseSwagger();
|
|
app.UseSwaggerUI(options =>
|
|
{
|
|
var apiDescriptionGroups = context.ServiceProvider.GetRequiredService<IApiDescriptionGroupCollectionProvider>().ApiDescriptionGroups.Items;
|
|
foreach (var description in apiDescriptionGroups)
|
|
{
|
|
if (description.GroupName is not null)
|
|
{
|
|
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName);
|
|
}
|
|
else
|
|
{
|
|
options.SwaggerEndpoint($"/swagger/Default/swagger.json", "Default");
|
|
}
|
|
}
|
|
});
|
|
app.UseAuditing();
|
|
app.UseAbpSerilogEnrichers();
|
|
app.UseConfiguredEndpoints();
|
|
context.ServiceProvider.InitSettings();
|
|
UseMultiTenancy(context);
|
|
}
|
|
|
|
protected virtual void Configure()
|
|
{
|
|
}
|
|
|
|
protected virtual void ConfigureAddCors()
|
|
{
|
|
var origins = ServiceConfigurationContext.Services.GetConfiguration().GetSection("App:CorsOrigins").Get<string[]>() ?? Array.Empty<string>();
|
|
|
|
ServiceConfigurationContext.Services.AddCors(options =>
|
|
{
|
|
options.AddDefaultPolicy(builder =>
|
|
{
|
|
builder
|
|
.WithOrigins(
|
|
origins
|
|
.Select(o => o.RemovePostFix("/"))
|
|
.ToArray()
|
|
)
|
|
//.AllowAnyOrigin()//允许所有跨域
|
|
.WithAbpExposedHeaders()
|
|
.SetIsOriginAllowedToAllowWildcardSubdomains()
|
|
.AllowAnyHeader()
|
|
.AllowAnyMethod()
|
|
.AllowCredentials();//按配置文件中 跨域
|
|
});
|
|
});
|
|
}
|
|
|
|
protected virtual void ConfigureAntiForgery()
|
|
{
|
|
Configure<AbpAntiForgeryOptions>(options =>
|
|
{
|
|
options.TokenCookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax;
|
|
options.TokenCookie.Expiration = TimeSpan.FromDays(365);
|
|
options.AutoValidateIgnoredHttpMethods.Add("POST");
|
|
});
|
|
}
|
|
|
|
protected virtual void ConfigureAuditing()
|
|
{
|
|
}
|
|
|
|
protected virtual void ConfigureAuthentication()
|
|
{
|
|
var configuration = ServiceConfigurationContext.Services.GetConfiguration();
|
|
|
|
ServiceConfigurationContext.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
.AddJwtBearer(options =>
|
|
{
|
|
options.Authority = configuration["AuthServer:Authority"];
|
|
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
|
|
options.Audience = configuration["AuthServer:Audience"];
|
|
});
|
|
var isAlwaysAllowAuthorization = configuration.GetValue<bool>("AlwaysAllowAuthorization");
|
|
if (isAlwaysAllowAuthorization)
|
|
{
|
|
//绕过授权服务,用于测试
|
|
ServiceConfigurationContext.Services.AddAlwaysAllowAuthorization();
|
|
}
|
|
}
|
|
|
|
protected virtual void ConfigureAutoApiControllers()
|
|
{
|
|
Configure<AbpAspNetCoreMvcOptions>(options =>
|
|
{
|
|
AppDomain.CurrentDomain.GetAssemblies()
|
|
.Where(assembly => assembly.FullName.StartsWith("Win_in.Sfs"))
|
|
.ForEach(assembly => options.ConventionalControllers.Create(assembly));
|
|
});
|
|
}
|
|
|
|
protected virtual void ConfigureAutoMapper()
|
|
{
|
|
Configure<AbpAutoMapperOptions>(options =>
|
|
{
|
|
options.AddMaps<T>();
|
|
});
|
|
}
|
|
|
|
protected virtual void ConfigureBundles()
|
|
{
|
|
Configure<AbpBundlingOptions>(options =>
|
|
{
|
|
options.StyleBundles.Configure(
|
|
BasicThemeBundles.Styles.Global,
|
|
bundle =>
|
|
{
|
|
bundle.AddFiles("/global-styles.css");
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
protected virtual void ConfigureDbContext()
|
|
{
|
|
}
|
|
|
|
protected virtual void ConfigureDistributedCache()
|
|
{
|
|
var evn = ServiceConfigurationContext.Services.GetHostingEnvironment();
|
|
var cfg = ServiceConfigurationContext.Services.GetConfiguration();
|
|
Configure<AbpDistributedCacheOptions>(options => { options.KeyPrefix = cfg["Redis:KeyPrefix"]; });
|
|
|
|
if (!evn.IsDevelopment())
|
|
{
|
|
var redis = ConnectionMultiplexer.Connect(cfg["Redis:Configuration"]);
|
|
ServiceConfigurationContext.Services
|
|
.AddDataProtection()
|
|
.PersistKeysToStackExchangeRedis(redis, $"{Assembly.GetEntryAssembly().GetName().Name}-Protection-Keys");
|
|
}
|
|
}
|
|
|
|
protected virtual void ConfigureExceptionHandling()
|
|
{
|
|
Configure<AbpExceptionHandlingOptions>(options =>
|
|
{
|
|
options.SendExceptionsDetailsToClients = true; //向前端返回完整错误日志
|
|
});
|
|
}
|
|
|
|
protected virtual void ConfigureHttpClientProxies()
|
|
{
|
|
}
|
|
|
|
protected virtual void ConfigureLocalizationServices()
|
|
{
|
|
Configure<AbpLocalizationOptions>(options =>
|
|
{
|
|
options.Languages.Add(new LanguageInfo("en", "en", "English"));
|
|
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
|
|
});
|
|
}
|
|
|
|
protected virtual void ConfigureSwaggerServices()
|
|
{
|
|
var services = ServiceConfigurationContext.Services;
|
|
var cfg = ServiceConfigurationContext.Services.GetConfiguration();
|
|
var name = Assembly.GetEntryAssembly().GetName().Name;
|
|
var urlBase = cfg["AuthServer:Authority"].EnsureEndsWith('/');
|
|
|
|
services.AddSingleton<IExportImportService, ClosedXmlExportImportService>();
|
|
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerConfigureOptions>();
|
|
services.AddSwaggerGen(options =>
|
|
{
|
|
var schemaFactory = () => new OpenApiSchema
|
|
{
|
|
Type = "string",
|
|
Format = "binary"
|
|
};
|
|
options.MapType<RemoteStreamContent>(schemaFactory);
|
|
options.MapType<IRemoteStreamContent>(schemaFactory);
|
|
//
|
|
options.DocumentFilter<SwaggerFilter>();
|
|
options.OperationFilter<SwaggerFilter>();
|
|
options.AddSecurityDefinition(nameof(SecuritySchemeType.Http), new OpenApiSecurityScheme
|
|
{
|
|
Type = SecuritySchemeType.Http,
|
|
Scheme = "bearer",
|
|
BearerFormat = "JWT",
|
|
In = ParameterLocation.Header,
|
|
});
|
|
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
|
{
|
|
{
|
|
new OpenApiSecurityScheme
|
|
{
|
|
Reference = new OpenApiReference {
|
|
Type = ReferenceType.SecurityScheme,
|
|
Id = nameof(SecuritySchemeType.Http)
|
|
}
|
|
},
|
|
Array.Empty<string>()
|
|
}
|
|
});
|
|
options.SwaggerDoc("v1", new OpenApiInfo { Title = $"{name} API", Version = "v1" });
|
|
options.DocInclusionPredicate((docName, description) => true);
|
|
options.CustomSchemaIds(type => type.FullName);
|
|
options.DocInclusionPredicate((docName, api) => api.GroupName == null || api.GroupName == docName);
|
|
});
|
|
}
|
|
|
|
protected virtual void SetFormLimit()
|
|
{
|
|
ServiceConfigurationContext.Services.Configure<FormOptions>(options =>
|
|
{
|
|
options.ValueCountLimit = 5000; // 5000 items max
|
|
options.ValueLengthLimit = 1024 * 1024 * 100; // 100MB max len form data
|
|
});
|
|
}
|
|
|
|
protected virtual void UseMultiTenancy(ApplicationInitializationContext context)
|
|
{
|
|
if (Convert.ToBoolean(context.GetConfiguration().GetValue("IsMultiTenancy", false)))
|
|
{
|
|
context.GetApplicationBuilder().UseMultiTenancy();
|
|
}
|
|
}
|
|
|
|
private void ConfigureExceptionHanding()
|
|
{
|
|
Configure<AbpExceptionHandlingOptions>(options =>
|
|
{
|
|
options.SendExceptionsDetailsToClients = true;
|
|
});
|
|
}
|
|
}
|
|
|