StartupHelper.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. using System.IdentityModel.Tokens.Jwt;
  2. using System.Reflection;
  3. using System.Text;
  4. using Hotline.Application.Jobs;
  5. using Hotline.Application.Orders;
  6. using Hotline.CallCenter.Configs;
  7. using Hotline.Identity;
  8. using Hotline.Repository.SqlSugar;
  9. using Hotline.Repository.SqlSugar.Ts;
  10. using Mapster;
  11. using MapsterMapper;
  12. using Microsoft.AspNetCore.Authentication.JwtBearer;
  13. using Microsoft.IdentityModel.Tokens;
  14. using Microsoft.OpenApi.Models;
  15. using Quartz;
  16. using Quartz.AspNetCore;
  17. using XF.Domain.Cache;
  18. using XF.Domain.Entities;
  19. using XF.Domain.Exceptions;
  20. using XF.Domain.Options;
  21. using XF.Domain.Repository;
  22. using XF.EasyCaching;
  23. namespace Hotline.Api
  24. {
  25. public static class StartupHelper
  26. {
  27. /// <summary>
  28. /// Authentication
  29. /// </summary>
  30. /// <param name="services"></param>
  31. /// <param name="configuration"></param>
  32. /// <returns></returns>
  33. public static IServiceCollection RegisterAuthentication(this IServiceCollection services, ConfigurationManager configuration)
  34. {
  35. var jwtOptions = configuration.GetSection("IdentityConfiguration").Get<IdentityConfiguration>().Jwt;
  36. #region remote ids
  37. //JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
  38. //services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  39. // .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, d =>
  40. // {
  41. // d.Authority = identityConfigs.IdentityUrl;
  42. // d.RequireHttpsMetadata = false;
  43. // d.TokenValidationParameters = new TokenValidationParameters
  44. // {
  45. // ValidateAudience = false
  46. // };
  47. // //d.Audience = "hotline_api";
  48. // d.Events = new JwtBearerEvents
  49. // {
  50. // OnMessageReceived = context =>
  51. // {
  52. // var accessToken = context.Request.Query["access_token"];
  53. // // If the request is for our hub...
  54. // var path = context.HttpContext.Request.Path;
  55. // if (!string.IsNullOrEmpty(accessToken) &&
  56. // (path.StartsWithSegments("/hubs/callcenter")))
  57. // {
  58. // // Read the token out of the query string
  59. // context.Token = accessToken;
  60. // }
  61. // return Task.CompletedTask;
  62. // }
  63. // };
  64. // });
  65. #endregion
  66. services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  67. .AddJwtBearer(d =>
  68. {
  69. byte[] bytes = Encoding.UTF8.GetBytes(jwtOptions.SecretKey);
  70. var secKey = new SymmetricSecurityKey(bytes);
  71. d.TokenValidationParameters = new()
  72. {
  73. ValidateIssuer = false,
  74. ValidateAudience = true,
  75. ValidateLifetime = true,
  76. ValidateIssuerSigningKey = true,
  77. IssuerSigningKey = secKey,
  78. AudienceValidator = (audiences, token, parameters) =>
  79. {
  80. if (token is JwtSecurityToken jwtToken)
  81. {
  82. using var serviceProvider = services.BuildServiceProvider();
  83. var cacheAudience = serviceProvider.GetService<ITypedCache<AudienceTicket>>();
  84. var audience = cacheAudience.Get(jwtToken.Subject);
  85. return audiences != null && audiences.Any(a => a == audience?.Ticket);
  86. }
  87. return false;
  88. }
  89. };
  90. //d.Audience = "hotline_api";
  91. d.Events = new JwtBearerEvents
  92. {
  93. OnMessageReceived = context =>
  94. {
  95. var accessToken = context.Request.Query["access_token"];
  96. // If the request is for our hub...
  97. var path = context.HttpContext.Request.Path;
  98. if (!string.IsNullOrEmpty(accessToken) &&
  99. (path.StartsWithSegments("/hubs/hotline")))
  100. {
  101. // Read the token out of the query string
  102. context.Token = accessToken;
  103. }
  104. return Task.CompletedTask;
  105. }
  106. };
  107. })
  108. ;
  109. return services;
  110. }
  111. /// <summary>
  112. /// Swagger
  113. /// </summary>
  114. /// <param name="services"></param>
  115. /// <returns></returns>
  116. public static IServiceCollection RegisterSwagger(this IServiceCollection services)
  117. {
  118. services.AddSwaggerGen(c =>
  119. {
  120. //添加文档
  121. c.SwaggerDoc("v1", new OpenApiInfo() { Title = "Hotline Api", Version = "v1.0", Description = "智慧热线api" });
  122. var files = Directory.GetFiles(AppContext.BaseDirectory).Where(d => d.EndsWith(".xml"));
  123. foreach (var file in files)
  124. {
  125. c.IncludeXmlComments(file, true);
  126. }
  127. var scheme = new OpenApiSecurityScheme()
  128. {
  129. Description = "Authorization header. \r\nExample: 'Bearer ***'",
  130. Reference = new OpenApiReference
  131. {
  132. Type = ReferenceType.SecurityScheme,
  133. Id = "Authorization"
  134. },
  135. Scheme = "oauth2",
  136. Name = "Authorization",
  137. In = ParameterLocation.Header,
  138. Type = SecuritySchemeType.ApiKey,
  139. };
  140. c.AddSecurityDefinition("Authorization", scheme);
  141. var requirement = new OpenApiSecurityRequirement();
  142. requirement[scheme] = new List<string>();
  143. c.AddSecurityRequirement(requirement);
  144. });
  145. return services;
  146. }
  147. /// <summary>
  148. /// Cors
  149. /// </summary>
  150. /// <param name="services"></param>
  151. /// <param name="configuration"></param>
  152. /// <param name="corsOrigins"></param>
  153. /// <returns></returns>
  154. public static IServiceCollection RegisterCors(this IServiceCollection services, ConfigurationManager configuration, string corsOrigins)
  155. {
  156. services.AddCors(options =>
  157. {
  158. options.AddPolicy(name: corsOrigins,
  159. builder =>
  160. {
  161. var origins = configuration.GetSection("Cors:Origins").Get<string[]>();
  162. builder.SetIsOriginAllowed(a =>
  163. {
  164. return origins.Any(origin => origin.StartsWith("*.", StringComparison.Ordinal)
  165. ? a.EndsWith(origin[1..], StringComparison.Ordinal)
  166. : a.Equals(origin, StringComparison.Ordinal));
  167. })
  168. .AllowAnyHeader()
  169. .AllowAnyMethod()
  170. .AllowCredentials();
  171. });
  172. });
  173. return services;
  174. }
  175. /// <summary>
  176. /// Mapper
  177. /// </summary>
  178. /// <param name="services"></param>
  179. /// <returns></returns>
  180. public static IServiceCollection RegisterMapper(this IServiceCollection services)
  181. {
  182. var config = TypeAdapterConfig.GlobalSettings;
  183. config.ForDestinationType<IDataPermission>()
  184. .Ignore(d => d.CreatorId)
  185. .Ignore(d => d.CreatorOrgId)
  186. //.Ignore(d => d.CreatorOrgCode)
  187. .Ignore(d => d.AreaId);
  188. config.ForDestinationType<IWorkflow>()
  189. .Ignore(d => d.ExpiredTimeConfigId);
  190. config.ForDestinationType<IHasCreationTime>()
  191. .Ignore(d => d.CreationTime);
  192. config.ForDestinationType<IHasDeletionTime>().Ignore(d => d.DeletionTime);
  193. config.ForDestinationType<ISoftDelete>().Ignore(d => d.IsDeleted);
  194. config.ForDestinationType<IHasModificationTime>().Ignore(d => d.LastModificationTime);
  195. config.ForDestinationType<Entity>().Ignore(d => d.Id);
  196. services.AddSingleton(config);
  197. services.AddScoped<IMapper, ServiceMapper>();
  198. return services;
  199. }
  200. /// <summary>
  201. /// SignalR
  202. /// </summary>
  203. /// <param name="services"></param>
  204. /// <param name="configuration"></param>
  205. /// <returns></returns>
  206. public static IServiceCollection RegisterSignalR(this IServiceCollection services, ConfigurationManager configuration)
  207. {
  208. //var connectionString = configuration.GetConnectionString("Redis");
  209. //if (string.IsNullOrEmpty(connectionString))
  210. // throw new UserFriendlyException("未配置signalR的redis连接");
  211. //services.AddSignalR().AddStackExchangeRedis(connectionString, options =>
  212. //{
  213. // options.Configuration.ChannelPrefix = "Hotline:signalr:";
  214. //});
  215. var cacheConfig = configuration.GetRequiredSection("Cache").Get<CacheOptions>();
  216. services.AddSignalR().AddStackExchangeRedis($"{cacheConfig.Host}:{cacheConfig.Port}", options =>
  217. {
  218. options.Configuration.ChannelPrefix = "Hotline:signalr:";
  219. options.Configuration.Password = cacheConfig.Password;
  220. options.Configuration.DefaultDatabase = cacheConfig.Database;
  221. });
  222. return services;
  223. }
  224. public static IServiceCollection RegisterJob(this IServiceCollection services, CallCenterConfiguration callCenterConfiguration)
  225. {
  226. services.AddQuartz(d =>
  227. {
  228. d.SchedulerId = "default_scheduler";
  229. d.InterruptJobsOnShutdown = true;
  230. d.InterruptJobsOnShutdownWithWait = true;
  231. d.MaxBatchSize = 3;
  232. //load send order job
  233. var autoSendOrderKey = new JobKey(nameof(SendOrderJob), "send order task");
  234. d.AddJob<SendOrderJob>(autoSendOrderKey);
  235. d.AddTrigger(t => t
  236. .WithIdentity("task-send-order-trigger")
  237. .ForJob(autoSendOrderKey)
  238. .StartNow()
  239. .WithCronSchedule("0 10 9 * * ?")
  240. );
  241. switch (callCenterConfiguration.CallCenterType)
  242. {
  243. case AppDefaults.CallCenterType.XingTang:
  244. // var getCallsJobKey = new JobKey(nameof(XingTangCallsSyncJob));
  245. // d.AddJob<XingTangCallsSyncJob>(getCallsJobKey);
  246. // d.AddTrigger(t => t
  247. // .WithIdentity("get-callsxt-trigger")
  248. // .ForJob(getCallsJobKey)
  249. // .StartNow()
  250. // .WithCronSchedule("0/5 * * * * ?")
  251. // );
  252. var getOperationsJobKey = new JobKey(nameof(XingTangTelOperationSyncJob));
  253. d.AddJob<XingTangTelOperationSyncJob>(getOperationsJobKey);
  254. d.AddTrigger(t => t
  255. .WithIdentity("get-operationsxt-trigger")
  256. .ForJob(getOperationsJobKey)
  257. .StartNow()
  258. .WithCronSchedule("0/10 * * * * ?")
  259. );
  260. break;
  261. }
  262. });
  263. services.AddQuartzServer(d =>
  264. {
  265. d.WaitForJobsToComplete = true;
  266. d.StartDelay = TimeSpan.FromSeconds(5);
  267. });
  268. return services;
  269. }
  270. public static IServiceCollection RegisterRepository(this IServiceCollection services)
  271. {
  272. services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>));
  273. services.AddScoped(typeof(IRepositoryWorkflow<>), typeof(BaseRepositoryWorkflow<>));
  274. services.AddScoped(typeof(IRepositoryTextSearch<>), typeof(BaseRepositoryTextSearch<>));
  275. return services;
  276. }
  277. }
  278. }