添加项目文件。
This commit is contained in:
parent
4ecf930230
commit
494251c76a
|
|
@ -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/**
|
||||
|
|
@ -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
|
||||
|
|
@ -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"]
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.2-pre01" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Controllers\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Learn.Archives.Core\Learn.Archives.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@Learn.Archives.API_HostAddress = http://localhost:5199
|
||||
|
||||
GET {{Learn.Archives.API_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
|
|
@ -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<BasicAuthMiddleware>("Swagger");
|
||||
|
||||
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
|
||||
//自定义 应用
|
||||
app.UseCorsExpand();
|
||||
app.UseSqlSugarExpand();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"profiles": {
|
||||
"http5199": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:5199"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
//}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 程序 公共变量
|
||||
/// </summary>
|
||||
public static class AppCommon
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用有效程序集
|
||||
/// </summary>
|
||||
public static readonly IEnumerable<Assembly> Assemblies;
|
||||
/// <summary>
|
||||
/// 主库数据库表类型
|
||||
/// </summary>
|
||||
public static readonly IEnumerable<Type> DbMatserType;
|
||||
public static readonly IEnumerable<Type> 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;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 程序配置
|
||||
/// </summary>
|
||||
public static AppConfig Config = new AppConfig();
|
||||
/// <summary>
|
||||
/// ServiceProvider
|
||||
/// </summary>
|
||||
public static IServiceProvider? Services;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拓展函数
|
||||
/// </summary>
|
||||
public static class ExpandFunction
|
||||
{
|
||||
|
||||
static Dictionary<string, string> FormulaData;
|
||||
static string FormulaDataKey;
|
||||
/// <summary>
|
||||
/// 帧文件名称
|
||||
/// </summary>
|
||||
public static string FrameName = "frame_";
|
||||
|
||||
/// <summary>
|
||||
/// 对象转化为JSON字符串
|
||||
/// </summary>
|
||||
/// <param name="o">拓展对象</param>
|
||||
/// <param name="WriteIndented">美化输出?</param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取应用有效程序集
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable</returns>
|
||||
public static List<Assembly> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取下一个枚举值
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="current"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public static T? NextEnum<T>(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];
|
||||
}
|
||||
/// <summary>
|
||||
/// 转化枚举
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static T? ToEnum<T>(this object value) where T : struct, Enum
|
||||
{
|
||||
try
|
||||
{
|
||||
if (value is null || string.IsNullOrEmpty(value.ToString()))
|
||||
return null;
|
||||
if (Enum.TryParse<T>(value.ToString(), true, out var result) && Enum.IsDefined(typeof(T), result))
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用程序配置
|
||||
/// </summary>
|
||||
public class AppConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序ID
|
||||
/// </summary>
|
||||
public string ID { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Admin
|
||||
/// </summary>
|
||||
public AdminConfig Admin { get; set; } = new AdminConfig();
|
||||
/// <summary>
|
||||
/// 子系统
|
||||
/// </summary>
|
||||
public SubsystemConfig Subsystem { get; set; } = new SubsystemConfig();
|
||||
/// <summary>
|
||||
/// redis
|
||||
/// </summary>
|
||||
public RedisConfig Redis { get; set; } = new RedisConfig();
|
||||
/// <summary>
|
||||
/// Whisper AI
|
||||
/// </summary>
|
||||
public WhisperConfig Whisper { get; set; } = new WhisperConfig();
|
||||
/// <summary>
|
||||
/// FFmpeg
|
||||
/// </summary>
|
||||
public FFmpegConfig FFmpeg { get; set; } = new FFmpegConfig();
|
||||
/// <summary>
|
||||
/// ChatGpt
|
||||
/// </summary>
|
||||
public ChatGptConfig ChatGpt { get; set; } = new ChatGptConfig();
|
||||
/// <summary>
|
||||
/// 阿里云视频点播配置
|
||||
/// </summary>
|
||||
public AlibabaCloudVodConfig AlibabaCloudVod { get; set; } = new AlibabaCloudVodConfig();
|
||||
public AliyunOSSConfig AliyunOSS { get; set; } = new AliyunOSSConfig();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 数据库配置
|
||||
/// </summary>
|
||||
public DBConfig DB { get; set; } = new DBConfig();
|
||||
/// <summary>
|
||||
/// 其他数据库配置
|
||||
/// </summary>
|
||||
public DBConfig[] OtherDBArr { get; set; } = Array.Empty<DBConfig>();
|
||||
/// <summary>
|
||||
/// 系统接收任务设置配置
|
||||
/// </summary>
|
||||
public TaskSettingConfig TaskSetting { get; set; } = new TaskSettingConfig();
|
||||
|
||||
/// <summary>
|
||||
/// SimpLetex配置
|
||||
/// </summary>
|
||||
public SimpLetexConfig SimpLetex { get; set; } = new SimpLetexConfig();
|
||||
|
||||
}
|
||||
|
||||
public class SimpLetexConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 请求 公开的服务地址
|
||||
/// </summary>
|
||||
public string Host { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// api的密钥
|
||||
/// </summary>
|
||||
public string AppSecret { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// 应用ID
|
||||
/// </summary>
|
||||
public string AppId { get; set; } = string.Empty;
|
||||
|
||||
}
|
||||
public class TaskSettingConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 下载速度MB/S
|
||||
/// </summary>
|
||||
public int DownloadSpeed { get; set; }
|
||||
/// <summary>
|
||||
/// 是服务端
|
||||
/// <para>不执行任务,不回调接口</para>
|
||||
/// </summary>
|
||||
public bool IS_Server { get; set; }
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// ffmpeg配置
|
||||
/// </summary>
|
||||
public class GptConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 请求 公开的服务地址
|
||||
/// </summary>
|
||||
public string Host { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// api的密钥
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
}
|
||||
/// <summary>
|
||||
/// 文本模型 配置
|
||||
/// </summary>
|
||||
public class ChatGptConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// KIMI
|
||||
/// <para></para>
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ffmpeg配置
|
||||
/// </summary>
|
||||
public class FFmpegConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 音频切片时间段
|
||||
/// <para>0不切片</para>
|
||||
/// </summary>
|
||||
public int TimeSlice { get; set; } = 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Whisper配置
|
||||
/// </summary>
|
||||
public class WhisperConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型名称
|
||||
/// </summary>
|
||||
public string ModelName { get; set; } = string.Empty;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 管理界面Admin账号
|
||||
/// </summary>
|
||||
public class AdminConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 账号
|
||||
/// </summary>
|
||||
public string Account { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
/// <summary>
|
||||
/// redis配置
|
||||
/// </summary>
|
||||
public class RedisConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// redis连接字符串
|
||||
/// </summary>
|
||||
public string ConnectionString { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class DBConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 主库链接
|
||||
/// </summary>
|
||||
public string ConnectionString { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// 数据库类型
|
||||
/// </summary>
|
||||
public IocDbType SqlType { get; set; }
|
||||
/// <summary>
|
||||
/// 启动时更新表结构
|
||||
/// </summary>
|
||||
public bool UpdateTable { get; set; }
|
||||
/// <summary>
|
||||
/// 配置ID
|
||||
/// </summary>
|
||||
public long ConfigId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 子系统配置
|
||||
/// </summary>
|
||||
public class SubsystemInfo
|
||||
{
|
||||
public string APIUrl { get; set; } = string.Empty;
|
||||
public string Token { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 子系统配置
|
||||
/// </summary>
|
||||
public class SubsystemConfig
|
||||
{
|
||||
public SubsystemInfo 蓝鲸智库 { get; set; } = new SubsystemInfo();
|
||||
}
|
||||
}
|
||||
|
|
@ -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<bool> IsAuthorized(string username, string password)
|
||||
{
|
||||
// 在这里验证用户名和密码
|
||||
return AppCommon.Config.Admin.Account == username
|
||||
&& AppCommon.Config.Admin.Password == password;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// id
|
||||
/// </summary>
|
||||
public string AccessKeyId { get; set; }
|
||||
/// <summary>
|
||||
///密钥
|
||||
/// </summary>
|
||||
public string AccessKeySecret { get; set; }
|
||||
public string Endpoint { get; set; } = "vod.cn-shanghai.aliyuncs.com";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 阿里云 视频点播拓展
|
||||
/// </summary>
|
||||
public static class AlibabaCloudVodExpand
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用阿里云 vod拓展
|
||||
/// </summary>
|
||||
/// <param name="service"></param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// id
|
||||
/// </summary>
|
||||
public string AccessKeyId { get; set; }
|
||||
/// <summary>
|
||||
///密钥
|
||||
/// </summary>
|
||||
public string AccessKeySecret { get; set; }
|
||||
/// <summary>
|
||||
/// 区域Url
|
||||
/// </summary>
|
||||
public string Region { get; set; }
|
||||
/// <summary>
|
||||
/// 筒域名
|
||||
/// </summary>
|
||||
public string BucketDomain { get; set; }
|
||||
/// <summary>
|
||||
/// 桶名称
|
||||
/// </summary>
|
||||
public string BucketName { get; set; }
|
||||
public string Endpoint { get; set; } = "oss-cn-chengdu.aliyuncs.com";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 阿里云 视频点播拓展
|
||||
/// </summary>
|
||||
public static class AliyunOSSExpand
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用阿里云 vod拓展
|
||||
/// </summary>
|
||||
/// <param name="service"></param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
}
|
||||
/// <summary>
|
||||
/// 上传文件
|
||||
/// </summary>
|
||||
/// <param name="oss"></param>
|
||||
/// <param name="fileArr">视频实体片段</param>
|
||||
// public static void AddVideoQuestionUrl(this OssClient oss, List<VideoQuestionOSSDto> fileArr)
|
||||
// {
|
||||
// var cached = new HashSet<string>();
|
||||
// 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)
|
||||
// {
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Learn.Archives.Core.Common.Expand
|
||||
{
|
||||
/// <summary>
|
||||
/// 跨域
|
||||
/// </summary>
|
||||
public static class CorsExpand
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加跨域拓展
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
public static void AddCorsExpand(this IServiceCollection services)
|
||||
{
|
||||
services.AddCors(c =>
|
||||
{
|
||||
c.AddPolicy("All", policy =>
|
||||
{
|
||||
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
|
||||
});
|
||||
});
|
||||
}
|
||||
/// <summary>
|
||||
/// 使用跨域
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
public static void UseCorsExpand(this IApplicationBuilder app)
|
||||
{
|
||||
app.UseCors("All");
|
||||
// 获取配置文件中的允许跨域的地址
|
||||
app.UseCors(options =>
|
||||
{
|
||||
options.WithOrigins("*") // 允许跨域请求的地址
|
||||
.AllowAnyHeader() // 允许的请求标头
|
||||
.AllowAnyMethod(); // 允许跨域请求的类型 (GET,POST等)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 请求参数
|
||||
/// </summary>
|
||||
public class SimpleTexOcrRequest
|
||||
{
|
||||
public SimpleTexOcrRequest(string filePath)
|
||||
{
|
||||
file = File.OpenRead(filePath);
|
||||
}
|
||||
/// <summary>
|
||||
/// 合法的图片二进制文件信息,包括png/jpg等格式,无法开启批量调用,仅支持一次上传一张图片
|
||||
/// </summary>
|
||||
public FileStream file { get; set; }
|
||||
/// <summary>
|
||||
/// 用以指定识别图片的类型,如果使用auto则会自动检测,使用document会返回markdown文档结果,使用formula会返回LaTeX结果
|
||||
/// <para>"auto", "document", "formula"</para>
|
||||
/// </summary>
|
||||
public string rec_mode { get; set; } = "document";
|
||||
/// <summary>
|
||||
/// 开启后,模型将基于0°,90°, 180°, 270°自动矫正上传图片的方向,默认不开启
|
||||
/// </summary>
|
||||
public bool enable_img_rot { get; set; } = false;
|
||||
/// <summary>
|
||||
/// 用于修改行内公式在markdown中的包裹符号。以Json形式填入,如果格式错误将使用默认的包裹符号
|
||||
/// <para>示例:["$","$"]</para>
|
||||
/// </summary>
|
||||
public string inline_formula_wrapper { get; set; }
|
||||
/// <summary>
|
||||
/// 用于修改独立行公式在markdown中的包裹符号。以Json形式填入,如果格式错误将使用默认的包裹符号
|
||||
/// <para>示例:["$$","$$"]</para>
|
||||
/// </summary>
|
||||
public string isolated_formula_wrapper { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// ocr响应
|
||||
/// </summary>
|
||||
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<SimpleTexOcrResponse> ProcessImageAsync(SimpleTexOcrRequest request)
|
||||
{
|
||||
var client = _httpClientFactory.CreateClient();
|
||||
using var content = new MultipartFormDataContent();
|
||||
var parameters = new Dictionary<string, string>();
|
||||
|
||||
// 添加文件内容
|
||||
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<SimpleTexOcrResponseData>();
|
||||
if (responseContent.res.DataInfo.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
responseContent.res.value = JsonSerializer.Deserialize<SimpleTexOcrResponseDataInfo>(
|
||||
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<string, string> 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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务注册扩展
|
||||
/// </summary>
|
||||
public static class SSimpLetexExtensions
|
||||
{
|
||||
public static IServiceCollection AddSimpleTexOcrClient(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<SimpLetexClient>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<IocConfig>() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Learn.Archives.Core.Common
|
||||
{
|
||||
public static class JsonExtractor
|
||||
{
|
||||
/// <summary>
|
||||
/// 提取json字符串
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public static List<string> ExtractJsonStrings(this string input)
|
||||
{
|
||||
List<string> jsonList = new List<string>();
|
||||
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<char> stack = new Stack<char>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// redis key
|
||||
/// </summary>
|
||||
public static class RedisExpandKey
|
||||
{
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// redis拓展
|
||||
/// </summary>
|
||||
public static class RedisExpand
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// redis 连接
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 队列池
|
||||
/// </summary>
|
||||
static SubscribeListObject? Subscribe;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 redis
|
||||
/// <para>需要在初始化配置文件时候调用</para>
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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<T> : SimpleClient<T> where T : class, new()
|
||||
{
|
||||
readonly Dictionary<Type, object?> CID = new Dictionary<Type, object?>();
|
||||
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<TenantAttribute>();
|
||||
if (!CID.ContainsKey(t))
|
||||
CID.Add(t, c?.configId);
|
||||
base.Context = c != null
|
||||
? DbScoped.Sugar.GetConnectionScope(c.configId)
|
||||
: DbScoped.Sugar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AlibabaCloud.SDK.Vod20170321" Version="3.6.1" />
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||
<PackageReference Include="Coravel" Version="6.0.2" />
|
||||
<PackageReference Include="FreeRedis" Version="1.3.2" />
|
||||
<PackageReference Include="Downloader" Version="3.2.1" />
|
||||
<PackageReference Include="Mapster" Version="7.4.1-pre01" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageReference Include="SqlSugar.IOC" Version="2.0.0" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.170" />
|
||||
<PackageReference Include="UserCenter.Model" Version="1.3.9" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Model\Dto\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 管理员
|
||||
/// </summary>
|
||||
[SugarTable("admin")]
|
||||
public class Admin : IDB
|
||||
{
|
||||
/// <summary>
|
||||
/// id
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public long Id { get; set; }
|
||||
/// <summary>
|
||||
/// 账号
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 12)]
|
||||
public string Name { get; set; }
|
||||
[SugarColumn(Length = 12)]
|
||||
public string Account { get; set; }
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 12)]
|
||||
public string Password { get; set; }
|
||||
/// <summary>
|
||||
/// 启用
|
||||
/// </summary>
|
||||
public bool Enable { get; set; }
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreateTime { get; set; } = DateTime.Now;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 表属于IDB
|
||||
/// </summary>
|
||||
interface IDB
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户
|
||||
/// <para>数据中心拓展用户</para>
|
||||
/// </summary>
|
||||
[SugarTable("user")]
|
||||
public class User : IDB
|
||||
{
|
||||
/// <summary>
|
||||
/// id
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public long Id { get; set; }
|
||||
/// <summary>
|
||||
/// 用户中心的id
|
||||
/// <see cref="UserCenter.Model.User.Id"/>
|
||||
/// </summary>
|
||||
public long UserCenterId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 减免金额
|
||||
/// </summary>
|
||||
[SugarColumn(DecimalDigits =2)]
|
||||
public decimal AmountRelief { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 学生状态
|
||||
/// </summary>
|
||||
public UserSourceEnum Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// <para></para>
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public string? Remark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退出时间
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public DateTime ExitTime { get; set; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// 退出时间
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public DateTime JoinTime { get; set; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreateTime { get; set; } = DateTime.Now;
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue