From 0210507de102a80ad9ef7993a30ce40b621dbf38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?old=E6=98=93?= <156663459@qq.com> Date: Fri, 17 Nov 2023 16:52:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0swagger=E6=9D=83?= =?UTF-8?q?=E9=99=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- New_College.Api/New_College.Api.csproj | 1 + New_College.Api/Startup.cs | 50 ++++- New_College.Api/appsettings.json | 2 +- .../wwwroot/BlogCore.Data.json/Department.tsv | 1 + New_College.Common/App.cs | 191 ++++++++++++++++++ .../Core/ConfigurableOptions.cs | 61 ++++++ .../Core/IConfigurableOptions.cs | 11 + New_College.Common/GlobalVar/GlobalVars.cs | 10 +- .../Helper/DatetimeJsonConverter.cs | 36 ++++ New_College.Common/Helper/JsonHelper.cs | 8 + New_College.Common/Helper/NumberConverter.cs | 177 ++++++++++++++++ New_College.Common/HttpContextExtension.cs | 19 ++ New_College.Common/InternalApp.cs | 45 +++++ New_College.Common/RuntimeExtension.cs | 86 ++++++++ .../Swagger/SwaggerAuthMiddleware.cs | 76 +++++++ .../Swagger/SwaggerContextExtension.cs | 47 +++++ .../New_College.Extensions.csproj | 5 +- .../ServiceExtensions/AllOptionRegister.cs | 20 ++ .../ServiceExtensions/HttpContextExtension.cs | 19 ++ .../ServiceExtensions/SwaggerSetup.cs | 1 + .../ServiceExtensions/UiFilesZipSetup.cs | 26 +++ .../ViewModels/Result/uniMajorSecond.cs | 3 + .../BASE/D_UniversityRankRepository.cs | 2 +- New_College.Services/D_LongIdMapServices.cs | 5 +- 24 files changed, 884 insertions(+), 18 deletions(-) create mode 100644 New_College.Api/wwwroot/BlogCore.Data.json/Department.tsv create mode 100644 New_College.Common/App.cs create mode 100644 New_College.Common/Core/ConfigurableOptions.cs create mode 100644 New_College.Common/Core/IConfigurableOptions.cs create mode 100644 New_College.Common/Helper/DatetimeJsonConverter.cs create mode 100644 New_College.Common/Helper/NumberConverter.cs create mode 100644 New_College.Common/HttpContextExtension.cs create mode 100644 New_College.Common/InternalApp.cs create mode 100644 New_College.Common/RuntimeExtension.cs create mode 100644 New_College.Common/Swagger/SwaggerAuthMiddleware.cs create mode 100644 New_College.Common/Swagger/SwaggerContextExtension.cs create mode 100644 New_College.Extensions/ServiceExtensions/AllOptionRegister.cs create mode 100644 New_College.Extensions/ServiceExtensions/HttpContextExtension.cs create mode 100644 New_College.Extensions/ServiceExtensions/UiFilesZipSetup.cs diff --git a/New_College.Api/New_College.Api.csproj b/New_College.Api/New_College.Api.csproj index ece8e5e..b8da50f 100644 --- a/New_College.Api/New_College.Api.csproj +++ b/New_College.Api/New_College.Api.csproj @@ -58,6 +58,7 @@ + diff --git a/New_College.Api/Startup.cs b/New_College.Api/Startup.cs index 56913b2..65f80c3 100644 --- a/New_College.Api/Startup.cs +++ b/New_College.Api/Startup.cs @@ -24,6 +24,10 @@ using Essensoft.AspNetCore.Payment.Alipay; using Microsoft.AspNetCore.Http; using IdentityModel; using Microsoft.AspNetCore.Authentication.Cookies; +using Newtonsoft.Json.Converters; +using New_College.Common.Helper; +using System.Text.Encodings.Web; +using System.Text.Unicode; namespace New_College { @@ -109,6 +113,7 @@ namespace New_College services.Configure(x => x.AllowSynchronousIO = true) .Configure(x => x.AllowSynchronousIO = true); + services.AddControllers(o => { // 全局异常过滤 @@ -117,18 +122,41 @@ namespace New_College //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention()); // 全局路由前缀,统一修改路由 o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name))); - }) - //全局配置Json序列化处理 - .AddNewtonsoftJson(options => + }).AddJsonOptions( + options => { - //忽略循环引用 - options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - //不使用驼峰样式的key - options.SerializerSettings.ContractResolver = new DefaultContractResolver(); - //设置时间格式 - //options.SerializerSettings.DateFormatString = "yyyy-MM-dd"; - }); + //格式化日期时间格式 + options.JsonSerializerOptions.Converters.Add(new DatetimeJsonConverter("yyyy-MM-dd HH:mm:ss")); + //数据格式首字母小写 + //options.JsonSerializerOptions.PropertyNamingPolicy =JsonNamingPolicy.CamelCase; + //数据格式原样输出 + options.JsonSerializerOptions.PropertyNamingPolicy = null; + //取消Unicode编码 + options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); + //忽略空值 + options.JsonSerializerOptions.IgnoreNullValues = true; + //允许额外符号 + options.JsonSerializerOptions.AllowTrailingCommas = true; + //反序列化过程中属性名称是否使用不区分大小写的比较 + options.JsonSerializerOptions.PropertyNameCaseInsensitive = false; + ////忽略循环引用 + //options.JsonSerializerOptions.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + ////不使用驼峰样式的key + //options.SerializerSettings.ContractResolver = new DefaultContractResolver(); + ////设置时间格式 + //options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + ////忽略Model中为null的属性 + ////options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + ////设置本地时间而非UTC时间 + //options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; + ////添加Enum转string + //options.SerializerSettings.Converters.Add(new StringEnumConverter()); + ////将long类型转为string + //options.SerializerSettings.Converters.Add(new NumberConverter(NumberConverterShip.Int64)); + }); + //全局配置Json序列化处理 + _services = services; } @@ -164,7 +192,7 @@ namespace New_College // 强制实施 HTTPS 在 ASP.NET Core,配合 app.UseHttpsRedirection //app.UseHsts(); } - + app.UseSwaggerAuthorized(); // 封装Swagger展示 app.UseSwaggerMildd(() => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("New_College.Api.index.html")); diff --git a/New_College.Api/appsettings.json b/New_College.Api/appsettings.json index 37c2ef4..5a5fb8d 100644 --- a/New_College.Api/appsettings.json +++ b/New_College.Api/appsettings.json @@ -26,7 +26,7 @@ "Name": "New_College" } }, - "urls": "http://*:8083", // IIS 部署,注释掉 + "urls": "http://*:8082", // IIS 部署,注释掉 "AllowedHosts": "*", "AppSettings": { "RedisCachingAOP": { diff --git a/New_College.Api/wwwroot/BlogCore.Data.json/Department.tsv b/New_College.Api/wwwroot/BlogCore.Data.json/Department.tsv new file mode 100644 index 0000000..dfb887b --- /dev/null +++ b/New_College.Api/wwwroot/BlogCore.Data.json/Department.tsv @@ -0,0 +1 @@ +[{"CodeRelationship":"0,","Name":"BCVP开发社区","Leader":"老张的哲学","OrderSort":0,"Status":true,"IsDeleted":false,"CreateBy":"老张的哲学","CreateTime":"2022-04-01 15:47:25","ModifyTime":"2022-04-01 15:47:25","hasChildren":true,"Pid":0,"Id":1},{"CodeRelationship":"0,","Name":"DDD思想社区组织","Leader":"DDD","OrderSort":0,"Status":true,"IsDeleted":false,"CreateBy":"老张的哲学","CreateTime":"2022-04-01 15:48:08","ModifyTime":"2022-04-01 15:48:08","hasChildren":true,"Pid":0,"Id":2},{"CodeRelationship":"0,1,","Name":"BCVP-北京分部","Leader":"北京","OrderSort":0,"Status":true,"IsDeleted":false,"CreateBy":"老张的哲学","CreateTime":"2022-04-01 15:48:41","ModifyTime":"2022-04-01 15:48:41","hasChildren":true,"Pid":1,"Id":3},{"CodeRelationship":"0,1,","Name":"BCVP-上海分部","Leader":"上海","OrderSort":0,"Status":true,"IsDeleted":false,"CreateBy":"老张的哲学","CreateTime":"2022-04-01 15:49:27","ModifyTime":"2022-04-01 15:49:27","hasChildren":true,"Pid":1,"Id":4},{"CodeRelationship":"0,1,","Name":"BCVP-广州分部","Leader":"广州","OrderSort":0,"Status":true,"IsDeleted":false,"CreateBy":"老张的哲学","CreateTime":"2022-04-01 15:50:23","ModifyTime":"2022-04-01 15:50:44","hasChildren":true,"Pid":1,"Id":5},{"CodeRelationship":"0,1,3,","Name":"前端小组(1群)","Leader":"--","OrderSort":0,"Status":true,"IsDeleted":false,"CreateBy":"老张的哲学","CreateTime":"2022-04-01 15:51:43","ModifyTime":"2022-04-01 15:51:43","hasChildren":true,"Pid":3,"Id":6},{"CodeRelationship":"0,1,4,","Name":"后端小组(2群)","Leader":"--","OrderSort":0,"Status":true,"IsDeleted":false,"CreateBy":"老张的哲学","CreateTime":"2022-04-01 15:58:13","ModifyTime":"2022-04-01 15:58:13","hasChildren":true,"Pid":4,"Id":7},{"CodeRelationship":"0,","Name":"VUE学习联盟","Leader":"vue","OrderSort":0,"Status":true,"IsDeleted":false,"CreateBy":"老张的哲学","CreateTime":"2022-04-01 16:14:21","ModifyTime":"2022-04-01 16:14:21","hasChildren":true,"Pid":0,"Id":8},{"CodeRelationship":"0,8,","Name":"ES指导(1组)","Leader":"es","OrderSort":0,"Status":true,"IsDeleted":false,"CreateBy":"老张的哲学","CreateTime":"2022-04-01 16:14:47","ModifyTime":"2022-04-01 16:15:00","hasChildren":true,"Pid":8,"Id":9}] \ No newline at end of file diff --git a/New_College.Common/App.cs b/New_College.Common/App.cs new file mode 100644 index 0000000..3262ac9 --- /dev/null +++ b/New_College.Common/App.cs @@ -0,0 +1,191 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using New_College.Common.Helper; +using New_College.Common.HttpContextUser; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace New_College.Common; + +public class App +{ + static App() + { + EffectiveTypes = Assemblies.SelectMany(GetTypes); + } + + private static bool _isRun; + + /// 是否正在运行 + public static bool IsBuild { get; set; } + + public static bool IsRun + { + get => _isRun; + set => _isRun = IsBuild = value; + } + + /// 应用有效程序集 + public static readonly IEnumerable Assemblies = RuntimeExtension.GetAllAssemblies(); + + /// 有效程序集类型 + public static readonly IEnumerable EffectiveTypes; + + /// 优先使用App.GetService()手动获取服务 + public static IServiceProvider RootServices => IsRun || IsBuild ? InternalApp.RootServices : null; + + /// 获取Web主机环境,如,是否是开发环境,生产环境等 + public static IWebHostEnvironment WebHostEnvironment => InternalApp.WebHostEnvironment; + + /// 获取泛型主机环境,如,是否是开发环境,生产环境等 + public static IHostEnvironment HostEnvironment => InternalApp.HostEnvironment; + + /// 全局配置选项 + public static IConfiguration Configuration => InternalApp.Configuration; + + /// + /// 获取请求上下文 + /// + public static HttpContext HttpContext => RootServices?.GetService()?.HttpContext; + + public static IUser User => GetService(); + + #region Service + + /// 解析服务提供器 + /// + /// + /// + /// + public static IServiceProvider GetServiceProvider(Type serviceType, bool mustBuild = false, bool throwException = true) + { + if (App.HostEnvironment == null || App.RootServices != null && + InternalApp.InternalServices + .Where((u => + u.ServiceType == + (serviceType.IsGenericType ? serviceType.GetGenericTypeDefinition() : serviceType))) + .Any((u => u.Lifetime == ServiceLifetime.Singleton))) + return App.RootServices; + + //获取请求生存周期的服务 + if (HttpContext?.RequestServices != null) + return HttpContext.RequestServices; + + if (App.RootServices != null) + { + IServiceScope scope = RootServices.CreateScope(); + return scope.ServiceProvider; + } + + if (mustBuild) + { + if (throwException) + { + throw new ApplicationException("当前不可用,必须要等到 WebApplication Build后"); + } + + return default; + } + + ServiceProvider serviceProvider = InternalApp.InternalServices.BuildServiceProvider(); + return serviceProvider; + } + + public static TService GetService(bool mustBuild = true) where TService : class => + App.GetService(typeof(TService), null, mustBuild) as TService; + + /// 获取请求生存周期的服务 + /// + /// + /// + /// + public static TService GetService(IServiceProvider serviceProvider, bool mustBuild = true) + where TService : class => (serviceProvider ?? App.GetServiceProvider(typeof(TService), mustBuild, false))?.GetService(); + + /// 获取请求生存周期的服务 + /// + /// + /// + /// + public static object GetService(Type type, IServiceProvider serviceProvider = null, bool mustBuild = true) => + (serviceProvider ?? App.GetServiceProvider(type, mustBuild, false))?.GetService(type); + + #endregion + + #region private + + /// 加载程序集中的所有类型 + /// + /// + private static IEnumerable GetTypes(Assembly ass) + { + Type[] source = Array.Empty(); + try + { + source = ass.GetTypes(); + } + catch + { + $@"Error load `{ass.FullName}` assembly.".WriteErrorLine(); + } + + return source.Where(u => u.IsPublic); + } + + #endregion + + #region Options + + /// 获取配置 + /// 强类型选项类 + /// TOptions + public static TOptions GetConfig() + where TOptions : class, IConfigurableOptions + { + TOptions instance = App.Configuration + .GetSection(ConfigurableOptions.GetConfigurationPath(typeof(TOptions))) + .Get(); + return instance; + } + + /// 获取选项 + /// 强类型选项类 + /// + /// TOptions + public static TOptions GetOptions(IServiceProvider serviceProvider = null) where TOptions : class, new() + { + IOptions service = App.GetService>(serviceProvider ?? App.RootServices, false); + return service?.Value; + } + + /// 获取选项 + /// 强类型选项类 + /// + /// TOptions + public static TOptions GetOptionsMonitor(IServiceProvider serviceProvider = null) + where TOptions : class, new() + { + IOptionsMonitor service = + App.GetService>(serviceProvider ?? App.RootServices, false); + return service?.CurrentValue; + } + + /// 获取选项 + /// 强类型选项类 + /// + /// TOptions + public static TOptions GetOptionsSnapshot(IServiceProvider serviceProvider = null) + where TOptions : class, new() + { + IOptionsSnapshot service = App.GetService>(serviceProvider, false); + return service?.Value; + } + + #endregion +} \ No newline at end of file diff --git a/New_College.Common/Core/ConfigurableOptions.cs b/New_College.Common/Core/ConfigurableOptions.cs new file mode 100644 index 0000000..5cba3e1 --- /dev/null +++ b/New_College.Common/Core/ConfigurableOptions.cs @@ -0,0 +1,61 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; + +namespace New_College.Common; + +public static class ConfigurableOptions +{ + /// 添加选项配置 + /// 选项类型 + /// 服务集合 + /// 服务集合 + public static IServiceCollection AddConfigurableOptions(this IServiceCollection services) + where TOptions : class, IConfigurableOptions + { + Type optionsType = typeof(TOptions); + string path = GetConfigurationPath(optionsType); + services.Configure(App.Configuration.GetSection(path)); + + return services; + } + + public static IServiceCollection AddConfigurableOptions(this IServiceCollection services, Type type) + { + string path = GetConfigurationPath(type); + var config = App.Configuration.GetSection(path); + + Type iOptionsChangeTokenSource = typeof(IOptionsChangeTokenSource<>); + Type iConfigureOptions = typeof(IConfigureOptions<>); + Type configurationChangeTokenSource = typeof(ConfigurationChangeTokenSource<>); + Type namedConfigureFromConfigurationOptions = typeof(NamedConfigureFromConfigurationOptions<>); + iOptionsChangeTokenSource = iOptionsChangeTokenSource.MakeGenericType(type); + iConfigureOptions = iConfigureOptions.MakeGenericType(type); + configurationChangeTokenSource = configurationChangeTokenSource.MakeGenericType(type); + namedConfigureFromConfigurationOptions = namedConfigureFromConfigurationOptions.MakeGenericType(type); + + services.AddOptions(); + services.AddSingleton(iOptionsChangeTokenSource, + Activator.CreateInstance(configurationChangeTokenSource, Options.DefaultName, config) ?? throw new InvalidOperationException()); + return services.AddSingleton(iConfigureOptions, + Activator.CreateInstance(namedConfigureFromConfigurationOptions, Options.DefaultName, config) ?? throw new InvalidOperationException()); + } + + /// 获取配置路径 + /// 选项类型 + /// + public static string GetConfigurationPath(Type optionsType) + { + var endPath = new[] {"Option", "Options"}; + var configurationPath = optionsType.Name; + foreach (var s in endPath) + { + if (configurationPath.EndsWith(s)) + { + return configurationPath[..^s.Length]; + } + } + + return configurationPath; + } +} \ No newline at end of file diff --git a/New_College.Common/Core/IConfigurableOptions.cs b/New_College.Common/Core/IConfigurableOptions.cs new file mode 100644 index 0000000..092c784 --- /dev/null +++ b/New_College.Common/Core/IConfigurableOptions.cs @@ -0,0 +1,11 @@ +namespace New_College.Common; + +/// +/// 应用选项依赖接口
+/// 自动注入配置文件
+/// 文件名为Option或Options结尾 +///
+public interface IConfigurableOptions +{ + +} \ No newline at end of file diff --git a/New_College.Common/GlobalVar/GlobalVars.cs b/New_College.Common/GlobalVar/GlobalVars.cs index 38c0086..d862b29 100644 --- a/New_College.Common/GlobalVar/GlobalVars.cs +++ b/New_College.Common/GlobalVar/GlobalVars.cs @@ -12,6 +12,14 @@ /// true:表示启动IDS4 /// false:表示使用JWT public static bool IsUseIds4 = false; + + + + /// + /// 当前项目是否启用Authing权限方案 + /// true:表示启动 + /// false:表示使用JWT + public static bool IsUseAuthing = false; } /// @@ -24,6 +32,6 @@ /// 如果不需要,尽量留空,不要修改 /// 除非一定要在所有的 api 前统一加上特定前缀 /// - public const string Name = ""; + public static string Name = ""; } } diff --git a/New_College.Common/Helper/DatetimeJsonConverter.cs b/New_College.Common/Helper/DatetimeJsonConverter.cs new file mode 100644 index 0000000..422ea38 --- /dev/null +++ b/New_College.Common/Helper/DatetimeJsonConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace New_College.Common.Helper +{ + /// + /// 格式化返回的时间格式 + /// + public class DatetimeJsonConverter : JsonConverter + { + private readonly string format; + public DatetimeJsonConverter(string _format) + { + format = _format; + } + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + if (DateTime.TryParse(reader.GetString(), out DateTime date)) + return date; + } + return reader.GetDateTime(); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(format)); + } + } +} diff --git a/New_College.Common/Helper/JsonHelper.cs b/New_College.Common/Helper/JsonHelper.cs index d35b566..d1a52dc 100644 --- a/New_College.Common/Helper/JsonHelper.cs +++ b/New_College.Common/Helper/JsonHelper.cs @@ -3,11 +3,19 @@ using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; +using System.Text.Json; namespace New_College.Common.Helper { + + + public class JsonHelper { + + + + /// /// Json 序列化配置 /// diff --git a/New_College.Common/Helper/NumberConverter.cs b/New_College.Common/Helper/NumberConverter.cs new file mode 100644 index 0000000..74c8930 --- /dev/null +++ b/New_College.Common/Helper/NumberConverter.cs @@ -0,0 +1,177 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace New_College.Common.Helper +{ + + + + /// + /// + /// 大数据json序列化重写 + /// + public sealed class NumberConverter : JsonConverter + { + /// + /// 转换成字符串的类型 + /// + private readonly NumberConverterShip _ship; + + /// + /// 大数据json序列化重写实例化 + /// + public NumberConverter() + { + _ship = (NumberConverterShip)0xFF; + } + + /// + /// 大数据json序列化重写实例化 + /// + /// 转换成字符串的类型 + public NumberConverter(NumberConverterShip ship) + { + _ship = ship; + } + + /// + /// + /// 确定此实例是否可以转换指定的对象类型。 + /// + /// 对象的类型。 + /// 如果此实例可以转换指定的对象类型,则为:true,否则为:false + public override bool CanConvert(Type objectType) + { + var typecode = Type.GetTypeCode(objectType.Name.Equals("Nullable`1") ? objectType.GetGenericArguments().First() : objectType); + switch (typecode) + { + case TypeCode.Decimal: + return (_ship & NumberConverterShip.Decimal) == NumberConverterShip.Decimal; + case TypeCode.Double: + return (_ship & NumberConverterShip.Double) == NumberConverterShip.Double; + case TypeCode.Int64: + return (_ship & NumberConverterShip.Int64) == NumberConverterShip.Int64; + case TypeCode.UInt64: + return (_ship & NumberConverterShip.UInt64) == NumberConverterShip.UInt64; + case TypeCode.Single: + return (_ship & NumberConverterShip.Single) == NumberConverterShip.Single; + default: return false; + } + } + + /// + /// + /// 读取对象的JSON表示。 + /// + /// 中读取。 + /// 对象的类型。 + /// 正在读取的对象的现有值。 + /// 调用的序列化器实例。 + /// 对象值。 + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return AsType(reader.Value.ObjToString(), objectType); + } + + /// + /// 字符串格式数据转其他类型数据 + /// + /// 输入的字符串 + /// 目标格式 + /// 转换结果 + public static object AsType(string input, Type destinationType) + { + try + { + var converter = TypeDescriptor.GetConverter(destinationType); + if (converter.CanConvertFrom(typeof(string))) + { + return converter.ConvertFrom(null, null, input); + } + + converter = TypeDescriptor.GetConverter(typeof(string)); + if (converter.CanConvertTo(destinationType)) + { + return converter.ConvertTo(null, null, input, destinationType); + } + } + catch + { + return null; + } + return null; + } + + /// + /// + /// 写入对象的JSON表示形式。 + /// + /// 要写入的 。 + /// 要写入对象值 + /// 调用的序列化器实例。 + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + } + else + { + var objectType = value.GetType(); + var typeCode = Type.GetTypeCode(objectType.Name.Equals("Nullable`1") ? objectType.GetGenericArguments().First() : objectType); + switch (typeCode) + { + case TypeCode.Decimal: + writer.WriteValue(((decimal)value).ToString("f6")); + break; + case TypeCode.Double: + writer.WriteValue(((double)value).ToString("f4")); + break; + case TypeCode.Single: + writer.WriteValue(((float)value).ToString("f2")); + break; + default: + writer.WriteValue(value.ToString()); + break; + } + } + } + } + + /// + /// 转换成字符串的类型 + /// + [Flags] + public enum NumberConverterShip + { + /// + /// 长整数 + /// + Int64 = 1, + + /// + /// 无符号长整数 + /// + UInt64 = 2, + + /// + /// 浮点数 + /// + Single = 4, + + /// + /// 双精度浮点数 + /// + Double = 8, + + /// + /// 大数字 + /// + Decimal =16 + } +} diff --git a/New_College.Common/HttpContextExtension.cs b/New_College.Common/HttpContextExtension.cs new file mode 100644 index 0000000..5bfd21c --- /dev/null +++ b/New_College.Common/HttpContextExtension.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.AspNetCore.Http; + +namespace New_College.Extensions; + +public static class HttpContextExtension +{ + public static ISession GetSession(this HttpContext context) + { + try + { + return context.Session; + } + catch (Exception) + { + return default; + } + } +} \ No newline at end of file diff --git a/New_College.Common/InternalApp.cs b/New_College.Common/InternalApp.cs new file mode 100644 index 0000000..8ab6db3 --- /dev/null +++ b/New_College.Common/InternalApp.cs @@ -0,0 +1,45 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; + +namespace New_College; + +/// +/// 内部只用于初始化使用 +/// +public static class InternalApp +{ + internal static IServiceCollection InternalServices; + + /// 根服务 + internal static IServiceProvider RootServices; + + /// 获取Web主机环境 + internal static IWebHostEnvironment WebHostEnvironment; + + /// 获取泛型主机环境 + internal static IHostEnvironment HostEnvironment; + + /// 配置对象 + internal static IConfiguration Configuration; + + public static void ConfigureApplication(this WebApplicationBuilder wab) + { + HostEnvironment = wab.Environment; + WebHostEnvironment = wab.Environment; + InternalServices = wab.Services; + } + + public static void ConfigureApplication(this IConfiguration configuration) + { + Configuration = configuration; + } + + public static void ConfigureApplication(this IHost app) + { + RootServices = app.Services; + } +} \ No newline at end of file diff --git a/New_College.Common/RuntimeExtension.cs b/New_College.Common/RuntimeExtension.cs new file mode 100644 index 0000000..433285c --- /dev/null +++ b/New_College.Common/RuntimeExtension.cs @@ -0,0 +1,86 @@ +using Microsoft.Extensions.DependencyModel; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; + +namespace New_College; + +public static class RuntimeExtension +{ + /// + /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包 + /// + /// + public static IList GetAllAssemblies() + { + var list = new List(); + var deps = DependencyContext.Default; + //只加载项目中的程序集 + var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type == "project"); //排除所有的系统程序集、Nuget下载包 + foreach (var lib in libs) + { + try + { + var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); + list.Add(assembly); + } + catch (Exception e) + { + Log.Debug(e, "GetAllAssemblies Exception:{ex}", e.Message); + } + } + + return list; + } + + public static Assembly GetAssembly(string assemblyName) + { + return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName)); + } + + public static IList GetAllTypes() + { + var list = new List(); + foreach (var assembly in GetAllAssemblies()) + { + var typeInfos = assembly.DefinedTypes; + foreach (var typeInfo in typeInfos) + { + list.Add(typeInfo.AsType()); + } + } + + return list; + } + + public static IList GetTypesByAssembly(string assemblyName) + { + var list = new List(); + var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName)); + var typeInfos = assembly.DefinedTypes; + foreach (var typeInfo in typeInfos) + { + list.Add(typeInfo.AsType()); + } + + return list; + } + + public static Type GetImplementType(string typeName, Type baseInterfaceType) + { + return GetAllTypes().FirstOrDefault(t => + { + if (t.Name == typeName && + t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name)) + { + var typeInfo = t.GetTypeInfo(); + return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType; + } + + return false; + }); + } +} \ No newline at end of file diff --git a/New_College.Common/Swagger/SwaggerAuthMiddleware.cs b/New_College.Common/Swagger/SwaggerAuthMiddleware.cs new file mode 100644 index 0000000..b923fc6 --- /dev/null +++ b/New_College.Common/Swagger/SwaggerAuthMiddleware.cs @@ -0,0 +1,76 @@ +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +namespace New_College.Common +{ + public class SwaggerAuthMiddleware + { + + private readonly RequestDelegate next; + + public SwaggerAuthMiddleware(RequestDelegate next) + { + this.next = next; + } + + public async Task InvokeAsync(HttpContext context) + { + // 也可以根据是否是本地做判断 IsLocalRequest + if (context.Request.Path.Value.ToLower().Contains("index.html")) + { + // 判断权限是否正确 + if (IsAuthorized(context)) + { + await next.Invoke(context); + return; + } + + // 无权限,跳转swagger登录页 + // context.RedirectSwaggerLogin(); + } + else + { + await next.Invoke(context); + } + } + + public bool IsAuthorized(HttpContext context) + { + //使用session模式 + //可以使用其他的 + return context.IsSuccessSwagger(); + } + + /// + /// 判断是不是本地访问 + /// 本地不用swagger拦截 + /// + /// + /// + public bool IsLocalRequest(HttpContext context) + { + if (context.Connection.RemoteIpAddress == null && context.Connection.LocalIpAddress == null) + { + return true; + } + if (context.Connection.RemoteIpAddress.Equals(context.Connection.LocalIpAddress)) + { + return true; + } + if (IPAddress.IsLoopback(context.Connection.RemoteIpAddress)) + { + return true; + } + return false; + } + } + public static class SwaggerAuthorizeExtensions + { + public static IApplicationBuilder UseSwaggerAuthorized(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} diff --git a/New_College.Common/Swagger/SwaggerContextExtension.cs b/New_College.Common/Swagger/SwaggerContextExtension.cs new file mode 100644 index 0000000..ee56e53 --- /dev/null +++ b/New_College.Common/Swagger/SwaggerContextExtension.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using New_College.Extensions; + +namespace New_College.Common; +public static class SwaggerContextExtension +{ + public const string SwaggerCodeKey = "swagger-code"; + public const string SwaggerJwt = "swagger-jwt"; + + public static bool IsSuccessSwagger() + { + return App.HttpContext?.GetSession()?.GetString(SwaggerCodeKey) == "success"; + } + + public static bool IsSuccessSwagger(this HttpContext context) + { + return context.GetSession()?.GetString(SwaggerCodeKey) == "success"; + } + + public static void SuccessSwagger() + { + App.HttpContext?.GetSession()?.SetString(SwaggerCodeKey, "success"); + } + + public static void SuccessSwagger(this HttpContext context) + { + context.GetSession()?.SetString(SwaggerCodeKey, "success"); + } + + public static void SuccessSwaggerJwt(this HttpContext context, string token) + { + context.GetSession()?.SetString(SwaggerJwt, token); + } + + public static string GetSuccessSwaggerJwt(this HttpContext context) + { + return context.GetSession()?.GetString(SwaggerJwt); + } + + + public static void RedirectSwaggerLogin(this HttpContext context) + { + var returnUrl = context.Request.GetDisplayUrl(); //获取当前url地址 + context.Response.Redirect("/swg-login.html?returnUrl=" + returnUrl); + } +} \ No newline at end of file diff --git a/New_College.Extensions/New_College.Extensions.csproj b/New_College.Extensions/New_College.Extensions.csproj index 273682d..52e0b1f 100644 --- a/New_College.Extensions/New_College.Extensions.csproj +++ b/New_College.Extensions/New_College.Extensions.csproj @@ -15,8 +15,9 @@ - - + + + diff --git a/New_College.Extensions/ServiceExtensions/AllOptionRegister.cs b/New_College.Extensions/ServiceExtensions/AllOptionRegister.cs new file mode 100644 index 0000000..af050e3 --- /dev/null +++ b/New_College.Extensions/ServiceExtensions/AllOptionRegister.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using New_College.Common; +using System; +using System.Linq; + +namespace New_College.ServiceExtensions; + +public static class AllOptionRegister +{ + public static void AddAllOptionRegister(this IServiceCollection services) + { + if (services == null) throw new ArgumentNullException(nameof(services)); + + foreach (var optionType in App.EffectiveTypes.Where(s => + !s.IsInterface && typeof(IConfigurableOptions).IsAssignableFrom(s))) + { + services.AddConfigurableOptions(optionType); + } + } +} \ No newline at end of file diff --git a/New_College.Extensions/ServiceExtensions/HttpContextExtension.cs b/New_College.Extensions/ServiceExtensions/HttpContextExtension.cs new file mode 100644 index 0000000..5bfd21c --- /dev/null +++ b/New_College.Extensions/ServiceExtensions/HttpContextExtension.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.AspNetCore.Http; + +namespace New_College.Extensions; + +public static class HttpContextExtension +{ + public static ISession GetSession(this HttpContext context) + { + try + { + return context.Session; + } + catch (Exception) + { + return default; + } + } +} \ No newline at end of file diff --git a/New_College.Extensions/ServiceExtensions/SwaggerSetup.cs b/New_College.Extensions/ServiceExtensions/SwaggerSetup.cs index a8a8139..d50519b 100644 --- a/New_College.Extensions/ServiceExtensions/SwaggerSetup.cs +++ b/New_College.Extensions/ServiceExtensions/SwaggerSetup.cs @@ -106,6 +106,7 @@ namespace New_College.Extensions }); + // services.AddSwaggerGenNewtonsoftSupport(); } } diff --git a/New_College.Extensions/ServiceExtensions/UiFilesZipSetup.cs b/New_College.Extensions/ServiceExtensions/UiFilesZipSetup.cs new file mode 100644 index 0000000..c531ad8 --- /dev/null +++ b/New_College.Extensions/ServiceExtensions/UiFilesZipSetup.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.IO; +using System.IO.Compression; + +namespace New_College.Extensions +{ + /// + /// 将前端UI压缩文件进行解压 + /// + public static class UiFilesZipSetup + { + public static void AddUiFilesZipSetup(this IServiceCollection services, IWebHostEnvironment _env) + { + if (services == null) throw new ArgumentNullException(nameof(services)); + + string wwwrootFolderPath = Path.Combine(_env.ContentRootPath, "wwwroot"); + string zipUiItemFiles = Path.Combine(wwwrootFolderPath, "ui.zip"); + if (!File.Exists(Path.Combine(wwwrootFolderPath, "ui", "index.html"))) + { + ZipFile.ExtractToDirectory(zipUiItemFiles, wwwrootFolderPath); + } + } + } +} diff --git a/New_College.Model/ViewModels/Result/uniMajorSecond.cs b/New_College.Model/ViewModels/Result/uniMajorSecond.cs index ca6314e..f2d7b80 100644 --- a/New_College.Model/ViewModels/Result/uniMajorSecond.cs +++ b/New_College.Model/ViewModels/Result/uniMajorSecond.cs @@ -10,6 +10,7 @@ namespace New_College.Model.ViewModels { public int Id { get; set; } public string Name { get; set; } + public string Code { get; set; } } public class uniMajorClassSelect @@ -17,6 +18,8 @@ namespace New_College.Model.ViewModels public int TradeId { get; set; } public int Id { get; set; } public string Name { get; set; } + + public string Code { get; set; } } /// diff --git a/New_College.Repository/BASE/D_UniversityRankRepository.cs b/New_College.Repository/BASE/D_UniversityRankRepository.cs index 754b13e..e994c66 100644 --- a/New_College.Repository/BASE/D_UniversityRankRepository.cs +++ b/New_College.Repository/BASE/D_UniversityRankRepository.cs @@ -26,7 +26,7 @@ namespace New_College.Repository var response = await this.Db.Queryable((rank, u) => new object[] { JoinType.Left, rank.UniversityName == u.Name }) .Where((rank, u) => rank.UniversityType == query.Type) .WhereIF(!string.IsNullOrWhiteSpace(query.Name), (rank, u) => SqlFunc.Contains(rank.UniversityName, query.Name)) - .WhereIF(query.SubjectType.HasValue, (rank, u) => u.Subject_Level == query.SubjectType) + .WhereIF(query.SubjectType.HasValue, (rank, u) => u.Type == query.SubjectType) .OrderBy((rank, u) => rank.Rank, OrderByType.Asc) .Select((rank, u) => new UniversityPcRankList { diff --git a/New_College.Services/D_LongIdMapServices.cs b/New_College.Services/D_LongIdMapServices.cs index d43fb69..f9a0612 100644 --- a/New_College.Services/D_LongIdMapServices.cs +++ b/New_College.Services/D_LongIdMapServices.cs @@ -220,7 +220,7 @@ namespace New_College.Services { var result = new List(); var classmajors = (await d_MajorClassRepository.Query(x => x.IsDelete == false && x.TradeId == tradeId)).Select(x => new uniMajorClassSelect() { Id = x.Id, Name = x.Name, TradeId = x.TradeId }).ToList(); - var majorinfo = (await d_MajorRepository.Query(x => x.IsDelete == false)).Select(s => new uniMajorClassSelect() { Id = s.Id, TradeId = s.CategoryClass_Id, Name = s.Name }).ToList(); + var majorinfo = (await d_MajorRepository.Query(x => x.IsDelete == false)).Select(s => new uniMajorClassSelect() { Id = s.Id, TradeId = s.CategoryClass_Id, Name = s.Name,Code=s.MajorCode }).ToList(); classmajors.ForEach(a => { var models = new TreeMajorInfoResult(); @@ -229,7 +229,8 @@ namespace New_College.Services models.MajorsInfo = majorinfo.Where(e => e.TradeId == a.Id).Select(s => new uniMajorSelect() { Id = s.Id, - Name = s.Name + Name = s.Name, + Code=s.Code }).ToList(); result.Add(models); });