diff --git a/src/CloudGaming/Api/CloudGaming.Api/CloudGaming.Api.csproj b/src/CloudGaming/Api/CloudGaming.Api/CloudGaming.Api.csproj
index 8a0c1f7..09cae8e 100644
--- a/src/CloudGaming/Api/CloudGaming.Api/CloudGaming.Api.csproj
+++ b/src/CloudGaming/Api/CloudGaming.Api/CloudGaming.Api.csproj
@@ -7,15 +7,28 @@
3936c093-f18d-46a7-91d6-96cc43ffe3b8
Linux
..\..
+ True
+
+
+
+ embedded
+
+
+
+ embedded
+
+
+
+
diff --git a/src/CloudGaming/Api/CloudGaming.Api/Program.cs b/src/CloudGaming/Api/CloudGaming.Api/Program.cs
index 48863a6..020c515 100644
--- a/src/CloudGaming/Api/CloudGaming.Api/Program.cs
+++ b/src/CloudGaming/Api/CloudGaming.Api/Program.cs
@@ -1,25 +1,136 @@
+using CloudGaming.Code.AppExtend;
+using CloudGaming.Code.DataAccess.MultiTenantUtil;
+using HuanMeng.DotNetCore.MiddlewareExtend;
+using HuanMeng.DotNetCore.CustomExtension;
+using System.Diagnostics;
+
+using System.Reflection;
+using HuanMeng.DotNetCore.Utility.AssemblyHelper;
+using HuanMeng.DotNetCore.SwaggerUtile;
+using Microsoft.OpenApi.Models;
+using Newtonsoft.Json.Serialization;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
-builder.Services.AddControllers();
+builder.Services.AddControllers()
+ .AddNewtonsoftJson(options =>
+ {
+ // 配置 Newtonsoft.Json 选项
+ options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; // 忽略循环引用
+ options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();// 首字母小写(驼峰样式)
+ options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";// 时间格式化
+#if !DEBUG
+ options.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.None;
+#endif
+ //options.SerializerSettings.Converters.Add()
+ // 其他配置...
+ });
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddSwaggerGen();
+builder.Services.AddSwaggerGen(c =>
+{
+
+ var securityScheme = new OpenApiSecurityScheme
+ {
+ Name = "JWT 身份验证(Authentication)",
+ Description = "请输入登录后获取JWT的**token**",
+ In = ParameterLocation.Header,
+ Type = SecuritySchemeType.Http,
+ Scheme = "bearer", //必须小写
+ BearerFormat = "JWT",
+ Reference = new OpenApiReference
+ {
+ Id = JwtBearerDefaults.AuthenticationScheme,
+ Type = ReferenceType.SecurityScheme
+ }
+ };
+ c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme);
+ c.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {securityScheme, Array.Empty()}
+ });
+
+ c.SwaggerDoc("v1", new OpenApiInfo { Title = "蒸汽云游戏", Version = "1.0.0", Description = "" });
+ foreach (var assemblies in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ // 添加 XML 注释文件路径
+ var xmlFile = $"{assemblies.GetName().Name}.xml";
+ var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
+ if (File.Exists(xmlPath))
+ {
+ c.IncludeXmlComments(xmlPath);
+ }
+ }
+ c.ParameterFilter();
+ c.RequestBodyFilter();
+});
+builder.AddAppConfigClient();
+//添加jwt验证
+builder.AddJwtConfig();
+#region 添加跨域
+var _myAllowSpecificOrigins = "_myAllowSpecificOrigins";
+builder.Services.AddCustomCors(_myAllowSpecificOrigins);
+#endregion
+
+//添加jwt验证
+//builder.AddJwtConfig();
var app = builder.Build();
// Configure the HTTP request pipeline.
-if (app.Environment.IsDevelopment())
+//if (app.Environment.IsDevelopment())
+//{
+// app.UseSwagger();
+// app.UseSwaggerUI();
+//}
+app.UseSwagger();
+app.UseSwaggerUI(c =>
{
- app.UseSwagger();
- app.UseSwaggerUI();
-}
+
+ c.EnableDeepLinking();
+ c.DefaultModelsExpandDepth(3);
+ c.DefaultModelExpandDepth(3);
+ c.EnableFilter("true");
+ //c.RoutePrefix = string.Empty;
+ c.SwaggerEndpoint("/swagger/v1/swagger.json", "蒸汽云游戏 API V1");
+});
app.UseHttpsRedirection();
app.UseAuthorization();
-
+//数据库中间件
+app.UseMultiTenant();
+//使用跨域
+app.UseCors(_myAllowSpecificOrigins);
app.MapControllers();
+app.UseStaticFiles();//静态文件访问配置
+//执行扩展中间件
+app.UseMiddlewareAll();
+#region 默认请求
+app.MapGet("/", () => "请求成功").WithName("默认请求");
+var startDateTime = DateTime.Now;
+var InformationalVersion = Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion;
+Console.WriteLine($"version:{InformationalVersion}");
+app.MapGet("/system", () =>
+{
+
+ using Process currentProcess = Process.GetCurrentProcess();
+ // CPU使用率 (一般是一个0-100之间的值,但实际是时间占比,需要转换)
+ double cpuUsage = currentProcess.TotalProcessorTime.TotalMilliseconds / Environment.TickCount * 100;
+ // 已用内存 (字节)
+ long memoryUsage = currentProcess.WorkingSet64;
+ return new
+ {
+ msg = $"系统信息:启动时间:{startDateTime.ToString("yyyy-MM-dd HH:mm:ss")},已安全运行时间:{DateTime.Now.Subtract(startDateTime).TotalMinutes.ToString("0.##")}分钟",
+
+ startDateTime,
+ MemoryUsage = $"{memoryUsage / (1024.0 * 1024.0):F2}MB",
+ CPUUsage = $"{cpuUsage:F2}%"
+
+ };
+}).WithName("获取系统数据");
+#endregion
app.Run();
diff --git a/src/CloudGaming/CloudGaming.sln b/src/CloudGaming/CloudGaming.sln
index a7356f6..ae7e883 100644
--- a/src/CloudGaming/CloudGaming.sln
+++ b/src/CloudGaming/CloudGaming.sln
@@ -32,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudGaming.CreateDataBase"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudGaming.GameModel", "Model\CloudGaming.GameModel\CloudGaming.GameModel.csproj", "{1120C146-6B83-4E4E-8A39-BD09466C7E1B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudGaming.DtoModel", "Model\CloudGaming.DtoModel\CloudGaming.DtoModel.csproj", "{96CD0865-0AD5-41B3-89A2-374FF17CDD16}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -62,6 +64,10 @@ Global
{1120C146-6B83-4E4E-8A39-BD09466C7E1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1120C146-6B83-4E4E-8A39-BD09466C7E1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1120C146-6B83-4E4E-8A39-BD09466C7E1B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {96CD0865-0AD5-41B3-89A2-374FF17CDD16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {96CD0865-0AD5-41B3-89A2-374FF17CDD16}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {96CD0865-0AD5-41B3-89A2-374FF17CDD16}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {96CD0865-0AD5-41B3-89A2-374FF17CDD16}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -73,6 +79,7 @@ Global
{5F851D79-E435-4D16-974A-6D5E3A3269A7} = {FCA3CA4B-1993-429A-B2E9-2B05DB44F10E}
{393ED915-3F88-4F84-AE2A-5C95F8867F16} = {9F7EF36C-17BB-4F93-927E-F462FE3C9337}
{1120C146-6B83-4E4E-8A39-BD09466C7E1B} = {A3F00FB0-49D6-48B1-99D9-4619634DF8D9}
+ {96CD0865-0AD5-41B3-89A2-374FF17CDD16} = {A3F00FB0-49D6-48B1-99D9-4619634DF8D9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1D299D92-FA27-47A0-8D78-43D1FAFE7628}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs
new file mode 100644
index 0000000..400800d
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.Code.AppExtend
+{
+ ///
+ /// 项目配置
+ ///
+ public class AppConfig
+ {
+ public AppConfig() { }
+ ///
+ /// 用户数据库连接字符串
+ ///
+ public string UserConnectionString { get; set; }
+ ///
+ /// 游戏
+ ///
+ public string GameConnectionString { get; set; }
+ ///
+ /// 扩展
+ ///
+ public string ExtConnectionString { get; set; }
+
+ ///
+ /// 手机app配置
+ ///
+ public string PhoneConnectionString { get; set; }
+
+ ///
+ /// redis连接字符串
+ ///
+ public string RedisConnectionString { get; set; }
+
+ ///
+ /// 域名
+ ///
+ public string DomainName { get; set; }
+ ///
+ /// 标识
+ ///
+ public string Identifier { get; set; }
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 租户
+ ///
+ public Guid TenantId { get; set; }
+ ///
+ /// 项目支付数据
+ ///
+ //public PaymentModel? Payment { get; set; }
+
+
+
+ ///
+ /// 获取数据库连接字符串
+ ///
+ /// user,game,ext,phone
+ ///
+ ///
+ public string GetConnectionString(AppDataBaseType appDataBaseType)
+ {
+ switch (appDataBaseType)
+ {
+ case AppDataBaseType.User:
+ return UserConnectionString;
+ case AppDataBaseType.Game:
+ return GameConnectionString;
+ case AppDataBaseType.Ext:
+ return ExtConnectionString;
+ case AppDataBaseType.App:
+ return PhoneConnectionString;
+ default:
+ throw new NotImplementedException("数据库连接字符串不存在");
+ }
+ }
+ }
+
+ ///
+ /// 数据库选项
+ ///
+
+ public enum AppDataBaseType
+ {
+ ///
+ /// 用户数据库
+ ///
+ User,
+ ///
+ /// 游戏数据库
+ ///
+ Game,
+ ///
+ /// 扩展数据库
+ ///
+ Ext,
+ ///
+ /// app数据库
+ ///
+ App
+ }
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs
new file mode 100644
index 0000000..525647b
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs
@@ -0,0 +1,184 @@
+using CloudGaming.Code.DataAccess.MultiTenantUtil;
+
+using System.Collections.Concurrent;
+using System.Collections.Frozen;
+
+using XLib.DotNetCore.CacheHelper;
+
+namespace CloudGaming.Code.AppExtend
+{
+ ///
+ /// app配置项扩展
+ ///
+ public static class AppConfigurationExtend
+ {
+ ///
+ /// 配置数据
+ ///
+ //public static ConfigClient AppConfigClient { get; set; }
+
+ ///
+ ///
+ ///
+ public static IConfigurationManager ConfigurationManager { get; set; }
+
+ ///
+ ///
+ ///
+ public static ConcurrentDictionary AppConfigs { get; set; } = new ConcurrentDictionary();
+
+ ///
+ /// 获取配置项
+ ///
+ ///
+ ///
+ public static AppConfig GetAppConfig(string domainName)
+ {
+ if (AppConfigs.TryGetValue(domainName, out var appConfig))
+ {
+
+ return appConfig;
+ }
+ if (AppConfigs.TryGetValue("default", out appConfig))
+ {
+ return appConfig;
+ }
+
+ return AppConfigs.FirstOrDefault().Value;
+ }
+
+
+ ///
+ /// 初始化app
+ ///
+ ///
+ ///
+ public static IHostApplicationBuilder AddAppConfigClient(this IHostApplicationBuilder builder)
+ {
+
+ //hostBuilder.
+ //builder..UseAgileConfig(configClient, ConfigClient_ConfigChanged);
+ //AppConfigClient = configClient;
+ ConfigurationManager = builder.Configuration;
+ AppConfigInit(builder.Configuration);
+ builder.Services.AddScoped();
+ builder.AddDataBase();
+ return builder;
+
+ }
+
+
+ ///
+ /// 获取配置项
+ ///
+ ///
+ ///
+ public static AppConfig? GetAppConfigIdentifier(string identifier)
+ {
+ var app = AppConfigs.Where(it => it.Value.Identifier == identifier).Select(it => it.Value).FirstOrDefault();
+
+ return app;
+ }
+
+
+ ///
+ /// 配置版本号
+ ///
+ public static string AppVersion
+ {
+ get;
+ set;
+ }
+
+
+
+ #region 租户
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static ITenantInfo ToITenantInfo(this AppConfig appConfig, ITenantInfo? tenantInfo = null)
+ {
+ if (tenantInfo == null)
+ {
+ tenantInfo = new TenantInfo();
+ }
+ tenantInfo.TenantId = appConfig.TenantId;
+ tenantInfo.Identifier = appConfig.Identifier;
+ tenantInfo.Name = appConfig.Name;
+ return tenantInfo;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static AppConfig ToAppConfig(this AppConfig appConfig, AppConfig? newAppConfig = null)
+ {
+ if (newAppConfig == null)
+ {
+ newAppConfig = new AppConfig();
+ }
+ if (appConfig == null)
+ {
+ appConfig = new AppConfig();
+ }
+ newAppConfig.TenantId = appConfig.TenantId;
+ newAppConfig.Identifier = appConfig.Identifier;
+ newAppConfig.Name = appConfig.Name;
+ newAppConfig.DomainName = appConfig.DomainName;
+ newAppConfig.RedisConnectionString = appConfig.RedisConnectionString;
+ newAppConfig.UserConnectionString = appConfig.UserConnectionString;
+ newAppConfig.GameConnectionString = appConfig.GameConnectionString;
+ newAppConfig.ExtConnectionString = appConfig.ExtConnectionString;
+ newAppConfig.PhoneConnectionString = appConfig.PhoneConnectionString;
+ return newAppConfig;
+ }
+
+
+ ///
+ /// 初始化租户数据
+ ///
+ ///
+ private static void AppConfigInit(IConfigurationManager configurationManager)
+ {
+ var tenants = configurationManager.GetSection("Tenant").Get>();
+ if (tenants != null)
+ {
+ ConcurrentDictionary _AppConfigs = new ConcurrentDictionary();
+ if (tenants.Count > 0)
+ {
+ tenants?.ForEach(t =>
+ {
+
+ if (!_AppConfigs.TryAdd(t.DomainName, t))
+ {
+ Console.WriteLine($"{t.DomainName}配置加载失败");
+ }
+ if (t.Name == "default")
+ {
+ _AppConfigs.TryAdd("default", t);
+ }
+ });
+ if (!_AppConfigs.TryGetValue("default", out var x))
+ {
+ _AppConfigs.TryAdd("default", tenants[0]);
+ }
+ }
+ else
+ {
+ _AppConfigs.TryAdd("default", new AppConfig());
+ }
+ AppConfigs = _AppConfigs;
+ }
+ }
+ #endregion
+
+
+ }
+
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JwtTokenManageExtension.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JwtTokenManageExtension.cs
new file mode 100644
index 0000000..c47534b
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JwtTokenManageExtension.cs
@@ -0,0 +1,74 @@
+using HuanMeng.DotNetCore.JwtInfrastructure.Interface;
+using HuanMeng.DotNetCore.JwtInfrastructure;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
+
+namespace CloudGaming.Code.AppExtend
+{
+ ///
+ ///
+ ///
+ public static class JwtTokenManageExtension
+ {
+ ///
+ /// 添加jwt安全验证,配置
+ ///
+ ///
+ ///
+ ///
+ public static void AddJwtConfig(this IHostApplicationBuilder builder)
+ {
+ var jwtTokenConfig = builder.Configuration.GetSection("JwtTokenConfig").Get()!;
+ if (jwtTokenConfig == null)
+ {
+ jwtTokenConfig = new JwtTokenConfig()
+ {
+ Secret = Guid.NewGuid().ToString(),
+ AccessTokenExpiration = 60 * 60
+ };
+ }
+ //注册一个jwtTokenConfig的单例服务
+ builder.Services.AddSingleton(jwtTokenConfig);
+ //
+ builder.Services.AddAuthentication(options =>
+ {
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ }).AddJwtBearer(options =>
+ {
+ options.RequireHttpsMetadata = true;
+ options.SaveToken = true;
+ //调试使用
+ //options.Events = new JwtDebugBearerEvents().GetJwtBearerEvents();
+ options.TokenValidationParameters = new TokenValidationParameters
+ {
+ //是否验证颁发者
+ ValidateIssuer = true,
+ //是否验证受众
+ ValidateAudience = true,
+ //指定是否验证令牌的生存期。设置为 true 表示要进行验证。
+ ValidateLifetime = true,
+ //指定是否验证颁发者签名密钥。设置为 true 表示要进行验证。
+ ValidateIssuerSigningKey = true,
+ //颁发者
+ ValidIssuer = jwtTokenConfig.Issuer,
+ //受众
+ ValidAudience = jwtTokenConfig.Audience,
+ //指定用于验证颁发者签名的密钥
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtTokenConfig.Secret)),
+ //指定允许令牌的时钟偏移。允许令牌的过期时间与实际时间之间存在的时间差。在这里设置为 5 分钟,表示允许令牌的时钟偏移为 5 分钟。
+ ClockSkew = TimeSpan.FromMinutes(5)
+ };
+ });
+ //注册一个JwtAuthManager的单例服务
+ builder.Services.AddSingleton();
+ }
+
+ }
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj b/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj
index 13eab54..ed85ee3 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj
+++ b/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj
@@ -4,9 +4,17 @@
net8.0
enable
enable
+ True
+ True
+
+
+
+
+
+
diff --git a/src/CloudGaming/Code/CloudGaming.Code/DataAccess/DAO.cs b/src/CloudGaming/Code/CloudGaming.Code/DataAccess/DAO.cs
index bc71a8d..aa17b91 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/DataAccess/DAO.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/DataAccess/DAO.cs
@@ -1,17 +1,4 @@
-using HuanMeng.DotNetCore.Base;
-using HuanMeng.DotNetCore.MultiTenant.Contract;
-using HuanMeng.DotNetCore.MultiTenant;
-using Microsoft.Extensions.DependencyInjection;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using CloudGaming.GameModel.Db.Db_Ext;
-using CloudGaming.GameModel.Db.Db_Game;
-using CloudGaming.Model.DbSqlServer.Db_Phone;
-using CloudGaming.Model.DbSqlServer.Db_User;
namespace CloudGaming.Code.DataAccess
{
@@ -20,8 +7,7 @@ namespace CloudGaming.Code.DataAccess
///
public class DAO : DaoBase
{
- //private IMultiTenantProvider _multiTenantProvider;
- private ITenantInfo _tenantInfo;
+ private ITenantInfo? _tenantInfo;
///
///
@@ -32,7 +18,8 @@ namespace CloudGaming.Code.DataAccess
{
if (_tenantInfo == null)
{
- this._tenantInfo = _serviceProvider.GetRequiredService();
+ var app = _serviceProvider.GetRequiredService();
+ this._tenantInfo = app.ToITenantInfo();
}
return this._tenantInfo;
}
@@ -43,16 +30,16 @@ namespace CloudGaming.Code.DataAccess
///
public DAO(IServiceProvider serviceProvider) : base(serviceProvider)
{
- this._tenantInfo = serviceProvider.GetRequiredService();
+
}
///
/// 构造函数
///
///
- public DAO(IServiceProvider serviceProvider, ITenantInfo tenantInfo) : base(serviceProvider)
+ public DAO(IServiceProvider serviceProvider, AppConfig appConfig) : base(serviceProvider)
{
- this._tenantInfo = tenantInfo;
+ this._tenantInfo = appConfig.ToITenantInfo();
}
#region 扩展
@@ -61,7 +48,7 @@ namespace CloudGaming.Code.DataAccess
/// 数据库[CloudGamingCBT],
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
///
- public EfCoreDaoBase daoExt
+ public EfCoreDaoBase DaoExt
{
get
{
@@ -69,7 +56,7 @@ namespace CloudGaming.Code.DataAccess
{
var dbContext = _serviceProvider.GetRequiredService();
_daoExt = new EfCoreDaoBase(dbContext);
- _daoExt.context.TenantInfo = TenantInfo;
+ _daoExt.Context.TenantInfo = TenantInfo;
}
return _daoExt;
}
@@ -81,7 +68,7 @@ namespace CloudGaming.Code.DataAccess
/// 数据库[CloudGamingGameContext],
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
///
- public EfCoreDaoBase daoGame
+ public EfCoreDaoBase DaoGame
{
get
{
@@ -89,7 +76,7 @@ namespace CloudGaming.Code.DataAccess
{
var dbContext = _serviceProvider.GetRequiredService();
_daoGame = new EfCoreDaoBase(dbContext);
- _daoGame.context.TenantInfo = TenantInfo;
+ _daoGame.Context.TenantInfo = TenantInfo;
}
return _daoGame;
}
@@ -102,7 +89,7 @@ namespace CloudGaming.Code.DataAccess
/// 数据库[CloudGamingGameContext],
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
///
- public EfCoreDaoBase daoPhone
+ public EfCoreDaoBase DaoPhone
{
get
{
@@ -110,20 +97,20 @@ namespace CloudGaming.Code.DataAccess
{
var dbContext = _serviceProvider.GetRequiredService();
_daoPhone = new EfCoreDaoBase(dbContext);
- _daoPhone.context.TenantInfo = TenantInfo;
+ _daoPhone.Context.TenantInfo = TenantInfo;
}
return _daoPhone;
}
}
#endregion
-
+
#region 用户
private EfCoreDaoBase? _daoUser;
///
/// 数据库[CloudGamingGameContext],
/// 更新删除,尽量只操作本bll实例dao获取到的对象,取和存要同一个dao
///
- public EfCoreDaoBase daoUser
+ public EfCoreDaoBase DaoUser
{
get
{
@@ -131,7 +118,7 @@ namespace CloudGaming.Code.DataAccess
{
var dbContext = _serviceProvider.GetRequiredService();
_daoUser = new EfCoreDaoBase(dbContext);
- _daoUser.context.TenantInfo = TenantInfo;
+ _daoUser.Context.TenantInfo = TenantInfo;
}
return _daoUser;
}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/DataAccess/MultiTenantUtil/MultiTenantExtension.cs b/src/CloudGaming/Code/CloudGaming.Code/DataAccess/MultiTenantUtil/MultiTenantExtension.cs
new file mode 100644
index 0000000..fa5831a
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/DataAccess/MultiTenantUtil/MultiTenantExtension.cs
@@ -0,0 +1,63 @@
+namespace CloudGaming.Code.DataAccess.MultiTenantUtil
+{
+ ///
+ /// 多租户扩展
+ ///
+ public static class MultiTenantExtension
+ {
+ ///
+ /// 多租户 IServiceCollection 扩展
+ ///
+ ///
+ public static void AddDataBase(this IHostApplicationBuilder builder)
+ {
+ // 获取初始连接字符串
+ string userConnectionString = builder.Configuration.GetConnectionString("UserConnectionString") ?? "";
+ string gameConnectionString = builder.Configuration.GetConnectionString("GameConnectionString") ?? "";
+ string phoneConnectionString = builder.Configuration.GetConnectionString("PhoneConnectionString") ?? "";
+ string extConnectionString = builder.Configuration.GetConnectionString("ExtConnectionString") ?? "";
+
+ // 注册 用户DbContext
+ RegisterDbContext(builder, userConnectionString, AppDataBaseType.User);
+ //注册 游戏Db
+ RegisterDbContext(builder, gameConnectionString, AppDataBaseType.Game);
+ //注册 app配置Db
+ RegisterDbContext(builder, phoneConnectionString, AppDataBaseType.App);
+ //注册 扩展Db
+ RegisterDbContext(builder, extConnectionString, AppDataBaseType.Ext);
+ }
+
+ ///
+ /// 注册 DbContext,避免重复代码
+ ///
+ /// 要注册的 DbContext 类型
+ /// 应用构建器
+ /// 默认连接字符串
+ /// AppConfig 中的连接字符串键
+ private static void RegisterDbContext(IHostApplicationBuilder builder, string defaultConnectionString, AppDataBaseType configKey) where TContext : DbContext
+ {
+ builder.Services.AddDbContext((serviceProvider, options) =>
+ {
+ var appConfig = serviceProvider.GetRequiredService();
+ string dbConnectionString = appConfig?.GetConnectionString(configKey) ?? defaultConnectionString;
+
+ if (string.IsNullOrEmpty(dbConnectionString))
+ {
+ dbConnectionString = defaultConnectionString;
+ }
+
+ options.UseSqlServer(dbConnectionString);
+ }, ServiceLifetime.Scoped);
+ }
+ ///
+ /// 多租户IApplicationBuilder扩展
+ ///
+ ///
+ ///
+ public static IApplicationBuilder UseMultiTenant(this IApplicationBuilder app)
+ {
+ return app.UseMiddleware();
+ }
+
+ }
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/DataAccess/MultiTenantUtil/MultiTenantTenantMiddleware.cs b/src/CloudGaming/Code/CloudGaming.Code/DataAccess/MultiTenantUtil/MultiTenantTenantMiddleware.cs
new file mode 100644
index 0000000..40c30bd
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/DataAccess/MultiTenantUtil/MultiTenantTenantMiddleware.cs
@@ -0,0 +1,42 @@
+using Microsoft.AspNetCore.Http;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.Code.DataAccess.MultiTenantUtil
+{
+ ///
+ /// 多租户中间件
+ ///
+ public class MultiTenantTenantMiddleware
+ {
+ private readonly RequestDelegate _next;
+ public MultiTenantTenantMiddleware(RequestDelegate next)
+ {
+ _next = next;
+ }
+
+
+ ///
+ /// 根据HttpContext获取并设置当前租户ID
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual async Task Invoke(HttpContext context,
+ IServiceProvider _serviceProvider,
+ AppConfig appConfig
+ )
+ {
+
+ var host = context.Request.Host.Host;
+ var app = AppConfigurationExtend.GetAppConfig(host);
+ app.ToAppConfig(appConfig);
+ await _next.Invoke(context);
+ }
+ }
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/GlobalUsings.cs b/src/CloudGaming/Code/CloudGaming.Code/GlobalUsings.cs
new file mode 100644
index 0000000..3876d89
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/GlobalUsings.cs
@@ -0,0 +1,15 @@
+global using CloudGaming.Code.AppExtend;
+global using CloudGaming.GameModel.Db.Db_Ext;
+global using CloudGaming.GameModel.Db.Db_Game;
+global using CloudGaming.Model.DbSqlServer.Db_Phone;
+global using CloudGaming.Model.DbSqlServer.Db_User;
+
+global using HuanMeng.DotNetCore.Base;
+global using HuanMeng.DotNetCore.MultiTenant;
+global using HuanMeng.DotNetCore.MultiTenant.Contract;
+
+global using Microsoft.AspNetCore.Builder;
+global using Microsoft.EntityFrameworkCore;
+global using Microsoft.Extensions.Configuration;
+global using Microsoft.Extensions.DependencyInjection;
+global using Microsoft.Extensions.Hosting;
\ No newline at end of file
diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/CloudGaming.DtoModel.csproj b/src/CloudGaming/Model/CloudGaming.DtoModel/CloudGaming.DtoModel.csproj
new file mode 100644
index 0000000..2558b98
--- /dev/null
+++ b/src/CloudGaming/Model/CloudGaming.DtoModel/CloudGaming.DtoModel.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net8.0
+ enable
+ enable
+ True
+ True
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/GlobalUsings.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/GlobalUsings.cs
new file mode 100644
index 0000000..c36c453
--- /dev/null
+++ b/src/CloudGaming/Model/CloudGaming.DtoModel/GlobalUsings.cs
@@ -0,0 +1,10 @@
+global using CloudGaming.GameModel.Db.Db_Ext;
+global using CloudGaming.GameModel.Db.Db_Game;
+global using CloudGaming.Model.DbSqlServer.Db_Phone;
+global using CloudGaming.Model.DbSqlServer.Db_User;
+
+global using HuanMeng.DotNetCore.Base;
+global using HuanMeng.DotNetCore.MultiTenant;
+global using HuanMeng.DotNetCore.MultiTenant.Contract;
+
+global using Microsoft.Extensions.DependencyInjection;
\ No newline at end of file
diff --git a/src/CloudGaming/Model/CloudGaming.GameModel/CloudGaming.GameModel.csproj b/src/CloudGaming/Model/CloudGaming.GameModel/CloudGaming.GameModel.csproj
index f46fdc8..8003f8b 100644
--- a/src/CloudGaming/Model/CloudGaming.GameModel/CloudGaming.GameModel.csproj
+++ b/src/CloudGaming/Model/CloudGaming.GameModel/CloudGaming.GameModel.csproj
@@ -4,6 +4,8 @@
net8.0
enable
enable
+ True
+ True
diff --git a/src/CloudGaming/Model/CloudGaming.Model/CloudGaming.Model.csproj b/src/CloudGaming/Model/CloudGaming.Model/CloudGaming.Model.csproj
index dd1dea5..5072c74 100644
--- a/src/CloudGaming/Model/CloudGaming.Model/CloudGaming.Model.csproj
+++ b/src/CloudGaming/Model/CloudGaming.Model/CloudGaming.Model.csproj
@@ -4,6 +4,8 @@
net8.0
enable
enable
+ True
+ True
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/EfCoreDaoBase.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/EfCoreDaoBase.cs
index 270a99b..3706142 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/EfCoreDaoBase.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/EfCoreDaoBase.cs
@@ -1,35 +1,18 @@
using Microsoft.EntityFrameworkCore;
-using Newtonsoft.Json;
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Linq.Expressions;
namespace HuanMeng.DotNetCore.Base
-{ ///
- /// 基本数据库操作,需要安装
- /// Microsoft.EntityFrameworkCore
- /// Microsoft.EntityFrameworkCore.Relational
- ///
- ///
+{
+ ///
+ /// 基本数据库操作,需要安装 Microsoft.EntityFrameworkCore 和 Microsoft.EntityFrameworkCore.Relational
+ ///
+ ///
public class EfCoreDaoBase where TDbContext : DbContext
- //, new()
{
private TDbContext _context;
- public TDbContext context
- {
- get
- {
- //return _context ?? (_context = new TDbContext());
- return _context;
- }
- //set { _context = value; }
- }
- //public EfCoreDaoBase() { }
+ public TDbContext Context => _context;
///
/// 构造函数
@@ -37,8 +20,9 @@ namespace HuanMeng.DotNetCore.Base
///
public EfCoreDaoBase(TDbContext context)
{
- _context = context;
+ _context = context ?? throw new ArgumentNullException(nameof(context));
}
+
///
/// 是否手动提交
///
@@ -47,149 +31,133 @@ namespace HuanMeng.DotNetCore.Base
///
/// SqlQueryRaw
///
- ///
- ///
- ///
- ///
- public T SqlQuery(string sql, params object[] parameters) where T : class
+ public async Task SqlQueryAsync(string sql, params object[] parameters) where T : class
{
- var quiry = context.Database.SqlQueryRaw(sql, parameters);
- return quiry.FirstOrDefault();
+ return await Context.Database.SqlQueryRaw(sql, parameters).FirstOrDefaultAsync();
}
///
/// SqlQueryList
///
- ///
- ///
- ///
- ///
- public List SqlQueryList(string sql, params object[] parameters)
+ public async Task> SqlQueryListAsync(string sql, params object[] parameters)
{
- var quiry = context.Database.SqlQueryRaw(sql, parameters);
- return quiry.ToList();
+ return await Context.Database.SqlQueryRaw(sql, parameters).ToListAsync();
}
///
/// ExecuteSql
///
- ///
- ///
- ///
- public int ExecuteSql(string sql, params object[] parameters)
+ public async Task ExecuteSqlAsync(string sql, params object[] parameters)
{
- int r = context.Database.ExecuteSqlRaw(sql, parameters);
- context.SaveChanges();
- return r;
+ var result = await Context.Database.ExecuteSqlRawAsync(sql, parameters);
+ await AutoSaveChangesAsync();
+ return result;
}
///
/// 添加实体
///
- ///
- public void Add(T entity) where T : class
+ public async Task AddAsync(T entity) where T : class
{
- context.Set().Add(entity);
- if (!IsManualSubmit)
- context.SaveChanges();
+ Context.Set().Add(entity);
+ await AutoSaveChangesAsync();
}
///
/// 批量添加实体
///
- ///
- ///
- public void AddRange(List entity) where T : class
+ public async Task AddRangeAsync(IEnumerable entities) where T : class
{
- context.Set().AddRange(entity);
- if (!IsManualSubmit)
- context.SaveChanges();
+ Context.Set().AddRange(entities);
+ await AutoSaveChangesAsync();
}
+
///
/// 删除某个实体
///
- ///
- public void Delete(T entity) where T : class
+ public async Task DeleteAsync(T entity) where T : class
{
- context.Entry(entity).State = EntityState.Deleted; //整条更新
- //context.Set().Remove(entity);
- if (!IsManualSubmit)
- context.SaveChanges();
+ Context.Entry(entity).State = EntityState.Deleted;
+ await AutoSaveChangesAsync();
}
///
/// 更新实体
///
- ///
- public void Update(T entity) where T : class
+ public async Task UpdateAsync(T entity) where T : class
{
- //一般不需要整条更新,按需更新字段即可
- if (context.Entry(entity).State == EntityState.Detached)
+ if (Context.Entry(entity).State == EntityState.Detached)
{
- context.Set().Attach(entity);
- context.Entry(entity).State = EntityState.Modified; //整条更新
+ Context.Set().Attach(entity);
+ Context.Entry(entity).State = EntityState.Modified;
}
+ await AutoSaveChangesAsync();
+ }
+
+ ///
+ /// 清除上下文跟踪
+ ///
+ public void RemoveTracking(T entity) where T : class
+ {
+ if (Context.Entry(entity).State != EntityState.Detached)
+ {
+ Context.Entry(entity).State = EntityState.Detached;
+ }
+ }
+
+ ///
+ /// 获取实体,根据主键
+ ///
+ public async Task GetModelAsync(params object[] keyValues) where T : class
+ {
+ return await Context.Set().FindAsync(keyValues);
+ }
+
+ ///
+ /// 获取实体,根据条件
+ ///
+ public async Task GetModelAsync(Expression> where, bool isNoTracking = false) where T : class
+ {
+ var query = Context.Set().AsQueryable();
+ if (isNoTracking)
+ query = query.AsNoTracking();
+ return await query.FirstOrDefaultAsync(where);
+ }
+
+ ///
+ /// 获取列表数据
+ ///
+ public IQueryable GetList(Expression> where, bool isNoTracking = false) where T : class
+ {
+ var query = Context.Set().Where(where);
+ return isNoTracking ? query.AsNoTracking() : query;
+ }
+
+ ///
+ /// 获取记录数量
+ ///
+ public async Task GetCountAsync(Expression> where) where T : class
+ {
+ return await Context.Set().AsNoTracking().CountAsync(where);
+ }
+
+ ///
+ /// 判断是否存在记录
+ ///
+ public async Task ExistsAsync(Expression> where) where T : class
+ {
+ return await Context.Set().AsNoTracking().AnyAsync(where);
+ }
+
+ ///
+ /// 根据提交状态决定是否自动保存更改
+ ///
+ private async Task AutoSaveChangesAsync()
+ {
if (!IsManualSubmit)
- context.SaveChanges();
+ {
+ await Context.SaveChangesAsync();
+ }
}
-
- ///
- /// 清除上下文跟踪(清除缓存) by wyg
- ///
- ///
- ///
- public void removeTracking(T entity) where T : class
- {
- if (context.Entry(entity).State != EntityState.Detached)
- context.Entry(entity).State = EntityState.Detached;
- }
-
- ///
- /// 获取实体,从缓存中,根据键值获取
- ///
- ///
- ///
- ///
- public T GetModel(params object[] keyValues) where T : class
- {
- return context.Set().Find(keyValues);
- }
- ///
- /// 获取实体,根据条件获取,从数据库获取
- ///
- /// 筛选条件
- /// 默认false会进行缓存并跟踪(可按需update字段),true不需要缓存和跟踪(一般只读查询使用)
- ///
- public T GetModel(System.Linq.Expressions.Expression> where, bool isNoTracking = false) where T : class
- {
- if (!isNoTracking)
- return context.Set().FirstOrDefault(where); //可按需update字段
- return context.Set().AsNoTracking().FirstOrDefault(where); //一般只读查询使用
- }
-
- ///
- /// 获取列表数据,返回IQueryable
- ///
- /// 筛选条件
- /// 默认false会进行缓存并跟踪(可按需update字段),true不需要缓存和跟踪(一般只读查询使用)
- ///
- public IQueryable GetList(System.Linq.Expressions.Expression> where, bool isNoTracking = false) where T : class
- {
- if (!isNoTracking)
- return context.Set().Where(where).AsQueryable();
- return context.Set().Where(where).AsNoTracking().AsQueryable();
- }
-
- public int GetCount(System.Linq.Expressions.Expression> where) where T : class
- {
- return context.Set().AsNoTracking().Count(where);
- }
- public bool Exists(System.Linq.Expressions.Expression> where) where T : class
- {
- //return context.Set().AsNoTracking().Any(where);
- return context.Set().AsNoTracking().Count(where) > 0;
- }
-
-
}
}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/HuanMeng.DotNetCore.csproj b/src/CloudGaming/Utile/HuanMeng.DotNetCore/HuanMeng.DotNetCore.csproj
index beb049e..e95534e 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/HuanMeng.DotNetCore.csproj
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/HuanMeng.DotNetCore.csproj
@@ -4,6 +4,8 @@
net8.0
enable
enable
+ True
+ True
@@ -20,6 +22,7 @@
+
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/JwtInfrastructure/JwtManager.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/JwtInfrastructure/JwtManager.cs
new file mode 100644
index 0000000..bb76c5e
--- /dev/null
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/JwtInfrastructure/JwtManager.cs
@@ -0,0 +1,167 @@
+using HuanMeng.DotNetCore.JwtInfrastructure.Interface;
+using HuanMeng.DotNetCore.JwtInfrastructure;
+using Microsoft.IdentityModel.Tokens;
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IdentityModel.Tokens.Jwt;
+using System.Linq;
+using System.Security.Claims;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HuanMeng.DotNetCore.JwtInfrastructure
+{
+ ///
+ /// jwt,不保存
+ ///
+ ///
+ public class JwtManager(JwtTokenConfig jwtTokenConfig) : IJwtAuthManager
+ {
+
+ ///
+ /// 后面可以存储在数据库或分布式缓存中
+ ///
+ private readonly ConcurrentDictionary _usersRefreshTokens = new();
+ ///
+ /// 获取加密字段
+ ///
+ private readonly byte[] _secret = Encoding.UTF8.GetBytes(jwtTokenConfig.Secret);
+
+ ///
+ /// 删除过期token
+ ///
+ ///
+ public void RemoveExpiredRefreshTokens(DateTime now)
+ {
+ var expiredTokens = _usersRefreshTokens.Where(x => x.Value.ExpireAt < now).ToList();
+ foreach (var expiredToken in expiredTokens)
+ {
+ _usersRefreshTokens.TryRemove(expiredToken.Key, out _);
+ }
+ }
+
+ ///
+ /// 根据用户名删除token
+ ///
+ ///
+ public void RemoveRefreshTokenByUserName(string userName)
+ {
+ var refreshTokens = _usersRefreshTokens.Where(x => x.Value.UserName == userName).ToList();
+ foreach (var refreshToken in refreshTokens)
+ {
+ _usersRefreshTokens.TryRemove(refreshToken.Key, out _);
+ }
+ }
+
+ ///
+ /// 创建token
+ ///
+ /// 用户名
+ /// 用户项
+ /// 过期时间
+ ///
+ public JwtAuthResult GenerateTokens(string username, Claim[] claims, DateTime now)
+ {
+ var shouldAddAudienceClaim = string.IsNullOrWhiteSpace(claims.FirstOrDefault(x => x.Type == JwtRegisteredClaimNames.Aud)?.Value);
+ //创建token
+ var jwtToken = new JwtSecurityToken(
+ jwtTokenConfig.Issuer,
+ shouldAddAudienceClaim ? jwtTokenConfig.Audience : string.Empty,
+ claims,
+ expires: now.AddMinutes(jwtTokenConfig.AccessTokenExpiration),
+ signingCredentials: new SigningCredentials(new SymmetricSecurityKey(_secret), SecurityAlgorithms.HmacSha256Signature));
+ var accessToken = new JwtSecurityTokenHandler().WriteToken(jwtToken);
+
+ //创建刷新token
+ var refreshToken = new JwtRefreshToken
+ {
+ UserName = username,
+ TokenString = GenerateRefreshTokenString(),
+ ExpireAt = now.AddMinutes(jwtTokenConfig.RefreshTokenExpiration)
+ };
+ //_usersRefreshTokens.AddOrUpdate(refreshToken.TokenString, refreshToken, (_, _) => refreshToken);
+
+ return new JwtAuthResult
+ {
+ AccessToken = accessToken,
+ RefreshToken = refreshToken
+ };
+ }
+
+ ///
+ /// 刷新token
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public JwtAuthResult Refresh(string refreshToken, string accessToken, DateTime now)
+ {
+ var (principal, jwtToken) = DecodeJwtToken(accessToken);
+ if (jwtToken == null || !jwtToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256Signature))
+ {
+ throw new SecurityTokenException("无效的token");
+ }
+
+ var userName = principal.Identity?.Name;
+ if (!_usersRefreshTokens.TryGetValue(refreshToken, out var existingRefreshToken))
+ {
+ throw new SecurityTokenException("token已失效");
+ }
+ if (existingRefreshToken.UserName != userName || existingRefreshToken.ExpireAt < now)
+ {
+ throw new SecurityTokenException("token不匹配");
+ }
+ //创建新的token
+ return GenerateTokens(userName, principal.Claims.ToArray(), now);
+ }
+ ///
+ /// 解析token
+ ///
+ ///
+ ///
+ ///
+ public (ClaimsPrincipal, JwtSecurityToken?) DecodeJwtToken(string token)
+ {
+ if (string.IsNullOrWhiteSpace(token))
+ {
+ throw new SecurityTokenException("token不能为空");
+ }
+ var principal = new JwtSecurityTokenHandler()
+ .ValidateToken(token,
+ new TokenValidationParameters
+ {
+ ValidateIssuer = true,
+ ValidIssuer = jwtTokenConfig.Issuer,
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKey = new SymmetricSecurityKey(_secret),
+ ValidAudience = jwtTokenConfig.Audience,
+ ValidateAudience = true,
+ ValidateLifetime = true,
+ ClockSkew = TimeSpan.FromMinutes(5)
+ },
+ out var validatedToken);
+ return (principal, validatedToken as JwtSecurityToken);
+ }
+
+
+ ///
+ /// 获取刷新的token
+ ///
+ ///
+ private static string GenerateRefreshTokenString()
+ {
+ var randomNumber = new byte[32];
+ using var randomNumberGenerator = RandomNumberGenerator.Create();
+ randomNumberGenerator.GetBytes(randomNumber);
+ return Convert.ToBase64String(randomNumber);
+ }
+
+ public IImmutableDictionary UsersRefreshTokensReadOnlyDictionary => throw new NotImplementedException();
+ }
+}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/MultiTenant/Contract/ITenantInfo.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/MultiTenant/Contract/ITenantInfo.cs
index 2fe50e6..252e6e6 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/MultiTenant/Contract/ITenantInfo.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/MultiTenant/Contract/ITenantInfo.cs
@@ -20,9 +20,5 @@ namespace HuanMeng.DotNetCore.MultiTenant.Contract
///
string? Name { get; set; }
- ///
- /// 数据库连接字符串
- ///
- string? ConnectionString { get; set; }
}
}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/MultiTenant/TenantInfo.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/MultiTenant/TenantInfo.cs
index 04feb94..45a74c7 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/MultiTenant/TenantInfo.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/MultiTenant/TenantInfo.cs
@@ -33,9 +33,6 @@ namespace HuanMeng.DotNetCore.MultiTenant
///
public string? Name { get; set; }
- ///
- /// 租户数据库连接字符串
- ///
- public string? ConnectionString { get; set; }
+
}
}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/SwaggerUtile/LowercaseParameterFilter.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/SwaggerUtile/LowercaseParameterFilter.cs
new file mode 100644
index 0000000..4e2df45
--- /dev/null
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/SwaggerUtile/LowercaseParameterFilter.cs
@@ -0,0 +1,59 @@
+using Microsoft.OpenApi.Models;
+
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HuanMeng.DotNetCore.SwaggerUtile
+{
+ ///
+ /// 自定义参数过滤器
+ ///
+ public class LowercaseParameterFilter : IParameterFilter
+ {
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void Apply(OpenApiParameter parameter, ParameterFilterContext context)
+ {
+ // 将参数名称改为小写开头
+ parameter.Name = Char.ToLower(parameter.Name[0]) + parameter.Name.Substring(1);
+ }
+ }
+
+ ///
+ /// 自定义参数过滤器
+ ///
+ public class LowercaseRequestFilter : IRequestBodyFilter
+ {
+ public void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext context)
+ {
+
+ if (requestBody.Content != null)
+ {
+ foreach (var mediaType in requestBody.Content.Values)
+ {
+ if (mediaType.Schema?.Properties != null)
+ {
+ var propertiesToRename = new Dictionary(mediaType.Schema.Properties);
+ // 清空旧的属性
+ mediaType.Schema.Properties.Clear();
+
+ foreach (var property in propertiesToRename)
+ {
+ // 创建新的属性,并将名称改为小写开头
+ var newPropertyName = Char.ToLower(property.Key[0]) + property.Key.Substring(1);
+ mediaType.Schema.Properties.Add(newPropertyName, property.Value);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/AssemblyHelper/AssemblyInfo.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/AssemblyHelper/AssemblyInfo.cs
index d1d089f..e028cd2 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/AssemblyHelper/AssemblyInfo.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/AssemblyHelper/AssemblyInfo.cs
@@ -14,22 +14,22 @@ namespace HuanMeng.DotNetCore.Utility.AssemblyHelper
///
/// 获取或设置程序集的版本。
///
- public string Version { get; set; }
+ public string? Version { get; set; }
///
/// 获取或设置程序集的文件版本。
///
- public string FileVersion { get; set; }
+ public string? FileVersion { get; set; }
///
/// 获取或设置程序集版本。
///
- public string AssemblyVersion { get; set; }
+ public string? AssemblyVersion { get; set; }
///
/// 获取或设置程序集的信息性版本。
///
- public string InformationalVersion { get; set; }
+ public string? InformationalVersion { get; set; }
/////
///// 获取或设置与程序集关联的公司名称。
@@ -44,12 +44,12 @@ namespace HuanMeng.DotNetCore.Utility.AssemblyHelper
///
/// 获取或设置与程序集关联的版权信息。
///
- public string Copyright { get; set; }
+ public string? Copyright { get; set; }
///
/// 获取或设置程序集的描述信息。
///
- public string Description { get; set; }
+ public string? Description { get; set; }
}
}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/HttpContextExtensions.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/HttpContextExtensions.cs
index 3904fb9..65d3311 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/HttpContextExtensions.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/HttpContextExtensions.cs
@@ -8,6 +8,9 @@ using System.Threading.Tasks;
namespace HuanMeng.DotNetCore.Utility
{
+ ///
+ ///
+ ///
public static class HttpContextExtensions
{
///
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/WeChat/WXBizDataCrypt.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/WeChat/WXBizDataCrypt.cs
index 55fde7b..7c21252 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/WeChat/WXBizDataCrypt.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/WeChat/WXBizDataCrypt.cs
@@ -98,6 +98,7 @@ namespace HuanMeng.DotNetCore.WeChat
public static class ErrorCode
{
+
public const int OK = 0;
public const int IllegalAesKey = -41001;
public const int IllegalIv = -41002;
@@ -106,14 +107,15 @@ namespace HuanMeng.DotNetCore.WeChat
public class WXBizDataCryptModel
{
+ public WXBizDataCryptModel() { }
///
///
///
- public string EncryptedData;
+ public string? EncryptedData;
///
///
///
- public string Iv;
+ public string? Iv;
public int UserId { get; set; }