Merge branch 'main' of http://123.207.203.228:3000/server/CloudGaming
This commit is contained in:
commit
631e7ac35f
|
|
@ -8,6 +8,7 @@
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
<DockerfileContext>..\..</DockerfileContext>
|
<DockerfileContext>..\..</DockerfileContext>
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
|
<IsPackable>true</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
using Bogus.DataSets;
|
||||||
|
|
||||||
|
using CloudGaming.Api.Base;
|
||||||
|
using CloudGaming.Code.Account;
|
||||||
|
using CloudGaming.Code.AppExtend;
|
||||||
|
using CloudGaming.Code.DataAccess;
|
||||||
|
using CloudGaming.Code.MiddlewareExtend;
|
||||||
|
using CloudGaming.DtoModel.Account;
|
||||||
|
using CloudGaming.DtoModel.Account.Login;
|
||||||
|
using CloudGaming.GameModel.Db.Db_Ext;
|
||||||
|
|
||||||
|
using HuanMeng.DotNetCore.AttributeExtend;
|
||||||
|
using HuanMeng.DotNetCore.Base;
|
||||||
|
|
||||||
|
using HuanMeng.DotNetCore.Utility;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace CloudGaming.Api.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 账号管理
|
||||||
|
/// </summary>
|
||||||
|
public class AccountController : CloudGamingControllerBase
|
||||||
|
{
|
||||||
|
public AccountController(IServiceProvider _serviceProvider) : base(_serviceProvider)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送验证码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="phoneNumber"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost]
|
||||||
|
[Message("发送成功")]
|
||||||
|
public async Task<bool> SendPhoneNumber([FromBody] PhoneNumberRequest phoneNumber)
|
||||||
|
{
|
||||||
|
AccountBLL account = new AccountBLL(ServiceProvider);
|
||||||
|
return await account.SendPhoneNumber(phoneNumber.PhoneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登录接口
|
||||||
|
/// 登录数据格式(设备号的目的是用于以后的多设备登录)
|
||||||
|
/// 短信登录:{"phoneNumber":"手机号","verificationCode":"验证码","deviceNumber":"设备号"}
|
||||||
|
/// token登录(请求标头需要带上Authorized的token) {"deviceNumber":"设备号"}
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<AccountLogInResponse> LoginAsync([FromBody] BaseLoginParams baseLogin)
|
||||||
|
{
|
||||||
|
AccountBLL account = new AccountBLL(ServiceProvider);
|
||||||
|
return await account.LoginAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<bool> GetUserInfo()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ using CloudGaming.Code.Config;
|
||||||
using CloudGaming.DtoModel;
|
using CloudGaming.DtoModel;
|
||||||
using CloudGaming.GameModel.Db.Db_Ext;
|
using CloudGaming.GameModel.Db.Db_Ext;
|
||||||
|
|
||||||
|
using HuanMeng.DotNetCore.AttributeExtend;
|
||||||
using HuanMeng.DotNetCore.Base;
|
using HuanMeng.DotNetCore.Base;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
@ -24,6 +25,7 @@ namespace CloudGaming.Api.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
[Message("发送成功")]
|
||||||
public async Task<AppConfigDto> GetAppConfigAsync()
|
public async Task<AppConfigDto> GetAppConfigAsync()
|
||||||
{
|
{
|
||||||
AppConfigBLL appConfigBLL = new AppConfigBLL(ServiceProvider);
|
AppConfigBLL appConfigBLL = new AppConfigBLL(ServiceProvider);
|
||||||
|
|
|
||||||
|
|
@ -74,5 +74,18 @@ namespace CloudGaming.Api.Controllers
|
||||||
GameBLL gamebll = new GameBLL(this.ServiceProvider);
|
GameBLL gamebll = new GameBLL(this.ServiceProvider);
|
||||||
return gamebll.GameRecommendations(gameId);
|
return gamebll.GameRecommendations(gameId);
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏搜索
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gameId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet]
|
||||||
|
public List<GameListDto> GameSearch([FromQuery] string? gameId)
|
||||||
|
{
|
||||||
|
|
||||||
|
GameBLL gamebll = new GameBLL(this.ServiceProvider);
|
||||||
|
return gamebll.GameSearch(gameId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace CloudGaming.Api.Controllers;
|
namespace CloudGaming.Api.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 首页
|
||||||
|
/// </summary>
|
||||||
public class HomeController : CloudGamingControllerBase
|
public class HomeController : CloudGamingControllerBase
|
||||||
{
|
{
|
||||||
public HomeController(IServiceProvider _serviceProvider) : base(_serviceProvider)
|
public HomeController(IServiceProvider _serviceProvider) : base(_serviceProvider)
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||||
USER app
|
USER app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 8080
|
EXPOSE 80
|
||||||
EXPOSE 8081
|
|
||||||
|
|
||||||
|
|
||||||
# 此阶段用于生成服务项目
|
# 此阶段用于生成服务项目
|
||||||
|
|
@ -14,8 +13,10 @@ ARG BUILD_CONFIGURATION=Release
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY ["Api/CloudGaming.Api/CloudGaming.Api.csproj", "Api/CloudGaming.Api/"]
|
COPY ["Api/CloudGaming.Api/CloudGaming.Api.csproj", "Api/CloudGaming.Api/"]
|
||||||
COPY ["Code/CloudGaming.Code/CloudGaming.Code.csproj", "Code/CloudGaming.Code/"]
|
COPY ["Code/CloudGaming.Code/CloudGaming.Code.csproj", "Code/CloudGaming.Code/"]
|
||||||
COPY ["Model/CloudGaming.Model/CloudGaming.Model.csproj", "Model/CloudGaming.Model/"]
|
COPY ["Model/CloudGaming.DtoModel/CloudGaming.DtoModel.csproj", "Model/CloudGaming.DtoModel/"]
|
||||||
|
COPY ["Model/CloudGaming.GameModel/CloudGaming.GameModel.csproj", "Model/CloudGaming.GameModel/"]
|
||||||
COPY ["Utile/HuanMeng.DotNetCore/HuanMeng.DotNetCore.csproj", "Utile/HuanMeng.DotNetCore/"]
|
COPY ["Utile/HuanMeng.DotNetCore/HuanMeng.DotNetCore.csproj", "Utile/HuanMeng.DotNetCore/"]
|
||||||
|
COPY ["Model/CloudGaming.Model/CloudGaming.Model.csproj", "Model/CloudGaming.Model/"]
|
||||||
RUN dotnet restore "./Api/CloudGaming.Api/CloudGaming.Api.csproj"
|
RUN dotnet restore "./Api/CloudGaming.Api/CloudGaming.Api.csproj"
|
||||||
COPY . .
|
COPY . .
|
||||||
WORKDIR "/src/Api/CloudGaming.Api"
|
WORKDIR "/src/Api/CloudGaming.Api"
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using CloudGaming.GameModel.Db.Db_Ext;
|
using CloudGaming.GameModel.Db.Db_Ext;
|
||||||
using CloudGaming.Code.MiddlewareExtend;
|
using CloudGaming.Code.MiddlewareExtend;
|
||||||
|
using CloudGaming.Code.Filter;
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
#region 日志
|
#region 日志
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
|
@ -51,6 +52,7 @@ builder.Services.AddControllers(options =>
|
||||||
{
|
{
|
||||||
// 添加自定义的 ResultFilter 到全局过滤器中
|
// 添加自定义的 ResultFilter 到全局过滤器中
|
||||||
options.Filters.Add<CustomResultFilter>();
|
options.Filters.Add<CustomResultFilter>();
|
||||||
|
options.Filters.Add<CustomExceptionFilter>();
|
||||||
})
|
})
|
||||||
.AddNewtonsoftJson(options =>
|
.AddNewtonsoftJson(options =>
|
||||||
{
|
{
|
||||||
|
|
@ -68,7 +70,9 @@ builder.Services.AddControllers(options =>
|
||||||
//options.SerializerSettings.Converters.Add()
|
//options.SerializerSettings.Converters.Add()
|
||||||
// 其他配置...
|
// 其他配置...
|
||||||
});
|
});
|
||||||
builder.Services.AddSingleton<ObjectResultExecutor, CustomObjectResultExecutor>();
|
//CustomResultFilter
|
||||||
|
//builder.Services.AddSingleton<ObjectResultExecutor, CustomObjectResultExecutor>();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,15 @@
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"AgileConfig": {
|
||||||
|
"appId": "CloudGaming",
|
||||||
|
"secret": "95BB717C61D1ECB0E9FB82C932CC77FF",
|
||||||
|
"nodes": "http://124.220.55.158:94", //多个节点使用逗号分隔
|
||||||
|
"url": "http://124.220.55.158:94",
|
||||||
|
"env": "DEV",
|
||||||
|
"UserName": "admin",
|
||||||
|
"Password": "dbt@com@1234"
|
||||||
|
},
|
||||||
//服务器配置
|
//服务器配置
|
||||||
"Kestrel": {
|
"Kestrel": {
|
||||||
"Endpoints": {
|
"Endpoints": {
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@
|
||||||
"secret": "95BB717C61D1ECB0E9FB82C932CC77FF",
|
"secret": "95BB717C61D1ECB0E9FB82C932CC77FF",
|
||||||
"nodes": "http://124.220.55.158:94", //多个节点使用逗号分隔
|
"nodes": "http://124.220.55.158:94", //多个节点使用逗号分隔
|
||||||
"url": "http://124.220.55.158:94",
|
"url": "http://124.220.55.158:94",
|
||||||
"env": "DEV",
|
"env": "TEST",
|
||||||
"UserName": "admin",
|
"UserName": "admin",
|
||||||
"Password": "dbt@com@1234"
|
"Password": "dbt@com@1234"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
294
src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs
Normal file
294
src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs
Normal file
|
|
@ -0,0 +1,294 @@
|
||||||
|
using Alipay.EasySDK.Kernel;
|
||||||
|
|
||||||
|
using CloudGaming.Code.Account.Contract;
|
||||||
|
using CloudGaming.Code.Sms;
|
||||||
|
using CloudGaming.DtoModel.Account;
|
||||||
|
using CloudGaming.DtoModel.Account.Login;
|
||||||
|
|
||||||
|
using HuanMeng.DotNetCore.JwtInfrastructure;
|
||||||
|
using HuanMeng.DotNetCore.Redis;
|
||||||
|
using HuanMeng.DotNetCore.Utility;
|
||||||
|
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.Account
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 账号操作
|
||||||
|
/// </summary>
|
||||||
|
public class AccountBLL : CloudGamingBase
|
||||||
|
{
|
||||||
|
public AccountBLL(IServiceProvider serviceProvider) : base(serviceProvider)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送手机号码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="PhoneNumber"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> SendPhoneNumber(string PhoneNumber)
|
||||||
|
{
|
||||||
|
if (!PhoneNumberValidator.IsPhoneNumber(PhoneNumber))
|
||||||
|
{
|
||||||
|
throw MessageBox.Show(ResonseCode.PhoneNumberException, "手机号格式错误");
|
||||||
|
}
|
||||||
|
var day = int.Parse(DateTime.Now.ToString("yyyyMMdd"));
|
||||||
|
var smsCount = await Dao.DaoExt.Context.T_Sms_Log.Where(it => it.SendTimeDay == day && it.PhoneNumber == PhoneNumber).CountAsync();
|
||||||
|
if (smsCount >= 5)
|
||||||
|
{
|
||||||
|
throw MessageBox.Show(ResonseCode.PhoneNumberMaxException, "当日发送以达到上限");
|
||||||
|
}
|
||||||
|
var phoneNumberCache = RedisCache.StringGetAsync($"App:sms:{PhoneNumber}");
|
||||||
|
var verificationCode = new Random().Next(1000, 9999).ToString();
|
||||||
|
var sms = AppConfig.AliyunConfig.GetPhoneNumberVerificationService();
|
||||||
|
bool isSend = false;
|
||||||
|
string exMsg = "";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
isSend = await sms.SendVerificationCodeAsync(PhoneNumber, verificationCode);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
exMsg = ex.Message;
|
||||||
|
if (exMsg.Length > 200)
|
||||||
|
{
|
||||||
|
exMsg = exMsg.Substring(0, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isSend)
|
||||||
|
{
|
||||||
|
await RedisCache.StringSetAsync($"App:sms:{PhoneNumber}", verificationCode, TimeSpan.FromMinutes(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
T_Sms_Log t_Sms_Log = new T_Sms_Log()
|
||||||
|
{
|
||||||
|
VerificationCode = verificationCode,
|
||||||
|
ErrorMessage = exMsg,
|
||||||
|
PhoneNumber = PhoneNumber,
|
||||||
|
SendStatus = isSend ? 1 : 0,
|
||||||
|
SendTime = DateTime.Now,
|
||||||
|
SendTimeDay = day
|
||||||
|
};
|
||||||
|
Dao.DaoExt.Context.T_Sms_Log.Add(t_Sms_Log);
|
||||||
|
await Dao.DaoExt.Context.SaveChangesAsync();
|
||||||
|
if (!isSend)
|
||||||
|
{
|
||||||
|
throw MessageBox.ErrorShow("发送失败");
|
||||||
|
}
|
||||||
|
return isSend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登录接口
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<AccountLogInResponse> LoginAsync()
|
||||||
|
{
|
||||||
|
this.HttpContextAccessor.HttpContext.Request.Body.Position = 0;
|
||||||
|
var json = await new StreamReader(this.HttpContextAccessor.HttpContext.Request.Body).ReadToEndAsync();
|
||||||
|
if (string.IsNullOrEmpty(json))
|
||||||
|
{
|
||||||
|
throw MessageBox.Show(ResonseCode.NullOrEmpty, "登录方式不合格");
|
||||||
|
}
|
||||||
|
|
||||||
|
var account = AccountExtend.GetUserAccount(json, this);
|
||||||
|
if (account == null)
|
||||||
|
{
|
||||||
|
throw MessageBox.Show(ResonseCode.NullOrEmpty, "未找到登录方式");
|
||||||
|
}
|
||||||
|
|
||||||
|
var userId = await account.LoginAsync();
|
||||||
|
bool isNew = false;
|
||||||
|
if (userId == 0)
|
||||||
|
{
|
||||||
|
isNew = true;
|
||||||
|
}
|
||||||
|
var ip = this.HttpContextAccessor.HttpContext.GetClientIpAddress();
|
||||||
|
//登录或者注册用户
|
||||||
|
var user = await RegisterOrUpdateUserAsync(userId, account, ip);
|
||||||
|
//注册用户详细信息
|
||||||
|
var userData = await EnsureUserDataExistsAsync(user.Id, account);
|
||||||
|
//创建jwt登录
|
||||||
|
var jwt = GenerateJwtToken(user);
|
||||||
|
var accountLogIn = new AccountLogInResponse
|
||||||
|
{
|
||||||
|
NickName = user.NickName,
|
||||||
|
Token = jwt.AccessToken,
|
||||||
|
UserId = user.Id,
|
||||||
|
};
|
||||||
|
//创建设备号
|
||||||
|
var dev = await ManageUserDevicesAsync(user, account, jwt.AccessToken);
|
||||||
|
var key = $"user:login:{user.Id}";
|
||||||
|
var accountUserLoginInfos = await RedisCache.StringGetAsync<List<AccountUserLoginInfo>>(key);
|
||||||
|
accountUserLoginInfos ??= new List<AccountUserLoginInfo>();
|
||||||
|
//创建redis缓存
|
||||||
|
await RedisCache.StringSetAsync(key, $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", TimeSpan.FromHours(1));
|
||||||
|
//accountUserLoginInfos.Add(new AccountUserLoginInfo());
|
||||||
|
T_User_Login_Log login_Log = new T_User_Login_Log()
|
||||||
|
{
|
||||||
|
Channel = this.AppRequestInfo.Channel,
|
||||||
|
CreateTime = DateTime.Now,
|
||||||
|
CreateTimeDay = 0,
|
||||||
|
CreateTimeHour = 0,
|
||||||
|
AppVersion = this.AppRequestInfo.Version,
|
||||||
|
Ip = ip,
|
||||||
|
IsNew = isNew,
|
||||||
|
LoginType = account.LastLoginType,
|
||||||
|
PlatformType = this.AppRequestInfo.Platform,
|
||||||
|
UserId = user.Id,
|
||||||
|
};
|
||||||
|
await Dao.DaoExt.Context.AddAsync(login_Log);
|
||||||
|
await Dao.DaoExt.Context.SaveChangesAsync();
|
||||||
|
return accountLogIn;
|
||||||
|
}
|
||||||
|
#region 注册用户
|
||||||
|
/// <summary>
|
||||||
|
/// 注册新用户或更新现有用户信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <param name="account"></param>
|
||||||
|
/// <param name="ip"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task<T_User> RegisterOrUpdateUserAsync(int userId, IUserAccount account, string ip)
|
||||||
|
{
|
||||||
|
var user = userId > 0
|
||||||
|
? await Dao.DaoUser.Context.T_User.FirstOrDefaultAsync(it => it.Id == userId)
|
||||||
|
: null;
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
user = new T_User
|
||||||
|
{
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
LastLoginAt = DateTime.Now,
|
||||||
|
UpdatedAt = DateTime.Now,
|
||||||
|
IsTest = false,
|
||||||
|
LastLoginType = account.LastLoginType,
|
||||||
|
RegisterType = account.LastLoginType,
|
||||||
|
State = 0,
|
||||||
|
UserIconUrl = AppConfig.UserConfig.UserIconUrl,
|
||||||
|
NickName = $"{AppConfig.UserConfig.NickName}{new Random().Next(1000, 9999)}",
|
||||||
|
Ip = ip
|
||||||
|
};
|
||||||
|
await Dao.DaoUser.Context.T_User.AddAsync(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.LastLoginAt = DateTime.Now;
|
||||||
|
user.UpdatedAt = DateTime.Now;
|
||||||
|
user.Ip = ip;
|
||||||
|
await Dao.DaoUser.Context.SaveChangesAsync();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保用户数据存在
|
||||||
|
private async Task<T_User_Data> EnsureUserDataExistsAsync(int userId, IUserAccount account)
|
||||||
|
{
|
||||||
|
var userData = await Dao.DaoUser.Context.T_User_Data.FirstOrDefaultAsync(it => it.UserId == userId);
|
||||||
|
if (userData == null)
|
||||||
|
{
|
||||||
|
userData = new T_User_Data
|
||||||
|
{
|
||||||
|
CreateAt = DateTime.Now,
|
||||||
|
UpdateAt = DateTime.Now,
|
||||||
|
PhoneNum = account.GetUserDataProperty(UserDataPropertyEnum.PhoneNum),
|
||||||
|
UserId = userId,
|
||||||
|
Email = account.GetUserDataProperty(UserDataPropertyEnum.Email)
|
||||||
|
};
|
||||||
|
await Dao.DaoUser.Context.T_User_Data.AddAsync(userData);
|
||||||
|
await Dao.DaoUser.Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
return userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成JWT令牌
|
||||||
|
private JwtAuthResult GenerateJwtToken(T_User user)
|
||||||
|
{
|
||||||
|
var claims = new[]
|
||||||
|
{
|
||||||
|
new Claim("NickName", user.NickName),
|
||||||
|
new Claim("UserId", user.Id.ToString()),
|
||||||
|
};
|
||||||
|
return JwtAuthManager.GenerateTokens(user.NickName, claims, DateTime.Now);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 管理用户设备
|
||||||
|
private async Task<T_User_Token> ManageUserDevicesAsync(T_User user, IUserAccount account, string accessToken)
|
||||||
|
{
|
||||||
|
var currentTime = DateTime.Now;
|
||||||
|
var dev = string.IsNullOrEmpty(account.DeviceNumber)
|
||||||
|
? MD5Encryption.ComputeMD5Hash($"{user.Id}:{account.LastLoginType}")
|
||||||
|
: account.DeviceNumber;
|
||||||
|
|
||||||
|
var userLoginList = await Dao.DaoUser.Context.T_User_Token
|
||||||
|
.Where(it => it.UserId == user.Id)
|
||||||
|
.OrderBy(it => it.LastLoginAt)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
// 删除多余的设备记录
|
||||||
|
if (userLoginList.Count > AppConfig.UserConfig.MaxDeviceCount)
|
||||||
|
{
|
||||||
|
var excessDevices = userLoginList.Take(userLoginList.Count - AppConfig.UserConfig.MaxDeviceCount).ToList();
|
||||||
|
Dao.DaoUser.Context.T_User_Token.RemoveRange(excessDevices);
|
||||||
|
await Dao.DaoUser.Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingDevice = userLoginList.FirstOrDefault(it => it.DeviceNumber == dev);
|
||||||
|
|
||||||
|
if (existingDevice == null)
|
||||||
|
{
|
||||||
|
if (userLoginList.Count == AppConfig.UserConfig.MaxDeviceCount)
|
||||||
|
{
|
||||||
|
// 替换最早登录的设备
|
||||||
|
existingDevice = userLoginList.First();
|
||||||
|
UpdateDeviceToken(existingDevice, dev, accessToken, currentTime);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 新增设备记录
|
||||||
|
existingDevice = new T_User_Token
|
||||||
|
{
|
||||||
|
CreateAt = currentTime,
|
||||||
|
ExpiresAt = currentTime.AddDays(5),
|
||||||
|
LastLoginAt = currentTime,
|
||||||
|
Token = accessToken,
|
||||||
|
UserId = user.Id,
|
||||||
|
DeviceNumber = dev,
|
||||||
|
TokenMd5 = MD5Encryption.ComputeMD5Hash(accessToken)
|
||||||
|
};
|
||||||
|
await Dao.DaoUser.Context.T_User_Token.AddAsync(existingDevice);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 更新现有设备信息
|
||||||
|
UpdateDeviceToken(existingDevice, dev, accessToken, currentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Dao.DaoUser.Context.SaveChangesAsync();
|
||||||
|
return existingDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新设备令牌信息
|
||||||
|
private void UpdateDeviceToken(T_User_Token device, string deviceNumber, string accessToken, DateTime currentTime)
|
||||||
|
{
|
||||||
|
device.DeviceNumber = deviceNumber;
|
||||||
|
device.TokenMd5 = MD5Encryption.ComputeMD5Hash(accessToken);
|
||||||
|
device.LastLoginAt = currentTime;
|
||||||
|
device.ExpiresAt = currentTime.AddDays(5);
|
||||||
|
device.Token = accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
using CloudGaming.Code.Account.Contract;
|
||||||
|
using CloudGaming.Code.Account.Login;
|
||||||
|
using CloudGaming.DtoModel.Account.Login;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.Account
|
||||||
|
{
|
||||||
|
public static class AccountExtend
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回对应的登录方式
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jsonString"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IUserAccount? GetUserAccount(string jsonString, CloudGamingBase cloudGamingBase)
|
||||||
|
{
|
||||||
|
JObject jsonObject = JObject.Parse(jsonString);
|
||||||
|
if (AllKeysExistIgnoreCase(jsonObject, "PhoneNumber", "VerificationCode"))
|
||||||
|
{
|
||||||
|
var loginParams = JsonConvert.DeserializeObject<PhoneLoginParams>(jsonString);
|
||||||
|
PhoneUserLogin phoneUserLogin = new PhoneUserLogin(loginParams, cloudGamingBase);
|
||||||
|
return phoneUserLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
static bool AllKeysExistIgnoreCase(JObject jsonObject, params string[] keysToCheck)
|
||||||
|
{
|
||||||
|
var propertie = jsonObject.Properties();
|
||||||
|
return keysToCheck.All(key => propertie.Any(p => string.Equals(p.Name, key, StringComparison.OrdinalIgnoreCase)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
using CloudGaming.DtoModel.Account;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.Account.Contract
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 登录接口
|
||||||
|
/// </summary>
|
||||||
|
public interface IUserAccount
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登录方式
|
||||||
|
/// </summary>
|
||||||
|
public int LastLoginType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设备号
|
||||||
|
/// </summary>
|
||||||
|
public string DeviceNumber { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// 登录
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> LoginAsync();
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建账号
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task CreateLoginAsync(T_User user);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取用户属性
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userDataPropertyEnum"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
string GetUserDataProperty(UserDataPropertyEnum userDataPropertyEnum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
|
||||||
|
|
||||||
|
using Bogus.DataSets;
|
||||||
|
|
||||||
|
using CloudGaming.Code.Account.Contract;
|
||||||
|
using CloudGaming.DtoModel.Account;
|
||||||
|
using CloudGaming.DtoModel.Account.Login;
|
||||||
|
|
||||||
|
using HuanMeng.DotNetCore.Redis;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.Account.Login
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 手机号登录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loginParams"></param>
|
||||||
|
/// <param name="cloudGamingBase"></param>
|
||||||
|
public class PhoneUserLogin(PhoneLoginParams loginParams, CloudGamingBase cloudGamingBase) : IUserAccount
|
||||||
|
{
|
||||||
|
public async Task<int> LoginAsync()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(loginParams.PhoneNumber))
|
||||||
|
{
|
||||||
|
throw MessageBox.Show(ResonseCode.ParamError, "手机号不能为空");
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(loginParams.VerificationCode))
|
||||||
|
{
|
||||||
|
throw MessageBox.Show(ResonseCode.ParamError, "验证码不能为空");
|
||||||
|
}
|
||||||
|
//判断是否是测试账号
|
||||||
|
if (!loginParams.PhoneNumber.Contains("999999999") && loginParams.VerificationCode == "1112")
|
||||||
|
{
|
||||||
|
if (!PhoneNumberValidator.IsPhoneNumber(loginParams.PhoneNumber))
|
||||||
|
{
|
||||||
|
throw MessageBox.Show(ResonseCode.PhoneNumberException, "手机号格式错误");
|
||||||
|
}
|
||||||
|
var verificationCodeRedis = await cloudGamingBase.RedisCache.StringGetAsync($"App:sms:{loginParams.PhoneNumber}");
|
||||||
|
if (verificationCodeRedis.IsNullOrEmpty)
|
||||||
|
{
|
||||||
|
throw MessageBox.Show(ResonseCode.ParamError, "验证码已过期,请重新发送!");
|
||||||
|
}
|
||||||
|
var verificationCode = (string)verificationCodeRedis;
|
||||||
|
if (verificationCode != loginParams.VerificationCode)
|
||||||
|
{
|
||||||
|
throw MessageBox.Show(ResonseCode.ParamError, "验证码错误!");
|
||||||
|
}
|
||||||
|
await cloudGamingBase.RedisCache.KeyDeleteAsync($"App:sms:{loginParams.PhoneNumber}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var userAccount = await cloudGamingBase.Dao.DaoUser.Context.T_User_Phone_Account.AsNoTracking().Where(it => it.PhoneNum == loginParams.PhoneNumber && !it.IsLogout).FirstOrDefaultAsync();
|
||||||
|
return userAccount?.UserId ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建登录账号
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task CreateLoginAsync(T_User user)
|
||||||
|
{
|
||||||
|
|
||||||
|
T_User_Phone_Account t_User_Phone_Account = new T_User_Phone_Account()
|
||||||
|
{
|
||||||
|
UserId = user.Id,
|
||||||
|
IsLogout = false,
|
||||||
|
NikeName = user.NickName,
|
||||||
|
PhoneNum = loginParams.PhoneNumber,
|
||||||
|
UpdatedAt = DateTime.Now,
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
};
|
||||||
|
await cloudGamingBase.Dao.DaoUser.Context.T_User_Phone_Account.AddAsync(t_User_Phone_Account);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int LastLoginType { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回登录信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userDataPropertyEnum"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string GetUserDataProperty(UserDataPropertyEnum userDataPropertyEnum)
|
||||||
|
{
|
||||||
|
if (userDataPropertyEnum == UserDataPropertyEnum.PhoneNum)
|
||||||
|
{
|
||||||
|
return loginParams.PhoneNumber;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设备号
|
||||||
|
/// </summary>
|
||||||
|
public string DeviceNumber
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (loginParams.DeviceNumber.Length > 100)
|
||||||
|
{
|
||||||
|
loginParams.DeviceNumber = loginParams.DeviceNumber.Substring(0, 100);
|
||||||
|
}
|
||||||
|
return loginParams.DeviceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
|
using CloudGaming.Code.AppExtend.Config;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace CloudGaming.Code.AppExtend
|
namespace CloudGaming.Code.AppExtend;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 项目配置
|
/// 项目配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -64,8 +66,12 @@ namespace CloudGaming.Code.AppExtend
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// oss阿里云配置
|
/// oss阿里云配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AliyunOssConfig AliyunConfig { get; set; }
|
public AliyunConfig AliyunConfig { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户默认配置
|
||||||
|
/// </summary>
|
||||||
|
public UserConfig UserConfig { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取数据库连接字符串
|
/// 获取数据库连接字符串
|
||||||
|
|
@ -114,4 +120,3 @@ namespace CloudGaming.Code.AppExtend
|
||||||
/// </summary>
|
/// </summary>
|
||||||
App
|
App
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,11 @@ namespace CloudGaming.Code.AppExtend
|
||||||
newAppConfig.PhoneConnectionString = appConfig.PhoneConnectionString;
|
newAppConfig.PhoneConnectionString = appConfig.PhoneConnectionString;
|
||||||
newAppConfig.AliyunConfig = appConfig.AliyunConfig;
|
newAppConfig.AliyunConfig = appConfig.AliyunConfig;
|
||||||
newAppConfig.DefaultLanguage = appConfig.DefaultLanguage;
|
newAppConfig.DefaultLanguage = appConfig.DefaultLanguage;
|
||||||
|
if (appConfig.UserConfig == null)
|
||||||
|
{
|
||||||
|
appConfig.UserConfig = new UserConfig();
|
||||||
|
}
|
||||||
|
newAppConfig.UserConfig = appConfig.UserConfig;
|
||||||
return newAppConfig;
|
return newAppConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.AppExtend.Config;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 阿里云配置
|
||||||
|
/// </summary>
|
||||||
|
public class AliyunConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 配置环境变量
|
||||||
|
/// </summary>
|
||||||
|
public string AccessKeySecret { get; set; }
|
||||||
|
|
||||||
|
#region 阿里云OSS配置
|
||||||
|
/// <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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 短信签名名称
|
||||||
|
/// </summary>
|
||||||
|
public string SmsSignName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// string 短信模板配置
|
||||||
|
/// </summary>
|
||||||
|
public string SmsTemplateCode { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.AppExtend.Config;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户默认配置
|
||||||
|
/// </summary>
|
||||||
|
public class UserConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用户默认头像
|
||||||
|
/// </summary>
|
||||||
|
public string UserIconUrl { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 用户默认昵称
|
||||||
|
/// </summary>
|
||||||
|
public string NickName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户最大登录设备
|
||||||
|
/// </summary>
|
||||||
|
public int MaxDeviceCount { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Org.BouncyCastle.Asn1.Ocsp;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CloudGaming.Code.AppExtend
|
|
||||||
{
|
|
||||||
public class CustomObjectResultExecutor : ObjectResultExecutor
|
|
||||||
{
|
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
||||||
|
|
||||||
public CustomObjectResultExecutor(OutputFormatterSelector formatterSelector, IHttpResponseStreamWriterFactory writerFactory, ILoggerFactory loggerFactory, IOptions<MvcOptions> mvcOptions):base(formatterSelector, writerFactory, loggerFactory, mvcOptions)
|
|
||||||
|
|
||||||
{
|
|
||||||
//_httpContextAccessor = httpContextAccessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task ExecuteAsync(ActionContext context, ObjectResult result)
|
|
||||||
{
|
|
||||||
var httpContext = _httpContextAccessor.HttpContext;
|
|
||||||
var user = httpContext.User.Identity.IsAuthenticated ? httpContext.User.Identity.Name : "Anonymous";
|
|
||||||
|
|
||||||
//// 动态修改返回的结果数据(示例:修改 Message 字段)
|
|
||||||
//if (result.Value is ResponseData responseData)
|
|
||||||
//{
|
|
||||||
// if (user == "admin")
|
|
||||||
// {
|
|
||||||
// responseData.Message += " (admin)";
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
return base.ExecuteAsync(context, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
using CloudGaming.Code.DataAccess;
|
|
||||||
using CloudGaming.GameModel.Db.Db_Ext;
|
|
||||||
|
|
||||||
using HuanMeng.DotNetCore.AttributeExtend;
|
|
||||||
using HuanMeng.DotNetCore.Base;
|
|
||||||
using HuanMeng.DotNetCore.Utility;
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
|
||||||
using Microsoft.Identity.Client;
|
|
||||||
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Net;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace CloudGaming.Code.AppExtend
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class CustomResultFilter : IResultFilter
|
|
||||||
{
|
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
||||||
private readonly IServiceProvider _serviceProvider;
|
|
||||||
private readonly AppConfig _appConfig;
|
|
||||||
public CustomResultFilter(IHttpContextAccessor httpContextAccessor, AppConfig appConfig, IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
_httpContextAccessor = httpContextAccessor;
|
|
||||||
_appConfig = appConfig;
|
|
||||||
_serviceProvider = serviceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnResultExecuting(ResultExecutingContext context)
|
|
||||||
{
|
|
||||||
// 获取当前的 HttpContext
|
|
||||||
var httpContext = context.HttpContext;
|
|
||||||
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";
|
|
||||||
//Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
|
|
||||||
if (context.Result is ObjectResult objectResult && objectResult.Value != null)
|
|
||||||
{
|
|
||||||
var x = objectResult.Value.GetType();
|
|
||||||
object? value = null;
|
|
||||||
if (!x.FullName.Contains("HuanMeng.DotNetCore.Base.BaseResponse"))
|
|
||||||
{
|
|
||||||
BaseResponse<object> baseResponse = new BaseResponse<object>(ResonseCode.Success, "", objectResult.Value);
|
|
||||||
value = baseResponse;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
value = objectResult.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dic = value.ToDictionaryOrList(apiPrefix
|
|
||||||
, it => cloudGamingBase.Cache.ImageEntityCache[it]
|
|
||||||
);
|
|
||||||
objectResult.Value = dic;
|
|
||||||
sw.Stop();
|
|
||||||
context.HttpContext.Response.Headers.TryAdd("X-Request-Duration-Filter", sw.Elapsed.TotalMilliseconds.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void OnResultExecuted(ResultExecutedContext context)
|
|
||||||
{
|
|
||||||
// 可在执行完结果后处理其他逻辑
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace CloudGaming.Code.AppExtend
|
namespace CloudGaming.Code.AppExtend
|
||||||
{
|
{
|
||||||
|
|
@ -65,6 +66,57 @@ namespace CloudGaming.Code.AppExtend
|
||||||
//指定允许令牌的时钟偏移。允许令牌的过期时间与实际时间之间存在的时间差。在这里设置为 5 分钟,表示允许令牌的时钟偏移为 5 分钟。
|
//指定允许令牌的时钟偏移。允许令牌的过期时间与实际时间之间存在的时间差。在这里设置为 5 分钟,表示允许令牌的时钟偏移为 5 分钟。
|
||||||
ClockSkew = TimeSpan.FromMinutes(5)
|
ClockSkew = TimeSpan.FromMinutes(5)
|
||||||
};
|
};
|
||||||
|
options.Events = new JwtBearerEvents
|
||||||
|
{
|
||||||
|
OnTokenValidated = async context =>
|
||||||
|
{
|
||||||
|
var userId = context.Principal.FindFirst("userId")?.Value;
|
||||||
|
if (userId == null || true)
|
||||||
|
{
|
||||||
|
context.Fail("Token missing userId claim.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//// 你可以调用数据库或其他服务来验证用户状态
|
||||||
|
//var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
|
||||||
|
//var user = await userService.GetUserByIdAsync(userId);
|
||||||
|
//if (user == null || !user.IsActive)
|
||||||
|
//{
|
||||||
|
// context.Fail("User is inactive or not found.");
|
||||||
|
//}
|
||||||
|
},
|
||||||
|
// 处理认证失败的事件
|
||||||
|
OnAuthenticationFailed = context =>
|
||||||
|
{
|
||||||
|
// 在这里可以根据需要设置自定义的HTTP状态码
|
||||||
|
context.Response.StatusCode = StatusCodes.Status403Forbidden; // 设置为 403 Forbidden
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
|
||||||
|
// 返回自定义的错误消息
|
||||||
|
var result = JsonSerializer.Serialize(new { error = context.Exception?.Message });
|
||||||
|
return context.Response.WriteAsync(result);
|
||||||
|
},
|
||||||
|
// 在认证失败并被 Challenge 时触发该事件
|
||||||
|
OnChallenge = context =>
|
||||||
|
{
|
||||||
|
// 如果已经有错误消息,则根据错误原因返回自定义状态码
|
||||||
|
if (context.AuthenticateFailure != null)
|
||||||
|
{
|
||||||
|
context.HandleResponse(); // 确保不再执行默认的挑战响应
|
||||||
|
context.Response.Clear(); // 清空现有响应内容
|
||||||
|
context.Response.StatusCode = StatusCodes.Status200OK; // 设置状态码为403 Forbidden
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
|
||||||
|
// 构建自定义的 JSON 响应
|
||||||
|
var result = JsonSerializer.Serialize(new { error = context.AuthenticateFailure.Message });
|
||||||
|
return context.Response.WriteAsync(result); // 写入响应
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认情况下返回 401 Unauthorized
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
//注册一个JwtAuthManager的单例服务
|
//注册一个JwtAuthManager的单例服务
|
||||||
builder.Services.AddSingleton<IJwtAuthManager, JwtManager>();
|
builder.Services.AddSingleton<IJwtAuthManager, JwtManager>();
|
||||||
|
|
|
||||||
|
|
@ -69,14 +69,12 @@ namespace CloudGaming.Code.Cache.Special
|
||||||
string chineseName = faker.Internet.UserName();
|
string chineseName = faker.Internet.UserName();
|
||||||
NickName = chineseName;
|
NickName = chineseName;
|
||||||
}
|
}
|
||||||
gameInfo.GameShare = $"此游戏由{NickName}分享。";
|
gameInfo.GameShare = $"{NickName}";
|
||||||
gameInfo.GameShareUserIcon = 90001;
|
gameInfo.GameShareUserIcon = 90001;
|
||||||
if (gameInfo.ConsumeDiamondNumHour == 0)
|
if (gameInfo.ConsumeDiamondNumHour == 0)
|
||||||
{
|
{
|
||||||
gameInfo.ConsumeDiamondNumHour = defaultConsumeDiamondNumHour;
|
gameInfo.ConsumeDiamondNumHour = defaultConsumeDiamondNumHour;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (gameInfo.ConsumeDiamondNumHour > 0)
|
if (gameInfo.ConsumeDiamondNumHour > 0)
|
||||||
{
|
{
|
||||||
gameInfo.GameDetailsofCharges = $"游戏资费:游玩按分钟计费,{gameInfo.ConsumeDiamondNumHour}钻石/小时。";
|
gameInfo.GameDetailsofCharges = $"游戏资费:游玩按分钟计费,{gameInfo.ConsumeDiamondNumHour}钻石/小时。";
|
||||||
|
|
@ -178,7 +176,11 @@ namespace CloudGaming.Code.Cache.Special
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return GameInfoDic[gameId] ?? null;
|
if (GameInfoDic.TryGetValue(gameId, out var gameInfo))
|
||||||
|
{
|
||||||
|
return gameInfo;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AgileConfig.Client" Version="1.7.3" />
|
<PackageReference Include="AgileConfig.Client" Version="1.7.3" />
|
||||||
|
<PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="3.1.0" />
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
<PackageReference Include="Bogus" Version="35.6.1" />
|
<PackageReference Include="Bogus" Version="35.6.1" />
|
||||||
<PackageReference Include="FastMember" Version="1.5.0" />
|
<PackageReference Include="FastMember" Version="1.5.0" />
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ namespace CloudGaming.Code.Config
|
||||||
IsAuthRealName = true
|
IsAuthRealName = true
|
||||||
};
|
};
|
||||||
appConfigDto.IsChecking = IsChecking;
|
appConfigDto.IsChecking = IsChecking;
|
||||||
|
appConfigDto.SignKey = AppConfig.TenantId.ToString("N");
|
||||||
return appConfigDto;
|
return appConfigDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,8 @@ namespace CloudGaming.Code.Epg
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var epgInfo = item.ToEpgInfo(Cache.GameEntityCache);
|
|
||||||
|
var epgInfo = item.ToEpgInfo(Cache.GameEntityCache,it.DefaultImageStyle);
|
||||||
if (epgInfo != null)
|
if (epgInfo != null)
|
||||||
{
|
{
|
||||||
list.Add(epgInfo);
|
list.Add(epgInfo);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using CloudGaming.Code.Cache.Special;
|
using CloudGaming.Code.Cache.Special;
|
||||||
using CloudGaming.DtoModel.Epg;
|
using CloudGaming.DtoModel.Epg;
|
||||||
|
using CloudGaming.DtoModel.Game;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -20,7 +21,7 @@ namespace CloudGaming.Code.Epg
|
||||||
/// <param name="epgCfg"></param>
|
/// <param name="epgCfg"></param>
|
||||||
/// <param name="gameEntityCache"></param>
|
/// <param name="gameEntityCache"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static EpgInfo ToEpgInfo(this T_Epg_Cfg epgCfg, GameEntityCache gameEntityCache)
|
public static EpgInfo ToEpgInfo(this T_Epg_Cfg epgCfg, GameEntityCache gameEntityCache, int defaultImageStyle)
|
||||||
{
|
{
|
||||||
if (epgCfg.ResType == (int)EpgEnum.EpgResType.游戏 && string.IsNullOrEmpty(epgCfg.ResId))
|
if (epgCfg.ResType == (int)EpgEnum.EpgResType.游戏 && string.IsNullOrEmpty(epgCfg.ResId))
|
||||||
{
|
{
|
||||||
|
|
@ -55,18 +56,15 @@ namespace CloudGaming.Code.Epg
|
||||||
{
|
{
|
||||||
epgInfo.SubTitle = gameInfo.Title2;
|
epgInfo.SubTitle = gameInfo.Title2;
|
||||||
}
|
}
|
||||||
|
if (epgCfg.ImageResStyle == 0)
|
||||||
if (epgInfo.ImageUrl == 0 && !string.IsNullOrEmpty(epgCfg.ImageResStyle))
|
|
||||||
{
|
{
|
||||||
epgInfo.ImageUrl = epgCfg.ImageResStyle switch
|
epgCfg.ImageResStyle = defaultImageStyle;
|
||||||
|
}
|
||||||
|
epgInfo.ImageResStyle = epgCfg.ImageResStyle;
|
||||||
|
//defaultImageStyle
|
||||||
|
if (epgInfo.ImageUrl == 0 && epgCfg.ImageResStyle != 0)
|
||||||
{
|
{
|
||||||
var style when style == ((int)ImageResStyle.Game尊享推荐_312_420).ToString() => gameInfo.ImageId_ZXTJ,
|
epgInfo.ImageUrl = ((ImageResStyle)epgCfg.ImageResStyle).GetImageResStyle(gameInfo);
|
||||||
var style when style == ((int)ImageResStyle.Game推荐位大图_984_520).ToString() => gameInfo.ImageId_TJ,
|
|
||||||
var style when style == ((int)ImageResStyle.Game游戏库_180_180).ToString() => gameInfo.GameImageId,
|
|
||||||
var style when style == ((int)ImageResStyle.Game最近推出_360_548).ToString() => gameInfo.ImageId_ZJTC,
|
|
||||||
var style when style == ((int)ImageResStyle.Game精选推荐_474_300).ToString() => gameInfo.ImageId_JXTJ,
|
|
||||||
_ => epgInfo.ImageUrl
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
epgInfo.Score = gameInfo.Score ?? string.Empty;
|
epgInfo.Score = gameInfo.Score ?? string.Empty;
|
||||||
|
|
@ -74,5 +72,7 @@ namespace CloudGaming.Code.Epg
|
||||||
|
|
||||||
return epgInfo;
|
return epgInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
using HuanMeng.DotNetCore.AttributeExtend;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.Extend;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class MessageAttributeExtend
|
||||||
|
{
|
||||||
|
private static readonly ConcurrentDictionary<MethodInfo, MessageAttribute?> _attributeCache = new ConcurrentDictionary<MethodInfo, MessageAttribute?>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controllerActionDescriptor"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static MessageAttribute? GetMessageAttribute(ControllerActionDescriptor controllerActionDescriptor)
|
||||||
|
{
|
||||||
|
// 获取方法信息
|
||||||
|
var methodInfo = controllerActionDescriptor.MethodInfo;
|
||||||
|
|
||||||
|
// 尝试从缓存中获取MessageAttribute
|
||||||
|
if (!_attributeCache.TryGetValue(methodInfo, out var messageAttribute))
|
||||||
|
{
|
||||||
|
// 如果缓存中没有,则使用反射获取并存储到缓存中
|
||||||
|
messageAttribute = methodInfo.GetCustomAttribute<MessageAttribute>();
|
||||||
|
_attributeCache.TryAdd(methodInfo, messageAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
return messageAttribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.Filter
|
||||||
|
{
|
||||||
|
public class CustomExceptionFilter : IExceptionFilter
|
||||||
|
{
|
||||||
|
public void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
// 检查异常是否是特定的异常类型
|
||||||
|
if (context.Exception is MessageBox message)
|
||||||
|
{
|
||||||
|
var obj = new BaseResponse<object>(message.Code, message.Message, message.Data);
|
||||||
|
//// 处理特定异常:记录日志、设置响应结果等
|
||||||
|
//Console.WriteLine($"Custom exception caught: {message.Message}");
|
||||||
|
|
||||||
|
// 设置自定义的响应结果
|
||||||
|
context.Result = new JsonResult(obj)
|
||||||
|
{
|
||||||
|
StatusCode = StatusCodes.Status200OK // 或者其他合适的HTTP状态码
|
||||||
|
};
|
||||||
|
|
||||||
|
// 标记异常已经被处理
|
||||||
|
context.ExceptionHandled = true;
|
||||||
|
}
|
||||||
|
sw.Stop();
|
||||||
|
context.HttpContext.Response.Headers.TryAdd("X-Request-Duration-CustomExceptionFilter", sw.Elapsed.TotalMilliseconds.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.Filter;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class CustomResultFilter : IResultFilter
|
||||||
|
{
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly AppConfig _appConfig;
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContextAccessor"></param>
|
||||||
|
/// <param name="appConfig"></param>
|
||||||
|
/// <param name="serviceProvider"></param>
|
||||||
|
public CustomResultFilter(IHttpContextAccessor httpContextAccessor, AppConfig appConfig, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
_appConfig = appConfig;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结果发送到客户端前
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public void OnResultExecuting(ResultExecutingContext context)
|
||||||
|
{
|
||||||
|
var httpContext = context.HttpContext;
|
||||||
|
var path = httpContext.Request.Path.Value ?? "";
|
||||||
|
var apiPrefix = path.Replace('/', '.').TrimStart('.');
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
CloudGamingBase cloudGamingBase = new CloudGamingBase(_serviceProvider);
|
||||||
|
//// 获取当前用户的信息
|
||||||
|
//var user = httpContext.User.Identity.IsAuthenticated ? httpContext.User.Identity.Name : "Anonymous";
|
||||||
|
if (context.Result is ObjectResult objectResult && objectResult.Value != null)
|
||||||
|
{
|
||||||
|
var x = objectResult.Value.GetType();
|
||||||
|
object? value = null;
|
||||||
|
if (!x.FullName.Contains("HuanMeng.DotNetCore.Base.BaseResponse"))
|
||||||
|
{
|
||||||
|
BaseResponse<object> baseResponse = new BaseResponse<object>(ResonseCode.Success, "", objectResult.Value);
|
||||||
|
value = baseResponse;
|
||||||
|
// 获取当前执行的Action方法的信息并进行类型检查
|
||||||
|
if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
|
||||||
|
{
|
||||||
|
var messageAttribute = MessageAttributeExtend.GetMessageAttribute(controllerActionDescriptor);
|
||||||
|
// 如果存在MessageAttribute,则设置响应消息
|
||||||
|
if (messageAttribute != null)
|
||||||
|
{
|
||||||
|
baseResponse.Message = messageAttribute.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = objectResult.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dic = value.ToDictionaryOrList(apiPrefix, it => cloudGamingBase.Cache.ImageEntityCache[it]);
|
||||||
|
objectResult.Value = dic;
|
||||||
|
sw.Stop();
|
||||||
|
context.HttpContext.Response.Headers.TryAdd("X-Request-Duration-Filter", sw.Elapsed.TotalMilliseconds.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结果发送到客户端后
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public void OnResultExecuted(ResultExecutedContext context)
|
||||||
|
{
|
||||||
|
// 可在执行完结果后处理其他逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,8 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using static CloudGaming.DtoModel.Epg.EpgEnum;
|
||||||
|
|
||||||
namespace CloudGaming.Code.Game
|
namespace CloudGaming.Code.Game
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -54,7 +56,7 @@ namespace CloudGaming.Code.Game
|
||||||
{
|
{
|
||||||
var gameList = Cache.GameEntityCache?.DataList ?? new List<GameInfo>();
|
var gameList = Cache.GameEntityCache?.DataList ?? new List<GameInfo>();
|
||||||
var x = gameList.Where(it => it.GameType?.Any(item => item.Id == typeId) ?? false).Select(it => new
|
var x = gameList.Where(it => it.GameType?.Any(item => item.Id == typeId) ?? false).Select(it => new
|
||||||
GameListDto(it)).ToList();
|
GameListDto(it, ImageResStyle.游戏库)).ToList();
|
||||||
return x;
|
return x;
|
||||||
});
|
});
|
||||||
return gameListDto;
|
return gameListDto;
|
||||||
|
|
@ -102,8 +104,47 @@ namespace CloudGaming.Code.Game
|
||||||
{
|
{
|
||||||
gameInfos = Cache.GameInfos.OrderBy(it => Guid.NewGuid()).Take(3).ToList();
|
gameInfos = Cache.GameInfos.OrderBy(it => Guid.NewGuid()).Take(3).ToList();
|
||||||
}
|
}
|
||||||
var gameList = gameInfos?.Select(it => new GameListDto(it)).ToList();
|
var gameList = gameInfos?.Select(it => new GameListDto(it, ImageResStyle.竖形图)).ToList();
|
||||||
return gameList ?? new List<GameListDto>();
|
return gameList ?? new List<GameListDto>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏搜索
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gameName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<GameListDto> GameSearch(string gameName)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
var gameIdMatches = Cache.GameInfos
|
||||||
|
.Where(it => it.GameId.Contains(gameName))
|
||||||
|
.Select(it => new GameListDto(it, ImageResStyle.中等LOGO))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var gameNameMatches = Cache.GameInfos
|
||||||
|
.Where(it => it.GameName.Contains(gameName))
|
||||||
|
.Select(it => new GameListDto(it, ImageResStyle.中等LOGO))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var gameIntroduceMatches = Cache.GameInfos
|
||||||
|
.Where(it => it.GameIntroduce.Contains(gameName) )
|
||||||
|
.Select(it => new GameListDto(it, ImageResStyle.中等LOGO))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var otherMatches = Cache.GameInfos
|
||||||
|
.Where(it => !it.GameId.Contains(gameName) && !it.GameName.Contains(gameName) && !it.GameIntroduce.Contains(gameName) &&
|
||||||
|
(it.GameTags.Any(tag => tag.Name.Contains(gameName)) || it.GameType.Any(types => types.Name.Contains(gameName))))
|
||||||
|
.Select(it => new GameListDto(it, ImageResStyle.中等LOGO))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var list = gameIdMatches
|
||||||
|
.Concat(gameNameMatches)
|
||||||
|
.Concat(gameIntroduceMatches)
|
||||||
|
.Concat(otherMatches)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
global using CloudGaming.Code.AppExtend;
|
global using CloudGaming.Code.AppExtend;
|
||||||
|
global using CloudGaming.Code.Extend;
|
||||||
global using CloudGaming.GameModel.Db.Db_Ext;
|
global using CloudGaming.GameModel.Db.Db_Ext;
|
||||||
global using CloudGaming.GameModel.Db.Db_Game;
|
global using CloudGaming.GameModel.Db.Db_Game;
|
||||||
global using CloudGaming.Model.DbSqlServer.Db_Phone;
|
global using CloudGaming.Model.DbSqlServer.Db_Phone;
|
||||||
|
|
@ -7,9 +8,16 @@ global using CloudGaming.Model.DbSqlServer.Db_User;
|
||||||
global using HuanMeng.DotNetCore.Base;
|
global using HuanMeng.DotNetCore.Base;
|
||||||
global using HuanMeng.DotNetCore.MultiTenant;
|
global using HuanMeng.DotNetCore.MultiTenant;
|
||||||
global using HuanMeng.DotNetCore.MultiTenant.Contract;
|
global using HuanMeng.DotNetCore.MultiTenant.Contract;
|
||||||
|
global using HuanMeng.DotNetCore.Utility;
|
||||||
|
|
||||||
global using Microsoft.AspNetCore.Builder;
|
global using Microsoft.AspNetCore.Builder;
|
||||||
|
global using Microsoft.AspNetCore.Http;
|
||||||
|
global using Microsoft.AspNetCore.Mvc;
|
||||||
|
global using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
global using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
global using Microsoft.EntityFrameworkCore;
|
global using Microsoft.EntityFrameworkCore;
|
||||||
global using Microsoft.Extensions.Configuration;
|
global using Microsoft.Extensions.Configuration;
|
||||||
global using Microsoft.Extensions.DependencyInjection;
|
global using Microsoft.Extensions.DependencyInjection;
|
||||||
global using Microsoft.Extensions.Hosting;
|
global using Microsoft.Extensions.Hosting;
|
||||||
|
global using CloudGaming.Code.AppExtend.Config;
|
||||||
|
global using System.Diagnostics;
|
||||||
|
|
@ -1,13 +1,5 @@
|
||||||
using HuanMeng.DotNetCore.MiddlewareExtend;
|
namespace CloudGaming.Code.MiddlewareExtend;
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CloudGaming.Code.MiddlewareExtend
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -21,6 +13,7 @@ namespace CloudGaming.Code.MiddlewareExtend
|
||||||
public static IApplicationBuilder UseCloudGamingMiddlewareAll(this IApplicationBuilder builder)
|
public static IApplicationBuilder UseCloudGamingMiddlewareAll(this IApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
return builder
|
return builder
|
||||||
|
.UseSignMiddleware()
|
||||||
.UseCacheMiddleware()
|
.UseCacheMiddleware()
|
||||||
.UseRedisCacheMiddleware()
|
.UseRedisCacheMiddleware()
|
||||||
;
|
;
|
||||||
|
|
@ -41,5 +34,13 @@ namespace CloudGaming.Code.MiddlewareExtend
|
||||||
return builder.UseMiddleware<RedisCacheMiddleware>();
|
return builder.UseMiddleware<RedisCacheMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加密验证
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IApplicationBuilder UseSignMiddleware(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<SignMiddleware>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.MiddlewareExtend;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参数请求加密验证
|
||||||
|
/// </summary>
|
||||||
|
public class SignMiddleware
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
public SignMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InvokeAsync(HttpContext context, AppConfig appConfig)
|
||||||
|
{ // 读取请求体
|
||||||
|
context.Request.EnableBuffering();
|
||||||
|
if (context.Request.Method != "POST" || context.Request.Host.Host == "localhost")
|
||||||
|
{
|
||||||
|
// 调用下一个中间件
|
||||||
|
await _next(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启用请求流的多次读取功能
|
||||||
|
var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
|
||||||
|
context.Request.Body.Position = 0; // 重置请求体的位置
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(requestBody))
|
||||||
|
{
|
||||||
|
await _next(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 解析请求体为 JSON 对象
|
||||||
|
var requestJson = JObject.Parse(requestBody);
|
||||||
|
// 获取请求中的 sign 值
|
||||||
|
var requestSign = requestJson["sign"]?.ToString();
|
||||||
|
if (string.IsNullOrEmpty(requestSign))
|
||||||
|
{
|
||||||
|
var response = GetSignError(context);
|
||||||
|
context.Response.ContentType = "application/json; charset=utf-8";
|
||||||
|
await context.Response.WriteAsync(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 获取所有的键值对,并排序
|
||||||
|
var sortedKeys = requestJson.Properties()
|
||||||
|
.Where(p => p.Name != "sign")
|
||||||
|
.OrderBy(p => p.Name)
|
||||||
|
.Select(p => p.Value.ToString())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 拼接所有的值,并加上固定字符串
|
||||||
|
var concatenatedValues = string.Join("", sortedKeys) + appConfig.TenantId.ToString("N");
|
||||||
|
|
||||||
|
// 计算 MD5 哈希值
|
||||||
|
var md5Hash = MD5Encryption.ComputeMD5Hash(concatenatedValues);
|
||||||
|
// 验证 MD5 哈希值与请求中的 sign 是否匹配
|
||||||
|
if (md5Hash != requestSign)
|
||||||
|
{
|
||||||
|
var response = GetSignError(context);
|
||||||
|
context.Response.ContentType = "application/json; charset=utf-8";
|
||||||
|
context.Response.Headers["X-Request-Sign-Value"] = concatenatedValues;
|
||||||
|
context.Response.Headers["X-Request-Sign"] = md5Hash;
|
||||||
|
// 将异常信息写入 HTTP 响应
|
||||||
|
await context.Response.WriteAsync(response);
|
||||||
|
//await context.Response.WriteAsync("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用下一个中间件
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetSignError(HttpContext context)
|
||||||
|
{
|
||||||
|
var settings = new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||||
|
};
|
||||||
|
// 返回 500 错误
|
||||||
|
context.Response.StatusCode = 200;
|
||||||
|
BaseResponse<object> baseResponse = new BaseResponse<object>(ResonseCode.SignError, "sign加密验证失败", null)
|
||||||
|
{ };
|
||||||
|
var json = JsonConvert.SerializeObject(baseResponse, settings);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
using CloudGaming.Code.Sms.Contract;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Tea;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.Sms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class AlibabaPhoneNumberVerificationService(AliyunConfig aliyunOssConfig) : IPhoneNumberVerificationService
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <term><b>Description:</b></term>
|
||||||
|
/// <description>
|
||||||
|
/// <para>使用AK&SK初始化账号Client</para>
|
||||||
|
/// </description>
|
||||||
|
///
|
||||||
|
/// <returns>
|
||||||
|
/// Client
|
||||||
|
/// </returns>
|
||||||
|
///
|
||||||
|
/// <term><b>Exception:</b></term>
|
||||||
|
/// Exception
|
||||||
|
public AlibabaCloud.SDK.Dysmsapi20170525.Client CreateClient()
|
||||||
|
{
|
||||||
|
// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
|
||||||
|
// 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378671.html。
|
||||||
|
AlibabaCloud.OpenApiClient.Models.Config config = new AlibabaCloud.OpenApiClient.Models.Config
|
||||||
|
{
|
||||||
|
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
|
||||||
|
AccessKeyId = "LTAI5tEMoHbcDC5d9CQWovJk",//aliyunOssConfig.AccessKeyId, //Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID"),
|
||||||
|
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
|
||||||
|
AccessKeySecret = "DgnYOJr0l9hTnl82vI4BxwVgtE1RdL"// aliyunOssConfig.AccessKeySecret,
|
||||||
|
};
|
||||||
|
// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
|
||||||
|
config.Endpoint = "dysmsapi.aliyuncs.com";
|
||||||
|
return new AlibabaCloud.SDK.Dysmsapi20170525.Client(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="phoneNumber"></param>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public async Task<bool> SendVerificationCodeAsync(string phoneNumber, string code)
|
||||||
|
{
|
||||||
|
AlibabaCloud.SDK.Dysmsapi20170525.Client client = CreateClient();
|
||||||
|
AlibabaCloud.SDK.Dysmsapi20170525.Models.SendSmsRequest sendSmsRequest = new AlibabaCloud.SDK.Dysmsapi20170525.Models.SendSmsRequest
|
||||||
|
{
|
||||||
|
SignName = aliyunOssConfig.SmsSignName,// "氢荷健康",
|
||||||
|
TemplateCode = aliyunOssConfig.SmsTemplateCode,// "SMS_154950909",
|
||||||
|
PhoneNumbers = phoneNumber,
|
||||||
|
TemplateParam = "{\"code\":\"" + code + "\"}"// "{\"code\":\"" + code + "\"}",
|
||||||
|
};
|
||||||
|
AlibabaCloud.TeaUtil.Models.RuntimeOptions runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 复制代码运行请自行打印 API 的返回值
|
||||||
|
// 复制代码运行请自行打印 API 的返回值
|
||||||
|
//client.SendSmsWithOptions(sendSmsRequest, runtime);
|
||||||
|
var response = await client.SendSmsWithOptionsAsync(sendSmsRequest, runtime);
|
||||||
|
}
|
||||||
|
catch (TeaException error)
|
||||||
|
{
|
||||||
|
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||||
|
// 错误 message
|
||||||
|
Console.WriteLine(error.Message);
|
||||||
|
// 诊断地址
|
||||||
|
Console.WriteLine(error.Data["Recommend"]);
|
||||||
|
var x = AlibabaCloud.TeaUtil.Common.AssertAsString(error.Message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
catch (Exception _error)
|
||||||
|
{
|
||||||
|
TeaException error = new TeaException(new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "message", _error.Message }
|
||||||
|
});
|
||||||
|
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||||
|
// 错误 message
|
||||||
|
Console.WriteLine(error.Message);
|
||||||
|
// 诊断地址
|
||||||
|
Console.WriteLine(error.Data["Recommend"]);
|
||||||
|
var x = AlibabaCloud.TeaUtil.Common.AssertAsString(error.Message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class SmsExtend
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取短信
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aliyunOssConfig"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IPhoneNumberVerificationService GetPhoneNumberVerificationService(this AliyunConfig aliyunOssConfig)
|
||||||
|
{
|
||||||
|
return new AlibabaPhoneNumberVerificationService(aliyunOssConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.Code.Sms.Contract
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 发送手机短信
|
||||||
|
/// </summary>
|
||||||
|
public interface IPhoneNumberVerificationService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 发送验证码到指定的手机号。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="phoneNumber">目标手机号。</param>
|
||||||
|
/// <param name="code">验证码</param>
|
||||||
|
/// <returns>返回操作是否成功。</returns>
|
||||||
|
Task<bool> SendVerificationCodeAsync(string phoneNumber, string code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Model\CloudGaming.DtoModel\CloudGaming.DtoModel.csproj" />
|
||||||
<ProjectReference Include="..\..\Model\CloudGaming.Model\CloudGaming.Model.csproj" />
|
<ProjectReference Include="..\..\Model\CloudGaming.Model\CloudGaming.Model.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
// See https://aka.ms/new-console-template for more information
|
// See https://aka.ms/new-console-template for more information
|
||||||
using CloudGaming.Model.DbSqlServer.Db_Phone;
|
using CloudGaming.Model.DbSqlServer.Db_Phone;
|
||||||
using CloudGaming.Model.DbSqlServer.Db_User;
|
using CloudGaming.Model.DbSqlServer.Db_User;
|
||||||
|
using CloudGaming.DtoModel.Account.Login;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
//var jsopn = JsonConvert.SerializeObject(new PhoneLoginParams());
|
||||||
//Server=192.168.1.17;Database=CloudGamingUser;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;
|
//Server=192.168.1.17;Database=CloudGamingUser;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;
|
||||||
var optionsBuilder = new DbContextOptionsBuilder<CloudGamingPhoneContext>();
|
var optionsBuilder = new DbContextOptionsBuilder<CloudGamingPhoneContext>();
|
||||||
var option = optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;").Options;
|
var option = optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingPhone;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;").Options;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.DtoModel.Account
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 登录返回的数据
|
||||||
|
/// </summary>
|
||||||
|
public class AccountLogInResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// token
|
||||||
|
/// </summary>
|
||||||
|
public string Token { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 昵称
|
||||||
|
/// </summary>
|
||||||
|
public string NickName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户id
|
||||||
|
/// </summary>
|
||||||
|
public int UserId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.DtoModel.Account.Login
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class AccountUserLoginInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 登录使用的tokens
|
||||||
|
/// </summary>
|
||||||
|
public string Token { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 过期时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime ExpirationDate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.DtoModel.Account.Login
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class BaseLoginParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 设备号
|
||||||
|
/// </summary>
|
||||||
|
public string? DeviceNumber { get; set; } = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.DtoModel.Account.Login;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证码登录
|
||||||
|
/// </summary>
|
||||||
|
public class PhoneLoginParams : BaseLoginParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 手机号码
|
||||||
|
/// </summary>
|
||||||
|
public string PhoneNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证码
|
||||||
|
/// </summary>
|
||||||
|
public string VerificationCode { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.DtoModel.Account
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class PhoneNumberRequest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 手机号码
|
||||||
|
/// </summary>
|
||||||
|
public string? PhoneNumber { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CloudGaming.DtoModel.Account
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用户数据属性
|
||||||
|
/// </summary>
|
||||||
|
public enum UserDataPropertyEnum
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 手机号
|
||||||
|
/// </summary>
|
||||||
|
PhoneNum = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// 邮箱
|
||||||
|
/// </summary>
|
||||||
|
Email = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,7 +31,10 @@ public class AppConfigDto
|
||||||
//[Images]
|
//[Images]
|
||||||
//public int OpenImage { get; set; }
|
//public int OpenImage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加密key
|
||||||
|
/// </summary>
|
||||||
|
public string SignKey { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
|
using CloudGaming.DtoModel.Game;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using static CloudGaming.DtoModel.Epg.EpgEnum;
|
||||||
|
|
||||||
namespace CloudGaming.DtoModel.Epg
|
namespace CloudGaming.DtoModel.Epg
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -29,6 +33,8 @@ namespace CloudGaming.DtoModel.Epg
|
||||||
public const string 一起玩 = "GameLive";
|
public const string 一起玩 = "GameLive";
|
||||||
public const string 一起看 = "VideoLive";
|
public const string 一起看 = "VideoLive";
|
||||||
public const string 游玩历史 = "MyHistory";
|
public const string 游玩历史 = "MyHistory";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -37,28 +43,32 @@ namespace CloudGaming.DtoModel.Epg
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ImageResStyle
|
public enum ImageResStyle
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 游戏-尊享推荐, 竖版 Image_Shu
|
|
||||||
/// </summary>
|
|
||||||
Game尊享推荐_312_420 = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 游戏-推荐位大图 Image_Block2
|
/// 小LOGO,适用于首页最近推出、游玩历史、游戏详情页logo。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Game推荐位大图_984_520 = 2,
|
小LOGO = 1,
|
||||||
/// <summary>
|
|
||||||
/// 游戏-精选推荐, 豆腐块图 Image_Block
|
|
||||||
/// </summary>
|
|
||||||
Game精选推荐_474_300 = 3,
|
|
||||||
/// <summary>
|
|
||||||
/// 游戏-最近推出, 竖版2,高一点的(最近推出) Image_Shu2
|
|
||||||
/// </summary>
|
|
||||||
Game最近推出_360_548 = 4,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 游戏-游戏库, 正方形 (GameImageId)
|
/// 中等LOGO,适用于首页体育竞技、搜索。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Game游戏库_180_180 = 5,
|
中等LOGO = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// 大LOGO,适用于首页爆款热门。
|
||||||
|
/// </summary>
|
||||||
|
大LOGO = 3,
|
||||||
|
/// <summary>
|
||||||
|
/// 竖形图,适用于首页蘑菇必玩、首页热血格斗、游戏详情页游戏推荐。
|
||||||
|
/// </summary>
|
||||||
|
竖形图 = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏库,适用于游戏库。
|
||||||
|
/// </summary>
|
||||||
|
游戏库 = 5,
|
||||||
|
|
||||||
|
系类 = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -91,4 +101,28 @@ namespace CloudGaming.DtoModel.Epg
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class EpgEnumExtend
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取对应样式的图片
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imageResStyle"></param>
|
||||||
|
/// <param name="gameInfo"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int GetImageResStyle(this ImageResStyle imageResStyle, GameInfo? gameInfo)
|
||||||
|
{
|
||||||
|
return imageResStyle switch
|
||||||
|
{
|
||||||
|
var style when style == ImageResStyle.小LOGO => gameInfo?.ImageIconId,
|
||||||
|
var style when style == ImageResStyle.中等LOGO => gameInfo?.ImageId_ShouSuo,
|
||||||
|
var style when style == ImageResStyle.大LOGO => gameInfo?.ImageId_RM,
|
||||||
|
var style when style == ImageResStyle.游戏库 => gameInfo?.ImageId_YXK,
|
||||||
|
var style when style == ImageResStyle.竖形图 => gameInfo?.ImageId_TJ,
|
||||||
|
var style when style == ImageResStyle.系类 => gameInfo?.ImageId_FK,
|
||||||
|
_ => 0
|
||||||
|
} ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,10 @@ namespace CloudGaming.DtoModel.Epg
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Images]
|
[Images]
|
||||||
public int ImageUrl { get; set; }
|
public int ImageUrl { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 图片样式
|
||||||
|
/// </summary>
|
||||||
|
public int ImageResStyle { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 角标图片信息
|
/// 角标图片信息
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ namespace CloudGaming.DtoModel.Game
|
||||||
public virtual string? GameIntroduce { get; set; }
|
public virtual string? GameIntroduce { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 游戏分享
|
/// 游戏分享者
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GameShare { get; set; }
|
public string GameShare { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
using CloudGaming.DtoModel.Epg;
|
||||||
|
|
||||||
using HuanMeng.DotNetCore.AttributeExtend;
|
using HuanMeng.DotNetCore.AttributeExtend;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -6,8 +8,10 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace CloudGaming.DtoModel.Game
|
using static CloudGaming.DtoModel.Epg.EpgEnum;
|
||||||
{
|
|
||||||
|
namespace CloudGaming.DtoModel.Game;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 游戏列表数据
|
/// 游戏列表数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -19,13 +23,14 @@ namespace CloudGaming.DtoModel.Game
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="gameInfo"></param>
|
/// <param name="gameInfo"></param>
|
||||||
public GameListDto(GameInfo gameInfo)
|
public GameListDto(GameInfo gameInfo, ImageResStyle imageResStyle)
|
||||||
{
|
{
|
||||||
if (gameInfo != null)
|
if (gameInfo != null)
|
||||||
{
|
{
|
||||||
this.GameName = gameInfo.GameName;
|
this.GameName = gameInfo.GameName;
|
||||||
this.GameId = gameInfo.GameId;
|
this.GameId = gameInfo.GameId;
|
||||||
this.GameIconImage = gameInfo.GameImageId;
|
this.GameIconImage = imageResStyle.GetImageResStyle(gameInfo);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -44,5 +49,18 @@ namespace CloudGaming.DtoModel.Game
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Images]
|
[Images]
|
||||||
public int GameIconImage { get; set; }
|
public int GameIconImage { get; set; }
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is GameListDto other)
|
||||||
|
{
|
||||||
|
return string.Equals(this.GameId, other.GameId, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return this.GameId != null ? this.GameId.GetHashCode() : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,16 @@ public partial class CloudGamingCBTContext : DbContext
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual DbSet<T_App_Image> T_App_Image { get; set; }
|
public virtual DbSet<T_App_Image> T_App_Image { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送短信日志表
|
||||||
|
/// </summary>
|
||||||
|
public virtual DbSet<T_Sms_Log> T_Sms_Log { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户登录日志表
|
||||||
|
/// </summary>
|
||||||
|
public virtual DbSet<T_User_Login_Log> T_User_Login_Log { get; set; }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
=> optionsBuilder.UseSqlServer("Server=192.168.1.17;Database=CloudGamingCBT;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
|
=> optionsBuilder.UseSqlServer("Server=192.168.1.17;Database=CloudGamingCBT;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
|
||||||
|
|
||||||
|
|
@ -139,6 +149,62 @@ public partial class CloudGamingCBTContext : DbContext
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<T_Sms_Log>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id).HasName("PK__T_Sms_Lo__3214EC07AD037B45");
|
||||||
|
|
||||||
|
entity.ToTable(tb => tb.HasComment("发送短信日志表"));
|
||||||
|
|
||||||
|
entity.HasIndex(e => new { e.SendTimeDay, e.PhoneNumber }, "T_Sms_Log_SendTimeDay_index_desc").IsDescending(true, false);
|
||||||
|
|
||||||
|
entity.Property(e => e.Id).HasComment("主键");
|
||||||
|
entity.Property(e => e.ErrorMessage)
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasComment("错误信息(如果发送失败)");
|
||||||
|
entity.Property(e => e.PhoneNumber)
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasComment("手机号码");
|
||||||
|
entity.Property(e => e.SendStatus).HasComment("发送状态(0: 失败, 1: 成功)\r\n");
|
||||||
|
entity.Property(e => e.SendTime)
|
||||||
|
.HasComment("发送时间")
|
||||||
|
.HasColumnType("datetime");
|
||||||
|
entity.Property(e => e.SendTimeDay).HasComment("发送时间,天");
|
||||||
|
entity.Property(e => e.VerificationCode)
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasComment("发送的验证码");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<T_User_Login_Log>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id).HasName("PK__T_User_L__3214EC07C2A990A7");
|
||||||
|
|
||||||
|
entity.ToTable(tb => tb.HasComment("用户登录日志表"));
|
||||||
|
|
||||||
|
entity.Property(e => e.Id).HasComment("主键");
|
||||||
|
entity.Property(e => e.AppVersion)
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasComment("app版本");
|
||||||
|
entity.Property(e => e.Channel)
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasComment("渠道");
|
||||||
|
entity.Property(e => e.CreateTime)
|
||||||
|
.HasComment("创建时间")
|
||||||
|
.HasColumnType("datetime");
|
||||||
|
entity.Property(e => e.CreateTimeDay).HasComment("创建时间天");
|
||||||
|
entity.Property(e => e.CreateTimeHour).HasComment("创建时间小时");
|
||||||
|
entity.Property(e => e.Ip)
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasComment("登录IP");
|
||||||
|
entity.Property(e => e.IsNew).HasComment("是否新增用户");
|
||||||
|
entity.Property(e => e.LoginType).HasComment("登录方式");
|
||||||
|
entity.Property(e => e.PlatformType)
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasComment("登录平台");
|
||||||
|
entity.Property(e => e.UserId).HasComment("用户Id");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
OnModelCreatingPartial(modelBuilder);
|
OnModelCreatingPartial(modelBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CloudGaming.GameModel.Db.Db_Ext;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送短信日志表
|
||||||
|
/// </summary>
|
||||||
|
public partial class T_Sms_Log
|
||||||
|
{
|
||||||
|
public T_Sms_Log() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主键
|
||||||
|
/// </summary>
|
||||||
|
public virtual int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 手机号码
|
||||||
|
/// </summary>
|
||||||
|
public virtual string PhoneNumber { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送的验证码
|
||||||
|
/// </summary>
|
||||||
|
public virtual string VerificationCode { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送状态(0: 失败, 1: 成功)
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public virtual int SendStatus { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送时间
|
||||||
|
/// </summary>
|
||||||
|
public virtual DateTime SendTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送时间,天
|
||||||
|
/// </summary>
|
||||||
|
public virtual int SendTimeDay { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 错误信息(如果发送失败)
|
||||||
|
/// </summary>
|
||||||
|
public virtual string? ErrorMessage { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CloudGaming.GameModel.Db.Db_Ext;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户登录日志表
|
||||||
|
/// </summary>
|
||||||
|
public partial class T_User_Login_Log
|
||||||
|
{
|
||||||
|
public T_User_Login_Log() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主键
|
||||||
|
/// </summary>
|
||||||
|
public virtual int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户Id
|
||||||
|
/// </summary>
|
||||||
|
public virtual int UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登录IP
|
||||||
|
/// </summary>
|
||||||
|
public virtual string Ip { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否新增用户
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool IsNew { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登录方式
|
||||||
|
/// </summary>
|
||||||
|
public virtual int LoginType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登录平台
|
||||||
|
/// </summary>
|
||||||
|
public virtual string PlatformType { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// app版本
|
||||||
|
/// </summary>
|
||||||
|
public virtual string AppVersion { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建时间天
|
||||||
|
/// </summary>
|
||||||
|
public virtual int CreateTimeDay { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建时间小时
|
||||||
|
/// </summary>
|
||||||
|
public virtual int CreateTimeHour { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建时间
|
||||||
|
/// </summary>
|
||||||
|
public virtual DateTime CreateTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渠道
|
||||||
|
/// </summary>
|
||||||
|
public virtual string Channel { get; set; } = null!;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace HuanMeng.DotNetCore.AttributeExtend
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 执行成功后返回的消息
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class MessageAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 执行成功后返回的消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
public MessageAttribute(string message)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Message { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,8 +2,8 @@ using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
namespace HuanMeng.DotNetCore.Base
|
namespace HuanMeng.DotNetCore.Base;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 基本数据库操作,需要安装 Microsoft.EntityFrameworkCore 和 Microsoft.EntityFrameworkCore.Relational
|
/// 基本数据库操作,需要安装 Microsoft.EntityFrameworkCore 和 Microsoft.EntityFrameworkCore.Relational
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -160,4 +160,3 @@ namespace HuanMeng.DotNetCore.Base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
namespace XLib.DotNetCore.Base
|
namespace XLib.DotNetCore.Base;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 接口和服务调用通用响应接口
|
/// 接口和服务调用通用响应接口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -20,4 +20,3 @@
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Message { get; set; }
|
string Message { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
115
src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/MessageBox.cs
Normal file
115
src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/MessageBox.cs
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
using Microsoft.AspNetCore.SignalR.Protocol;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace HuanMeng.DotNetCore.Base
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class MessageBox : Exception
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
public MessageBox(ResonseCode code)
|
||||||
|
{
|
||||||
|
Code = (int)code;
|
||||||
|
Message = "";
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
public MessageBox(ResonseCode code, string message, object? data = null)
|
||||||
|
{
|
||||||
|
Code = (int)code;
|
||||||
|
Message = message;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
public MessageBox(int code, string message, object? data = null)
|
||||||
|
{
|
||||||
|
Code = code;
|
||||||
|
Message = message;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
public MessageBox(string message, object? data = null)
|
||||||
|
{
|
||||||
|
Code = 0;
|
||||||
|
Message = message;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 功能执行返回代码
|
||||||
|
/// </summary>
|
||||||
|
public int Code { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 消息
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据
|
||||||
|
/// </summary>
|
||||||
|
public object? Data { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建错误消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static MessageBox ErrorShow(string message) => new(ResonseCode.Error, message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static MessageBox Show(string message) => new(message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建消息 输出消息和数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
public static MessageBox Show(string message, object data) => new(message, data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建消息 输出消息和数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static MessageBox Show(int code, string message, object? data = null) => new(code, message, data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建消息 输出消息和数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static MessageBox Show(ResonseCode code, string message, object? data = null) => new(code, message, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
namespace HuanMeng.DotNetCore.Base
|
namespace HuanMeng.DotNetCore.Base;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 响应编码参考,实际的项目使用可以自行定义
|
/// 响应编码参考,实际的项目使用可以自行定义
|
||||||
/// 基本规则:
|
/// 基本规则:
|
||||||
|
|
@ -21,11 +21,20 @@ namespace HuanMeng.DotNetCore.Base
|
||||||
/// 用户验证失败
|
/// 用户验证失败
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Unauthorized = 401,
|
Unauthorized = 401,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 重复请求
|
/// 重复请求
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ManyRequests = 429,
|
ManyRequests = 429,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 手机号异常
|
||||||
|
/// </summary>
|
||||||
|
PhoneNumberException = 530,
|
||||||
|
/// <summary>
|
||||||
|
/// 当日手机号发送已到达上限
|
||||||
|
/// </summary>
|
||||||
|
PhoneNumberMaxException = 531,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 正在处理中
|
/// 正在处理中
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -45,9 +54,13 @@ namespace HuanMeng.DotNetCore.Base
|
||||||
/// </summary>
|
/// </summary>
|
||||||
NotFoundRecord = -3,
|
NotFoundRecord = -3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据为null
|
||||||
|
/// </summary>
|
||||||
|
NullOrEmpty = -4,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 成功
|
/// 成功
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Success = 0,
|
Success = 0,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace HuanMeng.DotNetCore.MiddlewareExtend
|
||||||
return builder
|
return builder
|
||||||
.UseExceptionMiddleware()
|
.UseExceptionMiddleware()
|
||||||
.UseExecutionTimeMiddleware()
|
.UseExecutionTimeMiddleware()
|
||||||
.UseSignMiddleware()
|
//.SignBaseMiddleware()
|
||||||
;
|
;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -46,9 +46,9 @@ namespace HuanMeng.DotNetCore.MiddlewareExtend
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="builder"></param>
|
/// <param name="builder"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IApplicationBuilder UseSignMiddleware(this IApplicationBuilder builder)
|
public static IApplicationBuilder SignBaseMiddleware(this IApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<SignMiddleware>();
|
return builder.UseMiddleware<SignBaseMiddleware>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@ namespace HuanMeng.DotNetCore.MiddlewareExtend
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 参数请求加密验证
|
/// 参数请求加密验证
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SignMiddleware
|
public class SignBaseMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
private const string FixedString = "cccc"; // 固定字符串
|
private const string FixedString = "cccc"; // 固定字符串
|
||||||
public SignMiddleware(RequestDelegate next)
|
public SignBaseMiddleware(RequestDelegate next)
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
}
|
}
|
||||||
|
|
@ -84,6 +84,33 @@ namespace HuanMeng.DotNetCore.Redis
|
||||||
// 将 RedisValue 转换为 T 类型
|
// 将 RedisValue 转换为 T 类型
|
||||||
return JsonConvert.DeserializeObject<T>(value);
|
return JsonConvert.DeserializeObject<T>(value);
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取一个key的对象
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<T?> StringGetAsync<T>(this IDatabase database, string key)
|
||||||
|
{
|
||||||
|
var value = await database.StringGetAsync(key);
|
||||||
|
// 检查值是否为空
|
||||||
|
if (!value.HasValue)
|
||||||
|
{
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
if (typeof(T).IsPrimitive || typeof(T) == typeof(string) || typeof(T) == typeof(decimal))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (T)Convert.ChangeType(value.ToString(), typeof(T));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return default; // 或抛出异常,取决于业务需求
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 将 RedisValue 转换为 T 类型
|
||||||
|
return JsonConvert.DeserializeObject<T>(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
using HuanMeng.DotNetCore.AttributeExtend;
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace HuanMeng.DotNetCore.Utility;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// object转数据字典
|
||||||
|
/// </summary>
|
||||||
|
public static class ObjectExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用于存储每种类型的属性访问器数组的线程安全缓存。
|
||||||
|
/// </summary>
|
||||||
|
private static readonly ConcurrentDictionary<Type, PropertyAccessor[]> PropertyCache = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 缓存每个属性是否具有 ImagesAttribute 特性。
|
||||||
|
/// </summary>
|
||||||
|
public static readonly ConcurrentDictionary<PropertyInfo, bool> _PropertyCache = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断对象是否为原始类型或字符串类型。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">要检查的对象。</param>
|
||||||
|
/// <returns>如果对象是原始类型或字符串,返回 true;否则返回 false。</returns>
|
||||||
|
public static bool IsPrimitiveType(object obj) =>
|
||||||
|
obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断对象是否为集合类型(不包括字符串)。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">要检查的对象。</param>
|
||||||
|
/// <returns>如果对象是集合类型(但不是字符串),返回 true;否则返回 false。</returns>
|
||||||
|
public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据对象类型,将对象转换为字典或列表格式,支持可选的路径前缀。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">要转换的对象。</param>
|
||||||
|
/// <param name="prefix">属性路径的可选前缀。</param>
|
||||||
|
/// <returns>对象的字典或列表表示形式。</returns>
|
||||||
|
public static object ToDictionaryOrList(this object obj, string prefix = "", Func<int, string>? imageFunc = null)
|
||||||
|
{
|
||||||
|
if (obj == null) return null;
|
||||||
|
|
||||||
|
return obj switch
|
||||||
|
{
|
||||||
|
_ when IsPrimitiveType(obj) => obj,
|
||||||
|
IEnumerable enumerable => TransformCollection(enumerable, prefix, imageFunc),
|
||||||
|
_ => TransformObject(obj, prefix, imageFunc)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将集合对象转换为包含转换项的列表,每个项保留其路径前缀。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enumerable">要转换的集合。</param>
|
||||||
|
/// <param name="prefix">集合中每个属性路径的前缀。</param>
|
||||||
|
/// <returns>转换后的项列表。</returns>
|
||||||
|
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);
|
||||||
|
int index = 0;
|
||||||
|
foreach (var item in enumerable)
|
||||||
|
{
|
||||||
|
list.Add(ToDictionaryOrList(item, $"{prefix}.[{index}]", imageFunc)); // 为集合中每个项添加路径
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将对象的属性转换为带有路径前缀的字典,并应用前缀规则。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">要转换的对象。</param>
|
||||||
|
/// <param name="prefix">每个属性路径的前缀。</param>
|
||||||
|
/// <returns>包含属性名和属性值的字典。</returns>
|
||||||
|
private static Dictionary<string, object> TransformObject(object obj, string prefix = "", Func<int, string>? imageFunc = null)
|
||||||
|
{
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var type = obj.GetType();
|
||||||
|
var accessors = PropertyCache.GetOrAdd(type, CreatePropertyAccessors);
|
||||||
|
var keyValuePairs = new Dictionary<string, object>(accessors.Length);
|
||||||
|
|
||||||
|
foreach (var accessor in accessors)
|
||||||
|
{
|
||||||
|
var propertyPath = $"{prefix}.{accessor.PropertyName}"; // 构建完整的属性路径
|
||||||
|
|
||||||
|
// 使用访问器获取属性值
|
||||||
|
var propertyValue = accessor.Getter(obj);
|
||||||
|
|
||||||
|
// 如果属性是字符串,在其值前添加 "test"
|
||||||
|
if (propertyValue is string stringValue)
|
||||||
|
{
|
||||||
|
keyValuePairs[accessor.PropertyName] = stringValue;
|
||||||
|
//Console.WriteLine(propertyPath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果属性具有 ImagesAttribute,在其值前添加 "image"
|
||||||
|
// 否则,如果是集合类型,则递归转换
|
||||||
|
keyValuePairs[accessor.PropertyName] = accessor.HasImagesAttribute
|
||||||
|
? imageFunc?.Invoke((int)propertyValue) ?? ""
|
||||||
|
: ToDictionaryOrList(propertyValue, propertyPath, imageFunc); // IsCollectionType(propertyValue) ?: propertyValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyValuePairs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为给定类型创建属性访问器数组。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">要创建属性访问器的类型。</param>
|
||||||
|
/// <returns>属性访问器数组。</returns>
|
||||||
|
private static PropertyAccessor[] CreatePropertyAccessors(Type type)
|
||||||
|
{
|
||||||
|
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
return properties.Select(property =>
|
||||||
|
{
|
||||||
|
// 创建用于访问属性值的委托
|
||||||
|
var getter = CreatePropertyGetter(type, property);
|
||||||
|
// 检查属性是否具有 ImagesAttribute,并将结果存储在缓存中
|
||||||
|
var isImagesAttribute = _PropertyCache.GetOrAdd(property, p => p.GetCustomAttribute<ImagesAttribute>() != null);
|
||||||
|
return new PropertyAccessor(property.Name, getter, isImagesAttribute);
|
||||||
|
}).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static Func<object, object> CreatePropertyGetter(Type type, PropertyInfo property)
|
||||||
|
{
|
||||||
|
var parameter = Expression.Parameter(typeof(object), "obj");
|
||||||
|
var castParameter = Expression.Convert(parameter, type);
|
||||||
|
var propertyAccess = Expression.Property(castParameter, property);
|
||||||
|
var convertPropertyAccess = Expression.Convert(propertyAccess, typeof(object));
|
||||||
|
return Expression.Lambda<Func<object, object>>(convertPropertyAccess, parameter).Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PropertyAccessor
|
||||||
|
{
|
||||||
|
public string PropertyName { get; }
|
||||||
|
public Func<object, object> Getter { get; }
|
||||||
|
public bool HasImagesAttribute { get; }
|
||||||
|
|
||||||
|
public PropertyAccessor(string propertyName, Func<object, object> getter, bool hasImagesAttribute)
|
||||||
|
{
|
||||||
|
PropertyName = propertyName;
|
||||||
|
Getter = getter;
|
||||||
|
HasImagesAttribute = hasImagesAttribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,157 +11,6 @@ using Microsoft.IdentityModel.Tokens;
|
||||||
namespace HuanMeng.DotNetCore.Utility;
|
namespace HuanMeng.DotNetCore.Utility;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class ObjectExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 用于存储每种类型的属性访问器数组的线程安全缓存。
|
|
||||||
/// </summary>
|
|
||||||
private static readonly ConcurrentDictionary<Type, PropertyAccessor[]> PropertyCache = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 缓存每个属性是否具有 ImagesAttribute 特性。
|
|
||||||
/// </summary>
|
|
||||||
public static readonly ConcurrentDictionary<PropertyInfo, bool> _PropertyCache = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 判断对象是否为原始类型或字符串类型。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">要检查的对象。</param>
|
|
||||||
/// <returns>如果对象是原始类型或字符串,返回 true;否则返回 false。</returns>
|
|
||||||
public static bool IsPrimitiveType(object obj) =>
|
|
||||||
obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 判断对象是否为集合类型(不包括字符串)。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">要检查的对象。</param>
|
|
||||||
/// <returns>如果对象是集合类型(但不是字符串),返回 true;否则返回 false。</returns>
|
|
||||||
public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 根据对象类型,将对象转换为字典或列表格式,支持可选的路径前缀。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">要转换的对象。</param>
|
|
||||||
/// <param name="prefix">属性路径的可选前缀。</param>
|
|
||||||
/// <returns>对象的字典或列表表示形式。</returns>
|
|
||||||
public static object ToDictionaryOrList(this object obj, string prefix = "", Func<int, string>? imageFunc = null)
|
|
||||||
{
|
|
||||||
if (obj == null) return null;
|
|
||||||
|
|
||||||
return obj switch
|
|
||||||
{
|
|
||||||
_ when IsPrimitiveType(obj) => obj,
|
|
||||||
IEnumerable enumerable => TransformCollection(enumerable, prefix, imageFunc),
|
|
||||||
_ => TransformObject(obj, prefix, imageFunc)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将集合对象转换为包含转换项的列表,每个项保留其路径前缀。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="enumerable">要转换的集合。</param>
|
|
||||||
/// <param name="prefix">集合中每个属性路径的前缀。</param>
|
|
||||||
/// <returns>转换后的项列表。</returns>
|
|
||||||
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);
|
|
||||||
int index = 0;
|
|
||||||
foreach (var item in enumerable)
|
|
||||||
{
|
|
||||||
list.Add(ToDictionaryOrList(item, $"{prefix}.[{index}]", imageFunc)); // 为集合中每个项添加路径
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将对象的属性转换为带有路径前缀的字典,并应用前缀规则。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">要转换的对象。</param>
|
|
||||||
/// <param name="prefix">每个属性路径的前缀。</param>
|
|
||||||
/// <returns>包含属性名和属性值的字典。</returns>
|
|
||||||
private static Dictionary<string, object> TransformObject(object obj, string prefix = "", Func<int, string>? imageFunc = null)
|
|
||||||
{
|
|
||||||
if (obj == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var type = obj.GetType();
|
|
||||||
var accessors = PropertyCache.GetOrAdd(type, CreatePropertyAccessors);
|
|
||||||
var keyValuePairs = new Dictionary<string, object>(accessors.Length);
|
|
||||||
|
|
||||||
foreach (var accessor in accessors)
|
|
||||||
{
|
|
||||||
var propertyPath = $"{prefix}.{accessor.PropertyName}"; // 构建完整的属性路径
|
|
||||||
|
|
||||||
// 使用访问器获取属性值
|
|
||||||
var propertyValue = accessor.Getter(obj);
|
|
||||||
|
|
||||||
// 如果属性是字符串,在其值前添加 "test"
|
|
||||||
if (propertyValue is string stringValue)
|
|
||||||
{
|
|
||||||
keyValuePairs[accessor.PropertyName] = stringValue;
|
|
||||||
//Console.WriteLine(propertyPath);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果属性具有 ImagesAttribute,在其值前添加 "image"
|
|
||||||
// 否则,如果是集合类型,则递归转换
|
|
||||||
keyValuePairs[accessor.PropertyName] = accessor.HasImagesAttribute
|
|
||||||
? imageFunc?.Invoke((int)propertyValue) ?? ""
|
|
||||||
: ToDictionaryOrList(propertyValue, propertyPath, imageFunc); // IsCollectionType(propertyValue) ?: propertyValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyValuePairs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 为给定类型创建属性访问器数组。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">要创建属性访问器的类型。</param>
|
|
||||||
/// <returns>属性访问器数组。</returns>
|
|
||||||
private static PropertyAccessor[] CreatePropertyAccessors(Type type)
|
|
||||||
{
|
|
||||||
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
|
||||||
return properties.Select(property =>
|
|
||||||
{
|
|
||||||
// 创建用于访问属性值的委托
|
|
||||||
var getter = CreatePropertyGetter(type, property);
|
|
||||||
// 检查属性是否具有 ImagesAttribute,并将结果存储在缓存中
|
|
||||||
var isImagesAttribute = _PropertyCache.GetOrAdd(property, p => p.GetCustomAttribute<ImagesAttribute>() != null);
|
|
||||||
return new PropertyAccessor(property.Name, getter, isImagesAttribute);
|
|
||||||
}).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static Func<object, object> CreatePropertyGetter(Type type, PropertyInfo property)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "obj");
|
|
||||||
var castParameter = Expression.Convert(parameter, type);
|
|
||||||
var propertyAccess = Expression.Property(castParameter, property);
|
|
||||||
var convertPropertyAccess = Expression.Convert(propertyAccess, typeof(object));
|
|
||||||
return Expression.Lambda<Func<object, object>>(convertPropertyAccess, parameter).Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PropertyAccessor
|
|
||||||
{
|
|
||||||
public string PropertyName { get; }
|
|
||||||
public Func<object, object> Getter { get; }
|
|
||||||
public bool HasImagesAttribute { get; }
|
|
||||||
|
|
||||||
public PropertyAccessor(string propertyName, Func<object, object> getter, bool hasImagesAttribute)
|
|
||||||
{
|
|
||||||
PropertyName = propertyName;
|
|
||||||
Getter = getter;
|
|
||||||
HasImagesAttribute = hasImagesAttribute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete]
|
[Obsolete]
|
||||||
public static class ObjectExtensions11
|
public static class ObjectExtensions11
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ public class PhoneNumberValidator
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return phoneNumberRegex.IsMatch(input);
|
return phoneNumberRegex.IsMatch(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user