diff --git a/src/CloudGaming/Api/CloudGaming.Api/Controllers/AccountController.cs b/src/CloudGaming/Api/CloudGaming.Api/Controllers/AccountController.cs
new file mode 100644
index 0000000..723b9c8
--- /dev/null
+++ b/src/CloudGaming/Api/CloudGaming.Api/Controllers/AccountController.cs
@@ -0,0 +1,39 @@
+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.GameModel.Db.Db_Ext;
+
+using HuanMeng.DotNetCore.AttributeExtend;
+using HuanMeng.DotNetCore.Base;
+
+using HuanMeng.DotNetCore.Utility;
+
+using Microsoft.AspNetCore.Mvc;
+
+namespace CloudGaming.Api.Controllers;
+
+///
+/// 账号管理
+///
+public class AccountController : CloudGamingControllerBase
+{
+ public AccountController(IServiceProvider _serviceProvider) : base(_serviceProvider)
+ {
+ }
+
+ ///
+ /// 发送验证码
+ ///
+ ///
+ ///
+ [HttpPost]
+ [Message("发送成功")]
+ public async Task SendPhoneNumber([FromBody] PhoneNumberRequest phoneNumber)
+ {
+ AccountBLL account = new AccountBLL(ServiceProvider);
+ return await account.SendPhoneNumber(phoneNumber.PhoneNumber);
+ }
+}
diff --git a/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs b/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs
index 907168e..eafa2b7 100644
--- a/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs
+++ b/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs
@@ -3,6 +3,7 @@ using CloudGaming.Code.Config;
using CloudGaming.DtoModel;
using CloudGaming.GameModel.Db.Db_Ext;
+using HuanMeng.DotNetCore.AttributeExtend;
using HuanMeng.DotNetCore.Base;
using Microsoft.AspNetCore.Mvc;
@@ -24,6 +25,7 @@ namespace CloudGaming.Api.Controllers
///
///
[HttpGet]
+ [Message("发送成功")]
public async Task GetAppConfigAsync()
{
AppConfigBLL appConfigBLL = new AppConfigBLL(ServiceProvider);
diff --git a/src/CloudGaming/Api/CloudGaming.Api/Program.cs b/src/CloudGaming/Api/CloudGaming.Api/Program.cs
index 4265974..0bbf42c 100644
--- a/src/CloudGaming/Api/CloudGaming.Api/Program.cs
+++ b/src/CloudGaming/Api/CloudGaming.Api/Program.cs
@@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Options;
using CloudGaming.GameModel.Db.Db_Ext;
using CloudGaming.Code.MiddlewareExtend;
+using CloudGaming.Code.Filter;
var builder = WebApplication.CreateBuilder(args);
#region 日志
// Add services to the container.
@@ -51,6 +52,7 @@ builder.Services.AddControllers(options =>
{
// 添加自定义的 ResultFilter 到全局过滤器中
options.Filters.Add();
+ options.Filters.Add();
})
.AddNewtonsoftJson(options =>
{
@@ -68,7 +70,9 @@ builder.Services.AddControllers(options =>
//options.SerializerSettings.Converters.Add()
// 其他配置...
});
-builder.Services.AddSingleton();
+//CustomResultFilter
+//builder.Services.AddSingleton();
+
#endregion
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
diff --git a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs
new file mode 100644
index 0000000..d7ec67e
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs
@@ -0,0 +1,76 @@
+using CloudGaming.Code.Sms;
+
+using HuanMeng.DotNetCore.Utility;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.Code.Account
+{
+ ///
+ /// 账号操作
+ ///
+ public class AccountBLL : CloudGamingBase
+ {
+ public AccountBLL(IServiceProvider serviceProvider) : base(serviceProvider)
+ {
+
+ }
+
+ ///
+ /// 发送手机号码
+ ///
+ ///
+ ///
+ public async Task SendPhoneNumber(string PhoneNumber)
+ {
+ if (!PhoneNumberValidator.IsPhoneNumber(PhoneNumber))
+ {
+ throw new MessageException(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 new MessageException(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();
+ return isSend;
+ }
+ }
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AliyunOssConfig.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AliyunConfig.cs
similarity index 74%
rename from src/CloudGaming/Code/CloudGaming.Code/AppExtend/AliyunOssConfig.cs
rename to src/CloudGaming/Code/CloudGaming.Code/AppExtend/AliyunConfig.cs
index 5b5afd5..25f87fa 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AliyunOssConfig.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AliyunConfig.cs
@@ -6,7 +6,10 @@ using System.Threading.Tasks;
namespace CloudGaming.Code.AppExtend
{
- public class AliyunOssConfig
+ ///
+ /// 阿里云配置
+ ///
+ public class AliyunConfig
{
///
///
@@ -16,6 +19,8 @@ namespace CloudGaming.Code.AppExtend
/// 配置环境变量
///
public string AccessKeySecret { get; set; }
+
+ #region 阿里云OSS配置
///
/// 替换为Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
///
@@ -45,5 +50,18 @@ namespace CloudGaming.Code.AppExtend
return this.DomainName; //+ this.UploadPath
}
}
+
+ #endregion
+
+ ///
+ /// 短信签名名称
+ ///
+ public string SmsSignName { get; set; }
+
+ ///
+ /// string 短信模板配置
+ ///
+ public string SmsTemplateCode { get; set; }
+
}
}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs
index 301b08c..70d9c8f 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs
@@ -64,7 +64,7 @@ namespace CloudGaming.Code.AppExtend
///
/// oss阿里云配置
///
- public AliyunOssConfig AliyunConfig { get; set; }
+ public AliyunConfig AliyunConfig { get; set; }
///
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomObjectResultExecutor.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomObjectResultExecutor.cs
deleted file mode 100644
index 45d4d7d..0000000
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomObjectResultExecutor.cs
+++ /dev/null
@@ -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):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);
- }
- }
-}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomResultFilter.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomResultFilter.cs
deleted file mode 100644
index 42fd786..0000000
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomResultFilter.cs
+++ /dev/null
@@ -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
-{
- ///
- ///
- ///
- 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 keyValuePairs = new Dictionary();
- 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
public virtual DbSet T_App_Image { get; set; }
+ ///
+ /// 发送短信日志表
+ ///
+ public virtual DbSet T_Sms_Log { get; set; }
+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer("Server=192.168.1.17;Database=CloudGamingCBT;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
@@ -139,6 +144,32 @@ public partial class CloudGamingCBTContext : DbContext
});
+ modelBuilder.Entity(entity =>
+ {
+ entity.HasKey(e => e.Id).HasName("PK__T_Sms_Lo__3214EC07AD037B45");
+
+ entity.ToTable(tb => tb.HasComment("发送短信日志表"));
+
+ entity.HasIndex(e => e.SendTimeDay, "T_Sms_Log_SendTimeDay_index_desc").IsDescending();
+
+ entity.Property(e => e.Id).HasComment("主键");
+ entity.Property(e => e.ErrorMessage)
+ .HasMaxLength(255)
+ .HasComment("错误信息(如果发送失败)");
+ entity.Property(e => e.PhoneNumber)
+ .HasMaxLength(1)
+ .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(1)
+ .HasComment("发送的验证码");
+
+ });
+
OnModelCreatingPartial(modelBuilder);
}
diff --git a/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/T_Sms_Log.cs b/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/T_Sms_Log.cs
new file mode 100644
index 0000000..c63941c
--- /dev/null
+++ b/src/CloudGaming/Model/CloudGaming.GameModel/Db/Db_Ext/T_Sms_Log.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace CloudGaming.GameModel.Db.Db_Ext;
+
+///
+/// 发送短信日志表
+///
+public partial class T_Sms_Log
+{
+ public T_Sms_Log() { }
+
+ ///
+ /// 主键
+ ///
+ public virtual int Id { get; set; }
+
+ ///
+ /// 手机号码
+ ///
+ public virtual string PhoneNumber { get; set; } = null!;
+
+ ///
+ /// 发送的验证码
+ ///
+ public virtual string VerificationCode { get; set; } = null!;
+
+ ///
+ /// 发送状态(0: 失败, 1: 成功)
+ ///
+ ///
+ public virtual int SendStatus { get; set; }
+
+ ///
+ /// 发送时间
+ ///
+ public virtual DateTime SendTime { get; set; }
+
+ ///
+ /// 发送时间,天
+ ///
+ public virtual int SendTimeDay { get; set; }
+
+ ///
+ /// 错误信息(如果发送失败)
+ ///
+ public virtual string? ErrorMessage { get; set; }
+}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/AttributeExtend/MessageAttribute.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/AttributeExtend/MessageAttribute.cs
new file mode 100644
index 0000000..4800c70
--- /dev/null
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/AttributeExtend/MessageAttribute.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HuanMeng.DotNetCore.AttributeExtend
+{
+ ///
+ /// 执行成功后返回的消息
+ ///
+ [AttributeUsage(AttributeTargets.Method)]
+ public class MessageAttribute : Attribute
+ {
+ ///
+ /// 执行成功后返回的消息
+ ///
+ /// 消息内容
+ public MessageAttribute(string message)
+ {
+ Message = message;
+ }
+
+ public string Message { get; set; }
+ }
+}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/EfCoreDaoBase.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/EfCoreDaoBase.cs
index 3706142..971ad8c 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/EfCoreDaoBase.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/EfCoreDaoBase.cs
@@ -2,162 +2,161 @@ using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
-namespace HuanMeng.DotNetCore.Base
+namespace HuanMeng.DotNetCore.Base;
+
+///
+/// 基本数据库操作,需要安装 Microsoft.EntityFrameworkCore 和 Microsoft.EntityFrameworkCore.Relational
+///
+///
+public class EfCoreDaoBase where TDbContext : DbContext
{
+ private TDbContext _context;
+
+ public TDbContext Context => _context;
+
///
- /// 基本数据库操作,需要安装 Microsoft.EntityFrameworkCore 和 Microsoft.EntityFrameworkCore.Relational
+ /// 构造函数
///
- ///
- public class EfCoreDaoBase where TDbContext : DbContext
+ ///
+ public EfCoreDaoBase(TDbContext context)
{
- private TDbContext _context;
+ _context = context ?? throw new ArgumentNullException(nameof(context));
+ }
- public TDbContext Context => _context;
+ ///
+ /// 是否手动提交
+ ///
+ public bool IsManualSubmit { get; set; }
- ///
- /// 构造函数
- ///
- ///
- public EfCoreDaoBase(TDbContext context)
+ ///
+ /// SqlQueryRaw
+ ///
+ public async Task SqlQueryAsync(string sql, params object[] parameters) where T : class
+ {
+ return await Context.Database.SqlQueryRaw(sql, parameters).FirstOrDefaultAsync();
+ }
+
+ ///
+ /// SqlQueryList
+ ///
+ public async Task> SqlQueryListAsync(string sql, params object[] parameters)
+ {
+ return await Context.Database.SqlQueryRaw(sql, parameters).ToListAsync();
+ }
+
+ ///
+ /// ExecuteSql
+ ///
+ public async Task ExecuteSqlAsync(string sql, params object[] parameters)
+ {
+ var result = await Context.Database.ExecuteSqlRawAsync(sql, parameters);
+ await AutoSaveChangesAsync();
+ return result;
+ }
+
+ ///
+ /// 添加实体
+ ///
+ public async Task AddAsync(T entity) where T : class
+ {
+ Context.Set().Add(entity);
+ await AutoSaveChangesAsync();
+ }
+
+ ///
+ /// 批量添加实体
+ ///
+ public async Task AddRangeAsync(IEnumerable entities) where T : class
+ {
+ Context.Set().AddRange(entities);
+ await AutoSaveChangesAsync();
+ }
+
+ ///
+ /// 删除某个实体
+ ///
+ public async Task DeleteAsync(T entity) where T : class
+ {
+ Context.Entry(entity).State = EntityState.Deleted;
+ await AutoSaveChangesAsync();
+ }
+
+ ///
+ /// 更新实体
+ ///
+ public async Task UpdateAsync(T entity) where T : class
+ {
+ if (Context.Entry(entity).State == EntityState.Detached)
{
- _context = context ?? throw new ArgumentNullException(nameof(context));
+ Context.Set().Attach(entity);
+ Context.Entry(entity).State = EntityState.Modified;
}
+ await AutoSaveChangesAsync();
+ }
- ///
- /// 是否手动提交
- ///
- public bool IsManualSubmit { get; set; }
-
- ///
- /// SqlQueryRaw
- ///
- public async Task SqlQueryAsync(string sql, params object[] parameters) where T : class
+ ///
+ /// 清除上下文跟踪
+ ///
+ public void RemoveTracking(T entity) where T : class
+ {
+ if (Context.Entry(entity).State != EntityState.Detached)
{
- return await Context.Database.SqlQueryRaw(sql, parameters).FirstOrDefaultAsync();
+ Context.Entry(entity).State = EntityState.Detached;
}
+ }
- ///
- /// SqlQueryList
- ///
- public async Task> SqlQueryListAsync(string sql, params object[] parameters)
- {
- return await Context.Database.SqlQueryRaw(sql, parameters).ToListAsync();
- }
+ ///
+ /// 获取实体,根据主键
+ ///
+ public async Task GetModelAsync(params object[] keyValues) where T : class
+ {
+ return await Context.Set().FindAsync(keyValues);
+ }
- ///
- /// ExecuteSql
- ///
- public async Task ExecuteSqlAsync(string sql, params object[] parameters)
- {
- var result = await Context.Database.ExecuteSqlRawAsync(sql, parameters);
- await AutoSaveChangesAsync();
- return result;
- }
+ ///
+ /// 获取实体,根据条件
+ ///
+ public async Task GetModelAsync(Expression> where, bool isNoTracking = false) where T : class
+ {
+ var query = Context.Set().AsQueryable();
+ if (isNoTracking)
+ query = query.AsNoTracking();
+ return await query.FirstOrDefaultAsync(where);
+ }
- ///
- /// 添加实体
- ///
- public async Task AddAsync(T entity) where T : class
- {
- Context.Set().Add(entity);
- await AutoSaveChangesAsync();
- }
+ ///
+ /// 获取列表数据
+ ///
+ public IQueryable GetList(Expression> where, bool isNoTracking = false) where T : class
+ {
+ var query = Context.Set().Where(where);
+ return isNoTracking ? query.AsNoTracking() : query;
+ }
- ///
- /// 批量添加实体
- ///
- public async Task AddRangeAsync(IEnumerable entities) where T : class
- {
- Context.Set().AddRange(entities);
- await AutoSaveChangesAsync();
- }
+ ///
+ /// 获取记录数量
+ ///
+ public async Task GetCountAsync(Expression> where) where T : class
+ {
+ return await Context.Set().AsNoTracking().CountAsync(where);
+ }
- ///
- /// 删除某个实体
- ///
- public async Task DeleteAsync(T entity) where T : class
- {
- Context.Entry(entity).State = EntityState.Deleted;
- await AutoSaveChangesAsync();
- }
+ ///
+ /// 判断是否存在记录
+ ///
+ public async Task ExistsAsync(Expression> where) where T : class
+ {
+ return await Context.Set().AsNoTracking().AnyAsync(where);
+ }
- ///
- /// 更新实体
- ///
- public async Task UpdateAsync(T entity) where T : class
+ ///
+ /// 根据提交状态决定是否自动保存更改
+ ///
+ private async Task AutoSaveChangesAsync()
+ {
+ if (!IsManualSubmit)
{
- if (Context.Entry(entity).State == EntityState.Detached)
- {
- Context.Set().Attach(entity);
- Context.Entry(entity).State = EntityState.Modified;
- }
- await AutoSaveChangesAsync();
- }
-
- ///
- /// 清除上下文跟踪
- ///
- public void RemoveTracking(T entity) where T : class
- {
- if (Context.Entry(entity).State != EntityState.Detached)
- {
- Context.Entry(entity).State = EntityState.Detached;
- }
- }
-
- ///
- /// 获取实体,根据主键
- ///
- public async Task GetModelAsync(params object[] keyValues) where T : class
- {
- return await Context.Set().FindAsync(keyValues);
- }
-
- ///
- /// 获取实体,根据条件
- ///
- public async Task GetModelAsync(Expression> where, bool isNoTracking = false) where T : class
- {
- var query = Context.Set().AsQueryable();
- if (isNoTracking)
- query = query.AsNoTracking();
- return await query.FirstOrDefaultAsync(where);
- }
-
- ///
- /// 获取列表数据
- ///
- public IQueryable GetList(Expression> where, bool isNoTracking = false) where T : class
- {
- var query = Context.Set().Where(where);
- return isNoTracking ? query.AsNoTracking() : query;
- }
-
- ///
- /// 获取记录数量
- ///
- public async Task GetCountAsync(Expression> where) where T : class
- {
- return await Context.Set().AsNoTracking().CountAsync(where);
- }
-
- ///
- /// 判断是否存在记录
- ///
- public async Task ExistsAsync(Expression> where) where T : class
- {
- return await Context.Set().AsNoTracking().AnyAsync(where);
- }
-
- ///
- /// 根据提交状态决定是否自动保存更改
- ///
- private async Task AutoSaveChangesAsync()
- {
- if (!IsManualSubmit)
- {
- await Context.SaveChangesAsync();
- }
+ await Context.SaveChangesAsync();
}
}
}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/IResponse.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/IResponse.cs
index ecdf588..e4013fd 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/IResponse.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/IResponse.cs
@@ -1,23 +1,22 @@
-namespace XLib.DotNetCore.Base
+namespace XLib.DotNetCore.Base;
+
+///
+/// 接口和服务调用通用响应接口
+///
+public interface IResponse
{
+ /////
+ ///// Http状态码
+ /////
+ //HttpStatusCode StatusCode { get; set; }
+
///
- /// 接口和服务调用通用响应接口
+ /// 功能执行返回代码
///
- public interface IResponse
- {
- /////
- ///// Http状态码
- /////
- //HttpStatusCode StatusCode { get; set; }
+ int Code { get; set; }
- ///
- /// 功能执行返回代码
- ///
- int Code { get; set; }
-
- ///
- /// 消息
- ///
- string Message { get; set; }
- }
+ ///
+ /// 消息
+ ///
+ string Message { get; set; }
}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/MessageException.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/MessageException.cs
new file mode 100644
index 0000000..86f6879
--- /dev/null
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/MessageException.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HuanMeng.DotNetCore.Base
+{
+ ///
+ ///
+ ///
+ public class MessageException : Exception
+ {
+ ///
+ ///
+ ///
+ ///
+ public MessageException(ResonseCode code)
+ {
+ Code = (int)code;
+ Message = "";
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public MessageException(ResonseCode code, string message, object? data = null)
+ {
+ Code = (int)code;
+ Message = message;
+ Data = data;
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public MessageException(int code, string message, object? data = null)
+ {
+ Code = code;
+ Message = message;
+ Data = data;
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ public MessageException(string message, object? data = null)
+ {
+ Code = 0;
+ Message = message;
+ Data = data;
+ }
+ ///
+ /// 功能执行返回代码
+ ///
+ public int Code { get; set; }
+
+ ///
+ /// 消息
+ ///
+ public string Message { get; set; }
+
+ ///
+ /// 数据
+ ///
+ public object? Data { get; set; }
+ }
+}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/ResonseCode.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/ResonseCode.cs
index f5ffee2..c62a436 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/ResonseCode.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Base/ResonseCode.cs
@@ -1,53 +1,61 @@
-namespace HuanMeng.DotNetCore.Base
+namespace HuanMeng.DotNetCore.Base;
+
+///
+/// 响应编码参考,实际的项目使用可以自行定义
+/// 基本规则:
+/// 成功:大于等于0
+/// 失败:小于0
+///
+public enum ResonseCode
{
///
- /// 响应编码参考,实际的项目使用可以自行定义
- /// 基本规则:
- /// 成功:大于等于0
- /// 失败:小于0
+ /// Sign签名错误
///
- public enum ResonseCode
- {
- ///
- /// Sign签名错误
- ///
- SignError = -999,
- ///
- /// jwt用户签名错误
- ///
- TwtError = -998,
+ SignError = -999,
+ ///
+ /// jwt用户签名错误
+ ///
+ TwtError = -998,
- ///
- /// 用户验证失败
- ///
- Unauthorized = 401,
- ///
- /// 重复请求
- ///
- ManyRequests = 429,
+ ///
+ /// 用户验证失败
+ ///
+ Unauthorized = 401,
- ///
- /// 正在处理中
- ///
- Processing = 102,
- ///
- /// 通用错误
- ///
- Error = -1,
+ ///
+ /// 重复请求
+ ///
+ ManyRequests = 429,
- ///
- /// 参数错误
- ///
- ParamError = -2,
+ ///
+ /// 手机号异常
+ ///
+ PhoneNumberException = 530,
+ ///
+ /// 当日手机号发送已到达上限
+ ///
+ PhoneNumberMaxException = 531,
+ ///
+ /// 正在处理中
+ ///
+ Processing = 102,
+ ///
+ /// 通用错误
+ ///
+ Error = -1,
- ///
- /// 没找到数据记录
- ///
- NotFoundRecord = -3,
+ ///
+ /// 参数错误
+ ///
+ ParamError = -2,
- ///
- /// 成功
- ///
- Success = 0,
- }
+ ///
+ /// 没找到数据记录
+ ///
+ NotFoundRecord = -3,
+
+ ///
+ /// 成功
+ ///
+ Success = 0,
}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions.cs
new file mode 100644
index 0000000..83b139f
--- /dev/null
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions.cs
@@ -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;
+
+
+///
+/// object转数据字典
+///
+public static class ObjectExtensions
+{
+ ///
+ /// 用于存储每种类型的属性访问器数组的线程安全缓存。
+ ///
+ private static readonly ConcurrentDictionary PropertyCache = new();
+
+ ///
+ /// 缓存每个属性是否具有 ImagesAttribute 特性。
+ ///
+ public static readonly ConcurrentDictionary _PropertyCache = new();
+
+ ///
+ /// 判断对象是否为原始类型或字符串类型。
+ ///
+ /// 要检查的对象。
+ /// 如果对象是原始类型或字符串,返回 true;否则返回 false。
+ public static bool IsPrimitiveType(object obj) =>
+ obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType);
+
+ ///
+ /// 判断对象是否为集合类型(不包括字符串)。
+ ///
+ /// 要检查的对象。
+ /// 如果对象是集合类型(但不是字符串),返回 true;否则返回 false。
+ public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string;
+
+ ///
+ /// 根据对象类型,将对象转换为字典或列表格式,支持可选的路径前缀。
+ ///
+ /// 要转换的对象。
+ /// 属性路径的可选前缀。
+ /// 对象的字典或列表表示形式。
+ public static object ToDictionaryOrList(this object obj, string prefix = "", Func? imageFunc = null)
+ {
+ if (obj == null) return null;
+
+ return obj switch
+ {
+ _ when IsPrimitiveType(obj) => obj,
+ IEnumerable enumerable => TransformCollection(enumerable, prefix, imageFunc),
+ _ => TransformObject(obj, prefix, imageFunc)
+ };
+ }
+
+ ///
+ /// 将集合对象转换为包含转换项的列表,每个项保留其路径前缀。
+ ///
+ /// 要转换的集合。
+ /// 集合中每个属性路径的前缀。
+ /// 转换后的项列表。
+ private static List TransformCollection(IEnumerable enumerable, string prefix = "", Func? imageFunc = null)
+ {
+ var list = new List(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;
+ }
+
+ ///
+ /// 将对象的属性转换为带有路径前缀的字典,并应用前缀规则。
+ ///
+ /// 要转换的对象。
+ /// 每个属性路径的前缀。
+ /// 包含属性名和属性值的字典。
+ private static Dictionary TransformObject(object obj, string prefix = "", Func? imageFunc = null)
+ {
+ if (obj == null)
+ {
+ return null;
+ }
+ var type = obj.GetType();
+ var accessors = PropertyCache.GetOrAdd(type, CreatePropertyAccessors);
+ var keyValuePairs = new Dictionary(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;
+ }
+
+ ///
+ /// 为给定类型创建属性访问器数组。
+ ///
+ /// 要创建属性访问器的类型。
+ /// 属性访问器数组。
+ 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() != null);
+ return new PropertyAccessor(property.Name, getter, isImagesAttribute);
+ }).ToArray();
+ }
+
+
+
+ private static Func 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>(convertPropertyAccess, parameter).Compile();
+ }
+
+ private class PropertyAccessor
+ {
+ public string PropertyName { get; }
+ public Func Getter { get; }
+ public bool HasImagesAttribute { get; }
+
+ public PropertyAccessor(string propertyName, Func getter, bool hasImagesAttribute)
+ {
+ PropertyName = propertyName;
+ Getter = getter;
+ HasImagesAttribute = hasImagesAttribute;
+ }
+ }
+}
diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions1.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions1.cs
index 32a5b95..1bc2acf 100644
--- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions1.cs
+++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Utility/ObjectExtensions1.cs
@@ -11,157 +11,6 @@ using Microsoft.IdentityModel.Tokens;
namespace HuanMeng.DotNetCore.Utility;
-///
-///
-///
-public static class ObjectExtensions
-{
- ///
- /// 用于存储每种类型的属性访问器数组的线程安全缓存。
- ///
- private static readonly ConcurrentDictionary PropertyCache = new();
-
- ///
- /// 缓存每个属性是否具有 ImagesAttribute 特性。
- ///
- public static readonly ConcurrentDictionary _PropertyCache = new();
-
- ///
- /// 判断对象是否为原始类型或字符串类型。
- ///
- /// 要检查的对象。
- /// 如果对象是原始类型或字符串,返回 true;否则返回 false。
- public static bool IsPrimitiveType(object obj) =>
- obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType);
-
- ///
- /// 判断对象是否为集合类型(不包括字符串)。
- ///
- /// 要检查的对象。
- /// 如果对象是集合类型(但不是字符串),返回 true;否则返回 false。
- public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string;
-
- ///
- /// 根据对象类型,将对象转换为字典或列表格式,支持可选的路径前缀。
- ///
- /// 要转换的对象。
- /// 属性路径的可选前缀。
- /// 对象的字典或列表表示形式。
- public static object ToDictionaryOrList(this object obj, string prefix = "", Func? imageFunc = null)
- {
- if (obj == null) return null;
-
- return obj switch
- {
- _ when IsPrimitiveType(obj) => obj,
- IEnumerable enumerable => TransformCollection(enumerable, prefix, imageFunc),
- _ => TransformObject(obj, prefix, imageFunc)
- };
- }
-
- ///
- /// 将集合对象转换为包含转换项的列表,每个项保留其路径前缀。
- ///
- /// 要转换的集合。
- /// 集合中每个属性路径的前缀。
- /// 转换后的项列表。
- private static List TransformCollection(IEnumerable enumerable, string prefix = "", Func? imageFunc = null)
- {
- var list = new List(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;
- }
-
- ///
- /// 将对象的属性转换为带有路径前缀的字典,并应用前缀规则。
- ///
- /// 要转换的对象。
- /// 每个属性路径的前缀。
- /// 包含属性名和属性值的字典。
- private static Dictionary TransformObject(object obj, string prefix = "", Func? imageFunc = null)
- {
- if (obj == null)
- {
- return null;
- }
- var type = obj.GetType();
- var accessors = PropertyCache.GetOrAdd(type, CreatePropertyAccessors);
- var keyValuePairs = new Dictionary(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;
- }
-
- ///
- /// 为给定类型创建属性访问器数组。
- ///
- /// 要创建属性访问器的类型。
- /// 属性访问器数组。
- 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() != null);
- return new PropertyAccessor(property.Name, getter, isImagesAttribute);
- }).ToArray();
- }
-
-
-
- private static Func 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>(convertPropertyAccess, parameter).Compile();
- }
-
- private class PropertyAccessor
- {
- public string PropertyName { get; }
- public Func Getter { get; }
- public bool HasImagesAttribute { get; }
-
- public PropertyAccessor(string propertyName, Func getter, bool hasImagesAttribute)
- {
- PropertyName = propertyName;
- Getter = getter;
- HasImagesAttribute = hasImagesAttribute;
- }
- }
-}
-
[Obsolete]
public static class ObjectExtensions11
{