123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- 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
- {
- /// <summary>
- /// Authentication
- /// </summary>
- /// <param name="services"></param>
- /// <param name="configuration"></param>
- /// <returns></returns>
- public static IServiceCollection RegisterAuthentication(this IServiceCollection services, ConfigurationManager configuration)
- {
- var jwtOptions = configuration.GetSection("IdentityConfiguration").Get<IdentityConfiguration>().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<ITypedCache<AudienceTicket>>();
- 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;
- }
- /// <summary>
- /// Swagger
- /// </summary>
- /// <param name="services"></param>
- /// <returns></returns>
- 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<string>();
- c.AddSecurityRequirement(requirement);
- });
- return services;
- }
- /// <summary>
- /// Cors
- /// </summary>
- /// <param name="services"></param>
- /// <param name="configuration"></param>
- /// <param name="corsOrigins"></param>
- /// <returns></returns>
- 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<string[]>();
- 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;
- }
- /// <summary>
- /// Mapper
- /// </summary>
- /// <param name="services"></param>
- /// <returns></returns>
- public static IServiceCollection RegisterMapper(this IServiceCollection services)
- {
- var config = TypeAdapterConfig.GlobalSettings;
- config.ForDestinationType<IDataPermission>()
- .Ignore(d => d.CreatorId)
- .Ignore(d => d.CreatorOrgId)
- //.Ignore(d => d.CreatorOrgCode)
- .Ignore(d => d.AreaId);
- config.ForDestinationType<IWorkflow>()
- .Ignore(d => d.ExpiredTimeConfigId);
- config.ForDestinationType<IHasCreationTime>()
- .Ignore(d => d.CreationTime);
- config.ForDestinationType<IHasDeletionTime>().Ignore(d => d.DeletionTime);
- config.ForDestinationType<ISoftDelete>().Ignore(d => d.IsDeleted);
- config.ForDestinationType<IHasModificationTime>().Ignore(d => d.LastModificationTime);
- config.ForDestinationType<Entity>().Ignore(d => d.Id);
- services.AddSingleton(config);
- services.AddScoped<IMapper, ServiceMapper>();
- return services;
- }
- /// <summary>
- /// SignalR
- /// </summary>
- /// <param name="services"></param>
- /// <param name="configuration"></param>
- /// <returns></returns>
- 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<CacheOptions>();
- 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<SendOverTimeSmsJob>(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<CheckAiVisitStateJob>(aiVisitStatusKey);
- d.AddTrigger(t => t
- .WithIdentity("task-check-aivisit-state-trigger")
- .ForJob(aiVisitStatusKey)
- .StartNow()
- .WithCronSchedule("0 0/5 * * * ? *"));
- //d.AddJob<SendOrderJob>(autoSendOrderKey);
- //d.AddTrigger(t => t
- // .WithIdentity("task-send-order-trigger")
- // .ForJob(autoSendOrderKey)
- // .StartNow()
- // .WithCronSchedule("0 10 9 * * ?")
- //);
- break;
- case AppDefaults.AppScope.ZiGong:
- //d.AddJob<SendOrderJob>(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<XingTangCallsSyncJob>(getCallsJobKey);
- d.AddTrigger(t => t
- .WithIdentity("get-callsxt-trigger")
- .ForJob(getCallsJobKey)
- .StartNow()
- .WithCronSchedule("0/5 * * * * ?")
- );
- var getOperationsJobKey = new JobKey(nameof(XingTangTelOperationSyncJob));
- d.AddJob<XingTangTelOperationSyncJob>(getOperationsJobKey);
- d.AddTrigger(t => t
- .WithIdentity("get-operationsxt-trigger")
- .ForJob(getOperationsJobKey)
- .StartNow()
- .WithCronSchedule("0/10 * * * * ?")
- );
- var getCallSatisfactionJobKey = new JobKey(nameof(XingTangCallSatisfactionSyncJob));
- d.AddJob<XingTangCallSatisfactionSyncJob>(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<Publisher>();
- 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<Program>();
- cfg.RegisterServicesFromAssembly(typeof(ApplicationStartupExtensions).Assembly);
- });
- return services;
- }
- public static IServiceCollection RegisterCompression(this IServiceCollection services)
- {
- services.Configure<BrotliCompressionProviderOptions>(options =>
- {
- options.Level = CompressionLevel.Optimal;
- }).Configure<GzipCompressionProviderOptions>(options =>
- {
- options.Level = CompressionLevel.Optimal;
- }).AddResponseCompression(options =>
- {
- options.EnableForHttps = true;
- options.Providers.Add<BrotliCompressionProvider>();
- options.Providers.Add<GzipCompressionProvider>();
- 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;
- }
- }
- }
|