提交代码
This commit is contained in:
parent
d762ed726d
commit
56e3b1d901
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.AppExtend
|
||||||
|
{
|
||||||
|
public class AliyunOssConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 配置环境变量
|
||||||
|
/// </summary>
|
||||||
|
public string AccessKeySecret { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 替换为Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
|
||||||
|
/// </summary>
|
||||||
|
public string EndPoint { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Bucket名称。
|
||||||
|
/// </summary>
|
||||||
|
public string BucketName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上传路径
|
||||||
|
/// </summary>
|
||||||
|
public string UploadPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 域名
|
||||||
|
/// </summary>
|
||||||
|
public string? DomainName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 前缀
|
||||||
|
/// </summary>
|
||||||
|
public string ImagePrefix
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.DomainName + this.UploadPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -48,6 +48,10 @@ namespace CloudGaming.Code.AppExtend
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认语言
|
||||||
|
/// </summary>
|
||||||
|
public string DefaultLanguage { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 租户
|
/// 租户
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -57,6 +61,10 @@ namespace CloudGaming.Code.AppExtend
|
||||||
/// </summary>
|
/// </summary>
|
||||||
//public PaymentModel? Payment { get; set; }
|
//public PaymentModel? Payment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// oss阿里云配置
|
||||||
|
/// </summary>
|
||||||
|
public AliyunOssConfig AliyunConfig { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,8 @@ namespace CloudGaming.Code.AppExtend
|
||||||
newAppConfig.GameConnectionString = appConfig.GameConnectionString;
|
newAppConfig.GameConnectionString = appConfig.GameConnectionString;
|
||||||
newAppConfig.ExtConnectionString = appConfig.ExtConnectionString;
|
newAppConfig.ExtConnectionString = appConfig.ExtConnectionString;
|
||||||
newAppConfig.PhoneConnectionString = appConfig.PhoneConnectionString;
|
newAppConfig.PhoneConnectionString = appConfig.PhoneConnectionString;
|
||||||
|
newAppConfig.AliyunConfig= appConfig.AliyunConfig;
|
||||||
|
newAppConfig.DefaultLanguage = appConfig.DefaultLanguage;
|
||||||
return newAppConfig;
|
return newAppConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,9 +100,9 @@ namespace CloudGaming.Code.AppExtend
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(language))
|
if (string.IsNullOrEmpty(language))
|
||||||
{
|
{
|
||||||
if (!httpRequest.Headers.TryGetValue("Version", out var _language))
|
if (!httpRequest.Headers.TryGetValue("Language", out var _language))
|
||||||
{
|
{
|
||||||
_language = "1.0.0";
|
_language = "zh";
|
||||||
}
|
}
|
||||||
language = _language;
|
language = _language;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
using CloudGaming.Code.DataAccess;
|
||||||
|
using CloudGaming.GameModel.Db.Db_Ext;
|
||||||
|
|
||||||
using HuanMeng.DotNetCore.AttributeExtend;
|
using HuanMeng.DotNetCore.AttributeExtend;
|
||||||
using HuanMeng.DotNetCore.Base;
|
using HuanMeng.DotNetCore.Base;
|
||||||
using HuanMeng.DotNetCore.Utility;
|
using HuanMeng.DotNetCore.Utility;
|
||||||
|
|
@ -5,12 +8,14 @@ using HuanMeng.DotNetCore.Utility;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Identity.Client;
|
||||||
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
|
|
@ -22,12 +27,13 @@ namespace CloudGaming.Code.AppExtend
|
||||||
public class CustomResultFilter : IResultFilter
|
public class CustomResultFilter : IResultFilter
|
||||||
{
|
{
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly AppConfig _appConfig;
|
private readonly AppConfig _appConfig;
|
||||||
public CustomResultFilter(IHttpContextAccessor httpContextAccessor, AppConfig appConfig)
|
public CustomResultFilter(IHttpContextAccessor httpContextAccessor, AppConfig appConfig, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_appConfig = appConfig;
|
_appConfig = appConfig;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnResultExecuting(ResultExecutingContext context)
|
public void OnResultExecuting(ResultExecutingContext context)
|
||||||
|
|
@ -35,6 +41,10 @@ namespace CloudGaming.Code.AppExtend
|
||||||
// 获取当前的 HttpContext
|
// 获取当前的 HttpContext
|
||||||
var httpContext = context.HttpContext;
|
var httpContext = context.HttpContext;
|
||||||
var path = httpContext.Request.Path.Value ?? "";
|
var path = httpContext.Request.Path.Value ?? "";
|
||||||
|
var apiPrefix = path.Replace('/', '.').TrimStart('.');
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
//_appConfig.
|
||||||
|
CloudGamingBase cloudGamingBase = new CloudGamingBase(_serviceProvider);
|
||||||
// 获取当前用户的信息
|
// 获取当前用户的信息
|
||||||
var user = httpContext.User.Identity.IsAuthenticated ? httpContext.User.Identity.Name : "Anonymous";
|
var user = httpContext.User.Identity.IsAuthenticated ? httpContext.User.Identity.Name : "Anonymous";
|
||||||
//Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
|
//Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
|
||||||
|
|
@ -52,198 +62,17 @@ namespace CloudGaming.Code.AppExtend
|
||||||
value = objectResult.Value;
|
value = objectResult.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dic = value.ToDictionaryOrList();
|
var dic = value.ToDictionaryOrList(apiPrefix
|
||||||
|
, it => cloudGamingBase.Cache.ImageEntityCache[it]
|
||||||
|
);
|
||||||
objectResult.Value = dic;
|
objectResult.Value = dic;
|
||||||
|
sw.Stop();
|
||||||
|
context.HttpContext.Response.Headers.TryAdd("X-Request-Duration-Filter", sw.Elapsed.TotalMilliseconds.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindImagesAttributes(object obj)
|
|
||||||
{
|
|
||||||
if (obj == null) return;
|
|
||||||
|
|
||||||
// 使用反射获取对象的所有公共实例属性和字段
|
|
||||||
var membersWithImagesAttribute = obj.GetType()
|
|
||||||
.GetMembers(BindingFlags.Public | BindingFlags.Instance)
|
|
||||||
.Where(m => m is PropertyInfo || m is FieldInfo);
|
|
||||||
|
|
||||||
foreach (var member in membersWithImagesAttribute)
|
|
||||||
{
|
|
||||||
// 获取并输出带有 [Images] 特性的属性或字段
|
|
||||||
var imagesAttribute = member.GetCustomAttribute<ImagesAttribute>();
|
|
||||||
if (imagesAttribute != null)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"带有 [Images] 特性的成员:{member.Name}");
|
|
||||||
Console.WriteLine($"FieldName 属性值:{imagesAttribute.FieldName}");
|
|
||||||
|
|
||||||
object value = GetMemberValue(member, obj);
|
|
||||||
Console.WriteLine($"成员值:{value}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取成员值
|
|
||||||
var memberValue = GetMemberValue(member, obj);
|
|
||||||
|
|
||||||
// 如果成员是 IEnumerable(集合类型),递归处理每个元素
|
|
||||||
if (memberValue is IEnumerable enumerableValue && memberValue.GetType() != typeof(string))
|
|
||||||
{
|
|
||||||
foreach (var item in enumerableValue)
|
|
||||||
{
|
|
||||||
if (item != null && !IsSimpleType(item.GetType()))
|
|
||||||
{
|
|
||||||
FindImagesAttributes(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 如果成员是非集合的复杂类型,递归检查其内部成员
|
|
||||||
else if (memberValue != null && !IsSimpleType(memberValue.GetType()))
|
|
||||||
{
|
|
||||||
FindImagesAttributes(memberValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetMemberValue(MemberInfo member, object obj)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return member switch
|
|
||||||
{
|
|
||||||
PropertyInfo property when property.GetMethod?.IsStatic == false => property.GetValue(obj),
|
|
||||||
FieldInfo field when !field.IsStatic => field.GetValue(obj),
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (TargetParameterCountException ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error retrieving value for {member.Name}: {ex.Message}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsSimpleType(Type type)
|
|
||||||
{
|
|
||||||
return type.IsPrimitive || type.IsEnum || type == typeof(string) || type == typeof(decimal);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void OnResultExecuted(ResultExecutedContext context)
|
public void OnResultExecuted(ResultExecutedContext context)
|
||||||
{
|
{
|
||||||
// 可在执行完结果后处理其他逻辑
|
// 可在执行完结果后处理其他逻辑
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归处理对象的所有属性并打印路径
|
|
||||||
private void ProcessObjectProperties(object obj, string user, string language, string path)
|
|
||||||
{
|
|
||||||
if (obj == null || IsAnonymousType(obj.GetType()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var objType = obj.GetType();
|
|
||||||
|
|
||||||
// 如果对象是集合(数组或列表),递归处理每个元素
|
|
||||||
if (obj is IEnumerable enumerable && !(obj is string))
|
|
||||||
{
|
|
||||||
int index = 0;
|
|
||||||
foreach (var item in enumerable)
|
|
||||||
{
|
|
||||||
ProcessObjectProperties(item, user, language, $"{path}[{index}]");
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 遍历对象的所有属性
|
|
||||||
foreach (var property in objType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
|
||||||
{
|
|
||||||
// 递归构建属性路径,
|
|
||||||
var propertyPath = string.IsNullOrEmpty(path) ? property.Name : $"{path}.{property.Name}";
|
|
||||||
|
|
||||||
// 打印当前属性路径
|
|
||||||
Console.WriteLine($"Processing path: {propertyPath}");
|
|
||||||
|
|
||||||
// 判断是否是只读属性(可读不可写)
|
|
||||||
if (property.CanRead && !property.CanWrite)
|
|
||||||
{
|
|
||||||
// 为只读属性创建可写副本(仅复杂类型处理)
|
|
||||||
var originalValue = property.GetValue(obj);
|
|
||||||
if (originalValue != null && !IsPrimitiveOrString(property.PropertyType))
|
|
||||||
{
|
|
||||||
var newValue = CloneAndModifyObject(originalValue, user, language);
|
|
||||||
// 替换原始对象的只读属性
|
|
||||||
ReplaceReadOnlyProperty(obj, property, newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (property.CanRead && property.CanWrite)
|
|
||||||
{
|
|
||||||
var propertyValue = property.GetValue(obj);
|
|
||||||
|
|
||||||
// 针对字符串属性进行自定义处理
|
|
||||||
if (property.PropertyType == typeof(string))
|
|
||||||
{
|
|
||||||
if (propertyValue is string strValue)
|
|
||||||
{
|
|
||||||
property.SetValue(obj, $"{strValue} (modified by {user} with lang {language})");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
|
|
||||||
{
|
|
||||||
// 递归处理复杂类型,传递完整的路径
|
|
||||||
ProcessObjectProperties(propertyValue, user, language, propertyPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 克隆只读属性的对象,并进行必要的修改
|
|
||||||
private object CloneAndModifyObject(object source, string user, string language)
|
|
||||||
{
|
|
||||||
var sourceType = source.GetType();
|
|
||||||
|
|
||||||
// 如果是简单类型或者 string,不克隆
|
|
||||||
if (IsPrimitiveOrString(sourceType) || IsAnonymousType(sourceType))
|
|
||||||
return source;
|
|
||||||
|
|
||||||
var newInstance = Activator.CreateInstance(sourceType); // 创建新对象实例
|
|
||||||
|
|
||||||
foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
|
||||||
{
|
|
||||||
if (property.CanRead && property.CanWrite)
|
|
||||||
{
|
|
||||||
var value = property.GetValue(source);
|
|
||||||
// 针对字符串属性进行修改
|
|
||||||
if (property.PropertyType == typeof(string) && value is string strValue)
|
|
||||||
{
|
|
||||||
property.SetValue(newInstance, $"{strValue} (modified by {user} with lang {language})");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
property.SetValue(newInstance, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 替换只读属性的值
|
|
||||||
private void ReplaceReadOnlyProperty(object obj, PropertyInfo property, object newValue)
|
|
||||||
{
|
|
||||||
var backingField = obj.GetType().GetField($"<{property.Name}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
if (backingField != null)
|
|
||||||
{
|
|
||||||
backingField.SetValue(obj, newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断类型是否是基础类型或 string
|
|
||||||
private bool IsPrimitiveOrString(Type type)
|
|
||||||
{
|
|
||||||
return type.IsPrimitive || type == typeof(string) || type == typeof(decimal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断是否为匿名类型
|
|
||||||
private bool IsAnonymousType(Type type)
|
|
||||||
{
|
|
||||||
return type.Name.StartsWith("<>f__AnonymousType");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,18 +99,34 @@ namespace CloudGaming.Code.Cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region 图片缓存
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
private ImageEntityCache imageEntityCache;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图片缓存
|
||||||
|
/// </summary>
|
||||||
|
public ImageEntityCache ImageEntityCache
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (imageEntityCache == null)
|
||||||
|
{
|
||||||
|
imageEntityCache = new ImageEntityCache(_gamingBase.Dao, _gamingBase.AppConfig, _gamingBase.RedisCache, _gamingBase.AppRequestInfo);
|
||||||
|
}
|
||||||
|
return imageEntityCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region 首页缓存表
|
#region 首页缓存表
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//private CommonDataEntityCache<T_Game_List>? _gameList;
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// 菜单分类
|
|
||||||
///// </summary>
|
|
||||||
//public List<T_Game_List> GameList => GetCacheList(ref _gameList);
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
using AutoMapper;
|
||||||
|
|
||||||
|
using CloudGaming.Code.AppExtend;
|
||||||
|
using CloudGaming.Code.DataAccess;
|
||||||
|
using CloudGaming.DtoModel.Game;
|
||||||
|
|
||||||
|
using HuanMeng.DotNetCore.CacheHelper;
|
||||||
|
using HuanMeng.DotNetCore.CacheHelper.Contract;
|
||||||
|
using HuanMeng.DotNetCore.Redis;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
using StackExchange.Redis;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using static System.Net.Mime.MediaTypeNames;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.Cache.Special
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 图片缓存表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dao"></param>
|
||||||
|
/// <param name="appConfig"></param>
|
||||||
|
/// <param name="database"></param>
|
||||||
|
/// <param name="mapper"></param>
|
||||||
|
/// <param name="appRequestConfig"></param>
|
||||||
|
public class ImageEntityCache(DAO dao, AppConfig appConfig, IDatabase database, AppRequestConfig appRequestConfig) : ICacheClearData, ICacheReloadData
|
||||||
|
{
|
||||||
|
public static bool IsLoadImage { get; set; } = false;
|
||||||
|
private string key = $"{appConfig.Identifier}:App:Image";
|
||||||
|
private string redisKey = $"App:Image";
|
||||||
|
ConcurrentDictionary<string, ConcurrentDictionary<int, string>>? ImageData { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="language"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public ConcurrentDictionary<int, string> this[string language]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(language))
|
||||||
|
{
|
||||||
|
language = appRequestConfig.Language;
|
||||||
|
}
|
||||||
|
if (ImageData != null && ImageData.TryGetValue(language, out var images))
|
||||||
|
{
|
||||||
|
return images;
|
||||||
|
}
|
||||||
|
ImageData = MemoryCacheHelper.GetCache<ConcurrentDictionary<string, ConcurrentDictionary<int, string>>>(key);
|
||||||
|
if (ImageData == null)
|
||||||
|
{
|
||||||
|
ImageData = new ConcurrentDictionary<string, ConcurrentDictionary<int, string>>();
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!ImageData.TryGetValue(language, out images))
|
||||||
|
{
|
||||||
|
ImageData.TryAdd(language, images = new ConcurrentDictionary<int, string>());
|
||||||
|
MemoryCacheHelper.SetCache(key, ImageData, 60 * 60 * 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
return images;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="language"></param>
|
||||||
|
/// <param name="imageId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string this[string language, int imageId]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (imageId == 0)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
var _imageData = this[language];
|
||||||
|
if (_imageData == null)
|
||||||
|
{
|
||||||
|
_imageData = new ConcurrentDictionary<int, string>();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
//if (!_imageData.TryGetValue(imageId, out imageUrl))
|
||||||
|
//{
|
||||||
|
// if (string.IsNullOrEmpty(imageUrl))
|
||||||
|
// {
|
||||||
|
// imageUrl = "";
|
||||||
|
// }
|
||||||
|
|
||||||
|
//}//imageValue.ToString();
|
||||||
|
|
||||||
|
//MemoryCacheHelper.SetCache(key, ImageData, 60 * 60 * 24);
|
||||||
|
|
||||||
|
}
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentDictionary<string, ConcurrentDictionary<int, string>> LoadImage(DAO dao, AppConfig appConfig)
|
||||||
|
{
|
||||||
|
// 初始化_data字典
|
||||||
|
ConcurrentDictionary<string, ConcurrentDictionary<int, string>> _data = new ConcurrentDictionary<string, ConcurrentDictionary<int, string>>();
|
||||||
|
|
||||||
|
// 获取图像列表
|
||||||
|
var imageList = 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
|
||||||
|
.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 (!_data.TryGetValue(item.Language, out var languageImage))
|
||||||
|
{
|
||||||
|
languageImage = new ConcurrentDictionary<int, string>();
|
||||||
|
_data[item.Language] = languageImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置图像URL
|
||||||
|
languageImage[item.ImageId] = appConfig.AliyunConfig.ImagePrefix + item.Url;
|
||||||
|
|
||||||
|
// 如果默认图像字典中没有此ImageId,加入默认图像字典
|
||||||
|
if (!defaultImage.ContainsKey(item.ImageId))
|
||||||
|
{
|
||||||
|
defaultImage[item.ImageId] = appConfig.AliyunConfig.ImagePrefix + item.Url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新默认语言的图像字典
|
||||||
|
_data["default"] = new ConcurrentDictionary<int, string>(defaultImage);
|
||||||
|
foreach (var item in _data.Keys)
|
||||||
|
{
|
||||||
|
foreach (var image in _data[item])
|
||||||
|
{
|
||||||
|
string _redisKey = $"{redisKey}:{item}:{image.Key}";
|
||||||
|
database.StringSet(_redisKey, image.Value, TimeSpan.FromDays(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
database.StringSet(redisKey, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromDays(1));
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string this[int imageId]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var imageUrl = this[appRequestConfig.Language, imageId];
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ClearData()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadData()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,4 +17,8 @@
|
||||||
<ProjectReference Include="..\CloudGaming.Model\CloudGaming.Model.csproj" />
|
<ProjectReference Include="..\CloudGaming.Model\CloudGaming.Model.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Image\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -46,15 +46,15 @@ public static class ObjectExtensions
|
||||||
/// <param name="obj">要转换的对象。</param>
|
/// <param name="obj">要转换的对象。</param>
|
||||||
/// <param name="prefix">属性路径的可选前缀。</param>
|
/// <param name="prefix">属性路径的可选前缀。</param>
|
||||||
/// <returns>对象的字典或列表表示形式。</returns>
|
/// <returns>对象的字典或列表表示形式。</returns>
|
||||||
public static object ToDictionaryOrList(this object obj, string prefix = "")
|
public static object ToDictionaryOrList(this object obj, string prefix = "", Func<int, string>? imageFunc = null)
|
||||||
{
|
{
|
||||||
if (obj == null) return null;
|
if (obj == null) return null;
|
||||||
|
|
||||||
return obj switch
|
return obj switch
|
||||||
{
|
{
|
||||||
_ when IsPrimitiveType(obj) => obj,
|
_ when IsPrimitiveType(obj) => obj,
|
||||||
IEnumerable enumerable => TransformCollection(enumerable, prefix),
|
IEnumerable enumerable => TransformCollection(enumerable, prefix, imageFunc),
|
||||||
_ => TransformObject(obj, prefix)
|
_ => TransformObject(obj, prefix, imageFunc)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,13 +64,13 @@ public static class ObjectExtensions
|
||||||
/// <param name="enumerable">要转换的集合。</param>
|
/// <param name="enumerable">要转换的集合。</param>
|
||||||
/// <param name="prefix">集合中每个属性路径的前缀。</param>
|
/// <param name="prefix">集合中每个属性路径的前缀。</param>
|
||||||
/// <returns>转换后的项列表。</returns>
|
/// <returns>转换后的项列表。</returns>
|
||||||
private static List<object> TransformCollection(IEnumerable enumerable, string prefix = "")
|
private static List<object> TransformCollection(IEnumerable enumerable, string prefix = "", Func<int, string>? imageFunc = null)
|
||||||
{
|
{
|
||||||
var list = new List<object>(enumerable is ICollection collection ? collection.Count : 10);
|
var list = new List<object>(enumerable is ICollection collection ? collection.Count : 10);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (var item in enumerable)
|
foreach (var item in enumerable)
|
||||||
{
|
{
|
||||||
list.Add(ToDictionaryOrList(item, $"{prefix}.[{index}]")); // 为集合中每个项添加路径
|
list.Add(ToDictionaryOrList(item, $"{prefix}.[{index}]", imageFunc)); // 为集合中每个项添加路径
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
|
|
@ -82,7 +82,7 @@ public static class ObjectExtensions
|
||||||
/// <param name="obj">要转换的对象。</param>
|
/// <param name="obj">要转换的对象。</param>
|
||||||
/// <param name="prefix">每个属性路径的前缀。</param>
|
/// <param name="prefix">每个属性路径的前缀。</param>
|
||||||
/// <returns>包含属性名和属性值的字典。</returns>
|
/// <returns>包含属性名和属性值的字典。</returns>
|
||||||
private static Dictionary<string, object> TransformObject(object obj, string prefix = "")
|
private static Dictionary<string, object> TransformObject(object obj, string prefix = "", Func<int, string>? imageFunc = null)
|
||||||
{
|
{
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
{
|
{
|
||||||
|
|
@ -110,8 +110,8 @@ public static class ObjectExtensions
|
||||||
// 如果属性具有 ImagesAttribute,在其值前添加 "image"
|
// 如果属性具有 ImagesAttribute,在其值前添加 "image"
|
||||||
// 否则,如果是集合类型,则递归转换
|
// 否则,如果是集合类型,则递归转换
|
||||||
keyValuePairs[accessor.PropertyName] = accessor.HasImagesAttribute
|
keyValuePairs[accessor.PropertyName] = accessor.HasImagesAttribute
|
||||||
? $"image{propertyValue}"
|
? imageFunc?.Invoke((int)propertyValue) ?? ""
|
||||||
: IsCollectionType(propertyValue) ? ToDictionaryOrList(propertyValue, propertyPath) : propertyValue;
|
: IsCollectionType(propertyValue) ? ToDictionaryOrList(propertyValue, propertyPath, imageFunc) : propertyValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyValuePairs;
|
return keyValuePairs;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user