using System.IdentityModel.Tokens.Jwt;
using System.IO.Compression;
using System.Reflection;
using System.Text;
using Hotline.Api.Filter;
using Hotline.Application;
using Hotline.Application.Jobs;
using Hotline.Caching.Interfaces;
using Hotline.CallCenter.Configs;
using Hotline.Configurations;
using Hotline.DI;
using Hotline.EventBus;
using Hotline.Identity;
using Hotline.Repository.SqlSugar;
using Hotline.Repository.SqlSugar.Ts;
using Hotline.Settings;
using Mapster;
using MapsterMapper;
using MediatR.Pipeline;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Quartz;
using Quartz.AspNetCore;
using XF.Domain.Cache;
using XF.Domain.Entities;
using XF.Domain.Exceptions;
using XF.Domain.Options;
using XF.Domain.Repository;
using XF.EasyCaching;
namespace Hotline.Api
{
public static class StartupHelper
{
///
/// Authentication
///
///
///
///
public static IServiceCollection RegisterAuthentication(this IServiceCollection services, ConfigurationManager configuration)
{
var jwtOptions = configuration.GetSection("IdentityConfiguration").Get().Jwt;
#region remote ids
//JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
//services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
// .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, d =>
// {
// d.Authority = identityConfigs.IdentityUrl;
// d.RequireHttpsMetadata = false;
// d.TokenValidationParameters = new TokenValidationParameters
// {
// ValidateAudience = false
// };
// //d.Audience = "hotline_api";
// d.Events = new JwtBearerEvents
// {
// OnMessageReceived = context =>
// {
// var accessToken = context.Request.Query["access_token"];
// // If the request is for our hub...
// var path = context.HttpContext.Request.Path;
// if (!string.IsNullOrEmpty(accessToken) &&
// (path.StartsWithSegments("/hubs/callcenter")))
// {
// // Read the token out of the query string
// context.Token = accessToken;
// }
// return Task.CompletedTask;
// }
// };
// });
#endregion
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(d =>
{
byte[] bytes = Encoding.UTF8.GetBytes(jwtOptions.SecretKey);
var secKey = new SymmetricSecurityKey(bytes);
d.TokenValidationParameters = new()
{
ValidateIssuer = false,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = secKey,
AudienceValidator = (audiences, token, parameters) =>
{
if (token is /*JwtSecurityToken*/ JsonWebToken jwtToken)
{
using var serviceProvider = services.BuildServiceProvider();
var cacheAudience = serviceProvider.GetService>();
var audience = cacheAudience.Get(jwtToken.Subject);
return audiences != null && audiences.Any(a => a == audience?.Ticket);
}
return false;
}
};
//d.Audience = "hotline_api";
d.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/hubs/hotline")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
})
;
return services;
}
public static IServiceCollection RegisterAuthorization(this IServiceCollection services, ConfigurationManager configuration)
{
services.AddAuthorization(options =>
{
options.AddPolicy(AppDefaults.AuthPolicy.Hotline, d => d.RequireClaim("scope", AppDefaults.AuthPolicy.Hotline));
});
return services;
}
///
/// Swagger
///
///
///
public static IServiceCollection RegisterSwagger(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
//添加文档
c.SwaggerDoc("v1", new OpenApiInfo() { Title = "Hotline Api", Version = "v1.0", Description = "智慧热线api" });
var files = Directory.GetFiles(AppContext.BaseDirectory).Where(d => d.EndsWith(".xml"));
foreach (var file in files)
{
c.IncludeXmlComments(file, true);
}
var scheme = new OpenApiSecurityScheme()
{
Description = "Authorization header. \r\nExample: 'Bearer ***'",
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Authorization"
},
Scheme = "oauth2",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
};
c.AddSecurityDefinition("Authorization", scheme);
var requirement = new OpenApiSecurityRequirement();
requirement[scheme] = new List();
c.AddSecurityRequirement(requirement);
});
return services;
}
///
/// Cors
///
///
///
///
///
public static IServiceCollection RegisterCors(this IServiceCollection services, ConfigurationManager configuration, string corsOrigins)
{
services.AddCors(options =>
{
options.AddPolicy(name: corsOrigins,
builder =>
{
var origins = configuration.GetSection("Cors:Origins").Get();
builder.SetIsOriginAllowed(a =>
{
return origins.Any(origin => origin.StartsWith("*.", StringComparison.Ordinal)
? a.EndsWith(origin[1..], StringComparison.Ordinal)
: a.Equals(origin, StringComparison.Ordinal));
})
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
return services;
}
///
/// Mapper
///
///
///
public static IServiceCollection RegisterMapper(this IServiceCollection services)
{
var config = TypeAdapterConfig.GlobalSettings;
config.ForDestinationType()
.Ignore(d => d.CreatorId)
.Ignore(d => d.CreatorOrgId)
//.Ignore(d => d.CreatorOrgCode)
.Ignore(d => d.AreaId);
config.ForDestinationType()
.Ignore(d => d.ExpiredTimeConfigId);
config.ForDestinationType()
.Ignore(d => d.CreationTime);
config.ForDestinationType().Ignore(d => d.DeletionTime);
config.ForDestinationType().Ignore(d => d.IsDeleted);
config.ForDestinationType().Ignore(d => d.LastModificationTime);
config.ForDestinationType().Ignore(d => d.Id);
services.AddSingleton(config);
services.AddScoped();
return services;
}
///
/// SignalR
///
///
///
///
public static IServiceCollection RegisterSignalR(this IServiceCollection services, ConfigurationManager configuration)
{
//var connectionString = configuration.GetConnectionString("Redis");
//if (string.IsNullOrEmpty(connectionString))
// throw new UserFriendlyException("未配置signalR的redis连接");
//services.AddSignalR().AddStackExchangeRedis(connectionString, options =>
//{
// options.Configuration.ChannelPrefix = "Hotline:signalr:";
//});
var cacheConfig = configuration.GetRequiredSection("Cache").Get();
services.AddSignalR().AddStackExchangeRedis($"{cacheConfig.Host}:{cacheConfig.Port}", options =>
{
options.Configuration.ChannelPrefix = "Hotline:signalr:";
options.Configuration.Password = cacheConfig.Password;
options.Configuration.DefaultDatabase = cacheConfig.Database;
});
return services;
}
public static IServiceCollection RegisterJob(this IServiceCollection services,AppConfiguration appConfiguration )
{
services.AddQuartz(d =>
{
d.SchedulerId = "default_scheduler";
d.InterruptJobsOnShutdown = true;
d.InterruptJobsOnShutdownWithWait = true;
d.MaxBatchSize = 3;
//load send order job
//即将超期和超期短信
//var autoSendOverTimeSmsKey = new JobKey(nameof(SendOverTimeSmsJob), "send overtime order task");
//d.AddJob(autoSendOverTimeSmsKey);
//d.AddTrigger(t => t
// .WithIdentity("task-send-overtime-order-trigger")
// .ForJob(autoSendOverTimeSmsKey)
// .StartNow()
// .WithCronSchedule("0 30 09,14 * * ?"));
var autoSendOrderKey = new JobKey(nameof(SendOrderJob), "send order task");
switch (appConfiguration.AppScope)
{
//智能化任务
case AppDefaults.AppScope.YiBin:
var aiVisitStatusKey = new JobKey(nameof(CheckAiVisitStateJob), "check aivisit state task");
d.AddJob(aiVisitStatusKey);
d.AddTrigger(t => t
.WithIdentity("task-check-aivisit-state-trigger")
.ForJob(aiVisitStatusKey)
.StartNow()
.WithCronSchedule("0 0/5 * * * ? *"));
//d.AddJob(autoSendOrderKey);
//d.AddTrigger(t => t
// .WithIdentity("task-send-order-trigger")
// .ForJob(autoSendOrderKey)
// .StartNow()
// .WithCronSchedule("0 10 9 * * ?")
//);
break;
case AppDefaults.AppScope.ZiGong:
//d.AddJob(autoSendOrderKey);
//d.AddTrigger(t => t
// .WithIdentity("task-send-order-trigger")
// .ForJob(autoSendOrderKey)
// .StartNow()
// .WithCronSchedule("0 10 9 * * ?")
//);
break;
default:
break;
}
switch (appConfiguration.GetDefaultAppScopeConfiguration().CallCenterType)
{
case AppDefaults.CallCenterType.XingTang:
var getCallsJobKey = new JobKey(nameof(XingTangCallsSyncJob));
d.AddJob(getCallsJobKey);
d.AddTrigger(t => t
.WithIdentity("get-callsxt-trigger")
.ForJob(getCallsJobKey)
.StartNow()
.WithCronSchedule("0/5 * * * * ?")
);
//var getOperationsJobKey = new JobKey(nameof(XingTangTelOperationSyncJob));
//d.AddJob(getOperationsJobKey);
//d.AddTrigger(t => t
// .WithIdentity("get-operationsxt-trigger")
// .ForJob(getOperationsJobKey)
// .StartNow()
// .WithCronSchedule("0/10 * * * * ?")
//);
var getCallSatisfactionJobKey = new JobKey(nameof(XingTangCallSatisfactionSyncJob));
d.AddJob(getCallSatisfactionJobKey);
d.AddTrigger(t => t
.WithIdentity("get-callsatisfaction-trigger")
.ForJob(getCallSatisfactionJobKey)
.StartNow()
.WithCronSchedule("0/30 * * * * ?")
);
break;
}
});
services.AddQuartzServer(d =>
{
d.WaitForJobsToComplete = true;
d.StartDelay = TimeSpan.FromSeconds(5);
});
return services;
}
public static IServiceCollection RegisterRepository(this IServiceCollection services)
{
services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>));
services.AddScoped(typeof(IRepositoryWorkflow<>), typeof(BaseRepositoryWorkflow<>));
services.AddScoped(typeof(IRepositoryTextSearch<>), typeof(BaseRepositoryTextSearch<>));
return services;
}
public static IServiceCollection RegisterMediatR(this IServiceCollection services, AppConfiguration appConfiguration)
{
services.AddScoped();
services.AddMediatR(cfg =>
{
cfg.TypeEvaluator = d =>
{
var attr = d.GetCustomAttribute(typeof(InjectionAttribute)) as InjectionAttribute;
if (attr is null) return true;
return attr.IsEnable(appConfiguration.AppScope,
appConfiguration.GetDefaultAppScopeConfiguration().CallCenterType);
//if (!attr.Enable) return false;
//switch (attr.EventHandlerType)
//{
// case EEventHandlerType.CallCenter:
// return attr.Label == appConfiguration.GetDefaultAppScopeConfiguration().CallCenterType;
// default:
// return attr.AppScopes.ToString()
// .Split(',', StringSplitOptions.RemoveEmptyEntries)
// .Any(x => string.Compare(x, appConfiguration.AppScope, StringComparison.Ordinal) == 0);
//}
};
//cfg.RegisterServicesFromAssemblyContaining();
cfg.RegisterServicesFromAssembly(typeof(ApplicationStartupExtensions).Assembly);
});
return services;
}
public static IServiceCollection RegisterCompression(this IServiceCollection services)
{
services.Configure(options =>
{
options.Level = CompressionLevel.Optimal;
}).Configure(options =>
{
options.Level = CompressionLevel.Optimal;
}).AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add();
options.Providers.Add();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat([
"application/json; charset=utf-8",
"text/html; charset=utf-8",
"application/xhtml+xml",
"application/atom+xml",
"image/svg+xml"
]);
});
return services;
}
}
}