diff --git a/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs b/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs index 3c30d44..9d20e81 100644 --- a/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs +++ b/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs @@ -27,5 +27,16 @@ public class AppController : CloudGamingControllerBase return appConfig; } + /// + /// 清除本地缓存 + /// + /// + [HttpGet] + public bool ClearCacheData() + { + AppConfigBLL appConfigBLL = new AppConfigBLL(ServiceProvider); + var appConfig = appConfigBLL.ClearCacheData(); + return appConfig; + } } diff --git a/src/CloudGaming/Api/CloudGaming.Api/Controllers/HomeController.cs b/src/CloudGaming/Api/CloudGaming.Api/Controllers/HomeController.cs index 20935e0..38418a1 100644 --- a/src/CloudGaming/Api/CloudGaming.Api/Controllers/HomeController.cs +++ b/src/CloudGaming/Api/CloudGaming.Api/Controllers/HomeController.cs @@ -24,7 +24,7 @@ public class HomeController : CloudGamingControllerBase /// /// [HttpGet] - [RedisCache(1, 0, 0)] + //[RedisCache(1, 0, 0)] public async Task> GetHomeInfo() { EpgBLL epgBLL = new EpgBLL(ServiceProvider); diff --git a/src/CloudGaming/Api/CloudGaming.Api/Program.cs b/src/CloudGaming/Api/CloudGaming.Api/Program.cs index 4c36aad..1c804a0 100644 --- a/src/CloudGaming/Api/CloudGaming.Api/Program.cs +++ b/src/CloudGaming/Api/CloudGaming.Api/Program.cs @@ -159,7 +159,7 @@ if (type != null) builder.Services.AddAutoMapper(mapperDomain); #endregion -builder.AddAppConfigClient(); +await builder.AddAppConfigClient(); //添加jwt验证 builder.AddJwtConfig(); #region 添加跨域 diff --git a/src/CloudGaming/Api/CloudGaming.ExtApi/Program.cs b/src/CloudGaming/Api/CloudGaming.ExtApi/Program.cs index 2843f3c..6167435 100644 --- a/src/CloudGaming/Api/CloudGaming.ExtApi/Program.cs +++ b/src/CloudGaming/Api/CloudGaming.ExtApi/Program.cs @@ -104,7 +104,7 @@ builder.Services.AddSwaggerGen(c => c.ParameterFilter(); c.RequestBodyFilter(); }); -builder.AddAppConfigClient(); +await builder.AddAppConfigClient(); //添加游戏服务 builder.AddPlayGameServer(); builder.AddMonitorConfig(); diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/Program.cs b/src/CloudGaming/Api/CloudGaming.PayApi/Program.cs index 41fc814..516290a 100644 --- a/src/CloudGaming/Api/CloudGaming.PayApi/Program.cs +++ b/src/CloudGaming/Api/CloudGaming.PayApi/Program.cs @@ -85,7 +85,7 @@ builder.Services.AddSwaggerGen(c => c.ParameterFilter(); c.RequestBodyFilter(); }); -builder.AddAppConfigClient(); +await builder.AddAppConfigClient(); var app = builder.Build(); diff --git a/src/CloudGaming/Api/CloudGaming.Web/Program.cs b/src/CloudGaming/Api/CloudGaming.Web/Program.cs index 7c2bdcf..0ad353c 100644 --- a/src/CloudGaming/Api/CloudGaming.Web/Program.cs +++ b/src/CloudGaming/Api/CloudGaming.Web/Program.cs @@ -34,7 +34,7 @@ if (type != null) builder.Services.AddAutoMapper(mapperDomain); #endregion -builder.AddAppConfigClient(); +await builder.AddAppConfigClient(); builder.Services.AddRazorPages(); diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs index 32604da..f4bcfe6 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs @@ -2,14 +2,18 @@ using AgileConfig.Client; using CloudGaming.Code.DataAccess; using CloudGaming.Code.DataAccess.MultiTenantUtil; +using CloudGaming.Code.Game; using HuanMeng.DotNetCore.CacheHelper; using HuanMeng.DotNetCore.Redis; +using Refit; + using StackExchange.Redis; using System.Collections.Concurrent; using System.Collections.Frozen; +using System.Net.Http.Json; namespace CloudGaming.Code.AppExtend @@ -34,6 +38,11 @@ namespace CloudGaming.Code.AppExtend /// public static ConcurrentDictionary AppConfigs { get; set; } = new ConcurrentDictionary(); + /// + /// 多语言配置 + /// + public static ConcurrentDictionary> AppConfigLanguages { get; set; } = new ConcurrentDictionary>(); + /// /// 获取配置项 /// @@ -103,19 +112,29 @@ namespace CloudGaming.Code.AppExtend /// /// /// - public static IHostApplicationBuilder AddAppConfigClient(this WebApplicationBuilder builder) + public static async Task AddAppConfigClient(this WebApplicationBuilder builder) { var configClient = new ConfigClient(builder.Configuration); builder.Host.UseAgileConfig(configClient, ConfigClient_ConfigChanged); ConfigurationManager = builder.Configuration; - AppConfigInit(builder.Configuration); + await AppConfigInit(builder.Configuration); builder.Services.AddScoped(); builder.AddDataBase(); + builder.AddAppService(); return builder; } - + /// + /// 初始化app服务 + /// + /// + /// + public static IHostApplicationBuilder AddAppService(this WebApplicationBuilder builder) + { + builder.Services.AddHostedService(); + return builder; + } /// /// 获取配置项 @@ -226,6 +245,7 @@ namespace CloudGaming.Code.AppExtend newAppConfig.GameConfig = appConfig.GameConfig; newAppConfig.PrivacyAgreement = appConfig.PrivacyAgreement; newAppConfig.UserAgreement = appConfig.UserAgreement; + newAppConfig.LanguageRequestUrl = appConfig.LanguageRequestUrl; return newAppConfig; } @@ -269,7 +289,7 @@ namespace CloudGaming.Code.AppExtend /// 初始化租户数据 /// /// - private static void AppConfigInit(IConfigurationManager configurationManager) + private static async Task AppConfigInit(IConfigurationManager configurationManager) { var tenants = configurationManager.GetSection("Tenants").Get>(); if (tenants != null) @@ -277,18 +297,20 @@ namespace CloudGaming.Code.AppExtend ConcurrentDictionary _AppConfigs = new ConcurrentDictionary(); if (tenants.Count > 0) { - tenants?.ForEach(t => + foreach (var t in tenants) { - if (!_AppConfigs.TryAdd(t.DomainName, t)) { Console.WriteLine($"{t.DomainName}配置加载失败"); } + var list = await GetAppConfigLanguage(t.LanguageRequestUrl); + AppConfigLanguages.TryAdd(t.DomainName, list); if (t.Name == "default") { _AppConfigs.TryAdd("default", t); } - }); + } + if (!_AppConfigs.TryGetValue("default", out var x)) { _AppConfigs.TryAdd("default", tenants[0]); @@ -301,9 +323,59 @@ namespace CloudGaming.Code.AppExtend AppConfigs = _AppConfigs; } } + + /// + /// 获取项目的多语言 + /// + /// + /// + private static async Task> GetAppConfigLanguage(string url) + { + if (string.IsNullOrEmpty(url)) + { + return new List() { "zh" }; + } + using HttpClient _httpClient = new HttpClient(); + try + { + // 发送GET请求到指定的URL + var response = await _httpClient.GetFromJsonAsync>>>(url); + + // 检查响应是否为null,以及响应码是否为200 + if (response != null && response.Code == 200) + { + if (response.Data != null && response.Data.Count > 0) + { + var list = response.Data.Select(it => it["value"].ToString() ?? "").ToList(); + return list; + + } + } + } + catch (Exception ex) + { + + } + return new List() { "zh" }; + } #endregion - + /// + /// 获取多语言 + /// + /// + /// + public static List GetLanguages(this AppConfig appConfig) + { + if (appConfig != null) + { + if (AppConfigLanguages.TryGetValue(appConfig.DomainName, out var list)) + { + return list; + } + } + return new List() { "zh" }; + } } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppRequestConfig.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppRequestConfig.cs index 74451ba..08f3b6a 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppRequestConfig.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppRequestConfig.cs @@ -11,7 +11,7 @@ namespace CloudGaming.Code.AppExtend /// /// app 请求信息 /// - public class AppRequestConfig(HttpRequest httpRequest) + public class AppRequestConfig(HttpRequest? httpRequest) { private string channel { get; set; } /// @@ -23,7 +23,7 @@ namespace CloudGaming.Code.AppExtend { if (string.IsNullOrEmpty(channel)) { - if (!httpRequest.Headers.TryGetValue("Channel", out var _channel)) + if (!(httpRequest?.Headers?.TryGetValue("Channel", out var _channel) ?? false)) { _channel = "27001"; } @@ -48,7 +48,7 @@ namespace CloudGaming.Code.AppExtend { if (string.IsNullOrEmpty(platform)) { - if (!httpRequest.Headers.TryGetValue("Platform", out var _platform)) + if (!(httpRequest?.Headers.TryGetValue("Platform", out var _platform) ?? false)) { _platform = "android"; } @@ -68,7 +68,7 @@ namespace CloudGaming.Code.AppExtend { if (string.IsNullOrEmpty(version)) { - if (!httpRequest.Headers.TryGetValue("Version", out var _version)) + if (!(httpRequest?.Headers.TryGetValue("Version", out var _version) ?? false)) { _version = "1.0.0"; } @@ -104,7 +104,7 @@ namespace CloudGaming.Code.AppExtend { if (string.IsNullOrEmpty(language)) { - if (!httpRequest.Headers.TryGetValue("Language", out var _language)) + if (!(httpRequest?.Headers.TryGetValue("Language", out var _language) ?? false)) { _language = "zh"; } diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppService.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppService.cs new file mode 100644 index 0000000..3b084fc --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppService.cs @@ -0,0 +1,40 @@ +using CloudGaming.Code.Game; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.Code.AppExtend; + + +public class AppService(IServiceProvider scopeFactory) : IHostedService +{ + + public Task StartAsync(CancellationToken cancellationToken) + { + var appConfigs = AppConfigurationExtend.AppConfigs.Where(it => it.Key != "default") + .Select(it => it.Value) + .ToList(); + + // 为每个项目启动一个独立的任务 + foreach (var appConfig in appConfigs) + { + using var scope = scopeFactory.CreateScope(); + var scopedProvider = scope.ServiceProvider; + var app = scopedProvider.GetRequiredService(); + appConfig.ToAppConfig(app); + CloudGamingBase cloudGamingBase = new CloudGamingBase(scopedProvider); + //加载图片缓存 + cloudGamingBase.Cache.AppImageCache.ReloadData(); + } + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + + return Task.CompletedTask; + } +} diff --git a/src/CloudGaming/Code/CloudGaming.Code/Cache/CloudGamingCache.cs b/src/CloudGaming/Code/CloudGaming.Code/Cache/CloudGamingCache.cs index af6c39b..4866a9e 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Cache/CloudGamingCache.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Cache/CloudGamingCache.cs @@ -1,5 +1,6 @@ using AutoMapper; +using CloudGaming.Code.AppExtend; using CloudGaming.Code.Cache.Special; using CloudGaming.DtoModel; using CloudGaming.DtoModel.Game; @@ -8,8 +9,10 @@ using CloudGaming.DtoModel.RedemptionCode; using CloudGaming.DtoModel.SevenSign; using HuanMeng.DotNetCore.CacheHelper; +using HuanMeng.DotNetCore.CacheHelper.Contract; using System.Collections.Concurrent; +using System.Globalization; using System.Linq.Expressions; namespace CloudGaming.Code.Cache; @@ -42,25 +45,15 @@ public class CloudGamingCache #endregion - #region 图片缓存表 - private CommonDataEntityCache? _appImageCache; - - /// - /// 图片缓存列表 - /// - public List AppImageList => GetCacheList(ref _appImageCache); - - #endregion #region 配置缓存表 - private CommonDataEntityCache? _appConfigCache; - + private AppConfigEntityCache? _appConfigCache; /// - /// 配置缓存列表 + /// 系统配置缓存表 /// - public List AppConfigList + public AppConfigEntityCache AppConfigCache { get { @@ -68,7 +61,17 @@ public class CloudGamingCache { _appConfigCache = new AppConfigEntityCache(_gamingBase.Dao, _gamingBase.RedisCache, _gamingBase.Mapper, _gamingBase.AppConfig); } - return _appConfigCache.DataList ?? new List(); + return _appConfigCache; + } + } + /// + /// 配置缓存列表 + /// + public List AppConfigList + { + get + { + return AppConfigCache.DataList ?? new List(); } } @@ -119,20 +122,20 @@ public class CloudGamingCache /// /// /// - private ImageEntityCache imageEntityCache; + private ImageEntityCache appImageCache; /// /// 图片缓存 /// - public ImageEntityCache ImageEntityCache + public ImageEntityCache AppImageCache { get { - if (imageEntityCache == null) + if (appImageCache == null) { - imageEntityCache = new ImageEntityCache(_gamingBase.Dao, _gamingBase.AppConfig, _gamingBase.RedisCache, _gamingBase.AppRequestInfo); + appImageCache = new ImageEntityCache(_gamingBase.Dao, _gamingBase.AppConfig, _gamingBase.RedisCache, _gamingBase.AppRequestInfo.Language); } - return imageEntityCache; + return appImageCache; } } #endregion @@ -164,6 +167,7 @@ public class CloudGamingCache { get { + return ProductCacheEntityCache.DataList ?? new List(); } } @@ -234,11 +238,8 @@ public class CloudGamingCache } #endregion - #region 首页缓存表 - - #endregion } /// @@ -246,10 +247,10 @@ public class CloudGamingCache /// public static class CloudGamingCacheExtend { - private static readonly ConcurrentDictionary ExtCacheLockList = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary GameCacheLockList = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary AppCacheLockList = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary UserCacheLockList = new ConcurrentDictionary(); + public static readonly ConcurrentDictionary ExtCacheLockList = new ConcurrentDictionary(); + public static readonly ConcurrentDictionary GameCacheLockList = new ConcurrentDictionary(); + public static readonly ConcurrentDictionary AppCacheLockList = new ConcurrentDictionary(); + public static readonly ConcurrentDictionary UserCacheLockList = new ConcurrentDictionary(); /// /// 命名空间与缓存映射 @@ -267,6 +268,89 @@ public static class CloudGamingCacheExtend }; } + /// + /// 清除本地缓存 + /// + /// + public static void ClearLocalDataCache(this CloudGamingBase cloudGamingBase) + { + + foreach (var item in CloudGamingCacheExtend.ExtCacheLockList) + { + var c = cloudGamingBase.ToCacheBaseConfig(AppDataBaseType.Ext); + var t = item.Key; + Type cacheType = typeof(DataBaseEntityCache<>).MakeGenericType(t); + var shujuduixiang = Activator.CreateInstance(cacheType, c, item.Value, 36000, null); + var x = shujuduixiang as ICacheClearData; + if (x != null) + { + x.ClearData(); + } + } + + foreach (var item in CloudGamingCacheExtend.GameCacheLockList) + { + var c = cloudGamingBase.ToCacheBaseConfig(AppDataBaseType.Game); + var t = item.Key; + Type cacheType = typeof(DataBaseEntityCache<>).MakeGenericType(t); + var shujuduixiang = Activator.CreateInstance(cacheType, c, item.Value, 36000, null); + var x = shujuduixiang as ICacheClearData; + if (x != null) + { + x.ClearData(); + } + } + + foreach (var item in CloudGamingCacheExtend.AppCacheLockList) + { + var c = cloudGamingBase.ToCacheBaseConfig(AppDataBaseType.App); + var t = item.Key; + Type cacheType = typeof(DataBaseEntityCache<>).MakeGenericType(t); + var shujuduixiang = Activator.CreateInstance(cacheType, c, item.Value, 36000, null); + var x = shujuduixiang as ICacheClearData; + if (x != null) + { + x.ClearData(); + } + } + + foreach (var item in CloudGamingCacheExtend.UserCacheLockList) + { + var c = cloudGamingBase.ToCacheBaseConfig(AppDataBaseType.User); + var t = item.Key; + Type cacheType = typeof(DataBaseEntityCache<>).MakeGenericType(t); + var shujuduixiang = Activator.CreateInstance(cacheType, c, item.Value, 36000); + var x = shujuduixiang as ICacheClearData; + if (x != null) + { + x.ClearData(); + } + } + cloudGamingBase.Cache.AppConfigCache.ClearLocalData(); + cloudGamingBase.Cache.ProductCacheEntityCache.ClearLocalData(); + cloudGamingBase.Cache.AppImageCache.ClearLocalData(); + cloudGamingBase.Cache.GameEntityCache.ClearLocalData(); + cloudGamingBase.Cache.RedemptionCodeEntityCache.ClearLocalData(); + cloudGamingBase.Cache.SevenDayEntityCache.ClearLocalData(); + } + + + /// + /// 重新加载本地缓存 + /// + /// + public static void ReloadLocalDataCache(this CloudGamingBase cloudGamingBase) + { + + cloudGamingBase.Cache.AppConfigCache.ReloadData(); + cloudGamingBase.Cache.ProductCacheEntityCache.ReloadData(); + cloudGamingBase.Cache.AppImageCache.ReloadData(); + cloudGamingBase.Cache.GameEntityCache.ReloadData(); + cloudGamingBase.Cache.RedemptionCodeEntityCache.ReloadData(); + cloudGamingBase.Cache.SevenDayEntityCache.ReloadData(); + } + + /// /// 获取实体缓存 /// @@ -284,7 +368,7 @@ public static class CloudGamingCacheExtend object cacheLock = GetOrAddCacheLock(typeLock, cacheList); CacheBaseConfig cacheBaseConfig = cloudGamingBase.ToCacheBaseConfig(dbType); - return new DataBaseEntityCache(cacheBaseConfig, cacheLock, expWhere: expWhere, cacheTime: cacheTime); + return new DataBaseEntityCache(cacheBaseConfig, cacheLock, cacheTime, expWhere); } /// diff --git a/src/CloudGaming/Code/CloudGaming.Code/Cache/DataBaseEntityCache.cs b/src/CloudGaming/Code/CloudGaming.Code/Cache/DataBaseEntityCache.cs index 47f494e..9169ab5 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Cache/DataBaseEntityCache.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Cache/DataBaseEntityCache.cs @@ -7,48 +7,47 @@ using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; -namespace CloudGaming.Code.Cache +namespace CloudGaming.Code.Cache; + + +/// +/// +/// +/// +/// +/// +/// +/// +public class DataBaseEntityCache(CacheBaseConfig baseConfig, object lockObj, int cacheTime = 36000, Expression> expWhere = null) + : CommonDataEntityCache(lockObj, cacheTime) where T : class { /// - /// + /// 缓存的key /// - /// - /// - /// - /// - /// - public class DataBaseEntityCache(CacheBaseConfig baseConfig, object lockObj, int cacheTime = 36000, Expression> expWhere = null) - : CommonDataEntityCache(lockObj, cacheTime) where T : class + public override string key { - - /// - /// 缓存的key - /// - public override string key + get { - get - { - return $"Cache:{baseConfig.AppConfig.Identifier}:CloudGaming:{typeof(T).Name}"; - } - } - /// - /// - /// - /// - public override List GetDataList() - { - - var dbSet = baseConfig.DbContext.Set().AsNoTracking(); - if (dbSet == null) - { - return new List(); - } - if (expWhere != null) - { - dbSet = dbSet.Where(expWhere); - } - return dbSet.ToList(); + return $"Cache:{baseConfig.AppConfig.Identifier}:CloudGaming:{typeof(T).Name}"; } } + /// + /// + /// + /// + public override List GetDataList() + { + + var dbSet = baseConfig.DbContext.Set().AsNoTracking(); + if (dbSet == null) + { + return new List(); + } + if (expWhere != null) + { + dbSet = dbSet.Where(expWhere); + } + return dbSet.ToList(); + } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/Cache/Special/GameEntityCache.cs b/src/CloudGaming/Code/CloudGaming.Code/Cache/Special/GameEntityCache.cs index d2809fa..26f6dc3 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Cache/Special/GameEntityCache.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Cache/Special/GameEntityCache.cs @@ -9,6 +9,7 @@ using CloudGaming.DtoModel.Game; using CloudGaming.GameModel.Db.Db_Game; using HuanMeng.DotNetCore.CacheHelper; +using HuanMeng.DotNetCore.CacheHelper.Contract; using HuanMeng.DotNetCore.Redis; using Newtonsoft.Json; @@ -25,7 +26,7 @@ namespace CloudGaming.Code.Cache.Special /// /// 游戏缓存表 /// - public class GameEntityCache(DAO dao, IDatabase database, IMapper mapper, AppConfig appConfig) : CommonDataEntityCache(GameEntityCache.GameEntityCacheLock, 60 * 60 * 24 * 7) + public class GameEntityCache(DAO dao, IDatabase database, IMapper mapper, AppConfig appConfig) : CommonDataEntityCache(GameEntityCache.GameEntityCacheLock, 60 * 60 * 24 * 7), ICacheClearLocalData { public static object GameEntityCacheLock; @@ -142,14 +143,7 @@ namespace CloudGaming.Code.Cache.Special } } - //public Task> DataListAsync - //{ - // get - // { - // return new List(); - // } - //} private Dictionary gameInfoDic; /// /// 游戏详情 @@ -224,7 +218,7 @@ namespace CloudGaming.Code.Cache.Special { lock (GameEntityCacheLock) { - database.KeyDelete(key); + database.KeyDelete(RedisKey); MemoryCacheHelper.DelCache(key); _dataList = null; gameInfoDic = null; @@ -236,11 +230,20 @@ namespace CloudGaming.Code.Cache.Special { lock (lockObj) { - database.KeyDelete(key); + database.KeyDelete(RedisKey); MemoryCacheHelper.DelCache(key); _dataList = null; + gameInfoDic = null; var x = DataList; } } + + public bool ClearLocalData() + { + MemoryCacheHelper.DelCache(key); + _dataList = null; + gameInfoDic = null; + return true; + } } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/Cache/Special/ImageEntityCache.cs b/src/CloudGaming/Code/CloudGaming.Code/Cache/Special/ImageEntityCache.cs index e6dc019..68b6050 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Cache/Special/ImageEntityCache.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Cache/Special/ImageEntityCache.cs @@ -1,210 +1,236 @@ +using Bogus; + using CloudGaming.Code.DataAccess; +using Flurl; + using HuanMeng.DotNetCore.CacheHelper; using HuanMeng.DotNetCore.CacheHelper.Contract; +using Microsoft.Extensions.Caching.Memory; + +using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models; + using StackExchange.Redis; using System.Collections.Concurrent; +using static SKIT.FlurlHttpClient.Wechat.TenpayV3.ExtendedSDK.Global.Models.QueryPartnerRefundsResponse.Types; +using static SKIT.FlurlHttpClient.Wechat.TenpayV3.Models.CreateNewTaxControlFapiaoApplicationRequest.Types.Fapiao.Types; +using static System.Net.Mime.MediaTypeNames; + namespace CloudGaming.Code.Cache.Special; /// -/// 图片缓存表 +/// 图片缓存表 +/// 流程==》 +/// 1.先去本地内存中去找,如果没找打则去找本地缓存默认的图 +/// 2.如果本地默认的也没找到,则去redis中去找,如果redis中没找到当前语言,就去找redis默认的 /// -/// -/// -/// -/// -/// -public class ImageEntityCache(DAO dao, AppConfig appConfig, IDatabase database, AppRequestConfig appRequestConfig) : ICacheClearData, ICacheReloadData +public class ImageEntityCache : ICacheClearData, ICacheReloadData, ICacheClearLocalData { + private readonly DAO dao; + private readonly AppConfig appConfig; + private readonly IDatabase database; /// - /// 是否加载过图片 + /// 多语言列表 /// - public static bool IsLoadImage { get; set; } = false; + private List languages; + /// + /// 当前多语言 + /// + private string languae; /// /// 本地内存图片缓存key /// - private string key = $"{appConfig.Identifier}:App:Image"; - /// - /// redis图片缓存key - /// - private string redisKey = $"App:Image"; - /// - /// 当前使用的图片key - /// - ConcurrentDictionary>? ImageData { get; set; } + private readonly string memoryCacheKey; /// - /// + /// Redis图片缓存key /// - /// - /// - public ConcurrentDictionary this[string language] + private readonly string redisKey = "cache:Image"; + + /// + /// 图片缓存数据 + /// + private ConcurrentDictionary imageData; + + public ImageEntityCache(DAO dao, AppConfig appConfig, IDatabase database, string language) + { + this.dao = dao; + this.appConfig = appConfig; + this.database = database; + this.languae = language; + languages = appConfig.GetLanguages(); + if (!languages.Contains(languae)) + { + languae = appConfig.DefaultLanguage; + } + if (string.IsNullOrEmpty(languae)) + { + languae = appConfig.DefaultLanguage; + } + this.memoryCacheKey = $"{appConfig.Identifier}:App:Image:{languae}"; + + } + /// + /// 按语言和图片ID获取图片URL + /// + public string this[int imageId] { get { - if (string.IsNullOrEmpty(language)) - { - language = appRequestConfig.Language; - } - if (ImageData != null && ImageData.TryGetValue(language, out var images)) - { - return images; - } - ImageData = MemoryCacheHelper.GetCache>>(key); - if (ImageData == null) - { - ImageData = new ConcurrentDictionary>(); - } - if (!ImageData.TryGetValue(language, out images)) + if (imageId == 0) return string.Empty; + if (this.imageData == null) { - ImageData.TryAdd(language, images = new ConcurrentDictionary()); - //内存中缓存 - MemoryCacheHelper.SetCache(key, ImageData, 60 * 60 * 24); + var t = MemoryCacheHelper.GetCache>(memoryCacheKey); + if (t == null) + { + t = new ConcurrentDictionary(); + MemoryCacheHelper.SetCache(this.memoryCacheKey, t); + } + this.imageData = t; } - return images; + var images = this.imageData; + if (!images.TryGetValue(imageId, out var imageUrl)) + { + imageUrl = LoadImageFromRedis(this.languae, imageId); + if (!string.IsNullOrEmpty(imageUrl)) + { + images[imageId] = imageUrl; + } + } + + return imageUrl ?? string.Empty; } } - /// - /// - /// - /// - /// - /// - public string this[string language, int imageId] + + private string LoadImageFromRedis(string language, int imageId) { - get + var redisValue = database.StringGet($"{redisKey}:{language}:{imageId}"); + if (redisValue.IsNullOrEmpty) { - if (imageId == 0) - { - return ""; - } - var _imageData = this[language]; - if (_imageData == null) - { - _imageData = new ConcurrentDictionary(); - } - if (!_imageData.TryGetValue(imageId, out var imageUrl)) - { - var imageValue = database.StringGet($"{redisKey}:{language}:{imageId}"); - if (imageValue.IsNullOrEmpty) - { - imageValue = database.StringGet($"{redisKey}:default:{imageId}"); - } - if (!IsLoadImage && imageValue.IsNullOrEmpty) - { - if (!database.KeyExists(redisKey)) - { - ImageData = LoadImage(dao, appConfig); - MemoryCacheHelper.SetCache(key, ImageData, 60 * 60 * 24); - IsLoadImage = true; - _imageData = ImageData[language]; - if (!_imageData.TryGetValue(imageId, out imageUrl)) - { - imageUrl = ""; - } - return imageUrl; - } - } - imageUrl = imageValue; - _imageData.TryAdd(imageId, imageUrl); - } - return imageUrl; + redisValue = database.StringGet($"{redisKey}:default:{imageId}"); } + return redisValue; } - public ConcurrentDictionary> LoadImage(DAO dao, AppConfig appConfig) + private void LoadImagesFromDatabase() { - // 初始化_data字典 - ConcurrentDictionary> _data = new ConcurrentDictionary>(); - - // 获取图像列表 - var imageList = dao.DaoExt.Context.T_App_Image + var isadd = database.StringGet>($"{redisKey}:language"); + if (isadd != null && isadd.Count > 0) + { + foreach (var _language in isadd) + { + var mKey = $"{appConfig.Identifier}:App:Image:{_language}"; + var imageRedis = database.StringGet>($"{redisKey}:language:{_language}"); + //database.StringGet<> + var imagex = new ConcurrentDictionary(imageRedis ?? new Dictionary()); + MemoryCacheHelper.SetCache(mKey, imagex); + } + return; + } + var data = new ConcurrentDictionary>(); + var images = dao.DaoExt.Context.T_App_Image .Where(it => !string.IsNullOrEmpty(it.Url)) .AsNoTracking() .Select(it => new { it.ImageId, it.Language, it.Url }) .ToList(); - // 设置默认语言 if (string.IsNullOrEmpty(appConfig.DefaultLanguage)) { appConfig.DefaultLanguage = "zh"; } - // 创建默认语言的图像字典 - var defaultImage = imageList + var defaultImage = images .Where(it => it.Language == appConfig.DefaultLanguage) .GroupBy(it => it.ImageId) .ToDictionary(group => group.Key, group => appConfig.AliyunConfig.ImagePrefix + group.Last().Url); - - // 遍历图像列表,填充_data字典 - foreach (var item in imageList) + if (defaultImage == null) { - // 尝试获取语言字典,如果不存在则创建 - if (!_data.TryGetValue(item.Language, out var languageImage)) + defaultImage = new Dictionary(); + } + foreach (var image in images) + { + if (string.IsNullOrEmpty(image.Url)) { - languageImage = new ConcurrentDictionary(); - _data[item.Language] = languageImage; + continue; } - - // 设置图像URL - languageImage[item.ImageId] = appConfig.AliyunConfig.ImagePrefix + item.Url; - + var languageCache = data.GetOrAdd(image.Language, _ => new ConcurrentDictionary()); + var url = string.Empty; + if (image.Url.StartsWith("http://") || image.Url.StartsWith("https://")) + { + url = $"{image.Url}"; + } + else + { + url = $"{appConfig.AliyunConfig.ImagePrefix}{image.Url}"; + } + languageCache[image.ImageId] = url; // 如果默认图像字典中没有此ImageId,加入默认图像字典 - if (!defaultImage.ContainsKey(item.ImageId)) + if (!defaultImage.ContainsKey(image.ImageId)) { - defaultImage[item.ImageId] = appConfig.AliyunConfig.ImagePrefix + item.Url; + defaultImage[image.ImageId] = url; } } - - // 更新默认语言的图像字典 - _data["default"] = new ConcurrentDictionary(defaultImage); - foreach (var item in _data.Keys) + #region 保存默认图片 + var defaultLanguageCache = new ConcurrentDictionary(defaultImage); + foreach (var kv in defaultLanguageCache) { - foreach (var image in _data[item]) + database.StringSet($"{redisKey}:default:{kv.Key}", kv.Value); + } + //data.TryAdd("default", defaultLanguageCache); + #endregion + #region 保存其他图片 + foreach (var _language in languages) + { + var mKey = $"{appConfig.Identifier}:App:Image:{_language}"; + if (!data.TryGetValue(_language, out var d)) { - string _redisKey = $"{redisKey}:{item}:{image.Key}"; - database.StringSet(_redisKey, image.Value, TimeSpan.FromDays(1)); + d = defaultLanguageCache; } + foreach (var kv in d) + { + database.StringSet($"{redisKey}:{_language}:{kv.Key}", kv.Value); + } + MemoryCacheHelper.SetCache(mKey, d, 60 * 60 * 24); + database.StringSet($"{redisKey}:language:{_language}", d); } - database.StringSet(redisKey, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromDays(1)); - return _data; + database.StringSet($"{redisKey}:language", languages); + + #endregion + return; } - /// - /// 读取图片 - /// - /// - /// - public string this[int imageId] - { - get - { - var imageUrl = this[appRequestConfig.Language, imageId]; - return imageUrl; - } - } - /// - /// 清除缓存 - /// - /// - /// public bool ClearData() { - throw new NotImplementedException(); + + foreach (var _language in languages) + { + var mKey = $"{appConfig.Identifier}:App:Image:{_language}"; + MemoryCacheHelper.cache.Remove(mKey); + } + + + return true; } - /// - /// 重新加载缓存 - /// - /// public void ReloadData() { - throw new NotImplementedException(); + LoadImagesFromDatabase(); + } + + public bool ClearLocalData() + { + foreach (var _language in languages) + { + var mKey = $"{appConfig.Identifier}:App:Image:{_language}"; + MemoryCacheHelper.cache.Remove(mKey); + } + imageData = null; + return true; } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/Config/AppConfigBLL.cs b/src/CloudGaming/Code/CloudGaming.Code/Config/AppConfigBLL.cs index 47a6f58..bf135d7 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Config/AppConfigBLL.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Config/AppConfigBLL.cs @@ -1,3 +1,4 @@ +using CloudGaming.Code.Cache; using CloudGaming.DtoModel; using System; @@ -48,6 +49,24 @@ namespace CloudGaming.Code.Config return list.GetAppIsChecking(AppRequestInfo); } + /// + /// 清除缓存 + /// + /// + public bool ClearCacheData() + { + this.ClearLocalDataCache(); + return true; + } + /// + /// 加载redis缓存 + /// + /// + public bool ReloadLocalData() + { + this.ReloadLocalDataCache(); + return true; + } } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/Filter/CustomResultFilter.cs b/src/CloudGaming/Code/CloudGaming.Code/Filter/CustomResultFilter.cs index d115e2a..aa61198 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Filter/CustomResultFilter.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Filter/CustomResultFilter.cs @@ -88,7 +88,7 @@ public class CustomResultFilter : IResultFilter } } } - var dic = value.ToDictionaryOrList(apiPrefix, it => cloudGamingBase.Cache.ImageEntityCache[it]); + var dic = value.ToDictionaryOrList(apiPrefix, it => cloudGamingBase.Cache.AppImageCache[it]); objectResult.Value = dic; } //else diff --git a/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameExtend.cs index b8c954e..9b943ac 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameExtend.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameExtend.cs @@ -21,7 +21,7 @@ namespace CloudGaming.Code.Game { /// - /// 初始化app + /// 初始化游戏服务 /// /// /// diff --git a/src/CloudGaming/Model/CloudGaming.AppConfigModel/AppConfig.cs b/src/CloudGaming/Model/CloudGaming.AppConfigModel/AppConfig.cs index 55cd334..98bed38 100644 --- a/src/CloudGaming/Model/CloudGaming.AppConfigModel/AppConfig.cs +++ b/src/CloudGaming/Model/CloudGaming.AppConfigModel/AppConfig.cs @@ -85,6 +85,10 @@ namespace CloudGaming.AppConfigModel /// public string PrivacyAgreement { get; set; } + /// + /// 多语言列表请求地址 + /// + public string LanguageRequestUrl { get; set; } /// /// 获取数据库连接字符串 /// diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/CommonDataEntityCache.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/CommonDataEntityCache.cs index 00353bd..4a87e45 100644 --- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/CommonDataEntityCache.cs +++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/CommonDataEntityCache.cs @@ -105,7 +105,7 @@ public abstract class CommonDataEntityCache : ICacheClearData, ICacheReloadDa /// /// 缓存扩展- /// -public abstract class RedisDataEntityCache : CommonDataEntityCache where T : class +public abstract class RedisDataEntityCache : CommonDataEntityCache, ICacheClearLocalData where T : class { public IDatabase database; /// @@ -160,7 +160,7 @@ public abstract class RedisDataEntityCache : CommonDataEntityCache where T database.StringSet(key, tempDataList, TimeSpan.FromSeconds(cacheTime)); } } - _dataList = JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(tempDataList)); + //_dataList = JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(tempDataList)); database.KeyDeleteAsync($"lock:{key}").Wait(); } else @@ -214,4 +214,10 @@ public abstract class RedisDataEntityCache : CommonDataEntityCache where T database.KeyDeleteAsync($"lock:{key}").Wait(); } } + + public bool ClearLocalData() + { + _dataList = null; + return true; + } } diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/Contract/ICacheClearData.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/Contract/ICacheClearData.cs index 9b90538..f7604e7 100644 --- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/Contract/ICacheClearData.cs +++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/Contract/ICacheClearData.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace HuanMeng.DotNetCore.CacheHelper.Contract { /// - /// 清除缓存 + /// 清除缓存 /// public interface ICacheClearData { diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/Contract/ICacheClearLocalData.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/Contract/ICacheClearLocalData.cs new file mode 100644 index 0000000..dade31f --- /dev/null +++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/CacheHelper/Contract/ICacheClearLocalData.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HuanMeng.DotNetCore.CacheHelper.Contract; + +/// +/// 清除缓存 +/// +public interface ICacheClearLocalData +{ + /// + /// 清除数据 + /// + /// + bool ClearLocalData(); +}