wip: 解决配置管理+短信功能。优化大部分功能,解决启动bug

main
NoahLan 6 months ago
parent 76219dcfc3
commit f3382f9c8e

@ -0,0 +1,28 @@
using System.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.ExceptionHandling;
namespace NPin.Framework.AspNetCore.Abp;
/// <summary>
/// NPin框架自定义 HttpStatusCode 处理器
/// 将 IBusinessException 与 IUserFriendlyException 默认返回403改为500
/// </summary>
public class NPinHttpExceptionStatusCodeFinder : DefaultHttpExceptionStatusCodeFinder
{
public NPinHttpExceptionStatusCodeFinder(IOptions<AbpExceptionHttpStatusCodeOptions> options) : base(options)
{
}
public override HttpStatusCode GetStatusCode(HttpContext httpContext, Exception exception)
{
switch (exception)
{
case IBusinessException _:
return HttpStatusCode.InternalServerError;
default:
return base.GetStatusCode(httpContext, exception);
}
}
}

@ -1,9 +1,17 @@
using NPin.Framework.Core;
using Volo.Abp.Modularity;
namespace NPin.Framework.AspNetCore;
[DependsOn(typeof(NPinFrameworkCoreModule))]
public class NPinFrameworkAspNetCoreModule : AbpModule
{
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NPin.Framework.AspNetCore.Abp;
using NPin.Framework.Core;
using Volo.Abp.AspNetCore.ExceptionHandling;
namespace NPin.Framework.AspNetCore;
[DependsOn(typeof(NPinFrameworkCoreModule))]
public class NPinFrameworkAspNetCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Replace(new ServiceDescriptor(typeof(IHttpExceptionStatusCodeFinder),
typeof(NPinHttpExceptionStatusCodeFinder), ServiceLifetime.Transient));
}
}

@ -1,105 +1,105 @@
using SqlSugar;
namespace NPin.Framework.SqlSugarCore.Abstractions;
public class DbConnOptions
{
/// <summary>
/// 连接字符串 必填
/// </summary>
public string? Url { get; set; }
/// <summary>
/// 数据库类型
/// </summary>
public DbType? DbType { get; set; }
/// <summary>
/// 开启种子数据
/// </summary>
public bool EnabledDbSeed { get; set; } = false;
/// <summary>
/// 开启 CodeFirst 自动化表结构
/// </summary>
public bool EnabledCodeFirst { get; set; } = false;
/// <summary>
/// 开启sql日志
/// </summary>
public bool EnabledSqlLog { get; set; } = true;
/// <summary>
/// 实体程序集
/// </summary>
public List<string>? EntityAssembly { get; set; }
/// <summary>
/// 开启读写分离
/// </summary>
public bool EnabledReadWrite { get; set; } = false;
/// <summary>
/// 读写分离
/// </summary>
public List<string>? ReadUrl { get; set; }
/// <summary>
/// 开启Saas多租户
/// </summary>
public bool EnabledSaasMultiTenancy { get; set; } = false;
/// <summary>
/// 默认租户库连接,如果不填,那就是默认库的地址
/// </summary>
public string? MasterSaasMultiTenancyUrl { get; set; }
/// <summary>
/// Saas租户连接
/// </summary>
public List<SaasMultiTenancyOptions>? SaasMultiTenancy { get; set; }
public static string MasterTenantName = "Master";
public static string DefaultTenantName = "Default";
public SaasMultiTenancyOptions GetDefaultSaasMultiTenancy()
{
return new SaasMultiTenancyOptions { Name = DefaultTenantName, Url = Url };
}
public SaasMultiTenancyOptions? GetDefaultMasterSaasMultiTenancy()
{
if (EnabledSaasMultiTenancy == false)
{
return null;
}
if (string.IsNullOrEmpty(MasterSaasMultiTenancyUrl))
{
return new SaasMultiTenancyOptions { Name = MasterTenantName, Url = Url };
}
else
{
return new SaasMultiTenancyOptions()
{
Name = MasterTenantName,
Url = MasterSaasMultiTenancyUrl
};
}
}
}
public class SaasMultiTenancyOptions
{
/// <summary>
/// 租户名称标识
/// </summary>
public string Name { get; set; }
/// <summary>
/// 连接Url
/// </summary>
public string Url { get; set; }
using SqlSugar;
namespace NPin.Framework.SqlSugarCore.Abstractions;
public class DbConnOptions
{
/// <summary>
/// 连接字符串 必填
/// </summary>
public string? Url { get; set; }
/// <summary>
/// 数据库类型
/// </summary>
public DbType? DbType { get; set; }
/// <summary>
/// 开启种子数据
/// </summary>
public bool EnabledDbSeed { get; set; } = false;
/// <summary>
/// 开启 CodeFirst 自动化表结构
/// </summary>
public bool EnabledCodeFirst { get; set; } = false;
/// <summary>
/// 开启sql日志
/// </summary>
public bool EnabledSqlLog { get; set; } = true;
/// <summary>
/// 实体程序集
/// </summary>
public List<string>? EntityAssembly { get; set; }
/// <summary>
/// 开启读写分离
/// </summary>
public bool EnabledReadWrite { get; set; } = false;
/// <summary>
/// 读写分离
/// </summary>
public List<string>? ReadUrl { get; set; }
/// <summary>
/// 开启Saas多租户
/// </summary>
public bool EnabledSaasMultiTenancy { get; set; } = false;
/// <summary>
/// 默认租户库连接,如果不填,那就是默认库的地址
/// </summary>
public string? MasterSaasMultiTenancyUrl { get; set; }
/// <summary>
/// Saas租户连接
/// </summary>
public List<SaasMultiTenancyOptions>? SaasMultiTenancy { get; set; }
public static string MasterTenantName = "Master";
public static string DefaultTenantName = "Default";
public SaasMultiTenancyOptions GetDefaultSaasMultiTenancy()
{
return new SaasMultiTenancyOptions { Name = DefaultTenantName, Url = Url };
}
public SaasMultiTenancyOptions? GetMasterSaasMultiTenancy()
{
if (EnabledSaasMultiTenancy == false)
{
return null;
}
if (string.IsNullOrEmpty(MasterSaasMultiTenancyUrl))
{
return new SaasMultiTenancyOptions { Name = MasterTenantName, Url = Url };
}
else
{
return new SaasMultiTenancyOptions()
{
Name = MasterTenantName,
Url = MasterSaasMultiTenancyUrl
};
}
}
}
public class SaasMultiTenancyOptions
{
/// <summary>
/// 租户名称标识
/// </summary>
public string Name { get; set; }
/// <summary>
/// 连接Url
/// </summary>
public string Url { get; set; }
}

@ -1,94 +1,126 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using NPin.Framework.SqlSugarCore.Abstractions;
using NPin.Framework.SqlSugarCore.Repositories;
using NPin.Framework.SqlSugarCore.Uow;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.Domain;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Modularity;
namespace NPin.Framework.SqlSugarCore;
[DependsOn(typeof(AbpDddDomainModule))]
public class NPinFrameworkSqlSugarCoreModule : AbpModule
{
public override Task ConfigureServicesAsync(ServiceConfigurationContext context)
{
var service = context.Services;
var configuration = service.GetConfiguration();
Configure<DbConnOptions>(configuration.GetSection("DbConnOptions"));
// 开放 sqlSugarClient
service.TryAddScoped<ISqlSugarDbContext, SqlSugarDbContext>();
// 不开放 sqlSugarClient
//service.AddTransient<ISqlSugarClient>(x => x.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient);
service.AddTransient(typeof(IRepository<>), typeof(SqlSugarRepository<>));
service.AddTransient(typeof(IRepository<,>), typeof(SqlSugarRepository<,>));
service.AddTransient(typeof(ISqlSugarRepository<>), typeof(SqlSugarRepository<>));
service.AddTransient(typeof(ISqlSugarRepository<,>), typeof(SqlSugarRepository<,>));
service.AddTransient(typeof(ISugarDbContextProvider<>), typeof(UnitOfWorkSqlSugarDbContextProvider<>));
return Task.CompletedTask;
}
public override async Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context)
{
// CodeFirst & DataSeed
var service = context.ServiceProvider;
var options = service.GetRequiredService<IOptions<DbConnOptions>>().Value;
if (options.EnabledCodeFirst)
{
CodeFirst(service);
}
if (options.EnabledDbSeed)
{
await DataSeedAsync(service);
}
}
/// <summary>
/// CodeFirst 建库建表
/// </summary>
/// <param name="service"></param>
private void CodeFirst(IServiceProvider service)
{
var moduleContainer = service.GetRequiredService<IModuleContainer>();
var db = service.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient;
// 尝试创建数据库
db.DbMaintenance.CreateDatabase();
var types = new List<Type>();
foreach (var module in moduleContainer.Modules)
{
types.AddRange(module.Assembly.GetTypes()
.Where(x => x.GetCustomAttribute<IgnoreCodeFirstAttribute>() == null)
.Where(x => x.GetCustomAttribute<SugarTable>() != null)
.Where(x => x.GetCustomAttribute<SplitTableAttribute>() is null));
}
if (types.Count > 0)
{
db.CopyNew().CodeFirst.InitTables(types.ToArray());
}
}
/// <summary>
/// 同步种子数据
/// </summary>
/// <param name="service"></param>
private async Task DataSeedAsync(IServiceProvider service)
{
var dataSeeder = service.GetRequiredService<IDataSeeder>();
await dataSeeder.SeedAsync();
}
using System.Reflection;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NPin.Framework.SqlSugarCore.Abstractions;
using NPin.Framework.SqlSugarCore.Repositories;
using NPin.Framework.SqlSugarCore.Uow;
using SqlSugar;
using Volo.Abp.Data;
using Volo.Abp.Domain;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
namespace NPin.Framework.SqlSugarCore;
[DependsOn(typeof(AbpDddDomainModule))]
public class NPinFrameworkSqlSugarCoreModule : AbpModule
{
public override Task ConfigureServicesAsync(ServiceConfigurationContext context)
{
var service = context.Services;
var configuration = service.GetConfiguration();
Configure<DbConnOptions>(configuration.GetSection("DbConnOptions"));
// 开放 sqlSugarClient
service.TryAddScoped<ISqlSugarDbContext, SqlSugarDbContext>();
// 不开放 sqlSugarClient
//service.AddTransient<ISqlSugarClient>(x => x.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient);
service.AddTransient(typeof(IRepository<>), typeof(SqlSugarRepository<>));
service.AddTransient(typeof(IRepository<,>), typeof(SqlSugarRepository<,>));
service.AddTransient(typeof(ISqlSugarRepository<>), typeof(SqlSugarRepository<>));
service.AddTransient(typeof(ISqlSugarRepository<,>), typeof(SqlSugarRepository<,>));
service.AddTransient(typeof(ISugarDbContextProvider<>), typeof(UnitOfWorkSqlSugarDbContextProvider<>));
return Task.CompletedTask;
}
public override async Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context)
{
// CodeFirst & DataSeed
var service = context.ServiceProvider;
var options = service.GetRequiredService<IOptions<DbConnOptions>>().Value;
Configure<AbpSequentialGuidGeneratorOptions>(opt =>
{
switch (options.DbType)
{
case DbType.PostgreSQL:
case DbType.MySql:
opt.DefaultSequentialGuidType = SequentialGuidType.SequentialAsString;
break;
case DbType.Oracle:
opt.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary;
break;
default:
opt.DefaultSequentialGuidType = SequentialGuidType.SequentialAtEnd;
break;
}
});
var logger = service.GetRequiredService<ILogger<NPinFrameworkSqlSugarCoreModule>>();
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("==========NPin-SQL配置:==========");
sb.AppendLine($"数据库连接字符串:{options.Url}");
sb.AppendLine($"数据库类型:{options.DbType.ToString()}");
sb.AppendLine($"是否开启种子数据:{options.EnabledDbSeed}");
sb.AppendLine($"是否开启CodeFirst{options.EnabledCodeFirst}");
sb.AppendLine($"是否开启Saas多租户{options.EnabledSaasMultiTenancy}");
sb.AppendLine("===============================");
logger.LogInformation(sb.ToString());
if (options.EnabledCodeFirst)
{
CodeFirst(service);
}
if (options.EnabledDbSeed)
{
await DataSeedAsync(service);
}
}
/// <summary>
/// CodeFirst 建库建表
/// </summary>
/// <param name="service"></param>
private void CodeFirst(IServiceProvider service)
{
var moduleContainer = service.GetRequiredService<IModuleContainer>();
var db = service.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient;
// 尝试创建数据库
db.DbMaintenance.CreateDatabase();
var types = new List<Type>();
foreach (var module in moduleContainer.Modules)
{
types.AddRange(module.Assembly.GetTypes()
.Where(x => x.GetCustomAttribute<IgnoreCodeFirstAttribute>() == null)
.Where(x => x.GetCustomAttribute<SugarTable>() != null)
.Where(x => x.GetCustomAttribute<SplitTableAttribute>() is null));
}
if (types.Count > 0)
{
db.CopyNew().CodeFirst.InitTables(types.ToArray());
}
}
/// <summary>
/// 同步种子数据
/// </summary>
/// <param name="service"></param>
private async Task DataSeedAsync(IServiceProvider service)
{
var dataSeeder = service.GetRequiredService<IDataSeeder>();
await dataSeeder.SeedAsync();
}
}

@ -1,430 +1,431 @@
using System.Linq.Expressions;
using NPin.Framework.Core.Helper;
using NPin.Framework.SqlSugarCore.Abstractions;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
namespace NPin.Framework.SqlSugarCore.Repositories;
public class SqlSugarRepository<TEntity> : ISqlSugarRepository<TEntity>, IRepository<TEntity>
where TEntity : class, IEntity, new()
{
public ISqlSugarClient Db => GetDbContextAsync().Result;
public ISugarQueryable<TEntity> DbQueryable => GetDbContextAsync().Result.Queryable<TEntity>();
private ISugarDbContextProvider<ISqlSugarDbContext> _sugarDbContextProvider;
public IAsyncQueryableExecuter AsyncExecuter { get; }
public bool? IsChangeTrackingEnabled => false;
public SqlSugarRepository(ISugarDbContextProvider<ISqlSugarDbContext> sugarDbContextProvider)
{
_sugarDbContextProvider = sugarDbContextProvider;
}
public virtual async Task<ISqlSugarClient> GetDbContextAsync()
{
var db = (await _sugarDbContextProvider.GetDbContextAsync()).SqlSugarClient;
return db;
}
public virtual async Task<SimpleClient<TEntity>> GetDbSimpleClientAsync()
{
var db = await GetDbContextAsync();
return new SimpleClient<TEntity>(db);
}
#region Abp模块
public virtual async Task<TEntity?> FindAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetFirstAsync(predicate);
}
public virtual async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetFirstAsync(predicate);
}
public virtual async Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await this.DeleteAsync(predicate);
}
public virtual async Task DeleteDirectAsync(Expression<Func<TEntity, bool>> predicate,
CancellationToken cancellationToken = default)
{
await this.DeleteAsync(predicate);
}
public IQueryable<TEntity> WithDetails()
{
throw new NotImplementedException();
}
public IQueryable<TEntity> WithDetails(params Expression<Func<TEntity, object>>[] propertySelectors)
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> WithDetailsAsync()
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> WithDetailsAsync(params Expression<Func<TEntity, object>>[] propertySelectors)
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> GetQueryableAsync()
{
throw new NotImplementedException();
}
public virtual async Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate,
bool includeDetails = false, CancellationToken cancellationToken = default)
{
return await GetListAsync(predicate);
}
public virtual async Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false,
CancellationToken cancellationToken = default)
{
return await InsertReturnEntityAsync(entity);
}
public virtual async Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await InsertRangeAsync(entities.ToList());
}
public virtual async Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await UpdateAsync(entity);
return entity;
}
public virtual async Task UpdateManyAsync(IEnumerable<TEntity> entities, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await UpdateRangeAsync(entities.ToList());
}
public virtual async Task DeleteAsync(TEntity entity, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await DeleteAsync(entity);
}
public virtual async Task DeleteManyAsync(IEnumerable<TEntity> entities, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await DeleteAsync(entities.ToList());
}
public virtual async Task<List<TEntity>> GetListAsync(bool includeDetails = false,
CancellationToken cancellationToken = default)
{
return await GetListAsync();
}
public virtual async Task<long> GetCountAsync(CancellationToken cancellationToken = default)
{
return await this.CountAsync();
}
public virtual async Task<List<TEntity>> GetPagedListAsync(int skipCount, int maxResultCount, string sorting,
bool includeDetails = false, CancellationToken cancellationToken = default)
{
return await GetPageListAsync(_ => true, skipCount, maxResultCount);
}
#endregion
#region 内置DB快捷操作
public virtual async Task<IDeleteable<TEntity>> AsDeleteable()
{
return (await GetDbSimpleClientAsync()).AsDeleteable();
}
public virtual async Task<IInsertable<TEntity>> AsInsertable(List<TEntity> insertObjs)
{
return (await GetDbSimpleClientAsync()).AsInsertable(insertObjs);
}
public virtual async Task<IInsertable<TEntity>> AsInsertable(TEntity insertObj)
{
return (await GetDbSimpleClientAsync()).AsInsertable(insertObj);
}
public virtual async Task<IInsertable<TEntity>> AsInsertable(TEntity[] insertObjs)
{
return (await GetDbSimpleClientAsync()).AsInsertable(insertObjs);
}
public virtual async Task<ISugarQueryable<TEntity>> AsQueryable()
{
return (await GetDbSimpleClientAsync()).AsQueryable();
}
public virtual async Task<ISqlSugarClient> AsSugarClient()
{
return (await GetDbSimpleClientAsync()).AsSugarClient();
}
public virtual async Task<ITenant> AsTenant()
{
return (await GetDbSimpleClientAsync()).AsTenant();
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable(List<TEntity> updateObjs)
{
return (await GetDbSimpleClientAsync()).AsUpdateable(updateObjs);
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable(TEntity updateObj)
{
return (await GetDbSimpleClientAsync()).AsUpdateable(updateObj);
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable()
{
return (await GetDbSimpleClientAsync()).AsUpdateable();
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable(TEntity[] updateObjs)
{
return (await GetDbSimpleClientAsync()).AsUpdateable(updateObjs);
}
#endregion
#region SimpleClient模块
public virtual async Task<int> CountAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).CountAsync(whereExpression);
}
public virtual async Task<bool> DeleteAsync(TEntity deleteObj)
{
if (deleteObj is ISoftDelete)
{
ReflectHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, deleteObj);
return await (await GetDbSimpleClientAsync()).UpdateAsync(deleteObj);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteAsync(deleteObj);
}
}
public virtual async Task<bool> DeleteAsync(List<TEntity> deleteObjs)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
deleteObjs.ForEach(e => ReflectHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, e));
return await (await GetDbSimpleClientAsync()).UpdateRangeAsync(deleteObjs);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteAsync(deleteObjs);
}
}
public virtual async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> whereExpression)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
return await (await GetDbSimpleClientAsync()).AsUpdateable().SetColumns(nameof(ISoftDelete), true)
.Where(whereExpression).ExecuteCommandAsync() > 0;
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteAsync(whereExpression);
}
}
public virtual async Task<bool> DeleteByIdAsync(dynamic id)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
var entity = await GetByIdAsync(id);
//反射赋值
ReflectHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, entity);
return await UpdateAsync(entity);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteByIdAsync(id);
}
}
public virtual async Task<bool> DeleteByIdsAsync(dynamic[] ids)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
var simpleClient = (await GetDbSimpleClientAsync());
var entities = await simpleClient.AsQueryable().In(ids).ToListAsync();
if (entities.Count == 0)
{
return false;
}
// 反射赋值
entities.ForEach(e => ReflectHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, e));
return await UpdateRangeAsync(entities);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteByIdAsync(ids);
}
}
public virtual async Task<TEntity> GetByIdAsync(dynamic id)
{
return await (await GetDbSimpleClientAsync()).GetByIdAsync(id);
}
public virtual async Task<TEntity> GetFirstAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).GetFirstAsync(whereExpression);
}
public virtual async Task<List<TEntity>> GetListAsync()
{
return await (await GetDbSimpleClientAsync()).GetListAsync();
}
public virtual async Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).GetListAsync(whereExpression);
}
public virtual async Task<List<TEntity>> GetPageListAsync(Expression<Func<TEntity, bool>> whereExpression,
int pageNum, int pageSize)
{
return await (await GetDbSimpleClientAsync()).GetPageListAsync(whereExpression,
new PageModel() { PageIndex = pageNum, PageSize = pageSize });
}
public virtual async Task<List<TEntity>> GetPageListAsync(Expression<Func<TEntity, bool>> whereExpression,
int pageNum, int pageSize,
Expression<Func<TEntity, object>>? orderByExpression = null, OrderByType orderByType = OrderByType.Asc)
{
return await (await GetDbSimpleClientAsync()).GetPageListAsync(whereExpression,
new PageModel { PageIndex = pageNum, PageSize = pageSize }, orderByExpression, orderByType);
}
public virtual async Task<TEntity> GetSingleAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).GetSingleAsync(whereExpression);
}
public virtual async Task<bool> InsertAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertAsync(insertObj);
}
public virtual async Task<bool> InsertOrUpdateAsync(TEntity data)
{
return await (await GetDbSimpleClientAsync()).InsertOrUpdateAsync(data);
}
public virtual async Task<bool> InsertOrUpdateAsync(List<TEntity> datas)
{
return await (await GetDbSimpleClientAsync()).InsertOrUpdateAsync(datas);
}
public virtual async Task<bool> InsertRangeAsync(List<TEntity> insertObjs)
{
return await (await GetDbSimpleClientAsync()).InsertRangeAsync(insertObjs);
}
public virtual async Task<long> InsertReturnBigIdentityAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnBigIdentityAsync(insertObj);
}
public virtual async Task<TEntity> InsertReturnEntityAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnEntityAsync(insertObj);
}
public virtual async Task<int> InsertReturnIdentityAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnIdentityAsync(insertObj);
}
public virtual async Task<long> InsertReturnSnowflakeIdAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnSnowflakeIdAsync(insertObj);
}
public virtual async Task<bool> IsAnyAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).IsAnyAsync(whereExpression);
}
public virtual async Task<bool> UpdateAsync(TEntity updateObj)
{
return await (await GetDbSimpleClientAsync()).UpdateAsync(updateObj);
}
public virtual async Task<bool> UpdateAsync(Expression<Func<TEntity, TEntity>> columns,
Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).UpdateAsync(columns, whereExpression);
}
public virtual async Task<bool> UpdateRangeAsync(List<TEntity> updateObjs)
{
return await (await GetDbSimpleClientAsync()).UpdateRangeAsync(updateObjs);
}
#endregion
}
public class SqlSugarRepository<TEntity, TKey> : SqlSugarRepository<TEntity>, ISqlSugarRepository<TEntity, TKey>,
IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>, new()
{
public SqlSugarRepository(ISugarDbContextProvider<ISqlSugarDbContext> sugarDbContextProvider) : base(
sugarDbContextProvider)
{
}
public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)
{
await DeleteByIdAsync(id);
}
public virtual async Task DeleteManyAsync(IEnumerable<TKey> ids, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await DeleteByIdsAsync(ids.Select(x => (object)x).ToArray());
}
public virtual async Task<TEntity?> FindAsync(TKey id, bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetByIdAsync(id);
}
public virtual async Task<TEntity> GetAsync(TKey id, bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetByIdAsync(id);
}
using System.Linq.Expressions;
using NPin.Framework.Core.Helper;
using NPin.Framework.SqlSugarCore.Abstractions;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
namespace NPin.Framework.SqlSugarCore.Repositories;
public class SqlSugarRepository<TEntity> : ISqlSugarRepository<TEntity>, IRepository<TEntity>
where TEntity : class, IEntity, new()
{
public ISqlSugarClient Db => GetDbContextAsync().Result;
public ISugarQueryable<TEntity> DbQueryable => GetDbContextAsync().Result.Queryable<TEntity>();
private ISugarDbContextProvider<ISqlSugarDbContext> _sugarDbContextProvider;
public IAsyncQueryableExecuter AsyncExecuter { get; }
public bool? IsChangeTrackingEnabled => false;
public SqlSugarRepository(ISugarDbContextProvider<ISqlSugarDbContext> sugarDbContextProvider)
{
_sugarDbContextProvider = sugarDbContextProvider;
}
public virtual async Task<ISqlSugarClient> GetDbContextAsync()
{
var db = (await _sugarDbContextProvider.GetDbContextAsync()).SqlSugarClient;
// await Console.Out.WriteLineAsync("获取的id" + db.ContextID);
return db;
}
public virtual async Task<SimpleClient<TEntity>> GetDbSimpleClientAsync()
{
var db = await GetDbContextAsync();
return new SimpleClient<TEntity>(db);
}
#region Abp模块
public virtual async Task<TEntity?> FindAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetFirstAsync(predicate);
}
public virtual async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetFirstAsync(predicate);
}
public virtual async Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await this.DeleteAsync(predicate);
}
public virtual async Task DeleteDirectAsync(Expression<Func<TEntity, bool>> predicate,
CancellationToken cancellationToken = default)
{
await this.DeleteAsync(predicate);
}
public IQueryable<TEntity> WithDetails()
{
throw new NotImplementedException();
}
public IQueryable<TEntity> WithDetails(params Expression<Func<TEntity, object>>[] propertySelectors)
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> WithDetailsAsync()
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> WithDetailsAsync(params Expression<Func<TEntity, object>>[] propertySelectors)
{
throw new NotImplementedException();
}
public Task<IQueryable<TEntity>> GetQueryableAsync()
{
throw new NotImplementedException();
}
public virtual async Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate,
bool includeDetails = false, CancellationToken cancellationToken = default)
{
return await GetListAsync(predicate);
}
public virtual async Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false,
CancellationToken cancellationToken = default)
{
return await InsertReturnEntityAsync(entity);
}
public virtual async Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await InsertRangeAsync(entities.ToList());
}
public virtual async Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await UpdateAsync(entity);
return entity;
}
public virtual async Task UpdateManyAsync(IEnumerable<TEntity> entities, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await UpdateRangeAsync(entities.ToList());
}
public virtual async Task DeleteAsync(TEntity entity, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await DeleteAsync(entity);
}
public virtual async Task DeleteManyAsync(IEnumerable<TEntity> entities, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await DeleteAsync(entities.ToList());
}
public virtual async Task<List<TEntity>> GetListAsync(bool includeDetails = false,
CancellationToken cancellationToken = default)
{
return await GetListAsync();
}
public virtual async Task<long> GetCountAsync(CancellationToken cancellationToken = default)
{
return await this.CountAsync();
}
public virtual async Task<List<TEntity>> GetPagedListAsync(int skipCount, int maxResultCount, string sorting,
bool includeDetails = false, CancellationToken cancellationToken = default)
{
return await GetPageListAsync(_ => true, skipCount, maxResultCount);
}
#endregion
#region 内置DB快捷操作
public virtual async Task<IDeleteable<TEntity>> AsDeleteable()
{
return (await GetDbSimpleClientAsync()).AsDeleteable();
}
public virtual async Task<IInsertable<TEntity>> AsInsertable(List<TEntity> insertObjs)
{
return (await GetDbSimpleClientAsync()).AsInsertable(insertObjs);
}
public virtual async Task<IInsertable<TEntity>> AsInsertable(TEntity insertObj)
{
return (await GetDbSimpleClientAsync()).AsInsertable(insertObj);
}
public virtual async Task<IInsertable<TEntity>> AsInsertable(TEntity[] insertObjs)
{
return (await GetDbSimpleClientAsync()).AsInsertable(insertObjs);
}
public virtual async Task<ISugarQueryable<TEntity>> AsQueryable()
{
return (await GetDbSimpleClientAsync()).AsQueryable();
}
public virtual async Task<ISqlSugarClient> AsSugarClient()
{
return (await GetDbSimpleClientAsync()).AsSugarClient();
}
public virtual async Task<ITenant> AsTenant()
{
return (await GetDbSimpleClientAsync()).AsTenant();
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable(List<TEntity> updateObjs)
{
return (await GetDbSimpleClientAsync()).AsUpdateable(updateObjs);
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable(TEntity updateObj)
{
return (await GetDbSimpleClientAsync()).AsUpdateable(updateObj);
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable()
{
return (await GetDbSimpleClientAsync()).AsUpdateable();
}
public virtual async Task<IUpdateable<TEntity>> AsUpdateable(TEntity[] updateObjs)
{
return (await GetDbSimpleClientAsync()).AsUpdateable(updateObjs);
}
#endregion
#region SimpleClient模块
public virtual async Task<int> CountAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).CountAsync(whereExpression);
}
public virtual async Task<bool> DeleteAsync(TEntity deleteObj)
{
if (deleteObj is ISoftDelete)
{
ReflectHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, deleteObj);
return await (await GetDbSimpleClientAsync()).UpdateAsync(deleteObj);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteAsync(deleteObj);
}
}
public virtual async Task<bool> DeleteAsync(List<TEntity> deleteObjs)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
deleteObjs.ForEach(e => ReflectHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, e));
return await (await GetDbSimpleClientAsync()).UpdateRangeAsync(deleteObjs);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteAsync(deleteObjs);
}
}
public virtual async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> whereExpression)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
return await (await GetDbSimpleClientAsync()).AsUpdateable().SetColumns(nameof(ISoftDelete), true)
.Where(whereExpression).ExecuteCommandAsync() > 0;
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteAsync(whereExpression);
}
}
public virtual async Task<bool> DeleteByIdAsync(dynamic id)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
var entity = await GetByIdAsync(id);
//反射赋值
ReflectHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, entity);
return await UpdateAsync(entity);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteByIdAsync(id);
}
}
public virtual async Task<bool> DeleteByIdsAsync(dynamic[] ids)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
var simpleClient = (await GetDbSimpleClientAsync());
var entities = await simpleClient.AsQueryable().In(ids).ToListAsync();
if (entities.Count == 0)
{
return false;
}
// 反射赋值
entities.ForEach(e => ReflectHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, e));
return await UpdateRangeAsync(entities);
}
else
{
return await (await GetDbSimpleClientAsync()).DeleteByIdAsync(ids);
}
}
public virtual async Task<TEntity> GetByIdAsync(dynamic id)
{
return await (await GetDbSimpleClientAsync()).GetByIdAsync(id);
}
public virtual async Task<TEntity> GetFirstAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).GetFirstAsync(whereExpression);
}
public virtual async Task<List<TEntity>> GetListAsync()
{
return await (await GetDbSimpleClientAsync()).GetListAsync();
}
public virtual async Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).GetListAsync(whereExpression);
}
public virtual async Task<List<TEntity>> GetPageListAsync(Expression<Func<TEntity, bool>> whereExpression,
int pageNum, int pageSize)
{
return await (await GetDbSimpleClientAsync()).GetPageListAsync(whereExpression,
new PageModel() { PageIndex = pageNum, PageSize = pageSize });
}
public virtual async Task<List<TEntity>> GetPageListAsync(Expression<Func<TEntity, bool>> whereExpression,
int pageNum, int pageSize,
Expression<Func<TEntity, object>>? orderByExpression = null, OrderByType orderByType = OrderByType.Asc)
{
return await (await GetDbSimpleClientAsync()).GetPageListAsync(whereExpression,
new PageModel { PageIndex = pageNum, PageSize = pageSize }, orderByExpression, orderByType);
}
public virtual async Task<TEntity> GetSingleAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).GetSingleAsync(whereExpression);
}
public virtual async Task<bool> InsertAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertAsync(insertObj);
}
public virtual async Task<bool> InsertOrUpdateAsync(TEntity data)
{
return await (await GetDbSimpleClientAsync()).InsertOrUpdateAsync(data);
}
public virtual async Task<bool> InsertOrUpdateAsync(List<TEntity> datas)
{
return await (await GetDbSimpleClientAsync()).InsertOrUpdateAsync(datas);
}
public virtual async Task<bool> InsertRangeAsync(List<TEntity> insertObjs)
{
return await (await GetDbSimpleClientAsync()).InsertRangeAsync(insertObjs);
}
public virtual async Task<long> InsertReturnBigIdentityAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnBigIdentityAsync(insertObj);
}
public virtual async Task<TEntity> InsertReturnEntityAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnEntityAsync(insertObj);
}
public virtual async Task<int> InsertReturnIdentityAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnIdentityAsync(insertObj);
}
public virtual async Task<long> InsertReturnSnowflakeIdAsync(TEntity insertObj)
{
return await (await GetDbSimpleClientAsync()).InsertReturnSnowflakeIdAsync(insertObj);
}
public virtual async Task<bool> IsAnyAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).IsAnyAsync(whereExpression);
}
public virtual async Task<bool> UpdateAsync(TEntity updateObj)
{
return await (await GetDbSimpleClientAsync()).UpdateAsync(updateObj);
}
public virtual async Task<bool> UpdateAsync(Expression<Func<TEntity, TEntity>> columns,
Expression<Func<TEntity, bool>> whereExpression)
{
return await (await GetDbSimpleClientAsync()).UpdateAsync(columns, whereExpression);
}
public virtual async Task<bool> UpdateRangeAsync(List<TEntity> updateObjs)
{
return await (await GetDbSimpleClientAsync()).UpdateRangeAsync(updateObjs);
}
#endregion
}
public class SqlSugarRepository<TEntity, TKey> : SqlSugarRepository<TEntity>, ISqlSugarRepository<TEntity, TKey>,
IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>, new()
{
public SqlSugarRepository(ISugarDbContextProvider<ISqlSugarDbContext> sugarDbContextProvider) : base(
sugarDbContextProvider)
{
}
public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)
{
await DeleteByIdAsync(id);
}
public virtual async Task DeleteManyAsync(IEnumerable<TKey> ids, bool autoSave = false,
CancellationToken cancellationToken = default)
{
await DeleteByIdsAsync(ids.Select(x => (object)x).ToArray());
}
public virtual async Task<TEntity?> FindAsync(TKey id, bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetByIdAsync(id);
}
public virtual async Task<TEntity> GetAsync(TKey id, bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetByIdAsync(id);
}
}

@ -1,117 +1,124 @@
using System.Reflection;
using Microsoft.Extensions.Options;
using NPin.Framework.SqlSugarCore.Abstractions;
using SqlSugar;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace NPin.Framework.SqlSugarCore;
public class SqlSugarDbConnectionCreator : ISqlSugarDbConnectionCreator, ITransientDependency
{
[DisablePropertyInjection] public Action<ISqlSugarClient> OnSqlSugarClientConfig { get; set; }
[DisablePropertyInjection] public Action<object, DataAfterModel> DataExecuted { get; set; }
[DisablePropertyInjection] public Action<object, DataFilterModel> DataExecuting { get; set; }
[DisablePropertyInjection] public Action<string, SugarParameter[]> OnLogExecuting { get; set; }
[DisablePropertyInjection] public Action<string, SugarParameter[]> OnLogExecuted { get; set; }
[DisablePropertyInjection] public Action<PropertyInfo, EntityColumnInfo> EntityService { get; set; }
[DisablePropertyInjection] public Action<Type, EntityInfo> EntityNameService { get; set; }
public DbConnOptions Options { get; }
public SqlSugarDbConnectionCreator(IOptions<DbConnOptions> options)
{
Options = options.Value;
}
public void SetDbAop(ISqlSugarClient currentDb)
{
currentDb.Aop.OnLogExecuting = this.OnLogExecuting;
currentDb.Aop.OnLogExecuted = this.OnLogExecuted;
currentDb.Aop.DataExecuting = this.DataExecuting;
currentDb.Aop.DataExecuted = this.DataExecuted;
OnSqlSugarClientConfig(currentDb);
}
public ConnectionConfig Build(Action<ConnectionConfig>? action = null)
{
var dbConnOptions = Options;
#region 组装Options
if (dbConnOptions.DbType is null)
{
throw new ArgumentException("DbType配置为空");
}
// 读写分离 slave模式
var slaveConfigs = new List<SlaveConnectionConfig>();
if (dbConnOptions.EnabledReadWrite)
{
if (dbConnOptions.ReadUrl is null)
{
throw new ArgumentException("读写分离开启,但读库配置为空");
}
var readConn = dbConnOptions.ReadUrl;
readConn.ForEach(s =>
{
// 如果是动态分库,连接串不能写死,需要动态添加,所以此处只配置共享库的连接
slaveConfigs.Add(new SlaveConnectionConfig { ConnectionString = s });
});
}
#endregion
#region 组装连接配置
var connectionConfig = new ConnectionConfig
{
ConfigId = ConnectionStrings.DefaultConnectionStringName,
DbType = dbConnOptions.DbType ?? DbType.Sqlite,
ConnectionString = dbConnOptions.Url,
IsAutoCloseConnection = true,
SlaveConnectionConfigs = slaveConfigs,
// CodeFirst 非空值判断
ConfigureExternalServices = new ConfigureExternalServices
{
EntityService = (c, p) =>
{
if (new NullabilityInfoContext().Create(c).WriteState is NullabilityState.Nullable)
{
p.IsNullable = true;
}
EntityService(c, p);
},
EntityNameService = (t, e) =>
{
EntityNameService(t, e);
}
},
// Aop
AopEvents = new AopEvents
{
DataExecuted = DataExecuted,
DataExecuting = DataExecuting,
OnLogExecuted = OnLogExecuted,
OnLogExecuting = OnLogExecuting
}
};
if (action is not null)
{
action.Invoke(connectionConfig);
}
#endregion
return connectionConfig;
}
using System.Reflection;
using Microsoft.Extensions.Options;
using NPin.Framework.SqlSugarCore.Abstractions;
using SqlSugar;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace NPin.Framework.SqlSugarCore;
public class SqlSugarDbConnectionCreator : ISqlSugarDbConnectionCreator, ITransientDependency
{
[DisablePropertyInjection] public Action<ISqlSugarClient> OnSqlSugarClientConfig { get; set; }
[DisablePropertyInjection] public Action<object, DataAfterModel> DataExecuted { get; set; }
[DisablePropertyInjection] public Action<object, DataFilterModel> DataExecuting { get; set; }
[DisablePropertyInjection] public Action<string, SugarParameter[]> OnLogExecuting { get; set; }
[DisablePropertyInjection] public Action<string, SugarParameter[]> OnLogExecuted { get; set; }
[DisablePropertyInjection] public Action<PropertyInfo, EntityColumnInfo> EntityService { get; set; }
[DisablePropertyInjection] public Action<Type, EntityInfo> EntityNameService { get; set; }
public DbConnOptions Options { get; }
public SqlSugarDbConnectionCreator(IOptions<DbConnOptions> options)
{
Options = options.Value;
}
public void SetDbAop(ISqlSugarClient currentDb)
{
currentDb.Aop.OnLogExecuting = this.OnLogExecuting;
currentDb.Aop.OnLogExecuted = this.OnLogExecuted;
currentDb.Aop.DataExecuting = this.DataExecuting;
currentDb.Aop.DataExecuted = this.DataExecuted;
OnSqlSugarClientConfig(currentDb);
}
public ConnectionConfig Build(Action<ConnectionConfig>? action = null)
{
var dbConnOptions = Options;
#region 组装Options
if (dbConnOptions.DbType is null)
{
throw new ArgumentException("DbType配置为空");
}
// 读写分离 slave模式
var slaveConfigs = new List<SlaveConnectionConfig>();
if (dbConnOptions.EnabledReadWrite)
{
if (dbConnOptions.ReadUrl is null)
{
throw new ArgumentException("读写分离开启,但读库配置为空");
}
var readConn = dbConnOptions.ReadUrl;
readConn.ForEach(s =>
{
// 如果是动态分库,连接串不能写死,需要动态添加,所以此处只配置共享库的连接
slaveConfigs.Add(new SlaveConnectionConfig { ConnectionString = s });
});
}
#endregion
#region 组装连接配置
var connectionConfig = new ConnectionConfig
{
ConfigId = ConnectionStrings.DefaultConnectionStringName,
DbType = dbConnOptions.DbType ?? DbType.Sqlite,
ConnectionString = dbConnOptions.Url,
IsAutoCloseConnection = true,
SlaveConnectionConfigs = slaveConfigs,
LanguageType = LanguageType.Chinese,
MoreSettings = new ConnMoreSettings
{
SqliteCodeFirstEnableDescription = true,
PgSqlIsAutoToLower = true,
PgSqlIsAutoToLowerCodeFirst = true,
},
// CodeFirst 非空值判断
ConfigureExternalServices = new ConfigureExternalServices
{
EntityService = (c, p) =>
{
if (new NullabilityInfoContext().Create(c).WriteState is NullabilityState.Nullable)
{
p.IsNullable = true;
}
EntityService(c, p);
},
EntityNameService = (t, e) =>
{
EntityNameService(t, e);
}
},
// Aop
AopEvents = new AopEvents
{
DataExecuted = DataExecuted,
DataExecuting = DataExecuting,
OnLogExecuted = OnLogExecuted,
OnLogExecuting = OnLogExecuting
}
};
if (action is not null)
{
action.Invoke(connectionConfig);
}
#endregion
return connectionConfig;
}
}

@ -1,269 +1,306 @@
using System.Reflection;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NPin.Framework.SqlSugarCore.Abstractions;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
namespace NPin.Framework.SqlSugarCore;
public class SqlSugarDbContext : ISqlSugarDbContext
{
private IAbpLazyServiceProvider LazyServiceProvider { get; }
public ISqlSugarClient SqlSugarClient { get; private set; }
public DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
public ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>();
public ICurrentTenant? CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
public IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
private IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>();
protected ILoggerFactory Logger => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
public IEntityChangeEventHelper EntityChangeEventHelper =>
LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance);
public void SetSqlSugarClient(ISqlSugarClient sqlSugarClient)
{
SqlSugarClient = sqlSugarClient;
}
public SqlSugarDbContext(IAbpLazyServiceProvider lazyServiceProvider)
{
LazyServiceProvider = lazyServiceProvider;
var connectionCreator = LazyServiceProvider.LazyGetRequiredService<ISqlSugarDbConnectionCreator>();
connectionCreator.OnSqlSugarClientConfig = OnSqlSugarClientConfig;
connectionCreator.EntityService = EntityService;
connectionCreator.EntityNameService = EntityNameService;
connectionCreator.DataExecuting = DataExecuting;
connectionCreator.DataExecuted = DataExecuted;
connectionCreator.OnLogExecuting = OnLogExecuting;
connectionCreator.OnLogExecuted = OnLogExecuted;
SqlSugarClient = new SqlSugarClient(connectionCreator.Build());
connectionCreator.SetDbAop(SqlSugarClient);
}
/// <summary>
/// 上下文对象扩展
/// </summary>
/// <param name="sqlSugarClient"></param>
protected virtual void OnSqlSugarClientConfig(ISqlSugarClient sqlSugarClient)
{
//需自定义扩展
if (IsSoftDeleteFilterEnabled)
{
sqlSugarClient.QueryFilter.AddTableFilter<ISoftDelete>(u => u.IsDeleted == false);
}
if (IsMultiTenantFilterEnabled)
{
sqlSugarClient.QueryFilter.AddTableFilter<IMultiTenant>(u => u.TenantId == GuidGenerator.Create());
}
// 自定义其它Filter
CustomDataFilter(sqlSugarClient);
}
protected virtual void CustomDataFilter(ISqlSugarClient sqlSugarClient)
{
}
protected virtual void DataExecuted(object oldValue, DataAfterModel entityInfo)
{
}
protected virtual void DataExecuting(object? oldValue, DataFilterModel entityInfo)
{
// 审计日志
switch (entityInfo.OperationType)
{
case DataFilterType.UpdateByObject:
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModificationTime)))
{
if (!DateTime.MinValue.Equals(oldValue))
{
entityInfo.SetValue(DateTime.Now);
}
}
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModifierId)))
{
if (CurrentUser.Id != null)
{
entityInfo.SetValue(CurrentUser.Id);
}
}
break;
case DataFilterType.InsertByObject:
if (entityInfo.PropertyName.Equals(nameof(IEntity<Guid>.Id)))
{
//主键为空或者为默认最小值
if (Guid.Empty.Equals(oldValue))
{
entityInfo.SetValue(GuidGenerator.Create());
}
}
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreationTime)))
{
//为空或者为默认最小值
if (oldValue is null || DateTime.MinValue.Equals(oldValue))
{
entityInfo.SetValue(DateTime.Now);
}
}
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreatorId)))
{
if (CurrentUser.Id != null)
{
entityInfo.SetValue(CurrentUser.Id);
}
}
//插入时需要租户id,先预留
if (entityInfo.PropertyName.Equals(nameof(IMultiTenant.TenantId)))
{
if (CurrentTenant is not null)
{
entityInfo.SetValue(CurrentTenant.Id);
}
}
break;
}
//领域事件
switch (entityInfo.OperationType)
{
case DataFilterType.InsertByObject:
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
EntityChangeEventHelper.PublishEntityCreatedEvent(entityInfo.EntityValue);
}
break;
case DataFilterType.UpdateByObject:
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
//软删除,发布的是删除事件
if (entityInfo.EntityValue is ISoftDelete softDelete)
{
if (softDelete.IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityInfo.EntityValue);
}
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityInfo.EntityValue);
}
}
break;
case DataFilterType.DeleteByObject:
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityInfo.EntityValue);
}
break;
}
}
protected virtual void OnLogExecuting(string sql, SugarParameter[] pars)
{
if (Options.EnabledSqlLog)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("==========NPin-SQL==========");
sb.AppendLine(UtilMethods.GetSqlString(DbType.SqlServer, sql, pars));
sb.AppendLine("============================");
Logger.CreateLogger<SqlSugarDbContext>().LogDebug(sb.ToString());
}
}
protected virtual void OnLogExecuted(string sql, SugarParameter[] pars)
{
}
/// <summary>
/// 实体配置
/// 自动主键
/// 自动Ignore关联配置导航
/// 开启下划线
/// </summary>
/// <param name="property"></param>
/// <param name="column"></param>
protected virtual void EntityService(PropertyInfo property, EntityColumnInfo column)
{
if (property.PropertyType == typeof(ExtraPropertyDictionary))
{
column.IsIgnore = true;
}
if (property.Name == nameof(Entity<object>.Id))
{
column.IsPrimarykey = true;
}
column.DbColumnName = UtilMethods.ToUnderLine(column.DbColumnName);
}
/// <summary>
/// EntityName 配置
/// 开启下划线
/// </summary>
/// <param name="type"></param>
/// <param name="entity"></param>
protected virtual void EntityNameService(Type type, EntityInfo entity)
{
// 开启下划线
entity.DbTableName = UtilMethods.ToUnderLine(entity.DbTableName);
}
public void Backup()
{
string directoryName = "database_backup";
string fileName = $"{SqlSugarClient.Ado.Connection.Database}_" + DateTime.Now.ToString("yyyyMMdd_HHmmss");
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
switch (Options.DbType)
{
case DbType.MySql:
// mysql 只支持.net core
SqlSugarClient.DbMaintenance.BackupDataBase(SqlSugarClient.Ado.Connection.Database,
$"{Path.Combine(directoryName, fileName)}.sql");
break;
case DbType.Sqlite:
// sqlite 只支持.net core
SqlSugarClient.DbMaintenance.BackupDataBase(null, $"{fileName}.db");
break;
case DbType.SqlServer:
// 第一个参数库名
SqlSugarClient.DbMaintenance.BackupDataBase(SqlSugarClient.Ado.Connection.Database,
$"{Path.Combine(directoryName, fileName)}.bak");
break;
default:
throw new NotImplementedException("其他数据库备份未实现");
}
}
using System.Reflection;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NPin.Framework.SqlSugarCore.Abstractions;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
namespace NPin.Framework.SqlSugarCore;
public class SqlSugarDbContext : ISqlSugarDbContext
{
private IAbpLazyServiceProvider LazyServiceProvider { get; }
public ISqlSugarClient SqlSugarClient { get; private set; }
public DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
public ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>();
public ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
public IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
private IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>();
protected ILoggerFactory Logger => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
public IEntityChangeEventHelper EntityChangeEventHelper =>
LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance);
public void SetSqlSugarClient(ISqlSugarClient sqlSugarClient)
{
SqlSugarClient = sqlSugarClient;
}
public SqlSugarDbContext(IAbpLazyServiceProvider lazyServiceProvider)
{
LazyServiceProvider = lazyServiceProvider;
var connectionCreator = LazyServiceProvider.LazyGetRequiredService<ISqlSugarDbConnectionCreator>();
connectionCreator.OnSqlSugarClientConfig = OnSqlSugarClientConfig;
connectionCreator.EntityService = EntityService;
connectionCreator.EntityNameService = EntityNameService;
connectionCreator.DataExecuting = DataExecuting;
connectionCreator.DataExecuted = DataExecuted;
connectionCreator.OnLogExecuting = OnLogExecuting;
connectionCreator.OnLogExecuted = OnLogExecuted;
var connStr = GetCurrentConnectionString();
SqlSugarClient = new SqlSugarClient(connectionCreator.Build(action: opt =>
{
opt.ConnectionString = connStr;
}));
connectionCreator.SetDbAop(SqlSugarClient);
}
/// <summary>
/// db切换多库支持
/// </summary>
/// <returns></returns>
protected virtual string GetCurrentConnectionString()
{
var connectionStringResolver = LazyServiceProvider.LazyGetRequiredService<IConnectionStringResolver>();
var connectionString = connectionStringResolver.ResolveAsync().Result;
//没有检测到使用多租户功能,默认使用默认库即可
if (string.IsNullOrWhiteSpace(connectionString))
{
Volo.Abp.Check.NotNull(Options.Url, "租户默认库Default未找到");
connectionString = Options.Url;
}
//如果当前租户是主库,单独使用主要库
if (CurrentTenant.Name == DbConnOptions.MasterTenantName)
{
var conStrOrNull = Options.GetMasterSaasMultiTenancy();
Volo.Abp.Check.NotNull(conStrOrNull, "租户主库Master未找到");
connectionString = conStrOrNull.Url;
}
return connectionString!;
}
/// <summary>
/// 上下文对象扩展
/// </summary>
/// <param name="sqlSugarClient"></param>
protected virtual void OnSqlSugarClientConfig(ISqlSugarClient sqlSugarClient)
{
//需自定义扩展
if (IsSoftDeleteFilterEnabled)
{
sqlSugarClient.QueryFilter.AddTableFilter<ISoftDelete>(u => u.IsDeleted == false);
}
if (IsMultiTenantFilterEnabled)
{
sqlSugarClient.QueryFilter.AddTableFilter<IMultiTenant>(u => u.TenantId == CurrentTenant.Id);
}
// 自定义其它Filter
CustomDataFilter(sqlSugarClient);
}
protected virtual void CustomDataFilter(ISqlSugarClient sqlSugarClient)
{
}
protected virtual void DataExecuted(object oldValue, DataAfterModel entityInfo)
{
}
protected virtual void DataExecuting(object? oldValue, DataFilterModel entityInfo)
{
// 审计日志
switch (entityInfo.OperationType)
{
case DataFilterType.UpdateByObject:
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModificationTime)))
{
if (!DateTime.MinValue.Equals(oldValue))
{
entityInfo.SetValue(DateTime.Now);
}
}
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModifierId)))
{
if (CurrentUser.Id != null)
{
entityInfo.SetValue(CurrentUser.Id);
}
}
break;
case DataFilterType.InsertByObject:
if (entityInfo.PropertyName.Equals(nameof(IEntity<Guid>.Id)))
{
//主键为空或者为默认最小值
if (Guid.Empty.Equals(oldValue))
{
entityInfo.SetValue(GuidGenerator.Create());
}
}
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreationTime)))
{
//为空或者为默认最小值
if (oldValue is null || DateTime.MinValue.Equals(oldValue))
{
entityInfo.SetValue(DateTime.Now);
}
}
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.CreatorId)))
{
if (CurrentUser.Id != null)
{
entityInfo.SetValue(CurrentUser.Id);
}
}
//插入时需要租户id,先预留
if (entityInfo.PropertyName.Equals(nameof(IMultiTenant.TenantId)))
{
if (CurrentTenant is not null)
{
entityInfo.SetValue(CurrentTenant.Id);
}
}
break;
}
//领域事件
switch (entityInfo.OperationType)
{
case DataFilterType.InsertByObject:
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
EntityChangeEventHelper.PublishEntityCreatedEvent(entityInfo.EntityValue);
}
break;
case DataFilterType.UpdateByObject:
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
//软删除,发布的是删除事件
if (entityInfo.EntityValue is ISoftDelete softDelete)
{
if (softDelete.IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityInfo.EntityValue);
}
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityInfo.EntityValue);
}
}
break;
case DataFilterType.DeleteByObject:
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityInfo.EntityValue);
}
break;
}
}
protected virtual void OnLogExecuting(string sql, SugarParameter[] pars)
{
if (Options.EnabledSqlLog)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("==========NPin-SQL==========");
sb.AppendLine(UtilMethods.GetSqlString(DbType.SqlServer, sql, pars));
sb.AppendLine("============================");
Logger.CreateLogger<SqlSugarDbContext>().LogDebug(sb.ToString());
}
}
protected virtual void OnLogExecuted(string sql, SugarParameter[] pars)
{
if (Options.EnabledSqlLog)
{
var sqllog = $"=========NPin-SQL耗时{SqlSugarClient.Ado.SqlExecutionTime.TotalMilliseconds}毫秒=====";
Logger.CreateLogger<SqlSugarDbContext>().LogDebug(sqllog);
}
}
/// <summary>
/// 实体配置
/// 自动主键
/// 自动Ignore关联配置导航
/// 开启下划线
/// </summary>
/// <param name="property"></param>
/// <param name="column"></param>
protected virtual void EntityService(PropertyInfo property, EntityColumnInfo column)
{
if (property.PropertyType == typeof(ExtraPropertyDictionary))
{
column.IsIgnore = true;
}
if (property.Name == nameof(Entity<object>.Id))
{
column.IsPrimarykey = true;
}
column.DbColumnName = UtilMethods.ToUnderLine(column.DbColumnName);
}
/// <summary>
/// EntityName 配置
/// 开启下划线
/// </summary>
/// <param name="type"></param>
/// <param name="entity"></param>
protected virtual void EntityNameService(Type type, EntityInfo entity)
{
// 开启下划线
entity.DbTableName = UtilMethods.ToUnderLine(entity.DbTableName);
}
public void Backup()
{
string directoryName = "database_backup";
string fileName = $"{SqlSugarClient.Ado.Connection.Database}_" + DateTime.Now.ToString("yyyyMMdd_HHmmss");
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
switch (Options.DbType)
{
case DbType.MySql:
// mysql 只支持.net core
SqlSugarClient.DbMaintenance.BackupDataBase(SqlSugarClient.Ado.Connection.Database,
$"{Path.Combine(directoryName, fileName)}.sql");
break;
case DbType.Sqlite:
// sqlite 只支持.net core
SqlSugarClient.DbMaintenance.BackupDataBase(null, $"{fileName}.db");
break;
case DbType.SqlServer:
// 第一个参数库名
SqlSugarClient.DbMaintenance.BackupDataBase(SqlSugarClient.Ado.Connection.Database,
$"{Path.Combine(directoryName, fileName)}.bak");
break;
default:
throw new NotImplementedException("其他数据库备份未实现");
}
}
}

@ -1,135 +1,140 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using NPin.Framework.SqlSugarCore.Abstractions;
using Volo.Abp.Data;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace NPin.Framework.SqlSugarCore.Uow;
public class UnitOfWorkSqlSugarDbContextProvider<TDbContext> : ISugarDbContextProvider<TDbContext>
where TDbContext : ISqlSugarDbContext
{
private readonly ISqlSugarDbConnectionCreator _dbConnectionCreator;
private ILogger<UnitOfWorkSqlSugarDbContextProvider<TDbContext>> Logger { get; set; }
private IServiceProvider ServiceProvider { get; set; }
private static AsyncLocalDbContextAccessor ContextInstance => AsyncLocalDbContextAccessor.Instance;
protected readonly IUnitOfWorkManager UnitOfWorkManager;
protected readonly IConnectionStringResolver ConnectionStringResolver;
protected readonly ICancellationTokenProvider CancellationTokenProvider;
protected readonly ICurrentTenant CurrentTenant;
public UnitOfWorkSqlSugarDbContextProvider(
IUnitOfWorkManager unitOfWorkManager,
IConnectionStringResolver connectionStringResolver,
ICancellationTokenProvider cancellationTokenProvider,
ICurrentTenant currentTenant,
ISqlSugarDbConnectionCreator dbConnectionCreator
)
{
UnitOfWorkManager = unitOfWorkManager;
ConnectionStringResolver = connectionStringResolver;
CancellationTokenProvider = cancellationTokenProvider;
CurrentTenant = currentTenant;
Logger = NullLogger<UnitOfWorkSqlSugarDbContextProvider<TDbContext>>.Instance;
_dbConnectionCreator = dbConnectionCreator;
}
public virtual async Task<TDbContext> GetDbContextAsync()
{
var connectionStringName = ConnectionStrings.DefaultConnectionStringName;
// 获取当前的连接字符串,未开启多租户时为空
var connectionString = await ResolveConnectionStringAsync(connectionStringName);
var dbContextKey = $"{GetType().FullName}_{connectionString}";
var unitOfWork = UnitOfWorkManager.Current;
if (unitOfWork == null || !unitOfWork.Options.IsTransactional)
{
// set if is null
ContextInstance.Current ??= (TDbContext)ServiceProvider.GetRequiredService<ISqlSugarDbContext>();
var dbContext = (TDbContext)ContextInstance.Current;
// 提高体验取消uow的强制性
// 若不启用uow创建新db不开启事务
return dbContext;
}
// 尝试当前工作单元获取db
var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
if (databaseApi == null)
{
databaseApi =
new SqlSugarDatabaseApi(CreateDbContextAsync(unitOfWork, connectionStringName, connectionString)
.Result);
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
}
return (TDbContext)((SqlSugarDatabaseApi)databaseApi).DbContext;
}
/// <summary>
/// 解析连接字符串
/// </summary>
/// <param name="connectionStringName"></param>
/// <returns></returns>
protected virtual async Task<string> ResolveConnectionStringAsync(string connectionStringName)
{
if (typeof(TDbContext).IsDefined(typeof(IgnoreMultiTenancyAttribute), false))
{
using (CurrentTenant.Change(null))
{
return await ConnectionStringResolver.ResolveAsync(connectionStringName);
}
}
return await ConnectionStringResolver.ResolveAsync(connectionStringName);
}
protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionStringName,
string connectionString)
{
var creationContext = new SqlSugarDbContextCreationContext(connectionStringName, connectionString);
//将连接key进行传值
using (SqlSugarDbContextCreationContext.Use(creationContext))
{
var dbContext = await CreateDbContextAsync(unitOfWork);
return dbContext;
}
}
protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork)
{
return unitOfWork.Options.IsTransactional
? await CreateDbContextWithTransactionAsync(unitOfWork)
: unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
}
protected virtual async Task<TDbContext> CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork)
{
//事务key
var transactionApiKey = $"SqlSugarCore_{SqlSugarDbContextCreationContext.Current.ConnectionString}";
//尝试查找事务
var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as SqlSugarTransactionApi;
//该db还没有进行开启事务
if (activeTransaction == null)
{
//获取到db添加事务即可
var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
var transaction = new SqlSugarTransactionApi(
dbContext
);
unitOfWork.AddTransactionApi(transactionApiKey, transaction);
// await dbContext.SqlSugarClient.Ado.BeginTranAsync();
return dbContext;
}
return (TDbContext)activeTransaction.GetDbContext();
}
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using NPin.Framework.SqlSugarCore.Abstractions;
using Volo.Abp.Data;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace NPin.Framework.SqlSugarCore.Uow;
public class UnitOfWorkSqlSugarDbContextProvider<TDbContext> : ISugarDbContextProvider<TDbContext>
where TDbContext : ISqlSugarDbContext
{
private readonly ISqlSugarDbConnectionCreator _dbConnectionCreator;
private ILogger<UnitOfWorkSqlSugarDbContextProvider<TDbContext>> Logger { get; set; }
public IServiceProvider ServiceProvider { get; set; }
private static AsyncLocalDbContextAccessor ContextInstance => AsyncLocalDbContextAccessor.Instance;
protected readonly IUnitOfWorkManager UnitOfWorkManager;
protected readonly IConnectionStringResolver ConnectionStringResolver;
protected readonly ICancellationTokenProvider CancellationTokenProvider;
protected readonly ICurrentTenant CurrentTenant;
public UnitOfWorkSqlSugarDbContextProvider(
IUnitOfWorkManager unitOfWorkManager,
IConnectionStringResolver connectionStringResolver,
ICancellationTokenProvider cancellationTokenProvider,
ICurrentTenant currentTenant,
ISqlSugarDbConnectionCreator dbConnectionCreator
)
{
UnitOfWorkManager = unitOfWorkManager;
ConnectionStringResolver = connectionStringResolver;
CancellationTokenProvider = cancellationTokenProvider;
CurrentTenant = currentTenant;
Logger = NullLogger<UnitOfWorkSqlSugarDbContextProvider<TDbContext>>.Instance;
_dbConnectionCreator = dbConnectionCreator;
}
public virtual async Task<TDbContext> GetDbContextAsync()
{
var connectionStringName = ConnectionStrings.DefaultConnectionStringName;
// 获取当前的连接字符串,未开启多租户时为空
var connectionString = await ResolveConnectionStringAsync(connectionStringName);
var dbContextKey = $"{GetType().FullName}_{connectionString}";
var unitOfWork = UnitOfWorkManager.Current;
if (unitOfWork == null || !unitOfWork.Options.IsTransactional)
{
// set if is null
if (ContextInstance.Current is null)
{
ContextInstance.Current = (TDbContext)ServiceProvider.GetRequiredService<ISqlSugarDbContext>();
}
var dbContext = (TDbContext)ContextInstance.Current;
// 提高体验取消uow的强制性
// 若不启用uow创建新db不开启事务
return dbContext;
}
// 尝试当前工作单元获取db
var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
if (databaseApi == null)
{
// db根据连接字符串来创建
databaseApi = new SqlSugarDatabaseApi(
CreateDbContextAsync(unitOfWork, connectionStringName, connectionString).Result
);
// 创建的db加入到当前工作单元中
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
}
return (TDbContext)((SqlSugarDatabaseApi)databaseApi).DbContext;
}
/// <summary>
/// 解析连接字符串
/// </summary>
/// <param name="connectionStringName"></param>
/// <returns></returns>
protected virtual async Task<string> ResolveConnectionStringAsync(string connectionStringName)
{
if (typeof(TDbContext).IsDefined(typeof(IgnoreMultiTenancyAttribute), false))
{
using (CurrentTenant.Change(null))
{
return await ConnectionStringResolver.ResolveAsync(connectionStringName);
}
}
return await ConnectionStringResolver.ResolveAsync(connectionStringName);
}
protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionStringName,
string connectionString)
{
var creationContext = new SqlSugarDbContextCreationContext(connectionStringName, connectionString);
//将连接key进行传值
using (SqlSugarDbContextCreationContext.Use(creationContext))
{
var dbContext = await CreateDbContextAsync(unitOfWork);
return dbContext;
}
}
protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork)
{
return unitOfWork.Options.IsTransactional
? await CreateDbContextWithTransactionAsync(unitOfWork)
: unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
}
protected virtual async Task<TDbContext> CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork)
{
//事务key
var transactionApiKey = $"SqlSugarCore_{SqlSugarDbContextCreationContext.Current.ConnectionString}";
//尝试查找事务
var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as SqlSugarTransactionApi;
//该db还没有进行开启事务
if (activeTransaction == null)
{
//获取到db添加事务即可
var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
var transaction = new SqlSugarTransactionApi(
dbContext
);
unitOfWork.AddTransactionApi(transactionApiKey, transaction);
await dbContext.SqlSugarClient.Ado.BeginTranAsync();
return dbContext;
}
return (TDbContext)activeTransaction.GetDbContext();
}
}

@ -1,88 +1,89 @@
using NPin.Framework.AuditLogging.Domain.Entities;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Json;
namespace NPin.Framework.AuditLogging.Domain;
public class AuditLogInfoToAuditLogConverter : IAuditLogInfoToAuditLogConverter, ITransientDependency
{
protected IGuidGenerator GuidGenerator { get; }
protected IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; }
protected IJsonSerializer JsonSerializer { get; }
protected AbpExceptionHandlingOptions ExceptionHandlingOptions { get; }
public AuditLogInfoToAuditLogConverter(IGuidGenerator guidGenerator,
IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, IJsonSerializer jsonSerializer,
AbpExceptionHandlingOptions exceptionHandlingOptions)
{
GuidGenerator = guidGenerator;
ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter;
JsonSerializer = jsonSerializer;
ExceptionHandlingOptions = exceptionHandlingOptions;
}
public virtual Task<AuditLogAggregateRoot> ConvertAsync(AuditLogInfo auditLogInfo)
{
var auditLogId = GuidGenerator.Create();
var extraProperties = new ExtraPropertyDictionary();
foreach (var pair in auditLogInfo.ExtraProperties)
{
extraProperties.Add(pair.Key, pair.Value);
}
var entityChanges = auditLogInfo.EntityChanges?
.Select(info => new EntityChangeEntity(GuidGenerator, auditLogId, info, auditLogInfo.TenantId))
.ToList() ?? [];
var actions = auditLogInfo.Actions?
.Select(info => new AuditLogActionEntity(GuidGenerator.Create(), auditLogId, info, auditLogInfo.TenantId))
.ToList() ?? [];
var remoteServiceErrorInfos = auditLogInfo.Exceptions?
.Select(ex => ExceptionToErrorInfoConverter.Convert(ex, options =>
{
options.SendExceptionsDetailsToClients = ExceptionHandlingOptions.SendExceptionsDetailsToClients;
options.SendStackTraceToClients = ExceptionHandlingOptions.SendStackTraceToClients;
})) ?? [];
var exceptions = remoteServiceErrorInfos.Any()
? JsonSerializer.Serialize(remoteServiceErrorInfos, indented: true)
: null;
var comments = auditLogInfo.Comments?
.JoinAsString(Environment.NewLine);
var auditLog = new AuditLogAggregateRoot(
auditLogId,
auditLogInfo.ApplicationName,
auditLogInfo.UserId,
auditLogInfo.UserName,
auditLogInfo.TenantName,
auditLogInfo.ImpersonatorUserId,
auditLogInfo.ImpersonatorUserName,
auditLogInfo.ImpersonatorTenantId,
auditLogInfo.ImpersonatorTenantName,
auditLogInfo.ExecutionTime,
auditLogInfo.ExecutionDuration,
auditLogInfo.ClientIpAddress,
auditLogInfo.ClientName,
auditLogInfo.ClientId,
auditLogInfo.CorrelationId,
auditLogInfo.BrowserInfo,
auditLogInfo.HttpMethod,
auditLogInfo.Url,
exceptions,
comments,
auditLogInfo.HttpStatusCode,
auditLogInfo.TenantId,
entityChanges,
actions,
extraProperties
);
return Task.FromResult(auditLog);
}
using Microsoft.Extensions.Options;
using NPin.Framework.AuditLogging.Domain.Entities;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Json;
namespace NPin.Framework.AuditLogging.Domain;
public class AuditLogInfoToAuditLogConverter : IAuditLogInfoToAuditLogConverter, ITransientDependency
{
protected IGuidGenerator GuidGenerator { get; }
protected IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; }
protected IJsonSerializer JsonSerializer { get; }
protected AbpExceptionHandlingOptions ExceptionHandlingOptions { get; }
public AuditLogInfoToAuditLogConverter(IGuidGenerator guidGenerator,
IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, IJsonSerializer jsonSerializer,
IOptions<AbpExceptionHandlingOptions> exceptionHandlingOptions)
{
GuidGenerator = guidGenerator;
ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter;
JsonSerializer = jsonSerializer;
ExceptionHandlingOptions = exceptionHandlingOptions.Value;
}
public virtual Task<AuditLogAggregateRoot> ConvertAsync(AuditLogInfo auditLogInfo)
{
var auditLogId = GuidGenerator.Create();
var extraProperties = new ExtraPropertyDictionary();
foreach (var pair in auditLogInfo.ExtraProperties)
{
extraProperties.Add(pair.Key, pair.Value);
}
var entityChanges = auditLogInfo.EntityChanges?
.Select(info => new EntityChangeEntity(GuidGenerator, auditLogId, info, auditLogInfo.TenantId))
.ToList() ?? [];
var actions = auditLogInfo.Actions?
.Select(info => new AuditLogActionEntity(GuidGenerator.Create(), auditLogId, info, auditLogInfo.TenantId))
.ToList() ?? [];
var remoteServiceErrorInfos = auditLogInfo.Exceptions?
.Select(ex => ExceptionToErrorInfoConverter.Convert(ex, options =>
{
options.SendExceptionsDetailsToClients = ExceptionHandlingOptions.SendExceptionsDetailsToClients;
options.SendStackTraceToClients = ExceptionHandlingOptions.SendStackTraceToClients;
})) ?? [];
var exceptions = remoteServiceErrorInfos.Any()
? JsonSerializer.Serialize(remoteServiceErrorInfos, indented: true)
: null;
var comments = auditLogInfo.Comments?
.JoinAsString(Environment.NewLine);
var auditLog = new AuditLogAggregateRoot(
auditLogId,
auditLogInfo.ApplicationName,
auditLogInfo.UserId,
auditLogInfo.UserName,
auditLogInfo.TenantName,
auditLogInfo.ImpersonatorUserId,
auditLogInfo.ImpersonatorUserName,
auditLogInfo.ImpersonatorTenantId,
auditLogInfo.ImpersonatorTenantName,
auditLogInfo.ExecutionTime,
auditLogInfo.ExecutionDuration,
auditLogInfo.ClientIpAddress,
auditLogInfo.ClientName,
auditLogInfo.ClientId,
auditLogInfo.CorrelationId,
auditLogInfo.BrowserInfo,
auditLogInfo.HttpMethod,
auditLogInfo.Url,
exceptions,
comments,
auditLogInfo.HttpStatusCode,
auditLogInfo.TenantId,
entityChanges,
actions,
extraProperties
);
return Task.FromResult(auditLog);
}
}

@ -1,60 +1,61 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using NPin.Framework.AuditLogging.Domain.Repositories;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
namespace NPin.Framework.AuditLogging.Domain;
public class AuditingStore : IAuditingStore, ITransientDependency
{
public ILogger<AuditingStore> Logger { get; set; }
protected IAuditLogRepository AuditLogRepository { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected AbpAuditingOptions Options { get; }
protected IAuditLogInfoToAuditLogConverter Converter { get; }
public AuditingStore(ILogger<AuditingStore> logger, IAuditLogRepository auditLogRepository,
IUnitOfWorkManager unitOfWorkManager, AbpAuditingOptions options, IAuditLogInfoToAuditLogConverter converter)
{
Logger = logger;
AuditLogRepository = auditLogRepository;
UnitOfWorkManager = unitOfWorkManager;
Options = options;
Converter = converter;
}
public virtual async Task SaveAsync(AuditLogInfo auditInfo)
{
if (!Options.HideErrors)
{
await SaveLogAsync(auditInfo);
return;
}
try
{
await SaveLogAsync(auditInfo);
}
catch (Exception ex)
{
Logger.LogWarning($"无法保存审计日志: {Environment.NewLine}{auditInfo}");
Logger.LogException(ex, LogLevel.Error);
}
}
protected virtual async Task SaveLogAsync(AuditLogInfo auditInfo)
{
var timeConverter = new IsoDateTimeConverter
{
DateTimeFormat = "yyyy-MM-dd HH:mm:ss"
};
Logger.LogDebug($"NPin-请求日志:{JsonConvert.SerializeObject(auditInfo, Formatting.Indented, timeConverter)}");
using var uow = UnitOfWorkManager.Begin(true);
await AuditLogRepository.InsertAsync(await Converter.ConvertAsync(auditInfo));
await uow.CompleteAsync();
}
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using NPin.Framework.AuditLogging.Domain.Repositories;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
namespace NPin.Framework.AuditLogging.Domain;
public class AuditingStore : IAuditingStore, ITransientDependency
{
public ILogger<AuditingStore> Logger { get; set; }
protected IAuditLogRepository AuditLogRepository { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected AbpAuditingOptions Options { get; }
protected IAuditLogInfoToAuditLogConverter Converter { get; }
public AuditingStore(ILogger<AuditingStore> logger, IAuditLogRepository auditLogRepository,
IUnitOfWorkManager unitOfWorkManager, IOptions<AbpAuditingOptions> options, IAuditLogInfoToAuditLogConverter converter)
{
Logger = logger;
AuditLogRepository = auditLogRepository;
UnitOfWorkManager = unitOfWorkManager;
Options = options.Value;
Converter = converter;
}
public virtual async Task SaveAsync(AuditLogInfo auditInfo)
{
if (!Options.HideErrors)
{
await SaveLogAsync(auditInfo);
return;
}
try
{
await SaveLogAsync(auditInfo);
}
catch (Exception ex)
{
Logger.LogWarning($"无法保存审计日志: {Environment.NewLine}{auditInfo}");
Logger.LogException(ex, LogLevel.Error);
}
}
protected virtual async Task SaveLogAsync(AuditLogInfo auditInfo)
{
var timeConverter = new IsoDateTimeConverter
{
DateTimeFormat = "yyyy-MM-dd HH:mm:ss"
};
Logger.LogDebug($"NPin-请求日志:{JsonConvert.SerializeObject(auditInfo, Formatting.Indented, timeConverter)}");
using var uow = UnitOfWorkManager.Begin(true);
await AuditLogRepository.InsertAsync(await Converter.ConvertAsync(auditInfo));
await uow.CompleteAsync();
}
}

@ -1,51 +1,51 @@
using NPin.Framework.AuditLogging.Domain.Shared.Consts;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities;
[DisableAuditing]
[SugarTable("NPinAuditLogAction", "审计日志操作表")]
[SugarIndex($"index_{nameof(AuditLogId)}", nameof(AuditLogId), OrderByType.Asc)]
[SugarIndex($"index_{nameof(TenantId)}_{nameof(ExecutionTime)}", nameof(TenantId), OrderByType.Asc, nameof(ServiceName),
OrderByType.Asc, nameof(MethodName), OrderByType.Asc, nameof(ExecutionTime), OrderByType.Asc)]
public class AuditLogActionEntity : Entity<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid AuditLogId { get; protected set; }
public virtual string? ServiceName { get; protected set; }
public virtual string? MethodName { get; protected set; }
public virtual string? Parameters { get; protected set; }
public virtual DateTime? ExecutionTime { get; protected set; }
public virtual int? ExecutionDuration { get; protected set; }
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public AuditLogActionEntity()
{
}
public AuditLogActionEntity(Guid id, Guid auditLogId, AuditLogActionInfo actionInfo, Guid? tenantId = null)
{
Id = id;
TenantId = tenantId;
AuditLogId = auditLogId;
ExecutionTime = actionInfo.ExecutionTime;
ExecutionDuration = actionInfo.ExecutionDuration;
ServiceName = actionInfo.ServiceName.TruncateFromBeginning(AuditLogActionConsts.MaxServiceNameLength);
MethodName = actionInfo.MethodName.TruncateFromBeginning(AuditLogActionConsts.MaxMethodNameLength);
Parameters = actionInfo.Parameters.Length > AuditLogActionConsts.MaxParametersLength
? ""
: actionInfo.Parameters;
}
using NPin.Framework.AuditLogging.Domain.Shared.Consts;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities;
[DisableAuditing]
[SugarTable("SysAuditLogAction", "审计日志操作表")]
[SugarIndex($"index_{nameof(AuditLogId)}", nameof(AuditLogId), OrderByType.Asc)]
[SugarIndex($"index_{nameof(TenantId)}_{nameof(ExecutionTime)}", nameof(TenantId), OrderByType.Asc, nameof(ServiceName),
OrderByType.Asc, nameof(MethodName), OrderByType.Asc, nameof(ExecutionTime), OrderByType.Asc)]
public class AuditLogActionEntity : Entity<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid AuditLogId { get; protected set; }
public virtual string? ServiceName { get; protected set; }
public virtual string? MethodName { get; protected set; }
public virtual string? Parameters { get; protected set; }
public virtual DateTime? ExecutionTime { get; protected set; }
public virtual int? ExecutionDuration { get; protected set; }
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public AuditLogActionEntity()
{
}
public AuditLogActionEntity(Guid id, Guid auditLogId, AuditLogActionInfo actionInfo, Guid? tenantId = null)
{
Id = id;
TenantId = tenantId;
AuditLogId = auditLogId;
ExecutionTime = actionInfo.ExecutionTime;
ExecutionDuration = actionInfo.ExecutionDuration;
ServiceName = actionInfo.ServiceName.TruncateFromBeginning(AuditLogActionConsts.MaxServiceNameLength);
MethodName = actionInfo.MethodName.TruncateFromBeginning(AuditLogActionConsts.MaxMethodNameLength);
Parameters = actionInfo.Parameters.Length > AuditLogActionConsts.MaxParametersLength
? ""
: actionInfo.Parameters;
}
}

@ -1,111 +1,111 @@
using NPin.Framework.AuditLogging.Domain.Shared.Consts;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities;
[DisableAuditing]
[SugarTable("NPinAuditLog", "审计日志")]
[SugarIndex($"index_{nameof(ExecutionTime)}", nameof(TenantId), OrderByType.Asc, nameof(ExecutionTime),
OrderByType.Asc)]
[SugarIndex($"index_{nameof(ExecutionTime)}_{nameof(UserId)}", nameof(TenantId), OrderByType.Asc, nameof(UserId),
OrderByType.Asc, nameof(ExecutionTime), OrderByType.Asc)]
public class AuditLogAggregateRoot : AggregateRoot<Guid>, IMultiTenant
{
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public virtual string? ApplicationName { get; set; }
public virtual Guid? UserId { get; protected set; }
public virtual string? UserName { get; protected set; }
public virtual string? TenantName { get; protected set; }
public virtual Guid? ImpersonatorUserId { get; protected set; }
public virtual string? ImpersonatorUserName { get; protected set; }
public virtual Guid? ImpersonatorTenantId { get; protected set; }
public virtual string? ImpersonatorTenantName { get; protected set; }
public virtual DateTime? ExecutionTime { get; protected set; }
public virtual int? ExecutionDuration { get; protected set; }
public virtual string? ClientIpAddress { get; protected set; }
public virtual string? ClientName { get; protected set; }
public virtual string? ClientId { get; set; }
public virtual string? CorrelationId { get; set; }
public virtual string? BrowserInfo { get; protected set; }
public virtual string? HttpMethod { get; protected set; }
public virtual string? Url { get; protected set; }
public virtual string? Exceptions { get; protected set; }
public virtual string? Comments { get; protected set; }
public virtual int? HttpStatusCode { get; set; }
public virtual Guid? TenantId { get; protected set; }
// 导航属性
[Navigate(NavigateType.OneToMany, nameof(EntityChangeEntity.AuditLogId))]
public virtual List<EntityChangeEntity> EntityChanges { get; protected set; }
// 导航属性
[Navigate(NavigateType.OneToMany, nameof(AuditLogActionEntity.AuditLogId))]
public virtual List<AuditLogActionEntity> Actions { get; protected set; }
[SugarColumn(IsIgnore = true)] public override ExtraPropertyDictionary ExtraProperties { get; protected set; }
public AuditLogAggregateRoot()
{
}
public AuditLogAggregateRoot(Guid id, string? applicationName, Guid? userId, string? userName, string? tenantName,
Guid? impersonatorUserId, string? impersonatorUserName, Guid? impersonatorTenantId,
string? impersonatorTenantName, DateTime? executionTime, int? executionDuration, string? clientIpAddress,
string? clientName, string? clientId, string? correlationId, string? browserInfo, string? httpMethod,
string? url, string? exceptions, string? comments, int? httpStatusCode, Guid? tenantId,
List<EntityChangeEntity> entityChanges, List<AuditLogActionEntity> actions,
ExtraPropertyDictionary extraProperties) : base(id)
{
ApplicationName = applicationName.Truncate(AuditLogConsts.MaxApplicationNameLength);
UserId = userId;
UserName = userName.Truncate(AuditLogConsts.MaxUserNameLength);
TenantName = tenantName.Truncate(AuditLogConsts.MaxTenantNameLength);
ImpersonatorUserId = impersonatorUserId;
ImpersonatorUserName = impersonatorUserName.Truncate(AuditLogConsts.MaxUserNameLength);
ImpersonatorTenantId = impersonatorTenantId;
ImpersonatorTenantName = impersonatorTenantName.Truncate(AuditLogConsts.MaxTenantNameLength);
ExecutionTime = executionTime;
ExecutionDuration = executionDuration;
ClientIpAddress = clientIpAddress.Truncate(AuditLogConsts.MaxClientIpAddressLength);
ClientName = clientName.Truncate(AuditLogConsts.MaxClientNameLength);
ClientId = clientId.Truncate(AuditLogConsts.MaxClientIdLength);
CorrelationId = correlationId.Truncate(AuditLogConsts.MaxCorrelationIdLength);
BrowserInfo = browserInfo.Truncate(AuditLogConsts.MaxBrowserInfoLength);
HttpMethod = httpMethod.Truncate(AuditLogConsts.MaxHttpMethodLength);
Url = url.Truncate(AuditLogConsts.MaxUrlLength);
Exceptions = exceptions;
Comments = comments.Truncate(AuditLogConsts.MaxCommentsLength);
HttpStatusCode = httpStatusCode;
TenantId = tenantId;
EntityChanges = entityChanges;
Actions = actions;
ExtraProperties = extraProperties;
}
using NPin.Framework.AuditLogging.Domain.Shared.Consts;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities;
[DisableAuditing]
[SugarTable("SysAuditLog", "审计日志")]
[SugarIndex($"index_{nameof(ExecutionTime)}", nameof(TenantId), OrderByType.Asc, nameof(ExecutionTime),
OrderByType.Asc)]
[SugarIndex($"index_{nameof(ExecutionTime)}_{nameof(UserId)}", nameof(TenantId), OrderByType.Asc, nameof(UserId),
OrderByType.Asc, nameof(ExecutionTime), OrderByType.Asc)]
public class AuditLogAggregateRoot : AggregateRoot<Guid>, IMultiTenant
{
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public virtual string? ApplicationName { get; set; }
public virtual Guid? UserId { get; protected set; }
public virtual string? UserName { get; protected set; }
public virtual string? TenantName { get; protected set; }
public virtual Guid? ImpersonatorUserId { get; protected set; }
public virtual string? ImpersonatorUserName { get; protected set; }
public virtual Guid? ImpersonatorTenantId { get; protected set; }
public virtual string? ImpersonatorTenantName { get; protected set; }
public virtual DateTime? ExecutionTime { get; protected set; }
public virtual int? ExecutionDuration { get; protected set; }
public virtual string? ClientIpAddress { get; protected set; }
public virtual string? ClientName { get; protected set; }
public virtual string? ClientId { get; set; }
public virtual string? CorrelationId { get; set; }
public virtual string? BrowserInfo { get; protected set; }
public virtual string? HttpMethod { get; protected set; }
public virtual string? Url { get; protected set; }
public virtual string? Exceptions { get; protected set; }
public virtual string? Comments { get; protected set; }
public virtual int? HttpStatusCode { get; set; }
public virtual Guid? TenantId { get; protected set; }
// 导航属性
[Navigate(NavigateType.OneToMany, nameof(EntityChangeEntity.AuditLogId))]
public virtual List<EntityChangeEntity> EntityChanges { get; protected set; }
// 导航属性
[Navigate(NavigateType.OneToMany, nameof(AuditLogActionEntity.AuditLogId))]
public virtual List<AuditLogActionEntity> Actions { get; protected set; }
[SugarColumn(IsIgnore = true)] public override ExtraPropertyDictionary ExtraProperties { get; protected set; }
public AuditLogAggregateRoot()
{
}
public AuditLogAggregateRoot(Guid id, string? applicationName, Guid? userId, string? userName, string? tenantName,
Guid? impersonatorUserId, string? impersonatorUserName, Guid? impersonatorTenantId,
string? impersonatorTenantName, DateTime? executionTime, int? executionDuration, string? clientIpAddress,
string? clientName, string? clientId, string? correlationId, string? browserInfo, string? httpMethod,
string? url, string? exceptions, string? comments, int? httpStatusCode, Guid? tenantId,
List<EntityChangeEntity> entityChanges, List<AuditLogActionEntity> actions,
ExtraPropertyDictionary extraProperties) : base(id)
{
ApplicationName = applicationName.Truncate(AuditLogConsts.MaxApplicationNameLength);
UserId = userId;
UserName = userName.Truncate(AuditLogConsts.MaxUserNameLength);
TenantName = tenantName.Truncate(AuditLogConsts.MaxTenantNameLength);
ImpersonatorUserId = impersonatorUserId;
ImpersonatorUserName = impersonatorUserName.Truncate(AuditLogConsts.MaxUserNameLength);
ImpersonatorTenantId = impersonatorTenantId;
ImpersonatorTenantName = impersonatorTenantName.Truncate(AuditLogConsts.MaxTenantNameLength);
ExecutionTime = executionTime;
ExecutionDuration = executionDuration;
ClientIpAddress = clientIpAddress.Truncate(AuditLogConsts.MaxClientIpAddressLength);
ClientName = clientName.Truncate(AuditLogConsts.MaxClientNameLength);
ClientId = clientId.Truncate(AuditLogConsts.MaxClientIdLength);
CorrelationId = correlationId.Truncate(AuditLogConsts.MaxCorrelationIdLength);
BrowserInfo = browserInfo.Truncate(AuditLogConsts.MaxBrowserInfoLength);
HttpMethod = httpMethod.Truncate(AuditLogConsts.MaxHttpMethodLength);
Url = url.Truncate(AuditLogConsts.MaxUrlLength);
Exceptions = exceptions;
Comments = comments.Truncate(AuditLogConsts.MaxCommentsLength);
HttpStatusCode = httpStatusCode;
TenantId = tenantId;
EntityChanges = entityChanges;
Actions = actions;
ExtraProperties = extraProperties;
}
}

@ -1,63 +1,63 @@
using NPin.Framework.AuditLogging.Domain.Shared.Consts;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities;
[SugarTable("NPinEntityChange")]
[SugarIndex($"index_{nameof(AuditLogId)}", nameof(AuditLogId), OrderByType.Asc)]
[SugarIndex($"index_{nameof(TenantId)}_{nameof(EntityId)}", nameof(TenantId), OrderByType.Asc,
nameof(EntityTypeFullName), OrderByType.Asc, nameof(EntityId), OrderByType.Asc)]
public class EntityChangeEntity : Entity<Guid>, IMultiTenant
{
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public virtual Guid AuditLogId { get; protected set; }
public virtual Guid? TenantId { get; protected set; }
public virtual DateTime? ChangeTime { get; protected set; }
public virtual EntityChangeType? ChangeType { get; protected set; }
public virtual Guid? EntityTenantId { get; protected set; }
public virtual string? EntityId { get; protected set; }
public virtual string? EntityTypeFullName { get; protected set; }
// 关联
[Navigate(NavigateType.OneToMany, nameof(EntityPropertyChangeEntity.EntityChangeId))]
public virtual List<EntityPropertyChangeEntity> PropertyChanges { get; protected set; }
public EntityChangeEntity()
{
}
public EntityChangeEntity(
IGuidGenerator guidGenerator,
Guid auditLogId,
EntityChangeInfo entityChangeInfo,
Guid? tenantId = null)
{
Id = guidGenerator.Create();
AuditLogId = auditLogId;
TenantId = tenantId;
ChangeTime = entityChangeInfo.ChangeTime;
ChangeType = entityChangeInfo.ChangeType;
EntityTenantId = entityChangeInfo.EntityTenantId;
EntityId = entityChangeInfo.EntityId.Truncate(EntityChangeConsts.MaxEntityTypeFullNameLength);
EntityTypeFullName =
entityChangeInfo.EntityTypeFullName.TruncateFromBeginning(EntityChangeConsts.MaxEntityTypeFullNameLength);
PropertyChanges = entityChangeInfo
.PropertyChanges?
.Select(p => new EntityPropertyChangeEntity(guidGenerator, Id, p, tenantId))
.ToList()
?? new List<EntityPropertyChangeEntity>();
}
using NPin.Framework.AuditLogging.Domain.Shared.Consts;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities;
[SugarTable("SysEntityChange")]
[SugarIndex($"index_{nameof(AuditLogId)}", nameof(AuditLogId), OrderByType.Asc)]
[SugarIndex($"index_{nameof(TenantId)}_{nameof(EntityId)}", nameof(TenantId), OrderByType.Asc,
nameof(EntityTypeFullName), OrderByType.Asc, nameof(EntityId), OrderByType.Asc)]
public class EntityChangeEntity : Entity<Guid>, IMultiTenant
{
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public virtual Guid AuditLogId { get; protected set; }
public virtual Guid? TenantId { get; protected set; }
public virtual DateTime? ChangeTime { get; protected set; }
public virtual EntityChangeType? ChangeType { get; protected set; }
public virtual Guid? EntityTenantId { get; protected set; }
public virtual string? EntityId { get; protected set; }
public virtual string? EntityTypeFullName { get; protected set; }
// 关联
[Navigate(NavigateType.OneToMany, nameof(EntityPropertyChangeEntity.EntityChangeId))]
public virtual List<EntityPropertyChangeEntity> PropertyChanges { get; protected set; }
public EntityChangeEntity()
{
}
public EntityChangeEntity(
IGuidGenerator guidGenerator,
Guid auditLogId,
EntityChangeInfo entityChangeInfo,
Guid? tenantId = null)
{
Id = guidGenerator.Create();
AuditLogId = auditLogId;
TenantId = tenantId;
ChangeTime = entityChangeInfo.ChangeTime;
ChangeType = entityChangeInfo.ChangeType;
EntityTenantId = entityChangeInfo.EntityTenantId;
EntityId = entityChangeInfo.EntityId.Truncate(EntityChangeConsts.MaxEntityTypeFullNameLength);
EntityTypeFullName =
entityChangeInfo.EntityTypeFullName.TruncateFromBeginning(EntityChangeConsts.MaxEntityTypeFullNameLength);
PropertyChanges = entityChangeInfo
.PropertyChanges?
.Select(p => new EntityPropertyChangeEntity(guidGenerator, Id, p, tenantId))
.ToList()
?? new List<EntityPropertyChangeEntity>();
}
}

@ -1,50 +1,50 @@
using NPin.Framework.AuditLogging.Domain.Shared.Consts;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities;
[SugarTable("NPinEntityPropertyChange")]
[SugarIndex($"index_{nameof(EntityChangeId)}", nameof(EntityChangeId), OrderByType.Asc)]
public class EntityPropertyChangeEntity : Entity<Guid>, IMultiTenant
{
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public virtual Guid? TenantId { get; protected set; }
public virtual Guid? EntityChangeId { get; protected set; }
public virtual string? NewValue { get; protected set; }
public virtual string? OriginalValue { get; protected set; }
public virtual string? PropertyName { get; protected set; }
public virtual string? PropertyTypeFullName { get; protected set; }
public EntityPropertyChangeEntity()
{
}
public EntityPropertyChangeEntity(
IGuidGenerator guidGenerator,
Guid entityChangeId,
EntityPropertyChangeInfo entityChangeInfo,
Guid? tenantId = null)
{
Id = guidGenerator.Create();
TenantId = tenantId;
EntityChangeId = entityChangeId;
NewValue = entityChangeInfo.NewValue.Truncate(EntityPropertyChangeConsts.MaxNewValueLength);
OriginalValue = entityChangeInfo.OriginalValue.Truncate(EntityPropertyChangeConsts.MaxOriginalValueLength);
PropertyName =
entityChangeInfo.PropertyName.TruncateFromBeginning(EntityPropertyChangeConsts.MaxPropertyNameLength);
PropertyTypeFullName =
entityChangeInfo.PropertyTypeFullName.TruncateFromBeginning(EntityPropertyChangeConsts
.MaxPropertyTypeFullNameLength);
}
using NPin.Framework.AuditLogging.Domain.Shared.Consts;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities;
[SugarTable("SysEntityPropertyChange")]
[SugarIndex($"index_{nameof(EntityChangeId)}", nameof(EntityChangeId), OrderByType.Asc)]
public class EntityPropertyChangeEntity : Entity<Guid>, IMultiTenant
{
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public virtual Guid? TenantId { get; protected set; }
public virtual Guid? EntityChangeId { get; protected set; }
public virtual string? NewValue { get; protected set; }
public virtual string? OriginalValue { get; protected set; }
public virtual string? PropertyName { get; protected set; }
public virtual string? PropertyTypeFullName { get; protected set; }
public EntityPropertyChangeEntity()
{
}
public EntityPropertyChangeEntity(
IGuidGenerator guidGenerator,
Guid entityChangeId,
EntityPropertyChangeInfo entityChangeInfo,
Guid? tenantId = null)
{
Id = guidGenerator.Create();
TenantId = tenantId;
EntityChangeId = entityChangeId;
NewValue = entityChangeInfo.NewValue.Truncate(EntityPropertyChangeConsts.MaxNewValueLength);
OriginalValue = entityChangeInfo.OriginalValue.Truncate(EntityPropertyChangeConsts.MaxOriginalValueLength);
PropertyName =
entityChangeInfo.PropertyName.TruncateFromBeginning(EntityPropertyChangeConsts.MaxPropertyNameLength);
PropertyTypeFullName =
entityChangeInfo.PropertyTypeFullName.TruncateFromBeginning(EntityPropertyChangeConsts
.MaxPropertyTypeFullNameLength);
}
}

@ -1,38 +1,40 @@
using JetBrains.Annotations;
using SqlSugar;
using Volo.Abp.Domain.Entities;
using Check = Volo.Abp.Check;
namespace NPin.Framework.SettingManagement.Domain.Entities;
public class SettingEntity: Entity<Guid>, IAggregateRoot<Guid>
[SugarTable("SysSetting", "系统配置表")]
public class SettingEntity : Entity<Guid>, IAggregateRoot<Guid>
{
[NotNull]
public virtual string Name { get; protected set; }
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
[NotNull]
public virtual string Value { get; internal set; }
[SugarColumn(ColumnDescription = "配置名称")]
public string Name { get; protected set; }
[CanBeNull]
public virtual string ProviderName { get; protected set; }
[SugarColumn(ColumnDescription = "配置值", ColumnDataType = StaticConfig.CodeFirst_BigString)]
public string Value { get; internal set; }
[CanBeNull]
public virtual string ProviderKey { get; protected set; }
[SugarColumn(ColumnDescription = "配置提供者")]
public string? ProviderName { get; protected set; }
[SugarColumn(ColumnDescription = "配置提供者Key")]
public string? ProviderKey { get; protected set; }
public SettingEntity()
{
}
public SettingEntity(
Guid id,
[NotNull] string name,
[NotNull] string value,
[CanBeNull] string providerName = null,
[CanBeNull] string providerKey = null)
string name,
string value,
string? providerName = null,
string? providerKey = null)
{
Check.NotNull(name, nameof(name));
Check.NotNull(value, nameof(value));
Id = id;
Name = name;
Value = value;
ProviderName = providerName;
@ -41,6 +43,7 @@ public class SettingEntity: Entity<Guid>, IAggregateRoot<Guid>
public override string ToString()
{
return $"{base.ToString()}, Name = {Name}, Value = {Value}, ProviderName = {ProviderName}, ProviderKey = {ProviderKey}";
return
$"{base.ToString()}, Name = {Name}, Value = {Value}, ProviderName = {ProviderName}, ProviderKey = {ProviderKey}";
}
}

@ -10,5 +10,9 @@
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.SettingManagement.Domain.Shared" Version="$(AbpVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\NPin.Framework.SqlSugarCore.Abstractions\NPin.Framework.SqlSugarCore.Abstractions.csproj" />
</ItemGroup>
</Project>

@ -10,6 +10,8 @@ namespace NPin.Framework.SettingManagement.Domain;
public class SettingManager : ISettingManager, ISingletonDependency
{
private readonly Lazy<List<ISettingManagementProvider>> _lazyProviders;
protected readonly IServiceProvider ServiceProvider;
protected ISettingDefinitionManager SettingDefinitionManager { get; }
protected ISettingEncryptionService SettingEncryptionService { get; }
protected List<ISettingManagementProvider> Providers => _lazyProviders.Value;
@ -21,18 +23,21 @@ public class SettingManager : ISettingManager, ISingletonDependency
ISettingEncryptionService settingEncryptionService,
IOptions<SettingManagementOptions> options)
{
ServiceProvider = serviceProvider;
SettingDefinitionManager = settingDefinitionManager;
SettingEncryptionService = settingEncryptionService;
Options = options.Value;
// TODO use IServiceScopeFactory and create a scope ?
_lazyProviders = new Lazy<List<ISettingManagementProvider>>(
() => Options
.Providers
.Select(c => serviceProvider.GetRequiredService(c) as ISettingManagementProvider)
.ToList(),
true
);
_lazyProviders = new Lazy<List<ISettingManagementProvider>>(CreateSettingManagementProviders, true);
}
protected virtual List<ISettingManagementProvider> CreateSettingManagementProviders()
{
using var scope = ServiceProvider.CreateScope();
return Options.Providers
.Select(p => (scope.ServiceProvider.GetRequiredService(p) as ISettingManagementProvider)!)
.ToList();
}
public virtual Task<string?> GetOrNullAsync(string name, string providerName, string? providerKey,

@ -13,16 +13,13 @@ public class SettingManagementStore : ISettingManagementStore, ITransientDepende
protected IDistributedCache<SettingCacheItem> Cache { get; }
protected ISettingDefinitionManager SettingDefinitionManager { get; }
protected ISettingRepository SettingRepository { get; }
protected IGuidGenerator GuidGenerator { get; }
public SettingManagementStore(IDistributedCache<SettingCacheItem> cache,
ISettingDefinitionManager settingDefinitionManager, ISettingRepository settingRepository,
IGuidGenerator guidGenerator)
ISettingDefinitionManager settingDefinitionManager, ISettingRepository settingRepository)
{
Cache = cache;
SettingDefinitionManager = settingDefinitionManager;
SettingRepository = settingRepository;
GuidGenerator = guidGenerator;
}
[UnitOfWork]
@ -65,7 +62,7 @@ public class SettingManagementStore : ISettingManagementStore, ITransientDepende
var setting = await SettingRepository.FindAsync(name, providerName, providerKey);
if (setting == null)
{
setting = new SettingEntity(GuidGenerator.Create(), name, value, providerName, providerKey);
setting = new SettingEntity(name, value, providerName, providerKey);
await SettingRepository.InsertAsync(setting);
}
else

@ -1,143 +1,152 @@
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using NPin.Framework.Ddd.Application;
using NPin.Framework.SqlSugarCore.Abstractions;
using NPin.Framework.TenantManagement.Application.Contracts;
using NPin.Framework.TenantManagement.Application.Contracts.Dtos;
using NPin.Framework.TenantManagement.Domain;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Modularity;
namespace NPin.Framework.TenantManagement.Application;
public class TenantService : NPinCrudAppService<TenantAggregateRoot, TenantGetOutputDto, TenantGetListOutputDto, Guid,
TenantGetListInput, TenantCreateInput, TenantUpdateInput>, ITenantService
{
private ISqlSugarRepository<TenantAggregateRoot, Guid> _repository;
private IDataSeeder _dataSeeder;
public TenantService(IRepository<TenantAggregateRoot, Guid> repository,
ISqlSugarRepository<TenantAggregateRoot, Guid> sqlSugarRepository, IDataSeeder dataSeeder) : base(repository)
{
_repository = sqlSugarRepository;
_dataSeeder = dataSeeder;
}
/// <summary>
/// 多查
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<PagedResultDto<TenantGetListOutputDto>> GetListAsync(TenantGetListInput input)
{
RefAsync<int> total = 0;
var entities = await _repository.DbQueryable
.WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name.Contains(input.Name!))
.WhereIF(input.StartTime is not null && input.EndTime is not null,
x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<TenantGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}
/// <summary>
/// 租户选项列表(显示名称)
/// </summary>
/// <returns></returns>
public async Task<List<TenantSelectOutputDto>> GetSelectAsync()
{
var entities = await _repository.DbQueryable.ToListAsync();
return entities.Select(x => new TenantSelectOutputDto { Id = x.Id, Name = x.Name })
.ToList();
}
/// <summary>
/// 创建租户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<TenantGetOutputDto> CreateAsync(TenantCreateInput input)
{
// 检查是否存在
if (await _repository.IsAnyAsync(x => x.Name == input.Name))
{
throw new UserFriendlyException("创建失败,当前租户已存在");
}
return await base.CreateAsync(input);
}
/// <summary>
/// 更新租户信息
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<TenantGetOutputDto> UpdateAsync(Guid id, TenantUpdateInput input)
{
if (await _repository.IsAnyAsync(x => x.Name == input.Name && x.Id != id))
{
throw new UserFriendlyException("更新失败,更新后租户已存在");
}
return await base.UpdateAsync(id, input);
}
// 只是为了可以在swagger上展示?
/// <summary>
/// 租户删除
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
public override Task DeleteAsync(IEnumerable<Guid> ids)
{
return base.DeleteAsync(ids);
}
/// <summary>
/// 初始化租户
/// </summary>
/// <param name="id"></param>
[HttpPost("tenant/init/{id}")]
public async Task InitAsync([FromRoute] Guid id)
{
using (CurrentTenant.Change(id))
{
// 初始化 租户数据库/表 结构
await CodeFirst(LazyServiceProvider);
// 插入 种子数据
await _dataSeeder.SeedAsync(id);
}
}
/// <summary>
/// 数据库 / 表 初始化
/// </summary>
/// <param name="service"></param>
private async Task CodeFirst(IServiceProvider service)
{
var moduleContainer = service.GetRequiredService<IModuleContainer>();
var db = await _repository.GetDbContextAsync();
// 尝试创建数据库
db.DbMaintenance.CreateDatabase();
List<Type> types = new List<Type>();
foreach (var module in moduleContainer.Modules)
{
types.AddRange(module.Assembly.GetTypes()
.Where(x => x.GetCustomAttribute<IgnoreCodeFirstAttribute>() == null)
.Where(x => x.GetCustomAttribute<SugarTable>() != null)
.Where(x => x.GetCustomAttribute<SplitTableAttribute>() is null));
}
if (types.Count > 0)
{
db.CopyNew().CodeFirst.InitTables(types.ToArray());
}
}
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using NPin.Framework.Ddd.Application;
using NPin.Framework.SqlSugarCore.Abstractions;
using NPin.Framework.TenantManagement.Application.Contracts;
using NPin.Framework.TenantManagement.Application.Contracts.Dtos;
using NPin.Framework.TenantManagement.Domain;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Modularity;
namespace NPin.Framework.TenantManagement.Application;
public class TenantService : NPinCrudAppService<TenantAggregateRoot, TenantGetOutputDto, TenantGetListOutputDto, Guid,
TenantGetListInput, TenantCreateInput, TenantUpdateInput>, ITenantService
{
private ISqlSugarRepository<TenantAggregateRoot, Guid> _repository;
private IDataSeeder _dataSeeder;
public TenantService(IRepository<TenantAggregateRoot, Guid> repository,
ISqlSugarRepository<TenantAggregateRoot, Guid> sqlSugarRepository, IDataSeeder dataSeeder) : base(repository)
{
_repository = sqlSugarRepository;
_dataSeeder = dataSeeder;
}
/// <summary>
/// 租户单查
/// </summary>
/// <param name="id">唯一ID</param>
/// <returns></returns>
public override Task<TenantGetOutputDto> GetAsync(Guid id)
{
return base.GetAsync(id);
}
/// <summary>
/// 租户多查
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<PagedResultDto<TenantGetListOutputDto>> GetListAsync(TenantGetListInput input)
{
RefAsync<int> total = 0;
var entities = await _repository.DbQueryable
.WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name.Contains(input.Name!))
.WhereIF(input.StartTime is not null && input.EndTime is not null,
x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<TenantGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}
/// <summary>
/// 租户选项列表(显示名称)
/// </summary>
/// <returns></returns>
public async Task<List<TenantSelectOutputDto>> GetSelectAsync()
{
var entities = await _repository.DbQueryable.ToListAsync();
return entities.Select(x => new TenantSelectOutputDto { Id = x.Id, Name = x.Name })
.ToList();
}
/// <summary>
/// 创建租户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<TenantGetOutputDto> CreateAsync(TenantCreateInput input)
{
// 检查是否存在
if (await _repository.IsAnyAsync(x => x.Name == input.Name))
{
throw new UserFriendlyException("创建失败,当前租户已存在");
}
return await base.CreateAsync(input);
}
/// <summary>
/// 更新租户信息
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<TenantGetOutputDto> UpdateAsync(Guid id, TenantUpdateInput input)
{
if (await _repository.IsAnyAsync(x => x.Name == input.Name && x.Id != id))
{
throw new UserFriendlyException("更新失败,更新后租户已存在");
}
return await base.UpdateAsync(id, input);
}
/// <summary>
/// 租户删除
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
public override Task DeleteAsync(IEnumerable<Guid> ids)
{
return base.DeleteAsync(ids);
}
/// <summary>
/// 初始化租户
/// </summary>
/// <param name="id"></param>
[HttpPost("tenant/init/{id}")]
public async Task InitAsync([FromRoute] Guid id)
{
using (CurrentTenant.Change(id))
{
// 初始化 租户数据库/表 结构
await CodeFirst(LazyServiceProvider);
// 插入 种子数据
await _dataSeeder.SeedAsync(id);
}
}
/// <summary>
/// 数据库 / 表 初始化
/// </summary>
/// <param name="service"></param>
private async Task CodeFirst(IServiceProvider service)
{
var moduleContainer = service.GetRequiredService<IModuleContainer>();
var db = await _repository.GetDbContextAsync();
// 尝试创建数据库
db.DbMaintenance.CreateDatabase();
List<Type> types = new List<Type>();
foreach (var module in moduleContainer.Modules)
{
types.AddRange(module.Assembly.GetTypes()
.Where(x => x.GetCustomAttribute<IgnoreCodeFirstAttribute>() == null)
.Where(x => x.GetCustomAttribute<SugarTable>() != null)
.Where(x => x.GetCustomAttribute<SplitTableAttribute>() is null));
}
if (types.Count > 0)
{
db.CopyNew().CodeFirst.InitTables(types.ToArray());
}
}
}

@ -1,50 +1,50 @@
using JetBrains.Annotations;
using NPin.Framework.SqlSugarCore.Abstractions;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.TenantManagement;
using Check = Volo.Abp.Check;
namespace NPin.Framework.TenantManagement.Domain;
[SugarTable("NPinTenant")]
[MasterTenant]
public class TenantAggregateRoot : FullAuditedAggregateRoot<Guid>, IHasEntityVersion
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
public virtual string Name { get; protected set; }
public int EntityVersion { get; protected set; }
public string TenantConnectionString { get; protected set; }
public DbType DbType { get; protected set; }
[SugarColumn(IsIgnore = true)]
public override ExtraPropertyDictionary ExtraProperties
{
get => base.ExtraProperties;
protected set => base.ExtraProperties = value;
}
public virtual void SetConnectionString(DbType dbType, string connectionString)
{
DbType = dbType;
TenantConnectionString = connectionString;
}
protected internal virtual void SetName([NotNull] string name)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name), TenantConsts.MaxNameLength);
}
public TenantAggregateRoot()
{
}
protected internal TenantAggregateRoot(Guid id, [NotNull] string name) : base(id)
{
SetName(name);
}
using JetBrains.Annotations;
using NPin.Framework.SqlSugarCore.Abstractions;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.TenantManagement;
using Check = Volo.Abp.Check;
namespace NPin.Framework.TenantManagement.Domain;
[SugarTable("SysTenant", "租户表")]
[MasterTenant]
public class TenantAggregateRoot : FullAuditedAggregateRoot<Guid>, IHasEntityVersion
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
public virtual string Name { get; protected set; }
public int EntityVersion { get; protected set; }
public string TenantConnectionString { get; protected set; }
public DbType DbType { get; protected set; }
[SugarColumn(IsIgnore = true)]
public override ExtraPropertyDictionary ExtraProperties
{
get => base.ExtraProperties;
protected set => base.ExtraProperties = value;
}
public virtual void SetConnectionString(DbType dbType, string connectionString)
{
DbType = dbType;
TenantConnectionString = connectionString;
}
protected internal virtual void SetName([NotNull] string name)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name), TenantConsts.MaxNameLength);
}
public TenantAggregateRoot()
{
}
protected internal TenantAggregateRoot(Guid id, [NotNull] string name) : base(id)
{
SetName(name);
}
}

@ -1,13 +1,18 @@
namespace NPin.Framework.Upms.Domain.Shared.Consts;
public class ConfigConst
{
/// <summary>
/// 系统配置前缀
/// </summary>
public const string SysConfigPrefix = "Sys";
public const string AliyunConfigKey = "Aliyun";
public const string TencentConfigKey = "Tencent";
public const string SmsConfigKey = "Sms";
namespace NPin.Framework.Upms.Domain.Shared.Consts;
public class ConfigConst
{
/// <summary>
/// 系统配置前缀
/// </summary>
public const string SysConfigPrefix = "sys";
/// <summary>
/// 短信配置相关
/// </summary>
public const string SmsConfigKey = "sms";
public const string SmsProvidersKey = $"{SysConfigPrefix}.{SmsConfigKey}.providers";
public const string SmsTemplatesKey = $"{SysConfigPrefix}.{SmsConfigKey}.templates";
public const string SmsSettingsKey = $"{SysConfigPrefix}.{SmsConfigKey}.settings";
public const string SmsConfigModelKey = $"{SysConfigPrefix}.{SmsConfigKey}.config";
}

@ -1,22 +1,22 @@
using System.ComponentModel;
namespace NPin.Framework.Upms.Domain.Shared.Enums;
/// <summary>
/// 短信提供商枚举
/// </summary>
public enum SmsProviderEnum
{
Aliyun,
Tencent
}
/// <summary>
/// 短信类型枚举
/// </summary>
public enum SmsTypeEnum
{
[Description("登录")] Login,
[Description("注册")] Register,
[Description("找回密码(重置密码)")] ResetPassword,
using System.ComponentModel;
namespace NPin.Framework.Upms.Domain.Shared.Enums;
/// <summary>
/// 短信提供商枚举
/// </summary>
public enum SmsProviderTypeEnum
{
Aliyun,
Tencent
}
/// <summary>
/// 短信类型枚举
/// </summary>
public enum SmsTypeEnum
{
[Description("登录")] Login,
[Description("注册")] Register,
[Description("找回密码(重置密码)")] ResetPassword,
}

@ -1,24 +0,0 @@
namespace NPin.Framework.Upms.Domain.Shared.Model;
public class AliyunConfigModel
{
/// <summary>
/// 访问密钥Key
/// </summary>
public string AccessKeyId { get; set; }
/// <summary>
/// 访问密钥
/// </summary>
public string AccessKeySecret { get; set; }
/// <summary>
/// 默认 区域ID
/// </summary>
public string RegionId { get; set; }
/// <summary>
/// 默认 短信访问 端点
/// </summary>
public string SmsEndpoint { get; set; } = "dysmsapi.aliyuncs.com";
}

@ -1,54 +1,128 @@
using NPin.Framework.Upms.Domain.Shared.Enums;
namespace NPin.Framework.Upms.Domain.Shared.Model;
public class SmsConfigModel
{
/// <summary>
/// 是否启用(总控)
/// </summary>
public bool Enabled { get; set; } = false;
/// <summary>
/// 设定集
/// </summary>
public Dictionary<SmsTypeEnum, SmsSettings> Settings { get; set; }
}
public class SmsSettings
{
/// <summary>
/// 是否启用
/// </summary>
public bool Enabled { get; set; } = false;
/// <summary>
/// 服务提供商
/// </summary>
public SmsProviderEnum Provider { get; set; }
/// <summary>
/// 区域
/// </summary>
public string Region { get; set; }
/// <summary>
/// 短信访问 端点
/// </summary>
public string Endpoint { get; set; }
/// <summary>
/// 短信签名名称
/// </summary>
public string SignName { get; set; }
/// <summary>
/// 短信模板Code
/// </summary>
public string TemplateCode { get; set; }
/// <summary>
/// 过期时间,单位:秒
/// </summary>
public int Expires { get; set; }
using Newtonsoft.Json;
using NPin.Framework.Upms.Domain.Shared.Enums;
namespace NPin.Framework.Upms.Domain.Shared.Model;
public class SmsConfigModel
{
/// <summary>
/// 是否启用(总控)
/// </summary>
public bool Enabled { get; set; } = false;
/// <summary>
/// 提供商列表
/// </summary>
[JsonIgnore]
public List<SmsProvider> Providers { get; set; } = [];
/// <summary>
/// 短信模板列表
/// </summary>
[JsonIgnore]
public List<SmsTemplate> Templates { get; set; } = [];
/// <summary>
/// 系统短信设定集
/// 类型:设定
/// </summary>
[JsonIgnore]
public Dictionary<SmsTypeEnum, SmsSettings> Settings { get; set; } = new();
public SmsProvider GetProvider(SmsSettings settings)
{
return Providers.First(it => it.Id == settings.ProviderId);
}
public SmsTemplate GetTemplate(SmsSettings settings)
{
return Templates.First(it => it.Id == settings.TemplateId);
}
public SmsSettings? GetSetting(SmsTypeEnum typeEnum)
{
return Settings.GetValueOrDefault(typeEnum, null);
}
}
/// <summary>
/// 短信提供商配置
/// </summary>
public class SmsProvider
{
/// <summary>
/// ID
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// 服务提供商
/// </summary>
public SmsProviderTypeEnum ProviderType { get; set; }
/// <summary>
/// 访问密钥Key
/// </summary>
public string AccessKeyId { get; set; }
/// <summary>
/// 访问密钥
/// </summary>
public string AccessKeySecret { get; set; }
/// <summary>
/// 区域
/// </summary>
public string Region { get; set; }
/// <summary>
/// 短信访问 端点
/// </summary>
public string Endpoint { get; set; }
/// <summary>
/// 短信SdkAppId
/// </summary>
public string SmsSdkAppId { get; set; }
}
public class SmsTemplate
{
/// <summary>
/// ID
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// 短信签名名称
/// </summary>
public string SignName { get; set; }
/// <summary>
/// 短信模板Code
/// </summary>
public string TemplateCode { get; set; }
}
public class SmsSettings
{
/// <summary>
/// 短信类型
/// </summary>
public SmsTypeEnum SmsType { get; set; }
/// <summary>
/// 提供商ID
/// </summary>
public Guid ProviderId { get; set; }
/// <summary>
/// 模板ID
/// </summary>
public Guid TemplateId { get; set; }
/// <summary>
/// 过期时间,单位:秒
/// </summary>
public int Expires { get; set; }
}

@ -1,31 +0,0 @@
using Newtonsoft.Json;
namespace NPin.Framework.Upms.Domain.Shared.Model;
public class TencentConfigModel
{
/// <summary>
/// 访问密钥Key
/// </summary>
public string SecretId { get; set; }
/// <summary>
/// 访问密钥
/// </summary>
public string SecretKey { get; set; }
/// <summary>
/// 默认区域
/// </summary>
public string Region { get; set; }
/// <summary>
/// 默认短信访问 端点
/// </summary>
public string SmsEndpoint { get; set; } = "sms.tencentcloudapi.com";
/// <summary>
/// 短信SdkAppId
/// </summary>
public string SmsSdkAppId { get; set; }
}

@ -1,46 +1,46 @@
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Authorization;
[SugarTable("Social", "第三方授权表")]
public class SocialAggregateRoot: AggregateRoot<Guid>, ISoftDelete, IHasCreationTime
{
[SugarColumn(IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public Guid UserId { get; set; }
public string OpenId { get; set; }
public string? UnionId { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; }
[SugarColumn(IsIgnore = true)]
public override ExtraPropertyDictionary ExtraProperties { get; protected set; }
public SocialAggregateRoot()
{
}
public SocialAggregateRoot(string type, Guid userId, string openId, string? unionId)
{
UserId = userId;
OpenId = openId;
UnionId = unionId;
Type = type;
}
public SocialAggregateRoot(string type, Guid userId, string openId, string? unionId, string name): this(type, userId, openId, unionId)
{
Name = name;
}
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Authorization;
[SugarTable("SysSocial", "第三方授权表")]
public class SocialAggregateRoot: AggregateRoot<Guid>, ISoftDelete, IHasCreationTime
{
[SugarColumn(IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public Guid UserId { get; set; }
public string OpenId { get; set; }
public string? UnionId { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; }
[SugarColumn(IsIgnore = true)]
public override ExtraPropertyDictionary ExtraProperties { get; protected set; }
public SocialAggregateRoot()
{
}
public SocialAggregateRoot(string type, Guid userId, string openId, string? unionId)
{
UserId = userId;
OpenId = openId;
UnionId = unionId;
Type = type;
}
public SocialAggregateRoot(string type, Guid userId, string openId, string? unionId, string name): this(type, userId, openId, unionId)
{
Name = name;
}
}

@ -1,32 +1,32 @@
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("announcement", "系统公告表")]
public class AnnouncementEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "公告标题")]
public string Title { get; set; }
[SugarColumn(ColumnDescription = "公告分类")]
public string Category { get; set; }
[SugarColumn(ColumnDescription = "公告行为")]
public string Action { get; set; }
[SugarColumn(ColumnDescription = "公告内容", ColumnDataType = StaticConfig.CodeFirst_BigString)]
public string Content { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
public int OrderNum { get; set; }
public bool IsEnabled { get; set; }
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysAnnouncement", "系统公告表")]
public class AnnouncementEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "公告标题")]
public string Title { get; set; }
[SugarColumn(ColumnDescription = "公告分类")]
public string Category { get; set; }
[SugarColumn(ColumnDescription = "公告行为")]
public string Action { get; set; }
[SugarColumn(ColumnDescription = "公告内容", ColumnDataType = StaticConfig.CodeFirst_BigString)]
public string Content { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
public int OrderNum { get; set; }
public bool IsEnabled { get; set; }
}

@ -1,34 +1,34 @@
using Mapster;
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("Config", "系统配置表")]
[SugarIndex($"index_{nameof(Key)}", nameof(Key), OrderByType.Asc, true)]
public class ConfigEntity : Entity<Guid>, IEnabled, IOrderNum, ISoftDelete, IAuditedObject
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "配置名称")]
public string Name { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "配置键")]
public string Key { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "配置值")]
public string Value { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "配置描述")]
public string? Remark { get; set; } = string.Empty;
public bool IsEnabled { get; set; }
public int OrderNum { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
using Mapster;
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysConfig", "系统配置表")]
[SugarIndex($"index_{nameof(Key)}", nameof(Key), OrderByType.Asc, true)]
public class ConfigEntity : Entity<Guid>, IEnabled, IOrderNum, ISoftDelete, IAuditedObject
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "配置名称")]
public string Name { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "配置键")]
public string Key { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "配置值")]
public string Value { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "配置描述")]
public string? Remark { get; set; } = string.Empty;
public bool IsEnabled { get; set; }
public int OrderNum { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
}

@ -1,41 +1,41 @@
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("File", "文件信息表")]
public class FileAggregateRoot : AggregateRoot<Guid>, IAuditedObject
{
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "文件大小")]
public decimal FileSize { get; set; }
[SugarColumn(ColumnDescription = "文件名称")]
public string FileName { get; set; }
[SugarColumn(ColumnDescription = "文件后缀")]
public string FileExt { get; set; }
[SugarColumn(ColumnDescription = "文件路径")]
public string FilePath { get; set; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
[SugarColumn(IsIgnore = true)] public override ExtraPropertyDictionary ExtraProperties { get; protected set; }
public FileAggregateRoot()
{
}
public FileAggregateRoot(Guid id) : base(id)
{
Id = id;
}
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysFile", "文件信息表")]
public class FileAggregateRoot : AggregateRoot<Guid>, IAuditedObject
{
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "文件大小")]
public decimal FileSize { get; set; }
[SugarColumn(ColumnDescription = "文件名称")]
public string FileName { get; set; }
[SugarColumn(ColumnDescription = "文件后缀")]
public string FileExt { get; set; }
[SugarColumn(ColumnDescription = "文件路径")]
public string FilePath { get; set; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
[SugarColumn(IsIgnore = true)] public override ExtraPropertyDictionary ExtraProperties { get; protected set; }
public FileAggregateRoot()
{
}
public FileAggregateRoot(Guid id) : base(id)
{
Id = id;
}
}

@ -1,81 +1,82 @@
using System.Net;
using System.Net.Sockets;
using IPTools.Core;
using Microsoft.AspNetCore.Http;
using NPin.Framework.Core.Extensions;
using SqlSugar;
using UAParser;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("LoginLog", "登录日志表")]
public class LoginLogEntity : Entity<Guid>, ICreationAuditedObject
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
[SugarColumn(ColumnDescription = "登录用户")]
public string? LoginUser { get; set; }
[SugarColumn(ColumnDescription = "登录用户ID")]
public Guid LoginUserId { get; set; }
[SugarColumn(ColumnDescription = "登录地点")]
public string? LoginLocation { get; set; }
[SugarColumn(ColumnDescription = "Ipv4")]
public string? LoginIPv4 { get; set; }
[SugarColumn(ColumnDescription = "Ipv6")]
public string? LoginIPv6 { get; set; }
[SugarColumn(ColumnDescription = "浏览器")]
public string? Browser { get; set; }
[SugarColumn(ColumnDescription = "操作系统")]
public string? Os { get; set; }
[SugarColumn(ColumnDescription = "登录信息")]
public string? LoginMsg { get; set; }
public static LoginLogEntity GetInfoByHttpContext(HttpContext httpContext)
{
// var ipInfo = httpContext.GetRemoteIpInfo();
string ipv4AddrStr = null;
string ipv6AddrStr = null;
var ipAddr = httpContext.GetClientIpAddress();
if (ipAddr != null)
{
switch (ipAddr.AddressFamily)
{
case AddressFamily.InterNetwork:
ipv4AddrStr = ipAddr.ToString();
break;
case AddressFamily.InterNetworkV6:
ipv6AddrStr = ipAddr.ToString();
break;
}
}
var location = IPAddress.IsLoopback(ipAddr)
? new IpInfo { Province = "本地", City = "本机" }
: IpTool.Search(ipAddr.ToString());
var clientInfo = GetClientInfo(httpContext);
return new LoginLogEntity
{
Browser = clientInfo.Device.Family,
Os = clientInfo.OS.ToString(),
LoginIPv4 = ipv4AddrStr,
LoginIPv6 = ipv6AddrStr,
LoginLocation = $"{location.Country}-{location.Province}-{location.City}"
};
ClientInfo GetClientInfo(HttpContext ctx) => Parser.GetDefault().Parse(ctx.GetUserAgent());
}
using System.Net;
using System.Net.Sockets;
using IPTools.Core;
using Microsoft.AspNetCore.Http;
using NPin.Framework.Core.Extensions;
using SqlSugar;
using UAParser;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysLoginLog", "登录日志表")]
[SugarIndex($"index_{nameof(LoginUser)}", nameof(LoginUser), OrderByType.Asc)]
public class LoginLogEntity : Entity<Guid>, ICreationAuditedObject
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
[SugarColumn(ColumnDescription = "登录用户")]
public string? LoginUser { get; set; }
[SugarColumn(ColumnDescription = "登录用户ID")]
public Guid LoginUserId { get; set; }
[SugarColumn(ColumnDescription = "登录地点")]
public string? LoginLocation { get; set; }
[SugarColumn(ColumnDescription = "Ipv4")]
public string? LoginIpv4 { get; set; }
[SugarColumn(ColumnDescription = "Ipv6")]
public string? LoginIpv6 { get; set; }
[SugarColumn(ColumnDescription = "浏览器")]
public string? Browser { get; set; }
[SugarColumn(ColumnDescription = "操作系统")]
public string? Os { get; set; }
[SugarColumn(ColumnDescription = "登录信息")]
public string? LoginMsg { get; set; }
public static LoginLogEntity GetInfoByHttpContext(HttpContext httpContext)
{
// var ipInfo = httpContext.GetRemoteIpInfo();
string ipv4AddrStr = null;
string ipv6AddrStr = null;
var ipAddr = httpContext.GetClientIpAddress();
if (ipAddr != null)
{
switch (ipAddr.AddressFamily)
{
case AddressFamily.InterNetwork:
ipv4AddrStr = ipAddr.ToString();
break;
case AddressFamily.InterNetworkV6:
ipv6AddrStr = ipAddr.ToString();
break;
}
}
var location = IPAddress.IsLoopback(ipAddr)
? new IpInfo { Province = "本地", City = "本机" }
: IpTool.Search(ipAddr.ToString());
var clientInfo = GetClientInfo(httpContext);
return new LoginLogEntity
{
Browser = clientInfo.Device.Family,
Os = clientInfo.OS.ToString(),
LoginIpv4 = ipv4AddrStr,
LoginIpv6 = ipv6AddrStr,
LoginLocation = $"{location.Country}-{location.Province}-{location.City}"
};
ClientInfo GetClientInfo(HttpContext ctx) => Parser.GetDefault().Parse(ctx.GetUserAgent());
}
}

@ -1,46 +1,46 @@
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("Organization", "组织机构表")]
public class OrganizationEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "组织机构名称")]
public string Name { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "组织机构编码")]
public string Code { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "描述")]
public string? Remark { get; set; }
/// <summary>
/// 负责人ID
/// </summary>
public Guid LeaderId { get; set; }
/// <summary>
/// 负责人名称,仅用户展示,不存储
/// </summary>
[SugarColumn(IsIgnore = true)]
public string Leader { get; set; }
/// <summary>
/// 父节点 ID
/// </summary>
public Guid ParentId { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; } = DateTime.Now;
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
public int OrderNum { get; set; }
public bool IsEnabled { get; set; }
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysOrganization", "组织机构表")]
public class OrganizationEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "组织机构名称")]
public string Name { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "组织机构编码")]
public string Code { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "描述")]
public string? Remark { get; set; }
/// <summary>
/// 负责人ID
/// </summary>
public Guid LeaderId { get; set; }
/// <summary>
/// 负责人名称,仅用户展示,不存储
/// </summary>
[SugarColumn(IsIgnore = true)]
public string Leader { get; set; }
/// <summary>
/// 父节点 ID
/// </summary>
public Guid ParentId { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; } = DateTime.Now;
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
public int OrderNum { get; set; }
public bool IsEnabled { get; set; }
}

@ -1,30 +1,30 @@
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("Post", "岗位表")]
public class PostEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "岗位编码")]
public string Code { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "岗位名称")]
public string Name { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "岗位描述")]
public string? Remark { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; } = DateTime.Now;
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
public int OrderNum { get; set; }
public bool IsEnabled { get; set; }
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysPost", "岗位表")]
public class PostEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "岗位编码")]
public string Code { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "岗位名称")]
public string Name { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "岗位描述")]
public string? Remark { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; } = DateTime.Now;
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
public int OrderNum { get; set; }
public bool IsEnabled { get; set; }
}

@ -1,42 +1,42 @@
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using NPin.Framework.Upms.Domain.Shared.Enums;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("Role", "角色表")]
public class RoleEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "角色代码")]
public string Code { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "角色名称")]
public string Name { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "角色数据权限范围")]
public DataScopeEnum DataScope { get; set; } = DataScopeEnum.All;
[SugarColumn(ColumnDescription = "描述")]
public string? Remark { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; } = DateTime.Now;
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
public int OrderNum { get; set; }
public bool IsEnabled { get; set; }
#region 导航
[Navigate(typeof(RoleOrganizationEntity), nameof(RoleOrganizationEntity.RoleId),
nameof(RoleOrganizationEntity.OrgId))]
public List<OrganizationEntity> OrganizationList { get; set; }
#endregion
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using NPin.Framework.Upms.Domain.Shared.Enums;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysRole", "角色表")]
public class RoleEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "角色代码")]
public string Code { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "角色名称")]
public string Name { get; set; } = string.Empty;
[SugarColumn(ColumnDescription = "角色数据权限范围")]
public DataScopeEnum DataScope { get; set; } = DataScopeEnum.All;
[SugarColumn(ColumnDescription = "描述")]
public string? Remark { get; set; }
public bool IsDeleted { get; }
public DateTime CreationTime { get; } = DateTime.Now;
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
public int OrderNum { get; set; }
public bool IsEnabled { get; set; }
#region 导航
[Navigate(typeof(RoleOrganizationEntity), nameof(RoleOrganizationEntity.RoleId),
nameof(RoleOrganizationEntity.OrgId))]
public List<OrganizationEntity> OrganizationList { get; set; }
#endregion
}

@ -1,16 +1,16 @@
using SqlSugar;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("RelRoleOrganization", "角色-机构 关系表")]
public class RoleOrganizationEntity : Entity<Guid>
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "角色ID")]
public Guid RoleId { get; set; }
[SugarColumn(ColumnDescription = "机构ID")]
public Guid OrgId { get; set; }
using SqlSugar;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysRelRoleOrganization", "角色-机构 关系表")]
public class RoleOrganizationEntity : Entity<Guid>
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "角色ID")]
public Guid RoleId { get; set; }
[SugarColumn(ColumnDescription = "机构ID")]
public Guid OrgId { get; set; }
}

@ -8,7 +8,8 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("User", "用户表")]
[SugarTable("SysUser", "用户表")]
[SugarIndex($"index_{nameof(Username)}", nameof(Username), OrderByType.Asc)]
public class UserEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IEnabled, IOrderNum
{
#region User

@ -1,24 +1,24 @@
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("UserMetadata", "用户元数据表")]
public class UserMetaEntity : Entity<Guid>, IAuditedObject
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
public Guid UserId;
[SugarColumn(ColumnDescription = "元数据 键")]
public string Key { get; set; }
[SugarColumn(ColumnDescription = "元数据 值")]
public string Value { get; set; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysUserMetadata", "用户元数据表")]
public class UserMetaEntity : Entity<Guid>, IAuditedObject
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
public Guid UserId;
[SugarColumn(ColumnDescription = "元数据 键")]
public string Key { get; set; }
[SugarColumn(ColumnDescription = "元数据 值")]
public string Value { get; set; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
public DateTime? LastModificationTime { get; }
public Guid? LastModifierId { get; }
}

@ -1,16 +1,16 @@
using SqlSugar;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("RelUserOrganization", "用户-组织机构 关系表")]
public class UserOrganizationEntity : Entity<Guid>
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "用户ID")]
public Guid UserId { get; set; }
[SugarColumn(ColumnDescription = "组织机构ID")]
public Guid OrganizationId { get; set; }
using SqlSugar;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysRelUserOrganization", "用户-组织机构 关系表")]
public class UserOrganizationEntity : Entity<Guid>
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "用户ID")]
public Guid UserId { get; set; }
[SugarColumn(ColumnDescription = "组织机构ID")]
public Guid OrganizationId { get; set; }
}

@ -1,17 +1,17 @@
using SqlSugar;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("RelUserPost","用户-岗位 关系表")]
public class UserPostEntity: Entity<Guid>
{
[SugarColumn(IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "用户ID")]
public Guid UserId { get; set; }
[SugarColumn(ColumnDescription = "岗位ID")]
public Guid PostId { get; set; }
using SqlSugar;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysRelUserPost","用户-岗位 关系表")]
public class UserPostEntity: Entity<Guid>
{
[SugarColumn(IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "用户ID")]
public Guid UserId { get; set; }
[SugarColumn(ColumnDescription = "岗位ID")]
public Guid PostId { get; set; }
}

@ -1,17 +1,17 @@
using SqlSugar;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("RelUserRole", "用户-角色 关系表")]
public class UserRoleEntity: Entity<Guid>
{
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "角色ID")]
public Guid RoleId { get; set; }
[SugarColumn(ColumnDescription = "用户ID")]
public Guid UserId { get; set; }
using SqlSugar;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("SysRelUserRole", "用户-角色 关系表")]
public class UserRoleEntity: Entity<Guid>
{
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "角色ID")]
public Guid RoleId { get; set; }
[SugarColumn(ColumnDescription = "用户ID")]
public Guid UserId { get; set; }
}

@ -1,129 +1,78 @@
using AlibabaCloud.OpenApiClient.Models;
using AlibabaCloud.SDK.Dysmsapi20170525;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using NPin.Framework.Upms.Domain.Repositories;
using NPin.Framework.Upms.Domain.Shared.Enums;
using NPin.Framework.Upms.Domain.Shared.Model;
using NPin.Framework.Upms.Domain.Shared.Options;
using TencentCloud.Common;
using TencentCloud.Common.Profile;
using TencentCloud.Sms.V20210111;
using TencentCloud.Sms.V20210111.Models;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Services;
namespace NPin.Framework.Upms.Domain.Managers;
public class SmsManager : DomainService, ISms
{
private ILogger<SmsManager> _logger;
private IConfigRepository _configRepository;
private IDistributedCache<SmsConfigModel> _cache;
public SmsManager(ILogger<SmsManager> logger, IConfigRepository configRepository)
{
_logger = logger;
_configRepository = configRepository;
}
public async Task SendSmsAsync(SmsTypeEnum smsType, string phoneNumbers, object templateParam)
{
try
{
var smsSettings = SmsOptions.Config[smsType];
if (!smsSettings.Enabled)
{
return;
}
switch (smsSettings.Provider)
{
case SmsProviderEnum.Aliyun:
await SendAliyunSmsAsync(smsSettings, phoneNumbers, templateParam);
break;
case SmsProviderEnum.Tencent:
await SendTencentSmsAsync(smsSettings, phoneNumbers, templateParam);
break;
default:
throw new Exception("未实现该服务提供商");
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"短信发送失败: {ex.Message}");
throw new UserFriendlyException($"短信发送失败: {ex.Message}");
}
}
private async Task SendTencentSmsAsync(SmsSettings settings, string phoneNumbers,
object templateParam)
{
var client = SmsClientProvider.CreateClient(TencentOptions, settings);
var sendSmsRequest = new SendSmsRequest
{
PhoneNumberSet = phoneNumbers.Split(','),
SignName = settings.SignName,
TemplateId = settings.TemplateCode,
TemplateParamSet = templateParam as string[]
};
var response = await client.SendSms(sendSmsRequest);
}
private async Task SendAliyunSmsAsync(SmsSettings settings, string phoneNumbers,
object templateParam)
{
var client = SmsClientProvider.CreateClient(AliyunOptions, settings);
var sendSmsRequest = new AlibabaCloud.SDK.Dysmsapi20170525.Models.SendSmsRequest
{
PhoneNumbers = phoneNumbers,
SignName = settings.SignName,
TemplateCode = settings.TemplateCode,
TemplateParam = JsonConvert.SerializeObject(templateParam)
};
var response = await client.SendSmsAsync(sendSmsRequest);
}
}
public static class SmsClientProvider
{
public static Client CreateClient(AliyunOptions options, SmsSettings settings)
{
var config = new Config()
{
AccessKeyId = options.AccessKeyId,
AccessKeySecret = options.AccessKeySecret,
Endpoint = settings.Endpoint,
RegionId = settings.RegionId,
};
return new Client(config);
}
public static SmsClient CreateClient(TencentOptions options, SmsSettings settings)
{
var cred = new Credential
{
SecretId = options.SecretId,
SecretKey = options.SecretKey,
};
var httpProfile = new HttpProfile
{
Endpoint = settings.Endpoint,
};
var clientProfile = new ClientProfile()
{
HttpProfile = httpProfile
};
return new SmsClient(cred, settings.RegionId, clientProfile);
}
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using NPin.Framework.SettingManagement.Domain;
using NPin.Framework.Upms.Domain.Settings;
using NPin.Framework.Upms.Domain.Shared.Consts;
using NPin.Framework.Upms.Domain.Shared.Enums;
using NPin.Framework.Upms.Domain.Shared.Model;
using NPin.Framework.Upms.Domain.Sms;
using Volo.Abp.Domain.Services;
namespace NPin.Framework.Upms.Domain.Managers;
public class SmsManager : DomainService
{
private readonly ILogger<SmsManager> _logger;
private readonly ISettingManager _settingManager;
private readonly IEnumerable<ISms> _smsProviders;
public SmsManager(ILogger<SmsManager> logger, ISettingManager settingManager, IEnumerable<ISms> smsProviders)
{
_logger = logger;
_settingManager = settingManager;
_smsProviders = smsProviders;
}
public async Task<SmsConfigModel> GetSmsConfigModelAsync()
{
var configJson = await _settingManager.GetOrNullUpmsAsync(ConfigConst.SmsConfigModelKey);
var settingsJson = await _settingManager.GetOrNullUpmsAsync(ConfigConst.SmsSettingsKey);
var providersJson = await _settingManager.GetOrNullUpmsAsync(ConfigConst.SmsProvidersKey);
var templateJson = await _settingManager.GetOrNullUpmsAsync(ConfigConst.SmsTemplatesKey);
var ret = JsonConvert.DeserializeObject<SmsConfigModel>(configJson ?? string.Empty) ?? new SmsConfigModel();
ret.Settings =
JsonConvert.DeserializeObject<Dictionary<SmsTypeEnum, SmsSettings>>(settingsJson ?? string.Empty) ??
new Dictionary<SmsTypeEnum, SmsSettings>();
ret.Providers = JsonConvert.DeserializeObject<List<SmsProvider>>(providersJson ?? string.Empty) ?? [];
ret.Templates = JsonConvert.DeserializeObject<List<SmsTemplate>>(templateJson ?? string.Empty) ?? [];
return ret;
}
public async Task SendSmsAsync(SmsTypeEnum smsType, string phoneNumbers, object templateParam)
{
try
{
var smsConfig = await GetSmsConfigModelAsync();
if (!smsConfig.Enabled)
{
throw new UserFriendlyException("短信服务未启用,无法发送短信。");
}
var settings = smsConfig.GetSetting(smsType);
if (settings is null)
{
throw new UserFriendlyException("未找到对应短信类型,无法发送短信。");
}
var providerConfig = smsConfig.GetProvider(settings);
var provider = _smsProviders.FirstOrDefault(i => i.ProviderType == providerConfig.ProviderType);
if (provider is null)
{
throw new NotImplementedException("未实现该方式的短信控制器");
}
await provider.SendSmsAsync(smsConfig, settings, phoneNumbers, templateParam);
}
catch (Exception ex)
{
_logger.LogError(ex, $"短信发送失败: {ex.Message}");
throw new UserFriendlyException($"短信发送失败: {ex.Message}");
}
}
}

@ -3,6 +3,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\framework\NPin.Framework.Caching.FreeRedis\NPin.Framework.Caching.FreeRedis.csproj" />
<ProjectReference Include="..\..\..\framework\NPin.Framework.SqlSugarCore.Abstractions\NPin.Framework.SqlSugarCore.Abstractions.csproj" />
<ProjectReference Include="..\..\setting-management\NPin.Framework.SettingManagement.Domain\NPin.Framework.SettingManagement.Domain.csproj" />
<ProjectReference Include="..\NPin.Framework.Upms.Domain.Shared\NPin.Framework.Upms.Domain.Shared.csproj" />
</ItemGroup>
<ItemGroup>

@ -1,32 +1,40 @@
using Microsoft.Extensions.DependencyInjection;
using NPin.Framework.Caching.FreeRedis;
using NPin.Framework.Upms.Domain.Authorization;
using NPin.Framework.Upms.Domain.OperLog;
using NPin.Framework.Upms.Domain.Shared;
using Volo.Abp.AspNetCore.SignalR;
using Volo.Abp.Caching;
using Volo.Abp.Domain;
namespace NPin.Framework.Upms.Domain;
[DependsOn(
typeof(NPinFrameworkUpmsDomainSharedModule),
typeof(NPinFrameworkCachingFreeRedisModule),
// Abp
typeof(AbpAspNetCoreSignalRModule),
typeof(AbpDddDomainModule),
typeof(AbpCachingModule)
)]
public class NPinFrameworkUpmsDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
var configuration = services.GetConfiguration();
services.AddControllers(opts =>
{
opts.Filters.Add<PermissionGlobalAttribute>();
opts.Filters.Add<OperLogGlobalAttribute>();
});
}
using Microsoft.Extensions.DependencyInjection;
using NPin.Framework.Caching.FreeRedis;
using NPin.Framework.SettingManagement.Domain;
using NPin.Framework.SettingManagement.Domain.Options;
using NPin.Framework.Upms.Domain.Authorization;
using NPin.Framework.Upms.Domain.OperLog;
using NPin.Framework.Upms.Domain.Settings;
using NPin.Framework.Upms.Domain.Shared;
using Volo.Abp.AspNetCore.SignalR;
using Volo.Abp.Caching;
using Volo.Abp.Domain;
namespace NPin.Framework.Upms.Domain;
[DependsOn(
typeof(NPinFrameworkUpmsDomainSharedModule),
typeof(NPinFrameworkCachingFreeRedisModule),
// Settings
typeof(NPinFrameworkSettingManagementDomainModule),
// Abp
typeof(AbpAspNetCoreSignalRModule),
typeof(AbpDddDomainModule),
typeof(AbpCachingModule)
)]
public class NPinFrameworkUpmsDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
var configuration = services.GetConfiguration();
services.AddControllers(opts =>
{
opts.Filters.Add<PermissionGlobalAttribute>();
opts.Filters.Add<OperLogGlobalAttribute>();
});
// 添加Upms模块的设置提供者
Configure<SettingManagementOptions>(opt => { opt.Providers.Add<UpmsSettingManagementProvider>(); });
}
}

@ -1,86 +1,84 @@
using System.Net;
using System.Net.Sockets;
using IPTools.Core;
using Microsoft.AspNetCore.Http;
using NPin.Framework.Core.Extensions;
using NPin.Framework.Upms.Domain.Entities;
using NPin.Framework.Upms.Domain.Shared.OperLog;
using SqlSugar;
using UAParser;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.OperLog;
[SugarTable("OperationLog", "操作日志记录表")]
public class OperationLogEntity: Entity<Guid>, ICreationAuditedObject
{
[SugarColumn(IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "日志标题")]
public string? Title { get; set; }
[SugarColumn(ColumnDescription = "操作类型")]
public OperTypeEnum OperType { get; set; }
[SugarColumn(ColumnDescription = "请求方式")]
public string? RequestMethod { get; set; }
[SugarColumn(ColumnDescription = "操作者")]
public string? OperUser { get; set; }
[SugarColumn(ColumnDescription = "操作者Ipv4")]
public string? OperIPv4 { get; set; }
[SugarColumn(ColumnDescription = "操作者Ipv6")]
public string? OperIPv6 { get; set; }
[SugarColumn(ColumnDescription = "操作地点")]
public string? OperLocation { get; set; }
[SugarColumn(ColumnDescription = "操作方法")]
public string? Method { get; set; }
[SugarColumn(ColumnDescription = "请求参数")]
public string? RequestParam { get; set; }
[SugarColumn(ColumnDescription = "请求结果", Length = 9999)]
public string? RequestResult { get; set; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
public static OperationLogEntity GetInfoByHttpContext(HttpContext httpContext)
{
// var ipInfo = httpContext.GetRemoteIpInfo();
string ipv4AddrStr = null;
string ipv6AddrStr = null;
IpInfo info = new IpInfo { Province = "本地", City = "本机" };
var ipAddr = httpContext.GetClientIpAddress();
if (ipAddr != null)
{
switch (ipAddr.AddressFamily)
{
case AddressFamily.InterNetwork:
ipv4AddrStr = ipAddr.ToString();
break;
case AddressFamily.InterNetworkV6:
ipv6AddrStr = ipAddr.ToString();
break;
}
if (!IPAddress.IsLoopback(ipAddr))
{
info = IpTool.Search(ipAddr.ToString());
}
}
return new OperationLogEntity
{
OperIPv4 = ipv4AddrStr,
OperIPv6 = ipv6AddrStr,
OperLocation = $"{info.Country}-{info.Province}-{info.City}"
};
}
using System.Net;
using System.Net.Sockets;
using IPTools.Core;
using Microsoft.AspNetCore.Http;
using NPin.Framework.Core.Extensions;
using NPin.Framework.Upms.Domain.Shared.OperLog;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.OperLog;
[SugarTable("SysOperationLog", "操作日志记录表")]
public class OperationLogEntity : Entity<Guid>, ICreationAuditedObject
{
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "日志标题")]
public string? Title { get; set; }
[SugarColumn(ColumnDescription = "操作类型")]
public OperTypeEnum OperType { get; set; }
[SugarColumn(ColumnDescription = "请求方式")]
public string? RequestMethod { get; set; }
[SugarColumn(ColumnDescription = "操作者")]
public string? OperUser { get; set; }
[SugarColumn(ColumnDescription = "操作者Ipv4")]
public string? OperIPv4 { get; set; }
[SugarColumn(ColumnDescription = "操作者Ipv6")]
public string? OperIPv6 { get; set; }
[SugarColumn(ColumnDescription = "操作地点")]
public string? OperLocation { get; set; }
[SugarColumn(ColumnDescription = "操作方法")]
public string? Method { get; set; }
[SugarColumn(ColumnDescription = "请求参数")]
public string? RequestParam { get; set; }
[SugarColumn(ColumnDescription = "请求结果", Length = 9999)]
public string? RequestResult { get; set; }
public DateTime CreationTime { get; }
public Guid? CreatorId { get; }
public static OperationLogEntity GetInfoByHttpContext(HttpContext httpContext)
{
// var ipInfo = httpContext.GetRemoteIpInfo();
string ipv4AddrStr = null;
string ipv6AddrStr = null;
IpInfo info = new IpInfo { Province = "本地", City = "本机" };
var ipAddr = httpContext.GetClientIpAddress();
if (ipAddr != null)
{
switch (ipAddr.AddressFamily)
{
case AddressFamily.InterNetwork:
ipv4AddrStr = ipAddr.ToString();
break;
case AddressFamily.InterNetworkV6:
ipv6AddrStr = ipAddr.ToString();
break;
}
if (!IPAddress.IsLoopback(ipAddr))
{
info = IpTool.Search(ipAddr.ToString());
}
}
return new OperationLogEntity
{
OperIPv4 = ipv4AddrStr,
OperIPv6 = ipv6AddrStr,
OperLocation = $"{info.Country}-{info.Province}-{info.City}"
};
}
}

@ -0,0 +1,17 @@
using NPin.Framework.SettingManagement.Domain;
using NPin.Framework.SettingManagement.Domain.Store;
using Volo.Abp.DependencyInjection;
namespace NPin.Framework.Upms.Domain.Settings;
public class UpmsSettingManagementProvider: SettingManagementProvider, ITransientDependency
{
public const string ProviderName = "Upms";
public override string Name => ProviderName;
public UpmsSettingManagementProvider(ISettingManagementStore settingManagementStore) : base(settingManagementStore)
{
}
}

@ -0,0 +1,23 @@
using NPin.Framework.SettingManagement.Domain;
using Volo.Abp.Settings;
namespace NPin.Framework.Upms.Domain.Settings;
public static class UpmsSettingManagerExtensions
{
public static Task<string?> GetOrNullUpmsAsync(this ISettingManager settingManager, string name,
bool fallback = true)
{
return settingManager.GetOrNullAsync(name, UpmsSettingManagementProvider.ProviderName, null, fallback);
}
public static Task<List<SettingValue>> GetAllUpmsAsync(this ISettingManager settingManager, bool fallback = true)
{
return settingManager.GetAllAsync(UpmsSettingManagementProvider.ProviderName, null, fallback);
}
public static Task SetUpmsAsync(this ISettingManager settingManager, string name, string value)
{
return settingManager.SetAsync(name, value, UpmsSettingManagementProvider.ProviderName, null);
}
}

@ -0,0 +1,20 @@
using NPin.Framework.Upms.Domain.Shared.Consts;
using Volo.Abp.Settings;
namespace NPin.Framework.Upms.Domain.Settings;
public class UpmsStaticSettingProvider: SettingDefinitionProvider
{
public override void Define(ISettingDefinitionContext context)
{
DefineSmsSettingDefinitions(context);
}
private void DefineSmsSettingDefinitions(ISettingDefinitionContext context)
{
context.Add(new SettingDefinition(ConfigConst.SmsConfigModelKey));
context.Add(new SettingDefinition(ConfigConst.SmsProvidersKey));
context.Add(new SettingDefinition(ConfigConst.SmsSettingsKey));
context.Add(new SettingDefinition(ConfigConst.SmsTemplatesKey));
}
}

@ -0,0 +1,54 @@
using AlibabaCloud.OpenApiClient.Models;
using AlibabaCloud.SDK.Dysmsapi20170525;
using AlibabaCloud.SDK.Dysmsapi20170525.Models;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using NPin.Framework.Upms.Domain.Shared.Enums;
using NPin.Framework.Upms.Domain.Shared.Model;
using Volo.Abp.DependencyInjection;
namespace NPin.Framework.Upms.Domain.Sms.Handler;
public class AliyunSmsHandler : ISms, ISingletonDependency
{
private readonly ILogger<AliyunSmsHandler> _logger;
public AliyunSmsHandler(ILogger<AliyunSmsHandler> logger)
{
_logger = logger;
}
public SmsProviderTypeEnum ProviderType => SmsProviderTypeEnum.Aliyun;
public async Task SendSmsAsync(SmsConfigModel config, SmsSettings settings, string phoneNumbers,
object templateParam)
{
var client = CreateClient(config.GetProvider(settings));
var template = config.GetTemplate(settings);
var sendSmsRequest = new SendSmsRequest
{
PhoneNumbers = phoneNumbers,
SignName = template.SignName,
TemplateCode = template.TemplateCode,
TemplateParam = JsonConvert.SerializeObject(templateParam)
};
var response = await client.SendSmsAsync(sendSmsRequest);
// TODO 判断结果
_logger.LogDebug(response.ToString());
}
private Client CreateClient(SmsProvider provider)
{
var config = new Config()
{
AccessKeyId = provider.AccessKeyId,
AccessKeySecret = provider.AccessKeySecret,
Endpoint = provider.Endpoint,
RegionId = provider.Region,
};
return new Client(config);
}
}

@ -0,0 +1,64 @@
using Microsoft.Extensions.Logging;
using NPin.Framework.Upms.Domain.Shared.Enums;
using NPin.Framework.Upms.Domain.Shared.Model;
using TencentCloud.Common;
using TencentCloud.Common.Profile;
using TencentCloud.Sms.V20210111;
using TencentCloud.Sms.V20210111.Models;
using Volo.Abp.DependencyInjection;
namespace NPin.Framework.Upms.Domain.Sms.Handler;
public class TencentSmsHandler : ISms, ISingletonDependency
{
private readonly ILogger<AliyunSmsHandler> _logger;
public TencentSmsHandler(ILogger<AliyunSmsHandler> logger)
{
_logger = logger;
}
public SmsProviderTypeEnum ProviderType => SmsProviderTypeEnum.Tencent;
public async Task SendSmsAsync(SmsConfigModel config, SmsSettings settings, string phoneNumbers,
object templateParam)
{
var client = CreateClient(config.GetProvider(settings));
var template = config.GetTemplate(settings);
var sendSmsRequest = new SendSmsRequest
{
PhoneNumberSet = phoneNumbers.Split(','),
SignName = template.SignName,
TemplateId = template.TemplateCode,
TemplateParamSet = templateParam as string[]
};
var response = await client.SendSms(sendSmsRequest);
// TODO 判断结果
_logger.LogDebug(response.ToString());
}
private SmsClient CreateClient(SmsProvider provider)
{
var cred = new Credential
{
SecretId = provider.AccessKeyId,
SecretKey = provider.AccessKeySecret,
};
var httpProfile = new HttpProfile
{
Endpoint = provider.Endpoint,
};
var clientProfile = new ClientProfile()
{
HttpProfile = httpProfile
};
return new SmsClient(cred, provider.Region, clientProfile);
}
}

@ -1,11 +1,17 @@
using NPin.Framework.Upms.Domain.Shared.Enums;
namespace NPin.Framework.Upms.Domain.Managers;
/// <summary>
/// 短信接口
/// </summary>
public interface ISms
{
Task SendSmsAsync(SmsTypeEnum smsType, string phoneNumbers, object templateParam);
using NPin.Framework.Upms.Domain.Shared.Enums;
using NPin.Framework.Upms.Domain.Shared.Model;
namespace NPin.Framework.Upms.Domain.Sms;
/// <summary>
/// 统一短信接口
/// </summary>
public interface ISms
{
/// <summary>
/// 短信提供商类型
/// </summary>
SmsProviderTypeEnum ProviderType { get; }
Task SendSmsAsync(SmsConfigModel config, SmsSettings settings, string phoneNumbers, object templateParam);
}

@ -0,0 +1,46 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using NPin.Framework.SettingManagement.Domain;
using NPin.Framework.Upms.Domain.Managers;
using NPin.Framework.Upms.Domain.Settings;
using NPin.Framework.Upms.Domain.Shared.Consts;
using NPin.Framework.Upms.Domain.Shared.Model;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace NPin.Framework.Upms.SqlSugarCore.DataSeeds;
public class SmsConfigDataSeed : IDataSeedContributor, ITransientDependency
{
private readonly ILogger<SmsManager> _logger;
private readonly ISettingManager _settingManager;
public SmsConfigDataSeed(ILogger<SmsManager> logger, ISettingManager settingManager)
{
_logger = logger;
_settingManager = settingManager;
}
public async Task SeedAsync(DataSeedContext context)
{
var configJson = await _settingManager.GetOrNullUpmsAsync(ConfigConst.SmsConfigModelKey);
if (!string.IsNullOrEmpty(configJson))
{
return;
}
var configModel = new SmsConfigModel
{
Enabled = false,
Providers = [],
Settings = [],
Templates = []
};
await _settingManager.SetUpmsAsync(ConfigConst.SmsConfigModelKey, JsonConvert.SerializeObject(configModel));
await _settingManager.SetUpmsAsync(ConfigConst.SmsSettingsKey, JsonConvert.SerializeObject(configModel.Settings));
await _settingManager.SetUpmsAsync(ConfigConst.SmsProvidersKey, JsonConvert.SerializeObject(configModel.Providers));
await _settingManager.SetUpmsAsync(ConfigConst.SmsTemplatesKey, JsonConvert.SerializeObject(configModel.Templates));
}
}

@ -1,10 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
<ItemGroup>
<ProjectReference Include="..\..\..\framework\NPin.Framework.Mapster\NPin.Framework.Mapster.csproj" />
<ProjectReference Include="..\..\..\framework\NPin.Framework.SqlSugarCore\NPin.Framework.SqlSugarCore.csproj" />
<ProjectReference Include="..\NPin.Framework.Upms.Domain\NPin.Framework.Upms.Domain.csproj" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
<ItemGroup>
<ProjectReference Include="..\..\..\framework\NPin.Framework.Mapster\NPin.Framework.Mapster.csproj" />
<ProjectReference Include="..\..\..\framework\NPin.Framework.SqlSugarCore\NPin.Framework.SqlSugarCore.csproj" />
<ProjectReference Include="..\..\setting-management\NPin.Framework.SettingManagement.SqlSugarCore\NPin.Framework.SettingManagement.SqlSugarCore.csproj" />
<ProjectReference Include="..\NPin.Framework.Upms.Domain\NPin.Framework.Upms.Domain.csproj" />
</ItemGroup>
</Project>

@ -1,19 +1,22 @@
using NPin.Framework.Mapster;
using NPin.Framework.SqlSugarCore;
using NPin.Framework.Upms.Domain;
namespace NPin.Framework.Upms.SqlSugarCore;
[DependsOn(
typeof(NPinFrameworkUpmsDomainModule),
// framework
typeof(NPinFrameworkMapsterModule),
typeof(NPinFrameworkSqlSugarCoreModule)
)]
public class NPinFrameworkUpmsSqlSugarCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
}
using NPin.Framework.Mapster;
using NPin.Framework.SettingManagement.SqlSugarCore;
using NPin.Framework.SqlSugarCore;
using NPin.Framework.Upms.Domain;
namespace NPin.Framework.Upms.SqlSugarCore.Repositories;
[DependsOn(
typeof(NPinFrameworkUpmsDomainModule),
// settings
typeof(NPinFrameworkSettingManagementSqlSugarCoreModule),
// framework
typeof(NPinFrameworkMapsterModule),
typeof(NPinFrameworkSqlSugarCoreModule)
)]
public class NPinFrameworkUpmsSqlSugarCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
}
}

@ -1,49 +1,49 @@
using NPin.Framework.SqlSugarCore;
using NPin.Framework.Upms.Domain.Authorization;
using NPin.Framework.Upms.Domain.Shared.Consts;
using SqlSugar;
using Volo.Abp.DependencyInjection;
namespace NPin.Framework.Upms.SqlSugarCore;
public class NPinUpmsDbContext : SqlSugarDbContext
{
public NPinUpmsDbContext(IAbpLazyServiceProvider lazyServiceProvider) : base(lazyServiceProvider)
{
}
protected override void CustomDataFilter(ISqlSugarClient sqlSugarClient)
{
if (DataFilter.IsEnabled<IDataPermission>())
{
DataPermissionFilter(sqlSugarClient);
}
base.CustomDataFilter(sqlSugarClient);
}
/// <summary>
/// 数据权限过滤
/// </summary>
/// <param name="sqlSugarClient"></param>
protected void DataPermissionFilter(ISqlSugarClient sqlSugarClient)
{
// 当前无登录用户,无需过滤
if (CurrentUser.Id == null) return;
// 管理员角色 不进行过滤
if (CurrentUser.UserName.Equals(UserConst.Admin) ||
CurrentUser.Roles.Any(r => r.Equals(UserConst.AdminRoleCode)))
{
return;
}
// var expUser = Expressionable.Create<>();
// var expRole = Expressionable.Create<>();
// 无岗位 or 无角色 只能看自己
// sqlSugarClient.QueryFilter.AddTableFilter()
}
using NPin.Framework.SqlSugarCore;
using NPin.Framework.Upms.Domain.Authorization;
using NPin.Framework.Upms.Domain.Shared.Consts;
using SqlSugar;
using Volo.Abp.DependencyInjection;
namespace NPin.Framework.Upms.SqlSugarCore.Repositories;
public class NPinUpmsDbContext : SqlSugarDbContext
{
public NPinUpmsDbContext(IAbpLazyServiceProvider lazyServiceProvider) : base(lazyServiceProvider)
{
}
protected override void CustomDataFilter(ISqlSugarClient sqlSugarClient)
{
if (DataFilter.IsEnabled<IDataPermission>())
{
DataPermissionFilter(sqlSugarClient);
}
base.CustomDataFilter(sqlSugarClient);
}
/// <summary>
/// 数据权限过滤
/// </summary>
/// <param name="sqlSugarClient"></param>
protected void DataPermissionFilter(ISqlSugarClient sqlSugarClient)
{
// 当前无登录用户,无需过滤
if (CurrentUser.Id == null) return;
// 管理员角色 不进行过滤
if (CurrentUser.UserName.Equals(UserConst.Admin) ||
CurrentUser.Roles.Any(r => r.Equals(UserConst.AdminRoleCode)))
{
return;
}
// var expUser = Expressionable.Create<>();
// var expRole = Expressionable.Create<>();
// 无岗位 or 无角色 只能看自己
// sqlSugarClient.QueryFilter.AddTableFilter()
}
}

@ -1,30 +1,30 @@
using Newtonsoft.Json;
using NPin.Framework.SqlSugarCore.Abstractions;
using NPin.Framework.SqlSugarCore.Repositories;
using NPin.Framework.Upms.Domain.Entities;
using NPin.Framework.Upms.Domain.Repositories;
using Volo.Abp.DependencyInjection;
namespace NPin.Framework.Upms.SqlSugarCore;
public class ConfigRepository : SqlSugarRepository<ConfigEntity, Guid>, IConfigRepository, ITransientDependency
{
public ConfigRepository(ISugarDbContextProvider<ISqlSugarDbContext> sugarDbContextProvider) : base(
sugarDbContextProvider)
{
}
public async Task<TDestination> GetSingleByKeyAsync<TDestination>(string key) where TDestination : new()
{
var config = await DbQueryable
.Where(e => e.Key.Equals(key))
.FirstAsync();
if (config is null)
{
return new TDestination();
}
var ret = JsonConvert.DeserializeObject<TDestination>(config.Value);
return ret ?? new TDestination();
}
using Newtonsoft.Json;
using NPin.Framework.SqlSugarCore.Abstractions;
using NPin.Framework.SqlSugarCore.Repositories;
using NPin.Framework.Upms.Domain.Entities;
using NPin.Framework.Upms.Domain.Repositories;
using Volo.Abp.DependencyInjection;
namespace NPin.Framework.Upms.SqlSugarCore.Repositories;
public class ConfigRepository : SqlSugarRepository<ConfigEntity, Guid>, IConfigRepository, ITransientDependency
{
public ConfigRepository(ISugarDbContextProvider<ISqlSugarDbContext> sugarDbContextProvider) : base(
sugarDbContextProvider)
{
}
public async Task<TDestination> GetSingleByKeyAsync<TDestination>(string key) where TDestination : new()
{
var config = await DbQueryable
.Where(e => e.Key.Equals(key))
.FirstAsync();
if (config is null)
{
return new TDestination();
}
var ret = JsonConvert.DeserializeObject<TDestination>(config.Value);
return ret ?? new TDestination();
}
}

@ -1,11 +1,11 @@
using NPin.Framework.Upms.SqlSugarCore;
using Volo.Abp.DependencyInjection;
namespace NPin.SqlSugarCore;
public class NPinDbContext: NPinUpmsDbContext
{
public NPinDbContext(IAbpLazyServiceProvider lazyServiceProvider) : base(lazyServiceProvider)
{
}
using NPin.Framework.Upms.SqlSugarCore.Repositories;
using Volo.Abp.DependencyInjection;
namespace NPin.SqlSugarCore;
public class NPinDbContext: NPinUpmsDbContext
{
public NPinDbContext(IAbpLazyServiceProvider lazyServiceProvider) : base(lazyServiceProvider)
{
}
}

@ -1,29 +1,29 @@
using NPin.Domain;
using NPin.Framework.AuditLogging.SqlSugarCore;
using NPin.Framework.Mapster;
using NPin.Framework.SqlSugarCore;
using NPin.Framework.TenantManagement.SqlSugarCore;
using NPin.Framework.Upms.SqlSugarCore;
namespace NPin.SqlSugarCore;
[DependsOn(
typeof(NPinDomainModule),
typeof(NPinFrameworkUpmsSqlSugarCoreModule),
// TODO bbs
// TODO codegen
typeof(NPinFrameworkAuditLoggingSqlSugarCoreModule),
typeof(NPinFrameworkTenantManagementSqlSugarCoreModule),
//
typeof(NPinFrameworkMapsterModule),
typeof(NPinFrameworkSqlSugarCoreModule)
)]
public class NPinSqlSugarCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddNPinDbContext<NPinDbContext>();
// 默认不开放可根据项目需要是否直接对外开放db
// context.Services.AddTransient(x => x.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient);
}
using NPin.Domain;
using NPin.Framework.AuditLogging.SqlSugarCore;
using NPin.Framework.Mapster;
using NPin.Framework.SqlSugarCore;
using NPin.Framework.TenantManagement.SqlSugarCore;
using NPin.Framework.Upms.SqlSugarCore.Repositories;
namespace NPin.SqlSugarCore;
[DependsOn(
typeof(NPinDomainModule),
typeof(NPinFrameworkUpmsSqlSugarCoreModule),
// TODO bbs
// TODO codegen
typeof(NPinFrameworkAuditLoggingSqlSugarCoreModule),
typeof(NPinFrameworkTenantManagementSqlSugarCoreModule),
//
typeof(NPinFrameworkMapsterModule),
typeof(NPinFrameworkSqlSugarCoreModule)
)]
public class NPinSqlSugarCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddNPinDbContext<NPinDbContext>();
// 默认不开放可根据项目需要是否直接对外开放db
// context.Services.AddTransient(x => x.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient);
}
}

@ -30,7 +30,7 @@ using Volo.Abp.Caching;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Swashbuckle;
namespace NPin;
namespace NPin.Web;
[DependsOn(
typeof(NPinSqlSugarCoreModule),

@ -1,5 +1,5 @@
using NPin;
using NPin.Web;
using Serilog;
using Serilog.Events;

@ -1,15 +1,24 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"NPin.Web": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:19001"
}
}
}
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"NPin.Web": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:19000"
},
"Docker": {
"commandName": "Docker",
"launchBrowser": false,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_HTTP_PORTS": "19000"
},
"publishAllPorts": true
}
}
}

@ -8,8 +8,8 @@
},
//
"App": {
"SelfUrl": "http://*:19001",
"CorsOrigins": "http://localhost:19001;http://localhost:18000"
"SelfUrl": "http://*:19000",
"CorsOrigins": "http://localhost:19000;http://localhost:18000"
},
//
"DbList": [

Loading…
Cancel
Save