diff --git a/Tiobon.Core.Api/Program.cs b/Tiobon.Core.Api/Program.cs index 516b90a4..0dfa656e 100644 --- a/Tiobon.Core.Api/Program.cs +++ b/Tiobon.Core.Api/Program.cs @@ -56,7 +56,7 @@ builder.Services.AddCacheSetup(); builder.Services.AddSqlsugarSetup(); builder.Services.AddDbSetup(); builder.Services.AddInitializationHostServiceSetup(); -builder.Services.AddSimpleDapperSetup(); +builder.Services.c(); builder.Host.AddSerilogSetup(); diff --git a/Tiobon.Core.Common/DB/BaseDBConfig.cs b/Tiobon.Core.Common/DB/BaseDBConfig.cs index 2d91a706..627416d7 100644 --- a/Tiobon.Core.Common/DB/BaseDBConfig.cs +++ b/Tiobon.Core.Common/DB/BaseDBConfig.cs @@ -106,6 +106,19 @@ namespace Tiobon.Core.Common.DB return mutiDBOperate; } + + public static MutiDBOperate GetMainConnectionDb() + { + var mainConnetctDb = MutiConnectionString.allDbs.Find(x => x.ConnId == MainDb.CurrentDbConnId); + if (MutiConnectionString.allDbs.Count > 0) + { + if (mainConnetctDb == null) + mainConnetctDb = MutiConnectionString.allDbs[0]; + } + else + throw new Exception("请确保appsettigns.json中配置连接字符串,并设置Enabled为true;"); + return mainConnetctDb; + } } diff --git a/Tiobon.Core.Common/Extensions/Check.cs b/Tiobon.Core.Common/Extensions/Check.cs new file mode 100644 index 00000000..6f5df4f0 --- /dev/null +++ b/Tiobon.Core.Common/Extensions/Check.cs @@ -0,0 +1,163 @@ +using JetBrains.Annotations; +using System.Diagnostics; + +namespace Tiobon.Core.Common; + +[DebuggerStepThrough] +public static class Check +{ + [ContractAnnotation("value:null => halt")] + public static T NotNull( + T value, + [InvokerParameterName][NotNull] string parameterName) + { + if (value == null) + { + throw new ArgumentNullException(parameterName); + } + + return value; + } + + [ContractAnnotation("value:null => halt")] + public static T NotNull( + T value, + [InvokerParameterName][NotNull] string parameterName, + string message) + { + if (value == null) + { + throw new ArgumentNullException(parameterName, message); + } + + return value; + } + + [ContractAnnotation("value:null => halt")] + public static string NotNull( + string value, + [InvokerParameterName][NotNull] string parameterName, + int maxLength = int.MaxValue, + int minLength = 0) + { + if (value == null) + { + throw new ArgumentException($"{parameterName} can not be null!", parameterName); + } + + if (value.Length > maxLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName); + } + + if (minLength > 0 && value.Length < minLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName); + } + + return value; + } + + [ContractAnnotation("value:null => halt")] + public static string NotNullOrWhiteSpace( + string value, + [InvokerParameterName][NotNull] string parameterName, + int maxLength = int.MaxValue, + int minLength = 0) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException($"{parameterName} can not be null, empty or white space!", parameterName); + } + + if (value.Length > maxLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName); + } + + if (minLength > 0 && value.Length < minLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName); + } + + return value; + } + + [ContractAnnotation("value:null => halt")] + public static string NotNullOrEmpty( + string value, + [InvokerParameterName][NotNull] string parameterName, + int maxLength = int.MaxValue, + int minLength = 0) + { + if (value.IsNullOrEmpty()) + { + throw new ArgumentException($"{parameterName} can not be null or empty!", parameterName); + } + + if (value.Length > maxLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName); + } + + if (minLength > 0 && value.Length < minLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName); + } + + return value; + } + + [ContractAnnotation("value:null => halt")] + public static ICollection NotNullOrEmpty(ICollection value, [InvokerParameterName][NotNull] string parameterName) + { + if (value == null || value.Count <= 0) + { + throw new ArgumentException(parameterName + " can not be null or empty!", parameterName); + } + + return value; + } + + [ContractAnnotation("type:null => halt")] + public static Type AssignableTo( + Type type, + [InvokerParameterName][NotNull] string parameterName) + { + NotNull(type, parameterName); + + if (!type.IsAssignableTo()) + { + throw new ArgumentException($"{parameterName} (type of {type.AssemblyQualifiedName}) should be assignable to the {typeof(TBaseType).GetFullNameWithAssemblyName()}!"); + } + + return type; + } + + public static string Length( + [CanBeNull] string value, + [InvokerParameterName][NotNull] string parameterName, + int maxLength, + int minLength = 0) + { + if (minLength > 0) + { + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentException(parameterName + " can not be null or empty!", parameterName); + } + + if (value.Length < minLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName); + } + } + + if (value != null && value.Length > maxLength) + { + throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName); + } + + return value; + } +} diff --git a/Tiobon.Core.Common/Extensions/TypeExtensions.cs b/Tiobon.Core.Common/Extensions/TypeExtensions.cs new file mode 100644 index 00000000..c83222a5 --- /dev/null +++ b/Tiobon.Core.Common/Extensions/TypeExtensions.cs @@ -0,0 +1,92 @@ +using Tiobon.Core.Common; +using JetBrains.Annotations; + + +namespace System; + +public static class TypeExtensions +{ + public static string GetFullNameWithAssemblyName(this Type type) + { + return type.FullName + ", " + type.Assembly.GetName().Name; + } + + /// + /// Determines whether an instance of this type can be assigned to + /// an instance of the . + /// + /// Internally uses . + /// + /// Target type (as reverse). + public static bool IsAssignableTo([NotNull] this Type type) + { + Check.NotNull(type, nameof(type)); + + return type.IsAssignableTo(typeof(TTarget)); + } + + /// + /// Determines whether an instance of this type can be assigned to + /// an instance of the . + /// + /// Internally uses (as reverse). + /// + /// this type + /// Target type + public static bool IsAssignableTo([NotNull] this Type type, [NotNull] Type targetType) + { + Check.NotNull(type, nameof(type)); + Check.NotNull(targetType, nameof(targetType)); + + return targetType.IsAssignableFrom(type); + } + + /// + /// Gets all base classes of this type. + /// + /// The type to get its base classes. + /// True, to include the standard type in the returned array. + public static Type[] GetBaseClasses([NotNull] this Type type, bool includeObject = true) + { + Check.NotNull(type, nameof(type)); + + var types = new List(); + AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject); + return types.ToArray(); + } + + /// + /// Gets all base classes of this type. + /// + /// The type to get its base classes. + /// A type to stop going to the deeper base classes. This type will be be included in the returned array + /// True, to include the standard type in the returned array. + public static Type[] GetBaseClasses([NotNull] this Type type, Type stoppingType, bool includeObject = true) + { + Check.NotNull(type, nameof(type)); + + var types = new List(); + AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject, stoppingType); + return types.ToArray(); + } + + private static void AddTypeAndBaseTypesRecursively( + [NotNull] List types, + [CanBeNull] Type type, + bool includeObject, + [CanBeNull] Type stoppingType = null) + { + if (type == null || type == stoppingType) + { + return; + } + + if (!includeObject && type == typeof(object)) + { + return; + } + + AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject, stoppingType); + types.Add(type); + } +} \ No newline at end of file diff --git a/Tiobon.Core.Common/Tiobon.Core.Common.csproj b/Tiobon.Core.Common/Tiobon.Core.Common.csproj index 86d448d1..36b9ae10 100644 --- a/Tiobon.Core.Common/Tiobon.Core.Common.csproj +++ b/Tiobon.Core.Common/Tiobon.Core.Common.csproj @@ -19,6 +19,7 @@ + diff --git a/Tiobon.Core.Jobs/Helper.cs b/Tiobon.Core.Jobs/Helper.cs new file mode 100644 index 00000000..9c7009a4 --- /dev/null +++ b/Tiobon.Core.Jobs/Helper.cs @@ -0,0 +1,70 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using Tiobon.Core.Common; +using Tiobon.Core.Common.DB; +using Tiobon.Core.Common.Seed; +using Tiobon.Core.Repository.UnitOfWorks; +using Autofac; +using Tiobon.Core.Extensions; +using Tiobon.Core.Services.Extensions; +using Tiobon.Core.DataAccess; +using Microsoft.EntityFrameworkCore; +using Tiobon.Core.Extensions.HostedService; +using Tiobon.Core.Common.DB.Dapper.Extensions; +namespace Tiobon.Core.Jobs; + +/// +/// 任务处理中心 +/// +public class Helper +{ + public static void Init(ServiceCollection services) + { + var basePath = AppContext.BaseDirectory; + services.AddSingleton(new AppSettings(basePath)); + + services.AddTransient(); + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(o => + { + return new SqlSugar.SqlSugarScope(new SqlSugar.ConnectionConfig() + { + ConnectionString = BaseDBConfig.GetMainConnectionDb().Connection, //必填, 数据库连接字符串 + DbType = (SqlSugar.DbType)BaseDBConfig.GetMainConnectionDb().DbType, //必填, 数据库类型 + IsAutoCloseConnection = true, //默认false, 时候知道关闭数据库连接, 设置为true无需使用using或者Close操作 + }); + }); + var mainConnetctDb = BaseDBConfig.MutiConnectionString.allDbs.Find(x => x.ConnId == MainDb.CurrentDbConnId); + services.AddDbContext(options => + { + options.UseLazyLoadingProxies().UseSqlServer(mainConnetctDb.Connection, o => o.UseCompatibilityLevel(120)); + }); + var builder = new ContainerBuilder(); + builder.RegisterType().As() + .AsImplementedInterfaces() + .InstancePerLifetimeScope() + .PropertiesAutowired(); + builder.RegisterInstance(new LoggerFactory()) + .As(); + + + builder.RegisterGeneric(typeof(Logger<>)) + .As(typeof(ILogger<>)) + .SingleInstance(); + + services.AddAppServices(); + //services.AddTransient(); + //services.AddTransient, BaseRepository>(); + //services.AddTransient(); + //services.AddTransient, BaseRepository>(); + + // 注入事件服务 + services.AddLogging(); + services.AddJobSetup(); + services.AddSimpleDapperSetup(); + builder.Build(); + } +} diff --git a/Tiobon.Core.Jobs/Program.cs b/Tiobon.Core.Jobs/Program.cs new file mode 100644 index 00000000..a55c74b9 --- /dev/null +++ b/Tiobon.Core.Jobs/Program.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using System.Threading; +using Tiobon.Core.Jobs; +using Tiobon.Core.Tasks; + +class Program +{ + static void Main(string[] args) + { + var services = new ServiceCollection(); + + Helper.Init(services); + var sp = services.BuildServiceProvider(); + var schedulerCenter = sp.GetService(); + // 任务处理中心 + TaskCenter taskCenter = new TaskCenter(schedulerCenter); + taskCenter.Start(); + Thread.Sleep(Timeout.Infinite); + + } +} diff --git a/Tiobon.Core.Jobs/TaskCenter.cs b/Tiobon.Core.Jobs/TaskCenter.cs new file mode 100644 index 00000000..6c49a6f4 --- /dev/null +++ b/Tiobon.Core.Jobs/TaskCenter.cs @@ -0,0 +1,44 @@ +using Microsoft.Extensions.DependencyInjection; +using Serilog.Core; +using System; +using Tiobon.Core.Common; +using Tiobon.Core.Tasks; + +namespace Tiobon.Core.Jobs; + +/// +/// 任务处理中心 +/// +public class TaskCenter +{ + private readonly ISchedulerCenter _schedulerCenter; + + static TaskCenter() + { + //ReloadOnChange = true 当appsettings.json被修改时重新加载 + + } + + /// + /// 初始化 + /// + public TaskCenter(ISchedulerCenter schedulerCenter) + { + _schedulerCenter = schedulerCenter; + } + + #region 启动任务服务 + /// + /// 启动任务服务 + /// + public void Start() + { + var container = new ServiceCollection(); + + _schedulerCenter.InitJobAsync(); + + } + + #endregion + +} \ No newline at end of file diff --git a/Tiobon.Core.Jobs/Tiobon.Core.Jobs.csproj b/Tiobon.Core.Jobs/Tiobon.Core.Jobs.csproj new file mode 100644 index 00000000..74b2f909 --- /dev/null +++ b/Tiobon.Core.Jobs/Tiobon.Core.Jobs.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + Exe + favicon.ico + + + + + + + + + + PreserveNewest + true + PreserveNewest + + + + + + + + + + + diff --git a/Tiobon.Core.Jobs/appsettings.Development.json b/Tiobon.Core.Jobs/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/Tiobon.Core.Jobs/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/Tiobon.Core.Jobs/appsettings.json b/Tiobon.Core.Jobs/appsettings.json new file mode 100644 index 00000000..274dead7 --- /dev/null +++ b/Tiobon.Core.Jobs/appsettings.json @@ -0,0 +1,372 @@ +{ + "urls": "http://*:9292", //web服务端口,如果用IIS部署,把这个去掉 + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Information", + "Microsoft.AspNetCore": "Warning", + "System": "Warning", + "System.Net.Http.HttpClient": "Warning", + "Hangfire": "Information", + "Magicodes": "Warning", + "DotNetCore.CAP": "Information", + "Savorboard.CAP": "Information", + "Quartz": "Information" + } + } + }, + "AllowedHosts": "*", + "Redis": { + "Enable": false, + "ConnectionString": "127.0.0.1:6379", + "InstanceName": "" //前缀 + }, + "RabbitMQ": { + "Enabled": true, + "Connection": "101xxxx57", + "UserName": "xxxx", + "Password": "xxxxx", + "Port": "5672", + "RetryCount": 2 + }, + "Kafka": { + "Enabled": false, + "Servers": "localhost:9092", + "Topic": "Tiobon", + "GroupId": "Tiobon-consumer", + "NumPartitions": 3 //主题分区数量 + }, + "EventBus": { + "Enabled": false, + "SubscriptionClientName": "Tiobon.Core" + }, + "AppSettings": { + "CachingAOP": { + "Enabled": true + }, + "LogToDb": true, + "LogAOP": { + "Enabled": false, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": true + } + }, + "TranAOP": { + "Enabled": true + }, + "UserAuditAOP": { + "Enabled": false + }, + "SqlAOP": { + "Enabled": true, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": true + }, + "LogToConsole": { + "Enabled": true + } + }, + "Date": "2018-08-28", + "SeedDBEnabled": false, //只生成表结构 + "SeedDBDataEnabled": false, //生成表,并初始化数据 + "Author": "Tiobon.Core", + "SvcName": "", // /svc/Tiobon + "UseLoadTest": false + }, + + //优化DB配置、不会再区分单库多库 + //MainDb:标识当前项目的主库,所对应的连接字符串的Enabled必须为true + //Log:标识日志库,所对应的连接字符串的Enabled必须为true + //从库只需配置Slaves数组,要求数据库类型一致!,比如都是SqlServer + // + //新增,故障转移方案 + //如果主库挂了,会自动切换到备用连接(比如说主库+备用库) + //备用连接的ConnId配置为主库的ConnId+数字即可,比如主库的ConnId为Main,那么备用连接的ConnId为Mian1 + //主库、备用库无需数据库类型一致! + //备用库不会有程序维护,需要手动维护 + "MainDB": "WMTiobon_MSSQL_Main", //当前项目的主库,所对应的连接字符串的Enabled必须为true + "DBS": [ + /* + 对应下边的 DBType + MySql = 0, + SqlServer = 1, + Sqlite = 2, + Oracle = 3, + PostgreSQL = 4, + Dm = 5,//达梦 + Kdbndp = 6,//人大金仓 + */ + { + "ConnId": "WMTiobon_MSSQL_Main", + "DBType": 1, + "Enabled": true, + "Connection": "Data Source=47.99.54.186;User ID=GHR;Password=Tiobon20190101;Database=GHR30;Encrypt=True;TrustServerCertificate=True;", + //"Connection": "Data Source=116.204.98.209;User ID=Tiobon;Password=&($!4UGUyU#$2sp9O;Database=Tiobon;Encrypt=True;TrustServerCertificate=True;", + "ProviderName": "System.Data.SqlClient" + }, + { + "ConnId": "Main", + "DBType": 2, + "Enabled": false, + "Connection": "WMTiobon.db", //sqlite只写数据库名就行 + "Slaves": [ + { + "HitRate": 0, // 值越大,优先级越高 0不使用 + "Connection": "WMTiobon2.db" + } + ] + }, + { + "ConnId": "Main2", + "DBType": 2, + "Enabled": false, + "Connection": "WMTiobon3.db", //sqlite只写数据库名就行 + "Slaves": [ + { + "HitRate": 0, // 值越大,优先级越高 0不使用 + "Connection": "WMTiobon4.db" + } + ] + }, + { + "ConnId": "Log", //日志库连接固定名称,不要改,其他的可以改 + "DBType": 1, + "Enabled": true, + "HitRate": 50, + "Connection": "Data Source=47.99.54.186;User ID=GHR;Password=Tiobon20190101;Database=GHR30;Encrypt=True;TrustServerCertificate=True;", + "ProviderName": "System.Data.SqlClient" + }, + { + "ConnId": "WMTiobon_MSSQL_1", + "DBType": 1, + "Enabled": false, + "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMTiobon_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", + "ProviderName": "System.Data.SqlClient" + }, + { + "ConnId": "WMTiobon_MSSQL_2", + "DBType": 1, + "Enabled": false, + "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMTiobon_MSSQL_2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", + "ProviderName": "System.Data.SqlClient" + }, + { + "ConnId": "WMTiobon_MYSQL", + "DBType": 0, + "Enabled": false, + "Connection": "server=localhost;Database=Tiobon;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" + }, + { + "ConnId": "WMTiobon_MYSQL_2", + "DBType": 0, + "Enabled": false, + "Connection": "server=localhost;Database=Tioboncore001;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" + }, + { + "ConnId": "WMTiobon_ORACLE", + "DBType": 3, + "Enabled": false, + "Connection": "Data Source=127.0.0.1/ops;User ID=OPS;Password=123456;Persist Security Info=True;Connection Timeout=60;" + }, + { + "ConnId": "WMTiobon_DM", + "DBType": 5, + "Enabled": false, + "Connection": "Server=xxxxx:5236;User Id=xxxxx;PWD=xxxxx;SCHEMA=TESTDBA;" + }, + { + "ConnId": "WMTiobon_KDBNDP", + "DBType": 6, + "Enabled": false, + "Connection": "Server=127.0.0.1;Port=54321;UID=SYSTEM;PWD=system;database=SQLSUGAR4XTEST1;" + } + ], + "Audience": { + "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //不要太短,16位+ + "SecretFile": "C:\\my-file\\Tiobon.core.audience.secret.txt", //安全。内容就是Secret + "Issuer": "Tiobon.Core", //这个值一定要在自己的项目里修改!! + "Audience": "wr", //这个值一定要在自己的项目里修改!! + "ExpirationHour": 72 //过去时长,单位小时 + }, + "Mongo": { + "ConnectionString": "mongodb://nosql.data", + "Database": "TiobonCoreDb" + }, + "Startup": { + "Domain": "http://localhost:9291", + "Cors": { + "PolicyName": "CorsIpAccess", //策略名称 + "EnableAllIPs": false, //当为true时,开放所有IP均可访问。 + // 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的 + // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的 + "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://127.0.0.1:6688,http://localhost:6688" + }, + "AppConfigAlert": { + "Enabled": true + }, + "IdentityServer4": { + "Enabled": false, // 这里默认是false,表示使用jwt,如果设置为true,则表示系统使用Ids4模式 + "AuthorizationUrl": "http://localhost:5004", // 认证中心域名 + "ApiName": "Tiobon.core.api" // 资源服务器 + }, + "Authing": { + "Enabled": false, + "Issuer": "https://uldr24esx31h-demo.authing.cn/oidc", + "Audience": "63d51c4205c2849803be5178", + "JwksUri": "https://uldr24esx31h-demo.authing.cn/oidc/.well-known/jwks.json" + }, + "RedisMq": { + "Enabled": false //redis 消息队列 + }, + "MiniProfiler": { + "Enabled": false //性能分析开启 + }, + "Nacos": { + "Enabled": false //Nacos注册中心 + } + }, + "Middleware": { + "RequestResponseLog": { + "Enabled": true, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": true + } + }, + "IPLog": { + "Enabled": true, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": true + } + }, + "RecordAccessLogs": { + "Enabled": true, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": true + }, + "IgnoreApis": "/api/permission/getnavigationbar,/api/monitor/getids4users,/api/monitor/getaccesslogs,/api/monitor/server,/api/monitor/getactiveusers,/api/monitor/server," + }, + "SignalR": { + "Enabled": false + }, + "SignalRSendLog": { + "Enabled": false + }, + "QuartzNetJob": { + "Enabled": true + }, + "Consul": { + "Enabled": false + }, + "IpRateLimit": { + "Enabled": false + }, + "EncryptionResponse": { + "Enabled": true, + "AllApis": false, + "LimitApis": [ + "/api/Login/GetJwtTokenSecret" + ] + }, + "EncryptionRequest": { + "Enabled": true, + "AllApis": false, + "LimitApis": [ + "/api/Login/GetJwtTokenSecret" + ] + } + }, + "IpRateLimiting": { + "EnableEndpointRateLimiting": false, //False: globally executed, true: executed for each + "StackBlockedRequests": false, //False: Number of rejections should be recorded on another counter + "RealIpHeader": "X-Real-IP", + "ClientIdHeader": "X-ClientId", + "IpWhitelist": [], //白名单 + "EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ], + "ClientWhitelist": [ "dev-client-1", "dev-client-2" ], + "QuotaExceededResponse": { + "Content": "{{\"status\":429,\"msg\":\"访问过于频繁,请稍后重试\",\"success\":false}}", + "ContentType": "application/json", + "StatusCode": 429 + }, + "HttpStatusCode": 429, //返回状态码 + "GeneralRules": [ //api规则,结尾一定要带* + { + "Endpoint": "*:/api/Tiobon*", + "Period": "1m", + "Limit": 20 + }, + { + "Endpoint": "*/api/*", + "Period": "1s", + "Limit": 3 + }, + { + "Endpoint": "*/api/*", + "Period": "1m", + "Limit": 30 + }, + { + "Endpoint": "*/api/*", + "Period": "12h", + "Limit": 500 + } + ] + + }, + "ConsulSetting": { + "ServiceName": "TiobonCoreService", + "ServiceIP": "localhost", + "ServicePort": "9291", + "ServiceHealthCheck": "/healthcheck", + "ConsulAddress": "http://localhost:8500" + }, + "PayInfo": { //建行聚合支付信息 + "MERCHANTID": "", //商户号 + "POSID": "", //柜台号 + "BRANCHID": "", //分行号 + "pubKey": "", //公钥 + "USER_ID": "", //操作员号 + "PASSWORD": "", //密码 + "OutAddress": "http://127.0.0.1:12345" //外联地址 + }, + "nacos": { + "ServerAddresses": [ "http://localhost:8848" ], // nacos 连接地址 + "DefaultTimeOut": 15000, // 默认超时时间 + "Namespace": "public", // 命名空间 + "ListenInterval": 10000, // 监听的频率 + "ServiceName": "Tiobon.Core.Api", // 服务名 + "Port": "9291", // 服务端口号 + "RegisterEnabled": true // 是否直接注册nacos + }, + "LogFiedOutPutConfigs": { + "tcpAddressHost": "", // 输出elk的tcp连接地址 + "tcpAddressPort": 0, // 输出elk的tcp端口号 + "ConfigsInfo": [ // 配置的输出elk节点内容 常用语动态标识 + { + "FiedName": "applicationName", + "FiedValue": "Tiobon.Core.Api" + } + ] + }, + "Seq": { + "Enabled": true, + "Address": "http://localhost:5341/", + "ApiKey": "" + } +} \ No newline at end of file diff --git a/Tiobon.Core.Jobs/favicon.ico b/Tiobon.Core.Jobs/favicon.ico new file mode 100644 index 00000000..757237d0 Binary files /dev/null and b/Tiobon.Core.Jobs/favicon.ico differ diff --git a/Tiobon.Core.Services/Extensions/AppServiceExtensions.cs b/Tiobon.Core.Services/Extensions/AppServiceExtensions.cs new file mode 100644 index 00000000..f594a86c --- /dev/null +++ b/Tiobon.Core.Services/Extensions/AppServiceExtensions.cs @@ -0,0 +1,39 @@ +using Tiobon.Core.Repository.Base; +using Microsoft.Extensions.DependencyInjection; +using System.Reflection; +using Tiobon.Core.IRepository.Base; +using Tiobon.Core.Services.BASE; + +namespace Tiobon.Core.Services.Extensions; + +public static class AppServiceExtensions +{ + public static IServiceCollection AddAppServices(this IServiceCollection services) + { + services.AddScoped(typeof(IBaseRepository<>), typeof(BaseRepository<>)); + var types = Assembly.GetExecutingAssembly().GetTypes(); + + var assignedTypes = types + .Where(m => m.GetBaseClasses().Length > 1) + .ToList(); + + foreach (var assignedType in assignedTypes) + { + if (assignedType == typeof(BaseServices<,,,>)) + { + continue; + } + // 添加 XXXService 依赖注入 + services.AddScoped(assignedType); + + // 添加 IXXXService -> XXXService 依赖注入 + var interfaceType = assignedType.GetInterfaces().FirstOrDefault(i => i.Name[1..] == assignedType.Name); + if (interfaceType != null) + { + services.AddScoped(interfaceType, assignedType); + } + } + + return services; + } +} diff --git a/Tiobon.Core.Tasks/QuartzNet/ISchedulerCenter.cs b/Tiobon.Core.Tasks/QuartzNet/ISchedulerCenter.cs index 741c96d2..b85f5ceb 100644 --- a/Tiobon.Core.Tasks/QuartzNet/ISchedulerCenter.cs +++ b/Tiobon.Core.Tasks/QuartzNet/ISchedulerCenter.cs @@ -73,6 +73,8 @@ namespace Tiobon.Core.Tasks /// Task> ExecuteJobAsync(Ghre_TasksQz tasksQz); + Task> InitJobAsync(); + } } diff --git a/Tiobon.Core.Tasks/QuartzNet/SchedulerCenterServer.cs b/Tiobon.Core.Tasks/QuartzNet/SchedulerCenterServer.cs index a2535e36..55202179 100644 --- a/Tiobon.Core.Tasks/QuartzNet/SchedulerCenterServer.cs +++ b/Tiobon.Core.Tasks/QuartzNet/SchedulerCenterServer.cs @@ -10,6 +10,10 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Reflection; using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Tiobon.Core.Common.Helper; +using Tiobon.Core.Common; +using Tiobon.Core.Common.DB.Dapper; namespace Tiobon.Core.Tasks { @@ -125,7 +129,7 @@ namespace Tiobon.Core.Tasks result.Message = $"该任务计划已经在执行:【{tasksQz.Name}】,请勿重复启动!"; return result; } - if(tasksQz.TriggerType == 0 && tasksQz.CycleRunTimes != 0 && tasksQz.CycleHasRunTimes>=tasksQz.CycleRunTimes) + if (tasksQz.TriggerType == 0 && tasksQz.CycleRunTimes != 0 && tasksQz.CycleHasRunTimes >= tasksQz.CycleRunTimes) { result.Success = false; result.Message = $"该任务计划已完成:【{tasksQz.Name}】,无需重复启动,如需启动请修改已循环次数再提交"; @@ -209,14 +213,14 @@ namespace Tiobon.Core.Tasks /// /// public async Task IsExistScheduleJobAsync(Ghre_TasksQz sysSchedule) - { + { JobKey jobKey = new JobKey(sysSchedule.Id.ToString(), sysSchedule.JobGroup); if (await _scheduler.Result.CheckExists(jobKey)) - { + { return true; } else - { + { return false; } } @@ -411,7 +415,7 @@ namespace Tiobon.Core.Tasks .WithSimpleSchedule(x => x .WithIntervalInSeconds(sysSchedule.IntervalSecond) .WithRepeatCount(sysSchedule.CycleRunTimes - 1)) - .EndAt(sysSchedule.EndTime.Value) + .EndAt(sysSchedule.EndTime.Value) .Build(); return trigger; } @@ -424,7 +428,7 @@ namespace Tiobon.Core.Tasks .WithIntervalInSeconds(sysSchedule.IntervalSecond) .RepeatForever() ) - .EndAt(sysSchedule.EndTime.Value) + .EndAt(sysSchedule.EndTime.Value) .Build(); return trigger; } @@ -461,13 +465,13 @@ namespace Tiobon.Core.Tasks try { JobKey jobKey = new JobKey(tasksQz.Id.ToString(), tasksQz.JobGroup); - + //判断任务是否存在,存在则 触发一次,不存在则先添加一个任务,触发以后再 停止任务 if (!await _scheduler.Result.CheckExists(jobKey)) { //不存在 则 添加一个计划任务 await AddScheduleJobAsync(tasksQz); - + //触发执行一次 await _scheduler.Result.TriggerJob(jobKey); @@ -493,5 +497,48 @@ namespace Tiobon.Core.Tasks } + #region 初始化任务 + /// + /// 初始化任务 + /// + /// + public async Task> InitJobAsync() + { + var result = new ServiceResult(); + try + { + + if (AppSettings.app("Middleware", "QuartzNetJob", "Enabled").ObjToBool()) + { + var allQzServices = DbAccess.QueryList("select * from Ghre_TasksQz"); + foreach (var item in allQzServices) + { + if (item.IsStart) + { + result = await AddScheduleJobAsync(item); + if (result.Success) + { + Console.WriteLine($"QuartzNetJob{item.Name}启动成功!"); + } + else + { + Console.WriteLine($"QuartzNetJob{item.Name}启动失败!错误信息:{result.Message}"); + } + } + } + } + + result.Message = $"初始化计划任务成功!"; + } + catch (Exception ex) + { + result.Message = $"初始化计划任务失败:【{ex.Message}】"; + } + + return result; + } + #endregion + + } } diff --git a/Tiobon.Core.Tasks/Tiobon.Core.Tasks.csproj b/Tiobon.Core.Tasks/Tiobon.Core.Tasks.csproj index ba2f00e2..882489df 100644 --- a/Tiobon.Core.Tasks/Tiobon.Core.Tasks.csproj +++ b/Tiobon.Core.Tasks/Tiobon.Core.Tasks.csproj @@ -3,11 +3,14 @@ + + + diff --git a/Tiobon.Core.sln b/Tiobon.Core.sln index 0210cb2b..d5b2f737 100644 --- a/Tiobon.Core.sln +++ b/Tiobon.Core.sln @@ -68,6 +68,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tiobon.Core.OPS.Tool", "Tio EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tiobon.Core.PublishHelper", "Tiobon.Core.PublishHelper\Tiobon.Core.PublishHelper.csproj", "{9B5E9966-1B4E-427E-838F-4AF0B742BE6C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tiobon.Core.Jobs", "Tiobon.Core.Jobs\Tiobon.Core.Jobs.csproj", "{CF66C4DE-8E0C-4FAD-901A-2B7E1E485ABA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -150,6 +152,10 @@ Global {9B5E9966-1B4E-427E-838F-4AF0B742BE6C}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B5E9966-1B4E-427E-838F-4AF0B742BE6C}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B5E9966-1B4E-427E-838F-4AF0B742BE6C}.Release|Any CPU.Build.0 = Release|Any CPU + {CF66C4DE-8E0C-4FAD-901A-2B7E1E485ABA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF66C4DE-8E0C-4FAD-901A-2B7E1E485ABA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF66C4DE-8E0C-4FAD-901A-2B7E1E485ABA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF66C4DE-8E0C-4FAD-901A-2B7E1E485ABA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE