From 494251c76a9a22404652f5919bd91985f634b9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=82=A5=E7=BE=8A?= <1048382248@qq.com> Date: Fri, 11 Jul 2025 18:24:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 30 +++ Learn.Archives.API.sln | 31 +++ Learn.Archives.API/Dockerfile | 29 +++ Learn.Archives.API/Learn.Archives.API.csproj | 24 ++ Learn.Archives.API/Learn.Archives.API.http | 6 + Learn.Archives.API/Program.cs | 69 ++++++ .../Properties/launchSettings.json | 14 ++ .../appsettings.Production.json | 29 +++ Learn.Archives.API/appsettings.json | 32 +++ Learn.Archives.Core/Common/AppCommon.cs | 158 ++++++++++++ Learn.Archives.Core/Common/AppConfig.cs | 214 ++++++++++++++++ .../Common/BasicAuthMiddleware.cs | 56 +++++ Learn.Archives.Core/Common/ExceptionFilter.cs | 39 +++ .../Common/Expand/AlibabaCloudVodExpand.cs | 56 +++++ .../Common/Expand/AliyunOSSExpand.cs | 116 +++++++++ .../Common/Expand/AppConfigExpand.cs | 28 +++ .../Common/Expand/CorsExpand.cs | 41 ++++ .../Common/Expand/SimpLetexExpand.cs | 230 ++++++++++++++++++ .../Common/Expand/SqlSugarExpand.cs | 169 +++++++++++++ Learn.Archives.Core/Common/JsonExtractor.cs | 105 ++++++++ Learn.Archives.Core/Common/RedisExpand.cs | 54 ++++ Learn.Archives.Core/Common/Repository.cs | 37 +++ .../Learn.Archives.Core.csproj | 35 +++ Learn.Archives.Core/Model/Admin.cs | 43 ++++ .../Model/Enum/UserStatusEnum.cs | 14 ++ Learn.Archives.Core/Model/Interface/IDB.cs | 15 ++ Learn.Archives.Core/Model/User.cs | 65 +++++ 27 files changed, 1739 insertions(+) create mode 100644 .dockerignore create mode 100644 Learn.Archives.API.sln create mode 100644 Learn.Archives.API/Dockerfile create mode 100644 Learn.Archives.API/Learn.Archives.API.csproj create mode 100644 Learn.Archives.API/Learn.Archives.API.http create mode 100644 Learn.Archives.API/Program.cs create mode 100644 Learn.Archives.API/Properties/launchSettings.json create mode 100644 Learn.Archives.API/appsettings.Production.json create mode 100644 Learn.Archives.API/appsettings.json create mode 100644 Learn.Archives.Core/Common/AppCommon.cs create mode 100644 Learn.Archives.Core/Common/AppConfig.cs create mode 100644 Learn.Archives.Core/Common/BasicAuthMiddleware.cs create mode 100644 Learn.Archives.Core/Common/ExceptionFilter.cs create mode 100644 Learn.Archives.Core/Common/Expand/AlibabaCloudVodExpand.cs create mode 100644 Learn.Archives.Core/Common/Expand/AliyunOSSExpand.cs create mode 100644 Learn.Archives.Core/Common/Expand/AppConfigExpand.cs create mode 100644 Learn.Archives.Core/Common/Expand/CorsExpand.cs create mode 100644 Learn.Archives.Core/Common/Expand/SimpLetexExpand.cs create mode 100644 Learn.Archives.Core/Common/Expand/SqlSugarExpand.cs create mode 100644 Learn.Archives.Core/Common/JsonExtractor.cs create mode 100644 Learn.Archives.Core/Common/RedisExpand.cs create mode 100644 Learn.Archives.Core/Common/Repository.cs create mode 100644 Learn.Archives.Core/Learn.Archives.Core.csproj create mode 100644 Learn.Archives.Core/Model/Admin.cs create mode 100644 Learn.Archives.Core/Model/Enum/UserStatusEnum.cs create mode 100644 Learn.Archives.Core/Model/Interface/IDB.cs create mode 100644 Learn.Archives.Core/Model/User.cs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fe1152b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/Learn.Archives.API.sln b/Learn.Archives.API.sln new file mode 100644 index 0000000..a8eb312 --- /dev/null +++ b/Learn.Archives.API.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35312.102 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Learn.Archives.API", "Learn.Archives.API\Learn.Archives.API.csproj", "{A5B8EE1A-5049-40FE-8498-48DC66D3B549}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Learn.Archives.Core", "Learn.Archives.Core\Learn.Archives.Core.csproj", "{E7F191F2-A871-476F-8E12-CB542DAA2B82}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A5B8EE1A-5049-40FE-8498-48DC66D3B549}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5B8EE1A-5049-40FE-8498-48DC66D3B549}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5B8EE1A-5049-40FE-8498-48DC66D3B549}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5B8EE1A-5049-40FE-8498-48DC66D3B549}.Release|Any CPU.Build.0 = Release|Any CPU + {E7F191F2-A871-476F-8E12-CB542DAA2B82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7F191F2-A871-476F-8E12-CB542DAA2B82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7F191F2-A871-476F-8E12-CB542DAA2B82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7F191F2-A871-476F-8E12-CB542DAA2B82}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F40DADFE-A8CA-4AFB-9292-68E85076414B} + EndGlobalSection +EndGlobal diff --git a/Learn.Archives.API/Dockerfile b/Learn.Archives.API/Dockerfile new file mode 100644 index 0000000..7a3bfbe --- /dev/null +++ b/Learn.Archives.API/Dockerfile @@ -0,0 +1,29 @@ +# 请参阅 https://aka.ms/customizecontainer 以了解如何自定义调试容器,以及 Visual Studio 如何使用此 Dockerfile 生成映像以更快地进行调试。 + +# 此阶段用于在快速模式(默认为调试配置)下从 VS 运行时 +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER app +WORKDIR /app +EXPOSE 8080 + + +# 此阶段用于生成服务项目 +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["Learn.Archives.API/Learn.Archives.API.csproj", "Learn.Archives.API/"] +RUN dotnet restore "./Learn.Archives.API/Learn.Archives.API.csproj" +COPY . . +WORKDIR "/src/Learn.Archives.API" +RUN dotnet build "./Learn.Archives.API.csproj" -c $BUILD_CONFIGURATION -o /app/build + +# 此阶段用于发布要复制到最终阶段的服务项目 +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./Learn.Archives.API.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +# 此阶段在生产中使用,或在常规模式下从 VS 运行时使用(在不使用调试配置时为默认值) +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Learn.Archives.API.dll"] \ No newline at end of file diff --git a/Learn.Archives.API/Learn.Archives.API.csproj b/Learn.Archives.API/Learn.Archives.API.csproj new file mode 100644 index 0000000..df5547a --- /dev/null +++ b/Learn.Archives.API/Learn.Archives.API.csproj @@ -0,0 +1,24 @@ + + + + net8.0 + enable + enable + Linux + + + + + + + + + + + + + + + + + diff --git a/Learn.Archives.API/Learn.Archives.API.http b/Learn.Archives.API/Learn.Archives.API.http new file mode 100644 index 0000000..a49bb40 --- /dev/null +++ b/Learn.Archives.API/Learn.Archives.API.http @@ -0,0 +1,6 @@ +@Learn.Archives.API_HostAddress = http://localhost:5199 + +GET {{Learn.Archives.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/Learn.Archives.API/Program.cs b/Learn.Archives.API/Program.cs new file mode 100644 index 0000000..252e568 --- /dev/null +++ b/Learn.Archives.API/Program.cs @@ -0,0 +1,69 @@ +using Learn.Archives.Core.Common; +using Microsoft.OpenApi.Models; +using System.Text.Encodings.Web; +using System.Text.Unicode; +using Learn.Archives.Core.Common.Expand; +using Mapster; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddLogging(loggingBuilder => +{ + loggingBuilder.ClearProviders(); // Ĭϵ־ṩ + loggingBuilder.AddConsole(); // ӿ̨־ṩ + loggingBuilder.SetMinimumLevel(LogLevel.Warning); // С־Ϊ Warning +}); + +builder.Services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); + }); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new OpenApiInfo + { + Version = "v1", + Description = "ѧУϵͳv1" + }); + var file = Path.Combine(AppContext.BaseDirectory, "Learn.Archives.API.xml"); // xmlĵ· + c.IncludeXmlComments(file, true); // true : ʾע + c.OrderActionsBy(o => o.RelativePath); // actionƽжͿԿЧˡ +}); + +builder.Configuration.AddAppConfig(args); +builder.Services.AddSqlSugarExpand(); +builder.Services.AddRedisExpand(); +builder.Services.AddCorsExpand(); +builder.Services.AddMapster(); +builder.Services.AddCorsExpand(); +builder.Services.AddHttpClient(); +builder.Services.AddHttpContextAccessor(); + +//쳣 +builder.Services.AddControllersWithViews(options => +{ + options.Filters.Add(typeof(ExceptionFilter)); +}); + + +var app = builder.Build(); + +app.UseMiddleware("Swagger"); + + +app.UseSwagger(); +app.UseSwaggerUI(); + +//Զ Ӧ +app.UseCorsExpand(); +app.UseSqlSugarExpand(); + +app.UseAuthorization(); + + +app.MapControllers(); + +app.Run(); diff --git a/Learn.Archives.API/Properties/launchSettings.json b/Learn.Archives.API/Properties/launchSettings.json new file mode 100644 index 0000000..5aea7d4 --- /dev/null +++ b/Learn.Archives.API/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "profiles": { + "http5199": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5199" + }, + }, +} \ No newline at end of file diff --git a/Learn.Archives.API/appsettings.Production.json b/Learn.Archives.API/appsettings.Production.json new file mode 100644 index 0000000..5c5ba0a --- /dev/null +++ b/Learn.Archives.API/appsettings.Production.json @@ -0,0 +1,29 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft.AspNetCore": "Error" + } + }, + "Redis": { + "ConnectionString": "redis-external.23544.com:16379,password=poiuyt)(*&^%,defaultDatabase=3" + }, + "FFmpeg": { + " TimeSlice": 600 + }, + "AliyunOSS": { + "AccessKeyId": "LTAI5tDC6p9h747B7FHbgwkH", + "AccessKeySecret": "vRKgmbp1LB05LaGOjh3ZrZxbHSLYLF", + "BucketDomain": "https://learn-videoanalysis.oss-cn-chengdu.aliyuncs.com", + "Region": "cn-chengdu", + "BucketName": "learn-videoanalysis", + "EndPoint": "oss-cn-chengdu.aliyuncs.com" //上传节点 + }, + "DB": { + //"ConnectionString": "AllowLoadLocalInfile=true;Server=10.255.255.3;Port=3306;Database=learn.videoanalysis;User ID=marking;Password=qwe123!@#;CharSet=utf8mb4;pooling=true;SslMode=None", + "ConnectionString": "AllowLoadLocalInfile=true;Server=rm-2vc20nd3d11g0oh6g2o.rwlb.cn-chengdu.rds.aliyuncs.com;User ID=marking;Password=poiuytPOIUYT098765)(*&^%;Port=3306;Database=learn.videoanalysis;CharSet=utf8mb4;pooling=true;SslMode=None", + "SqlType": "MySql", + "UpdateTable": false + } + } +} diff --git a/Learn.Archives.API/appsettings.json b/Learn.Archives.API/appsettings.json new file mode 100644 index 0000000..1c0d039 --- /dev/null +++ b/Learn.Archives.API/appsettings.json @@ -0,0 +1,32 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "AppConfig": { + "ID": 1, //程序唯一值 + "SimpLetex": { + "Host": "https://server.simpletex.cn/api/", + "AppSecret": "05ZbPfCFZgTmfd4uIqHHc9pHgYR2V8bk", + "AppId": "GH2OXwuxSZEH5W28H61bdSzD" + }, + "Redis": { + "ConnectionString": "127.0.0.1:6379,password=Woshiren123,defaultDatabase=10" + }, + "DB": { + "ConnectionString": "AllowLoadLocalInfile=true;Server=192.168.2.9;User ID=root;Password=qwe123!@#;Port=3306;Database=learn.videoanalysis;CharSet=utf8mb4;pooling=true;SslMode=None", + "SqlType": "MySql", + "UpdateTable": false + }, + "OtherDBArr": [ + //{ + // "ConfigId": 1001, //ResourceBank + // "ConnectionString": "Server=47.109.35.116;Database=ResourceBank;UID=live;Password=Woshiren^&*();MultipleActiveResultSets=true;Encrypt=True;TrustServerCertificate=True;", + // "SqlType": "SqlServer" + //} + ] + } +} diff --git a/Learn.Archives.Core/Common/AppCommon.cs b/Learn.Archives.Core/Common/AppCommon.cs new file mode 100644 index 0000000..c4603f1 --- /dev/null +++ b/Learn.Archives.Core/Common/AppCommon.cs @@ -0,0 +1,158 @@ +using FreeRedis; +using Learn.Archives.Core.Model; +using Learn.Archives.Core.Model.Interface; +using Microsoft.Extensions.DependencyModel; +using Microsoft.IdentityModel.Tokens; +using SqlSugar; +using SqlSugar.IOC; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.PortableExecutable; +using System.Runtime.Loader; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using UserCenter.Model.Interface; + +namespace Learn.Archives.Core.Common +{ + + /// + /// 程序 公共变量 + /// + public static class AppCommon + { + /// + /// 应用有效程序集 + /// + public static readonly IEnumerable Assemblies; + /// + /// 主库数据库表类型 + /// + public static readonly IEnumerable DbMatserType; + public static readonly IEnumerable KnowsType; + static AppCommon() + { + try + { + Assemblies = ExpandFunction.GetAssemblies(); + var assembliesType = Assemblies.Where(s => s.FullName.Contains("VideoAnalysis")).SelectMany(s => s.ExportedTypes + .Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false))); + DbMatserType = assembliesType + .Where(u => u.GetInterfaces().Contains(typeof(IDB))); + } + catch + { + throw; + } + } + /// + /// 程序配置 + /// + public static AppConfig Config = new AppConfig(); + /// + /// ServiceProvider + /// + public static IServiceProvider? Services; + + } + + /// + /// 拓展函数 + /// + public static class ExpandFunction + { + + static Dictionary FormulaData; + static string FormulaDataKey; + /// + /// 帧文件名称 + /// + public static string FrameName = "frame_"; + + /// + /// 对象转化为JSON字符串 + /// + /// 拓展对象 + /// 美化输出? + /// + public static string ToJson(this object o, bool WriteIndented = false) + { + var jsonOptions = new JsonSerializerOptions + { + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + WriteIndented = WriteIndented // 如果需要美化输出 + }; + return JsonSerializer.Serialize(o, jsonOptions); + } + + + /// + /// 获取应用有效程序集 + /// + /// IEnumerable + public static List GetAssemblies() + { + // 获取当前解决方案的所有程序集 + var assembliesStr = DependencyContext.Default.RuntimeLibraries + .Where(u => !u.Name.StartsWith(nameof(Microsoft)) + && !u.Name.StartsWith(nameof(System)) + && !u.Name.StartsWith("netstandard") + && u.Type == "project"); + + var assemblies = assembliesStr.Select(a => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(a.Name))).ToList(); + var assemblies1 = Assembly.GetEntryAssembly().GetReferencedAssemblies().Where(x => x.Name.StartsWith("App.") || x.Name.StartsWith("UserCenter.")) + .Select(a => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(a.Name))).ToList(); + foreach (var item in assemblies1) + { + if (!assemblies.Contains(item)) + assemblies.Add(item); + } + return assemblies; + } + + /// + /// 获取下一个枚举值 + /// + /// + /// + /// + /// + public static T? NextEnum(this T current) where T : struct, Enum + { + if (!typeof(T).IsEnum) + throw new ArgumentException("传入类型不是枚举"); + T[] values = (T[])Enum.GetValues(typeof(T)); + int currentIndex = Array.IndexOf(values, current); + if (currentIndex == values.Length - 1) + return null; + int nextIndex = (currentIndex + 1) % values.Length; + return values[nextIndex]; + } + /// + /// 转化枚举 + /// + /// + /// + public static T? ToEnum(this object value) where T : struct, Enum + { + try + { + if (value is null || string.IsNullOrEmpty(value.ToString())) + return null; + if (Enum.TryParse(value.ToString(), true, out var result) && Enum.IsDefined(typeof(T), result)) + return result; + return null; + } + catch (Exception) + { + return null; + } + } + + } + +} diff --git a/Learn.Archives.Core/Common/AppConfig.cs b/Learn.Archives.Core/Common/AppConfig.cs new file mode 100644 index 0000000..a5f3200 --- /dev/null +++ b/Learn.Archives.Core/Common/AppConfig.cs @@ -0,0 +1,214 @@ +using Learn.Archives.Core.Common.Expand; +using SqlSugar.IOC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Learn.Archives.Core.Common +{ + /// + /// 应用程序配置 + /// + public class AppConfig + { + /// + /// 程序ID + /// + public string ID { get; set; } = string.Empty; + /// + /// Admin + /// + public AdminConfig Admin { get; set; } = new AdminConfig(); + /// + /// 子系统 + /// + public SubsystemConfig Subsystem { get; set; } = new SubsystemConfig(); + /// + /// redis + /// + public RedisConfig Redis { get; set; } = new RedisConfig(); + /// + /// Whisper AI + /// + public WhisperConfig Whisper { get; set; } = new WhisperConfig(); + /// + /// FFmpeg + /// + public FFmpegConfig FFmpeg { get; set; } = new FFmpegConfig(); + /// + /// ChatGpt + /// + public ChatGptConfig ChatGpt { get; set; } = new ChatGptConfig(); + /// + /// 阿里云视频点播配置 + /// + public AlibabaCloudVodConfig AlibabaCloudVod { get; set; } = new AlibabaCloudVodConfig(); + public AliyunOSSConfig AliyunOSS { get; set; } = new AliyunOSSConfig(); + + + /// + /// 数据库配置 + /// + public DBConfig DB { get; set; } = new DBConfig(); + /// + /// 其他数据库配置 + /// + public DBConfig[] OtherDBArr { get; set; } = Array.Empty(); + /// + /// 系统接收任务设置配置 + /// + public TaskSettingConfig TaskSetting { get; set; } = new TaskSettingConfig(); + + /// + /// SimpLetex配置 + /// + public SimpLetexConfig SimpLetex { get; set; } = new SimpLetexConfig(); + + } + + public class SimpLetexConfig + { + /// + /// 请求 公开的服务地址 + /// + public string Host { get; set; } = string.Empty; + /// + /// api的密钥 + /// + public string AppSecret { get; set; } = string.Empty; + /// + /// 应用ID + /// + public string AppId { get; set; } = string.Empty; + + } + public class TaskSettingConfig + { + /// + /// 下载速度MB/S + /// + public int DownloadSpeed { get; set; } + /// + /// 是服务端 + /// 不执行任务,不回调接口 + /// + public bool IS_Server { get; set; } + + } + /// + /// ffmpeg配置 + /// + public class GptConfig + { + /// + /// 请求 公开的服务地址 + /// + public string Host { get; set; } = string.Empty; + /// + /// api的密钥 + /// + public string ApiKey { get; set; } = string.Empty; + } + /// + /// 文本模型 配置 + /// + public class ChatGptConfig + { + /// + /// KIMI + /// + /// + public GptConfig ChatGpt { get; set; } = new GptConfig(); + public GptConfig DeepSeek { get; set; } = new GptConfig(); + public GptConfig KIMI { get; set; } = new GptConfig(); + public GptConfig aliyun { get; set; } = new GptConfig(); + } + + /// + /// ffmpeg配置 + /// + public class FFmpegConfig + { + /// + /// 音频切片时间段 + /// 0不切片 + /// + public int TimeSlice { get; set; } = 0; + } + /// + /// Whisper配置 + /// + public class WhisperConfig + { + /// + /// 模型名称 + /// + public string ModelName { get; set; } = string.Empty; + + } + /// + /// 管理界面Admin账号 + /// + public class AdminConfig + { + /// + /// 账号 + /// + public string Account { get; set; } = string.Empty; + /// + /// 密码 + /// + public string Password { get; set; } = string.Empty; + } + /// + /// redis配置 + /// + public class RedisConfig + { + /// + /// redis连接字符串 + /// + public string ConnectionString { get; set; } = string.Empty; + } + + public class DBConfig + { + /// + /// 主库链接 + /// + public string ConnectionString { get; set; } = string.Empty; + /// + /// 数据库类型 + /// + public IocDbType SqlType { get; set; } + /// + /// 启动时更新表结构 + /// + public bool UpdateTable { get; set; } + /// + /// 配置ID + /// + public long ConfigId { get; set; } + + } + + + /// + /// 子系统配置 + /// + public class SubsystemInfo + { + public string APIUrl { get; set; } = string.Empty; + public string Token { get; set; } = string.Empty; + } + + /// + /// 子系统配置 + /// + public class SubsystemConfig + { + public SubsystemInfo 蓝鲸智库 { get; set; } = new SubsystemInfo(); + } +} diff --git a/Learn.Archives.Core/Common/BasicAuthMiddleware.cs b/Learn.Archives.Core/Common/BasicAuthMiddleware.cs new file mode 100644 index 0000000..b175635 --- /dev/null +++ b/Learn.Archives.Core/Common/BasicAuthMiddleware.cs @@ -0,0 +1,56 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using OracleInternal.Secure.Network; +using System; +using System.Text; +using System.Threading.Tasks; + +namespace Learn.Archives.Core.Common +{ + public class BasicAuthMiddleware + { + private readonly RequestDelegate _next; + private readonly string _realm; + + + + public BasicAuthMiddleware(RequestDelegate next, string realm) + { + _next = next; + _realm = realm; + } + + public async Task InvokeAsync(HttpContext context) + { + if (context.Request.Path.StartsWithSegments("/swagger")) + { + string authHeader = context.Request.Headers["Authorization"]; + if (authHeader != null && authHeader.StartsWith("Basic ")) + { + var encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim(); + var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword)); + var usernamePassword = decodedUsernamePassword.Split(':'); + + if (await IsAuthorized(usernamePassword[0], usernamePassword[1])) + { + await _next(context); + return; + } + } + + context.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{_realm}\""; + context.Response.StatusCode = StatusCodes.Status401Unauthorized; + return; + } + + await _next(context); + } + + private async Task IsAuthorized(string username, string password) + { + // 在这里验证用户名和密码 + return AppCommon.Config.Admin.Account == username + && AppCommon.Config.Admin.Password == password; + } + } +} diff --git a/Learn.Archives.Core/Common/ExceptionFilter.cs b/Learn.Archives.Core/Common/ExceptionFilter.cs new file mode 100644 index 0000000..f6f4995 --- /dev/null +++ b/Learn.Archives.Core/Common/ExceptionFilter.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace Learn.Archives.Core.Common +{ + public class ExceptionFilter : IAsyncExceptionFilter + { + public ExceptionFilter() + { + } + + public async Task OnExceptionAsync(ExceptionContext context) + { + // 创建一个包含错误信息的对象 + var errorObject = new + { + ErrorMessage = context.Exception.Message, + context.Exception.StackTrace, + }; + // 将错误对象序列化为JSON格式 + var json = errorObject.ToJson(); + + // 设置响应内容类型为JSON + context.HttpContext.Response.ContentType = "application/json"; + // 设置状态码 + context.HttpContext.Response.StatusCode = 500; + // 将JSON数据写入响应体 + await context.HttpContext.Response.WriteAsync(json); + } + } +} diff --git a/Learn.Archives.Core/Common/Expand/AlibabaCloudVodExpand.cs b/Learn.Archives.Core/Common/Expand/AlibabaCloudVodExpand.cs new file mode 100644 index 0000000..df7f134 --- /dev/null +++ b/Learn.Archives.Core/Common/Expand/AlibabaCloudVodExpand.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AlibabaCloud.OpenApiClient.Models; +using AlibabaCloud.SDK.Vod20170321; +using AlibabaCloud.SDK.Vod20170321.Models; +using AlibabaCloud.TeaUtil.Models; +using Learn.Archives.Core.Common; +using Microsoft.Extensions.DependencyInjection; + +namespace Learn.Archives.Core.Common.Expand +{ + public class AlibabaCloudVodConfig + { + /// + /// id + /// + public string AccessKeyId { get; set; } + /// + ///密钥 + /// + public string AccessKeySecret { get; set; } + public string Endpoint { get; set; } = "vod.cn-shanghai.aliyuncs.com"; + } + + /// + /// 阿里云 视频点播拓展 + /// + public static class AlibabaCloudVodExpand + { + /// + /// 使用阿里云 vod拓展 + /// + /// + /// + public static void AddAlibabaCloudVod(this IServiceCollection service) + { + Console.WriteLine($"{DateTime.Now}=>初始化 阿里云VOD"); + // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。 + // 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378671.html。 + Config config = new() + { + // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。 + AccessKeyId = AppCommon.Config.AlibabaCloudVod.AccessKeyId, + // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。 + AccessKeySecret = AppCommon.Config.AlibabaCloudVod.AccessKeySecret, + Endpoint = AppCommon.Config.AlibabaCloudVod.Endpoint + }; + // Endpoint 请参考 https://api.aliyun.com/product/vod + var c = new Client(config); + service.AddSingleton(c); + } + } +} diff --git a/Learn.Archives.Core/Common/Expand/AliyunOSSExpand.cs b/Learn.Archives.Core/Common/Expand/AliyunOSSExpand.cs new file mode 100644 index 0000000..b45ba25 --- /dev/null +++ b/Learn.Archives.Core/Common/Expand/AliyunOSSExpand.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using AlibabaCloud.OpenApiClient.Models; +using AlibabaCloud.SDK.Vod20170321; +using AlibabaCloud.SDK.Vod20170321.Models; +using AlibabaCloud.TeaUtil.Models; +using Aliyun.OSS.Common; +using Aliyun.OSS; +using Microsoft.Extensions.DependencyInjection; +using System.Security.AccessControl; +using Aliyun.Credentials.Models; +using System.IO; +using Learn.Archives.Core.Common; + +namespace Learn.Archives.Core.Common.Expand +{ + public class AliyunOSSConfig + { + /// + /// id + /// + public string AccessKeyId { get; set; } + /// + ///密钥 + /// + public string AccessKeySecret { get; set; } + /// + /// 区域Url + /// + public string Region { get; set; } + /// + /// 筒域名 + /// + public string BucketDomain { get; set; } + /// + /// 桶名称 + /// + public string BucketName { get; set; } + public string Endpoint { get; set; } = "oss-cn-chengdu.aliyuncs.com"; + } + + /// + /// 阿里云 视频点播拓展 + /// + public static class AliyunOSSExpand + { + /// + /// 使用阿里云 vod拓展 + /// + /// + /// + public static void AddAliyunOSS(this IServiceCollection service) + { + Console.WriteLine($"{DateTime.Now}=>初始化 阿里云OSS"); + AliyunOSSConfig config = new() + { + // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。 + AccessKeyId = AppCommon.Config.AliyunOSS.AccessKeyId, + // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。 + AccessKeySecret = AppCommon.Config.AliyunOSS.AccessKeySecret, + Endpoint = AppCommon.Config.AliyunOSS.Endpoint, + Region = AppCommon.Config.AliyunOSS.Region, + };// 创建ClientConfiguration实例,按照您的需要修改默认参数。 + var conf = new ClientConfiguration(); + // 设置v4签名。 + conf.SignatureVersion = SignatureVersion.V4; + // 创建OssClient实例。 + var oss = new OssClient(config.Endpoint, config.AccessKeyId, config.AccessKeySecret, conf); + oss.SetRegion(config.Region); + + service.AddSingleton(oss); + } + /// + /// 上传文件 + /// + /// + /// 视频实体片段 +// public static void AddVideoQuestionUrl(this OssClient oss, List fileArr) +// { +// var cached = new HashSet(); +// foreach (var item in fileArr) +// { +// try +// { +// var isDebug = false; +//#if DEBUG +// isDebug = true; +//#endif +// var path = (isDebug ? "debug/" : string.Empty) + item.VideoTaskId.ToString() + "/" + Path.GetFileName(item.FilePath); +// if (cached.Contains(item.FilePath)) +// { +// item.PPTImageUrl = AppCommon.Config.AliyunOSS.BucketDomain + "/" + path; +// continue; +// } +// using var file = File.OpenRead(item.FilePath); +// var result = oss +// .PutObject( +// AppCommon.Config.AliyunOSS.BucketName, +// path, +// file); +// item.PPTImageUrl = AppCommon.Config.AliyunOSS.BucketDomain + "/" + path; +// cached.Add(item.FilePath); +// continue; +// } +// catch (Exception) +// { + +// } +// } +// } + } +} diff --git a/Learn.Archives.Core/Common/Expand/AppConfigExpand.cs b/Learn.Archives.Core/Common/Expand/AppConfigExpand.cs new file mode 100644 index 0000000..4c393bd --- /dev/null +++ b/Learn.Archives.Core/Common/Expand/AppConfigExpand.cs @@ -0,0 +1,28 @@ +using Coravel; +using Learn.Archives.Core.Common; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; + +namespace Learn.Archives.Core.Common.Expand +{ + public static class AppConfigExpand + { + public static void AddAppConfig(this IConfigurationManager cm, string[] args) + { + Console.WriteLine($"{DateTime.Now}=>初始化 AppConfig"); + cm.GetSection("AppConfig").Bind(AppCommon.Config); + var argList = args.ToList(); + var eArgs = Environment.GetEnvironmentVariable("va_args"); + if (!string.IsNullOrEmpty(eArgs)) + argList.AddRange(eArgs.Split(",")); + + Console.WriteLine("==========================================="); + Console.WriteLine("启动参数如下:"); + Console.WriteLine(string.Join(',', args)); + Console.WriteLine("==========================================="); + + if (args.Contains("IS_Server")) + AppCommon.Config.TaskSetting.IS_Server = true; + } + } +} diff --git a/Learn.Archives.Core/Common/Expand/CorsExpand.cs b/Learn.Archives.Core/Common/Expand/CorsExpand.cs new file mode 100644 index 0000000..b945f63 --- /dev/null +++ b/Learn.Archives.Core/Common/Expand/CorsExpand.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace Learn.Archives.Core.Common.Expand +{ + /// + /// 跨域 + /// + public static class CorsExpand + { + /// + /// 添加跨域拓展 + /// + /// + public static void AddCorsExpand(this IServiceCollection services) + { + services.AddCors(c => + { + c.AddPolicy("All", policy => + { + policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod(); + }); + }); + } + /// + /// 使用跨域 + /// + /// + public static void UseCorsExpand(this IApplicationBuilder app) + { + app.UseCors("All"); + // 获取配置文件中的允许跨域的地址 + app.UseCors(options => + { + options.WithOrigins("*") // 允许跨域请求的地址 + .AllowAnyHeader() // 允许的请求标头 + .AllowAnyMethod(); // 允许跨域请求的类型 (GET,POST等) + }); + } + } +} \ No newline at end of file diff --git a/Learn.Archives.Core/Common/Expand/SimpLetexExpand.cs b/Learn.Archives.Core/Common/Expand/SimpLetexExpand.cs new file mode 100644 index 0000000..54b0369 --- /dev/null +++ b/Learn.Archives.Core/Common/Expand/SimpLetexExpand.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Http.Json; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json.Serialization; +using System.Text.Json; +using System.Threading.Tasks; +using AlibabaCloud.OpenApiClient.Models; +using AlibabaCloud.SDK.Vod20170321; +using AlibabaCloud.SDK.Vod20170321.Models; +using AlibabaCloud.TeaUtil.Models; +using Azure; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Learn.Archives.Core.Common; + +namespace Learn.Archives.Core.Common.Expand +{ + + + public class SimpleTexOcrResponseData + { + public bool status { get; set; } + public SimpleTexOcrResponseDataRes res { get; set; } + public string request_id { get; set; } + } + + public class SimpleTexOcrResponseDataRes + { + public string type { get; set; } + + [JsonPropertyName("info")] // 替换为实际字段名 + public JsonElement DataInfo { get; set; } // 使用JsonElement接收未知类型 + public string value { get; set; } + } + + public class SimpleTexOcrResponseDataInfo + { + public string markdown { get; set; } + } + + + /// + /// 请求参数 + /// + public class SimpleTexOcrRequest + { + public SimpleTexOcrRequest(string filePath) + { + file = File.OpenRead(filePath); + } + /// + /// 合法的图片二进制文件信息,包括png/jpg等格式,无法开启批量调用,仅支持一次上传一张图片 + /// + public FileStream file { get; set; } + /// + /// 用以指定识别图片的类型,如果使用auto则会自动检测,使用document会返回markdown文档结果,使用formula会返回LaTeX结果 + /// "auto", "document", "formula" + /// + public string rec_mode { get; set; } = "document"; + /// + /// 开启后,模型将基于0°,90°, 180°, 270°自动矫正上传图片的方向,默认不开启 + /// + public bool enable_img_rot { get; set; } = false; + /// + /// 用于修改行内公式在markdown中的包裹符号。以Json形式填入,如果格式错误将使用默认的包裹符号 + /// 示例:["$","$"] + /// + public string inline_formula_wrapper { get; set; } + /// + /// 用于修改独立行公式在markdown中的包裹符号。以Json形式填入,如果格式错误将使用默认的包裹符号 + /// 示例:["$$","$$"] + /// + public string isolated_formula_wrapper { get; set; } + } + /// + /// ocr响应 + /// + public class SimpleTexOcrResponse + { + public bool Success { get; set; } + public SimpleTexOcrResponseData Result { get; set; } + public string ResultStr { get; set; } + public string Error { get; set; } + } + + + public class SimpLetexClient + { + private readonly IHttpClientFactory _httpClientFactory; + public SimpLetexClient( + IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + } + + public async Task ProcessImageAsync(SimpleTexOcrRequest request) + { + var client = _httpClientFactory.CreateClient(); + using var content = new MultipartFormDataContent(); + var parameters = new Dictionary(); + + // 添加文件内容 + var fileContent = new StreamContent(request.file); + content.Add(fileContent, nameof(request.file), Path.GetFileName(request.file.Name)); + + // 添加并收集其他参数 + if (request.rec_mode != "auto") + { + content.Add(new StringContent(request.rec_mode), nameof(request.rec_mode)); + parameters[nameof(request.rec_mode)] = request.rec_mode; + } + + var enableImgRotStr = request.enable_img_rot.ToString().ToLower(); + content.Add(new StringContent(enableImgRotStr), nameof(request.enable_img_rot)); + parameters[nameof(request.enable_img_rot)] = enableImgRotStr; + + if (request.inline_formula_wrapper != null) + { + content.Add(new StringContent(request.inline_formula_wrapper), nameof(request.inline_formula_wrapper)); + parameters[nameof(request.inline_formula_wrapper)] = request.inline_formula_wrapper; + } + + if (request.isolated_formula_wrapper != null) + { + var isolatedWrapper = request.isolated_formula_wrapper.ToJson(); + content.Add(new StringContent(isolatedWrapper), nameof(request.isolated_formula_wrapper)); + parameters[nameof(request.isolated_formula_wrapper)] = isolatedWrapper; + } + + // 生成鉴权参数 + var randomStr = Guid.NewGuid().ToString("N").Substring(16); + var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); + var appId = AppCommon.Config.SimpLetex.AppId; + + parameters["timestamp"] = timestamp; + parameters["random-str"] = randomStr; + parameters["app-id"] = appId; + + // 生成签名 + var signStr = string.Join("&", parameters + .OrderBy(p => p.Key) + .Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}")) + + $"&secret={AppCommon.Config.SimpLetex.AppSecret}"; + var sign = ComputeMD5(signStr); + + // 创建请求并添加Header + var requestMessage = new HttpRequestMessage(HttpMethod.Post, + AppCommon.Config.SimpLetex.Host + "simpletex_ocr") + { + Content = content + }; + + requestMessage.Headers.Add("random-str", randomStr); + requestMessage.Headers.Add("timestamp", timestamp); + requestMessage.Headers.Add("app-id", appId); + requestMessage.Headers.Add("sign", sign); + + try + { + var response = await client.SendAsync(requestMessage); + var resStr = await response.Content.ReadAsStringAsync(); + var responseContent = await response.Content.ReadFromJsonAsync(); + if (responseContent.res.DataInfo.ValueKind == JsonValueKind.Object) + { + responseContent.res.value = JsonSerializer.Deserialize( + responseContent.res.DataInfo.GetRawText(), + new JsonSerializerOptions { PropertyNameCaseInsensitive = true } + )?.markdown ?? string.Empty; + // 处理字符串 + } + else if (responseContent.res.DataInfo.ValueKind == JsonValueKind.String) + { + responseContent.res.value = responseContent.res.DataInfo.GetString(); + } + + request.file.Dispose(); + return new SimpleTexOcrResponse + { + Success = response.IsSuccessStatusCode, + Result = responseContent, + ResultStr = resStr, + Error = response.IsSuccessStatusCode ? null : $"HTTP Error: {response.StatusCode}" + }; + } + catch (Exception ex) + { + return new SimpleTexOcrResponse + { + Success = false, + Error = $"Request Failed: {ex.Message}" + }; + } + } + + private string GenerateSignatureString(IDictionary parameters, string secret) + { + var sortedParams = parameters + .OrderBy(p => p.Key) + .Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}"); + + return string.Join("&", sortedParams) + $"&secret={secret}"; + } + + private string ComputeMD5(string input) + { + using var md5 = MD5.Create(); + var inputBytes = Encoding.UTF8.GetBytes(input); + var hashBytes = md5.ComputeHash(inputBytes); + return BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); + } + } + + /// + /// 服务注册扩展 + /// + public static class SSimpLetexExtensions + { + public static IServiceCollection AddSimpleTexOcrClient(this IServiceCollection services) + { + services.AddSingleton(); + return services; + } + } +} diff --git a/Learn.Archives.Core/Common/Expand/SqlSugarExpand.cs b/Learn.Archives.Core/Common/Expand/SqlSugarExpand.cs new file mode 100644 index 0000000..9c8ae59 --- /dev/null +++ b/Learn.Archives.Core/Common/Expand/SqlSugarExpand.cs @@ -0,0 +1,169 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using MySqlConnector; +using SqlSugar; +using SqlSugar.IOC; +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Xml.Linq; +using Yitter.IdGenerator; + +namespace Learn.Archives.Core.Common.Expand +{ + public static class SqlSugarExpand + { + public static bool ShowSQL = false; + public static void AddSqlSugarExpand(this IServiceCollection services) + { + + services.AddHttpContextAccessor(); + Console.WriteLine($"{DateTime.Now}=>初始化 YitId雪花ID"); + var options = new IdGeneratorOptions(ushort.Parse(AppCommon.Config.ID)); + YitIdHelper.SetIdGenerator(options); + + #region SqlSugar注入 + var dbList = new List() { + new IocConfig() + { + ConfigId =1, + ConnectionString = AppCommon.Config.DB.ConnectionString, + DbType =AppCommon.Config.DB.SqlType, + IsAutoCloseConnection = true//自动释放 + }, + }; + dbList.AddRange(AppCommon.Config.OtherDBArr.Select(s => new IocConfig() + { + ConfigId = s.ConfigId, + ConnectionString = s.ConnectionString, + DbType = s.SqlType, + IsAutoCloseConnection = true + })); + services.AddTransient(typeof(Repository<>)); + + //注入SqlSugar 主库 + services.AddSqlSugar(dbList); + + + services.ConfigurationSugar(db => + { + var config = db.CurrentConnectionConfig; + // 设置超时时间 + db.Ado.CommandTimeOut = 61; +#if DEBUG + // 打印SQL语句 + db.Aop.OnLogExecuting = (sql, pars) => + { + if (!ShowSQL) return; + //var originColor = Console.ForegroundColor; + //if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase)) + // Console.ForegroundColor = ConsoleColor.Green; + //if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase)) + // Console.ForegroundColor = ConsoleColor.Yellow; + //if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase)) + // Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"【{DateTime.Now}——执行SQL - [{config.ConfigId}]】\r\n" + UtilMethods.GetSqlString(config.DbType, sql, pars) + "\r\n"); + //Console.ForegroundColor = originColor; + }; +#endif + db.Aop.OnError = (ex) => + { + if (ex.Parametres == null) return; + //var originColor = Console.ForegroundColor; + //Console.ForegroundColor = ConsoleColor.DarkRed; + Console.WriteLine($"【{DateTime.Now}——错误SQL - [{config.ConfigId}]】\r\n" + ex.Message + "\r\n" + UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n"); + Console.WriteLine(); + //Console.ForegroundColor = originColor; + }; + db.Aop.DataExecuting = (oldValue, entityInfo) => + { + if (entityInfo.OperationType == DataFilterType.InsertByObject) + { + // 主键(long类型)且没有值的---赋值雪花Id + if (entityInfo.EntityColumnInfo.IsPrimarykey && !entityInfo.EntityColumnInfo.IsIdentity && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long)) + { + var id = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue); + if (id == null || (long)id == 0) + entityInfo.SetValue(YitIdHelper.NextId()); + } + if (entityInfo.PropertyName == "CreateTime" && entityInfo.EntityValue is null) + entityInfo.SetValue(DateTime.Now); + } + if (entityInfo.OperationType == DataFilterType.UpdateByObject) + { + + } + }; + + }); + + #endregion + } + public static void UseSqlSugarExpand(this IApplicationBuilder app) + { + ShowSQL = false; + var builder = new MySqlConnectionStringBuilder(AppCommon.Config.DB.ConnectionString); + var dbName = builder.Database; + builder.Database = "mysql"; + using SqlSugarClient dbMysql = new SqlSugarClient(new ConnectionConfig + { + ConnectionString = builder.ConnectionString, + DbType = DbType.MySql, + IsAutoCloseConnection = true, + }); + + var dataBaseList = dbMysql.DbMaintenance.GetDataBaseList(); + if (dataBaseList.Contains(dbName)) + { + Console.WriteLine($"【0】数据库 {dbName} 已存在 【√】"); + } + else + { + Console.WriteLine($"【0】创建数据库{dbName} ... "); + var res = dbMysql.Ado.ExecuteCommand($"CREATE DATABASE `{dbName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci"); + try + { + dbMysql.Ado.ExecuteCommand($"alter database `{dbName}` character set utf8mb4;" + + $"alter database `{dbName}` character set utf8mb4 collate utf8mb4_general_ci;"); + //res 没有权限 + dbMysql.Ado.ExecuteCommand($"SET GLOBAL local_infile=1;"); + Console.WriteLine($"【0】数据库 {dbName} 创建完成 【√】"); + } + catch (Exception ex) + { + Console.WriteLine("【0】主库初始化配置 出现异常 " + ex.Message); + } + + + } + InitDbTable(); + } + public static void InitDbTable() + { + if (!AppCommon.Config.DB.UpdateTable) + { + Console.WriteLine($"【1】初始化主库表 跳过...."); + //ShowSQL = true; + return; + } + Console.WriteLine($"【1】初始化主库表 执行中...."); + var entityTypes = AppCommon.DbMatserType; + Console.WriteLine($"【1】数量{entityTypes.Count()} ...."); + if (!entityTypes.Any()) return; + var i = 0; + var totalCount = entityTypes.Count().ToString().Length; + foreach (var t in entityTypes) + { + Console.Write($"【1】{entityTypes.Count()}/{(++i).ToString().PadLeft(totalCount, '0')} 执行 {t.FullName}".PadRight(60, ' ')); + DbScoped.Sugar.CodeFirst.InitTables(t); + Console.WriteLine($"【√】"); + } + Console.WriteLine($"【1】数量{entityTypes.Count()} 执行完毕"); + ShowSQL = true; + } + } +} \ No newline at end of file diff --git a/Learn.Archives.Core/Common/JsonExtractor.cs b/Learn.Archives.Core/Common/JsonExtractor.cs new file mode 100644 index 0000000..fd23a51 --- /dev/null +++ b/Learn.Archives.Core/Common/JsonExtractor.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace Learn.Archives.Core.Common +{ + public static class JsonExtractor + { + /// + /// 提取json字符串 + /// + /// + /// + public static List ExtractJsonStrings(this string input) + { + List jsonList = new List(); + int index = 0; + while (index < input.Length) + { + if (input[index] == '{' || input[index] == '[') + { + int? endIndex = FindMatchingBracket(input, index); + if (endIndex.HasValue) + { + string candidate = input.Substring(index, endIndex.Value - index + 1); + if (IsValidJson(candidate)) + { + jsonList.Add(candidate); + index = endIndex.Value + 1; + continue; + } + } + } + index++; + } + return jsonList; + } + + private static int? FindMatchingBracket(string str, int start) + { + Stack stack = new Stack(); + bool inString = false; + bool inEscape = false; + + for (int i = start; i < str.Length; i++) + { + char c = str[i]; + + if (inEscape) + { + inEscape = false; + } + else if (inString) + { + if (c == '\\') + inEscape = true; + else if (c == '"') + inString = false; + } + else + { + switch (c) + { + case '{': + case '[': + stack.Push(c); + break; + case '}': + if (stack.Count == 0 || stack.Peek() != '{') + return null; + stack.Pop(); + break; + case ']': + if (stack.Count == 0 || stack.Peek() != '[') + return null; + stack.Pop(); + break; + case '"': + inString = true; + break; + } + } + + if (stack.Count == 0) + return i; + } + return null; // 括号未完全匹配 + } + + public static bool IsValidJson(string candidate) + { + if (string.IsNullOrEmpty(candidate)) + return false; + try + { + JsonDocument.Parse(candidate); + return true; + } + catch (Exception) + { + return false; + } + } + } +} \ No newline at end of file diff --git a/Learn.Archives.Core/Common/RedisExpand.cs b/Learn.Archives.Core/Common/RedisExpand.cs new file mode 100644 index 0000000..707b5c9 --- /dev/null +++ b/Learn.Archives.Core/Common/RedisExpand.cs @@ -0,0 +1,54 @@ +using FreeRedis; +using FreeRedis.Internal; +using Microsoft.Extensions.DependencyInjection; +using NetTaste; +using Newtonsoft.Json.Schema; +using SqlSugar.IOC; +using System; +using System.Security.Cryptography; +using System.Text.Json; +using System.Threading.Channels; +using System.Threading.Tasks; +using System.Xml.Linq; +using UserCenter.Model.Enum; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace Learn.Archives.Core.Common +{ + /// + /// redis key + /// + public static class RedisExpandKey + { + + } + /// + /// redis拓展 + /// + public static class RedisExpand + { + + /// + /// redis 连接 + /// + /// + /// 队列池 + /// + static SubscribeListObject? Subscribe; + + /// + /// 初始化 redis + /// 需要在初始化配置文件时候调用 + /// + public static void AddRedisExpand(this IServiceCollection service) + { + Console.WriteLine($"{DateTime.Now}=>初始化 Redis"); + RedisClient Redis = new RedisClient(AppCommon.Config.Redis.ConnectionString); + Redis.Serialize = obj => JsonSerializer.Serialize(obj); + Redis.Deserialize = (json, type) => JsonSerializer.Deserialize(json, type); + service.AddSingleton(Redis); + + } + + } +} diff --git a/Learn.Archives.Core/Common/Repository.cs b/Learn.Archives.Core/Common/Repository.cs new file mode 100644 index 0000000..959ce7c --- /dev/null +++ b/Learn.Archives.Core/Common/Repository.cs @@ -0,0 +1,37 @@ +using SqlSugar; +using SqlSugar.IOC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Learn.Archives.Core.Common +{ + public class Repository : SimpleClient where T : class, new() + { + readonly Dictionary CID = new Dictionary(); + public Repository() + { + SwitchConnection(); + } + public void SwitchConnection() + { + var t = typeof(T); + if (CID.ContainsKey(t)) + { + base.Context = CID[t] != null + ? DbScoped.Sugar.GetConnectionScope(CID[t]) + : DbScoped.Sugar; + return; + } + var c = t.GetCustomAttribute(); + if (!CID.ContainsKey(t)) + CID.Add(t, c?.configId); + base.Context = c != null + ? DbScoped.Sugar.GetConnectionScope(c.configId) + : DbScoped.Sugar; + } + } +} diff --git a/Learn.Archives.Core/Learn.Archives.Core.csproj b/Learn.Archives.Core/Learn.Archives.Core.csproj new file mode 100644 index 0000000..41408e8 --- /dev/null +++ b/Learn.Archives.Core/Learn.Archives.Core.csproj @@ -0,0 +1,35 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Learn.Archives.Core/Model/Admin.cs b/Learn.Archives.Core/Model/Admin.cs new file mode 100644 index 0000000..ccd4dd0 --- /dev/null +++ b/Learn.Archives.Core/Model/Admin.cs @@ -0,0 +1,43 @@ +using Learn.Archives.Core.Model.Interface; +using SqlSugar; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Text.Json; +using UserCenter.Model.Enum; + +namespace Learn.Archives.Core.Model +{ + /// + /// 管理员 + /// + [SugarTable("admin")] + public class Admin : IDB + { + /// + /// id + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + /// + /// 账号 + /// + [SugarColumn(Length = 12)] + public string Name { get; set; } + [SugarColumn(Length = 12)] + public string Account { get; set; } + /// + /// 密码 + /// + [SugarColumn(Length = 12)] + public string Password { get; set; } + /// + /// 启用 + /// + public bool Enable { get; set; } + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } = DateTime.Now; + + } +} diff --git a/Learn.Archives.Core/Model/Enum/UserStatusEnum.cs b/Learn.Archives.Core/Model/Enum/UserStatusEnum.cs new file mode 100644 index 0000000..149523d --- /dev/null +++ b/Learn.Archives.Core/Model/Enum/UserStatusEnum.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Learn.Archives.Core.Model.Enum +{ + public enum UserStatusEnum + { + 就读=1, + 退出=10, + } +} diff --git a/Learn.Archives.Core/Model/Interface/IDB.cs b/Learn.Archives.Core/Model/Interface/IDB.cs new file mode 100644 index 0000000..da29717 --- /dev/null +++ b/Learn.Archives.Core/Model/Interface/IDB.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Learn.Archives.Core.Model.Interface +{ + /// + /// 表属于IDB + /// + interface IDB + { + } +} diff --git a/Learn.Archives.Core/Model/User.cs b/Learn.Archives.Core/Model/User.cs new file mode 100644 index 0000000..715d3c1 --- /dev/null +++ b/Learn.Archives.Core/Model/User.cs @@ -0,0 +1,65 @@ +using Learn.Archives.Core.Model.Interface; +using SqlSugar; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Text.Json; +using UserCenter.Model; +using UserCenter.Model.Enum; + +namespace Learn.Archives.Core.Model +{ + /// + /// 用户 + /// 数据中心拓展用户 + /// + [SugarTable("user")] + public class User : IDB + { + /// + /// id + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + /// + /// 用户中心的id + /// + /// + public long UserCenterId { get; set; } + + /// + /// 减免金额 + /// + [SugarColumn(DecimalDigits =2)] + public decimal AmountRelief { get; set; } + + /// + /// 学生状态 + /// + public UserSourceEnum Status { get; set; } + + /// + /// 备注 + /// + /// + [SugarColumn(IsNullable = true)] + public string? Remark { get; set; } + + /// + /// 退出时间 + /// + [SugarColumn(IsNullable = true)] + public DateTime ExitTime { get; set; } = DateTime.Now; + + /// + /// 退出时间 + /// + [SugarColumn(IsNullable = true)] + public DateTime JoinTime { get; set; } = DateTime.Now; + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } = DateTime.Now; + + } +}