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;
+
+ }
+}