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 Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity; using Microsoft.Extensions.DependencyInjection.Extensions;
using NPin.Framework.AspNetCore.Abp;
using NPin.Framework.Core;
using Volo.Abp.AspNetCore.ExceptionHandling;
namespace NPin.Framework.AspNetCore; namespace NPin.Framework.AspNetCore;
[DependsOn(typeof(NPinFrameworkCoreModule))] [DependsOn(typeof(NPinFrameworkCoreModule))]
public class NPinFrameworkAspNetCoreModule : AbpModule public class NPinFrameworkAspNetCoreModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Replace(new ServiceDescriptor(typeof(IHttpExceptionStatusCodeFinder),
typeof(NPinHttpExceptionStatusCodeFinder), ServiceLifetime.Transient));
}
} }

@ -69,7 +69,7 @@ public class DbConnOptions
return new SaasMultiTenancyOptions { Name = DefaultTenantName, Url = Url }; return new SaasMultiTenancyOptions { Name = DefaultTenantName, Url = Url };
} }
public SaasMultiTenancyOptions? GetDefaultMasterSaasMultiTenancy() public SaasMultiTenancyOptions? GetMasterSaasMultiTenancy()
{ {
if (EnabledSaasMultiTenancy == false) if (EnabledSaasMultiTenancy == false)
{ {

@ -1,16 +1,17 @@
using System.Reflection; using System.Reflection;
using System.Text;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using NPin.Framework.SqlSugarCore.Abstractions; using NPin.Framework.SqlSugarCore.Abstractions;
using NPin.Framework.SqlSugarCore.Repositories; using NPin.Framework.SqlSugarCore.Repositories;
using NPin.Framework.SqlSugarCore.Uow; using NPin.Framework.SqlSugarCore.Uow;
using SqlSugar; using SqlSugar;
using Volo.Abp;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.Domain; using Volo.Abp.Domain;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.Modularity; using Volo.Abp.Guids;
namespace NPin.Framework.SqlSugarCore; namespace NPin.Framework.SqlSugarCore;
@ -44,6 +45,37 @@ public class NPinFrameworkSqlSugarCoreModule : AbpModule
var service = context.ServiceProvider; var service = context.ServiceProvider;
var options = service.GetRequiredService<IOptions<DbConnOptions>>().Value; 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) if (options.EnabledCodeFirst)
{ {
CodeFirst(service); CodeFirst(service);

@ -29,6 +29,7 @@ public class SqlSugarRepository<TEntity> : ISqlSugarRepository<TEntity>, IReposi
public virtual async Task<ISqlSugarClient> GetDbContextAsync() public virtual async Task<ISqlSugarClient> GetDbContextAsync()
{ {
var db = (await _sugarDbContextProvider.GetDbContextAsync()).SqlSugarClient; var db = (await _sugarDbContextProvider.GetDbContextAsync()).SqlSugarClient;
// await Console.Out.WriteLineAsync("获取的id" + db.ContextID);
return db; return db;
} }

@ -78,6 +78,13 @@ public class SqlSugarDbConnectionCreator : ISqlSugarDbConnectionCreator, ITransi
ConnectionString = dbConnOptions.Url, ConnectionString = dbConnOptions.Url,
IsAutoCloseConnection = true, IsAutoCloseConnection = true,
SlaveConnectionConfigs = slaveConfigs, SlaveConnectionConfigs = slaveConfigs,
LanguageType = LanguageType.Chinese,
MoreSettings = new ConnMoreSettings
{
SqliteCodeFirstEnableDescription = true,
PgSqlIsAutoToLower = true,
PgSqlIsAutoToLowerCodeFirst = true,
},
// CodeFirst 非空值判断 // CodeFirst 非空值判断
ConfigureExternalServices = new ConfigureExternalServices ConfigureExternalServices = new ConfigureExternalServices
{ {

@ -23,7 +23,7 @@ public class SqlSugarDbContext : ISqlSugarDbContext
public ISqlSugarClient SqlSugarClient { get; private set; } public ISqlSugarClient SqlSugarClient { get; private set; }
public DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value; public DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
public ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>(); public ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>();
public ICurrentTenant? CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>(); public ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
public IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>(); public IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
private IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>(); private IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>();
@ -51,10 +51,42 @@ public class SqlSugarDbContext : ISqlSugarDbContext
connectionCreator.DataExecuted = DataExecuted; connectionCreator.DataExecuted = DataExecuted;
connectionCreator.OnLogExecuting = OnLogExecuting; connectionCreator.OnLogExecuting = OnLogExecuting;
connectionCreator.OnLogExecuted = OnLogExecuted; connectionCreator.OnLogExecuted = OnLogExecuted;
SqlSugarClient = new SqlSugarClient(connectionCreator.Build()); var connStr = GetCurrentConnectionString();
SqlSugarClient = new SqlSugarClient(connectionCreator.Build(action: opt =>
{
opt.ConnectionString = connStr;
}));
connectionCreator.SetDbAop(SqlSugarClient); 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>
/// 上下文对象扩展 /// 上下文对象扩展
/// </summary> /// </summary>
@ -69,7 +101,7 @@ public class SqlSugarDbContext : ISqlSugarDbContext
if (IsMultiTenantFilterEnabled) if (IsMultiTenantFilterEnabled)
{ {
sqlSugarClient.QueryFilter.AddTableFilter<IMultiTenant>(u => u.TenantId == GuidGenerator.Create()); sqlSugarClient.QueryFilter.AddTableFilter<IMultiTenant>(u => u.TenantId == CurrentTenant.Id);
} }
// 自定义其它Filter // 自定义其它Filter
@ -200,6 +232,11 @@ public class SqlSugarDbContext : ISqlSugarDbContext
protected virtual void OnLogExecuted(string sql, SugarParameter[] pars) 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> /// <summary>

@ -14,7 +14,7 @@ public class UnitOfWorkSqlSugarDbContextProvider<TDbContext> : ISugarDbContextPr
{ {
private readonly ISqlSugarDbConnectionCreator _dbConnectionCreator; private readonly ISqlSugarDbConnectionCreator _dbConnectionCreator;
private ILogger<UnitOfWorkSqlSugarDbContextProvider<TDbContext>> Logger { get; set; } private ILogger<UnitOfWorkSqlSugarDbContextProvider<TDbContext>> Logger { get; set; }
private IServiceProvider ServiceProvider { get; set; } public IServiceProvider ServiceProvider { get; set; }
private static AsyncLocalDbContextAccessor ContextInstance => AsyncLocalDbContextAccessor.Instance; private static AsyncLocalDbContextAccessor ContextInstance => AsyncLocalDbContextAccessor.Instance;
protected readonly IUnitOfWorkManager UnitOfWorkManager; protected readonly IUnitOfWorkManager UnitOfWorkManager;
@ -50,7 +50,10 @@ public class UnitOfWorkSqlSugarDbContextProvider<TDbContext> : ISugarDbContextPr
if (unitOfWork == null || !unitOfWork.Options.IsTransactional) if (unitOfWork == null || !unitOfWork.Options.IsTransactional)
{ {
// set if is null // set if is null
ContextInstance.Current ??= (TDbContext)ServiceProvider.GetRequiredService<ISqlSugarDbContext>(); if (ContextInstance.Current is null)
{
ContextInstance.Current = (TDbContext)ServiceProvider.GetRequiredService<ISqlSugarDbContext>();
}
var dbContext = (TDbContext)ContextInstance.Current; var dbContext = (TDbContext)ContextInstance.Current;
// 提高体验取消uow的强制性 // 提高体验取消uow的强制性
@ -62,9 +65,11 @@ public class UnitOfWorkSqlSugarDbContextProvider<TDbContext> : ISugarDbContextPr
var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey); var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
if (databaseApi == null) if (databaseApi == null)
{ {
databaseApi = // db根据连接字符串来创建
new SqlSugarDatabaseApi(CreateDbContextAsync(unitOfWork, connectionStringName, connectionString) databaseApi = new SqlSugarDatabaseApi(
.Result); CreateDbContextAsync(unitOfWork, connectionStringName, connectionString).Result
);
// 创建的db加入到当前工作单元中
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi); unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
} }
@ -126,7 +131,7 @@ public class UnitOfWorkSqlSugarDbContextProvider<TDbContext> : ISugarDbContextPr
); );
unitOfWork.AddTransactionApi(transactionApiKey, transaction); unitOfWork.AddTransactionApi(transactionApiKey, transaction);
// await dbContext.SqlSugarClient.Ado.BeginTranAsync(); await dbContext.SqlSugarClient.Ado.BeginTranAsync();
return dbContext; return dbContext;
} }

@ -1,4 +1,5 @@
using NPin.Framework.AuditLogging.Domain.Entities; using Microsoft.Extensions.Options;
using NPin.Framework.AuditLogging.Domain.Entities;
using Volo.Abp.AspNetCore.ExceptionHandling; using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Auditing; using Volo.Abp.Auditing;
using Volo.Abp.Data; using Volo.Abp.Data;
@ -17,12 +18,12 @@ public class AuditLogInfoToAuditLogConverter : IAuditLogInfoToAuditLogConverter,
public AuditLogInfoToAuditLogConverter(IGuidGenerator guidGenerator, public AuditLogInfoToAuditLogConverter(IGuidGenerator guidGenerator,
IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, IJsonSerializer jsonSerializer, IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, IJsonSerializer jsonSerializer,
AbpExceptionHandlingOptions exceptionHandlingOptions) IOptions<AbpExceptionHandlingOptions> exceptionHandlingOptions)
{ {
GuidGenerator = guidGenerator; GuidGenerator = guidGenerator;
ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter; ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter;
JsonSerializer = jsonSerializer; JsonSerializer = jsonSerializer;
ExceptionHandlingOptions = exceptionHandlingOptions; ExceptionHandlingOptions = exceptionHandlingOptions.Value;
} }
public virtual Task<AuditLogAggregateRoot> ConvertAsync(AuditLogInfo auditLogInfo) public virtual Task<AuditLogAggregateRoot> ConvertAsync(AuditLogInfo auditLogInfo)

@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using NPin.Framework.AuditLogging.Domain.Repositories; using NPin.Framework.AuditLogging.Domain.Repositories;
@ -17,12 +18,12 @@ public class AuditingStore : IAuditingStore, ITransientDependency
protected IAuditLogInfoToAuditLogConverter Converter { get; } protected IAuditLogInfoToAuditLogConverter Converter { get; }
public AuditingStore(ILogger<AuditingStore> logger, IAuditLogRepository auditLogRepository, public AuditingStore(ILogger<AuditingStore> logger, IAuditLogRepository auditLogRepository,
IUnitOfWorkManager unitOfWorkManager, AbpAuditingOptions options, IAuditLogInfoToAuditLogConverter converter) IUnitOfWorkManager unitOfWorkManager, IOptions<AbpAuditingOptions> options, IAuditLogInfoToAuditLogConverter converter)
{ {
Logger = logger; Logger = logger;
AuditLogRepository = auditLogRepository; AuditLogRepository = auditLogRepository;
UnitOfWorkManager = unitOfWorkManager; UnitOfWorkManager = unitOfWorkManager;
Options = options; Options = options.Value;
Converter = converter; Converter = converter;
} }

@ -7,7 +7,7 @@ using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities; namespace NPin.Framework.AuditLogging.Domain.Entities;
[DisableAuditing] [DisableAuditing]
[SugarTable("NPinAuditLogAction", "审计日志操作表")] [SugarTable("SysAuditLogAction", "审计日志操作表")]
[SugarIndex($"index_{nameof(AuditLogId)}", nameof(AuditLogId), OrderByType.Asc)] [SugarIndex($"index_{nameof(AuditLogId)}", nameof(AuditLogId), OrderByType.Asc)]
[SugarIndex($"index_{nameof(TenantId)}_{nameof(ExecutionTime)}", nameof(TenantId), OrderByType.Asc, nameof(ServiceName), [SugarIndex($"index_{nameof(TenantId)}_{nameof(ExecutionTime)}", nameof(TenantId), OrderByType.Asc, nameof(ServiceName),
OrderByType.Asc, nameof(MethodName), OrderByType.Asc, nameof(ExecutionTime), OrderByType.Asc)] OrderByType.Asc, nameof(MethodName), OrderByType.Asc, nameof(ExecutionTime), OrderByType.Asc)]

@ -8,7 +8,7 @@ using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities; namespace NPin.Framework.AuditLogging.Domain.Entities;
[DisableAuditing] [DisableAuditing]
[SugarTable("NPinAuditLog", "审计日志")] [SugarTable("SysAuditLog", "审计日志")]
[SugarIndex($"index_{nameof(ExecutionTime)}", nameof(TenantId), OrderByType.Asc, nameof(ExecutionTime), [SugarIndex($"index_{nameof(ExecutionTime)}", nameof(TenantId), OrderByType.Asc, nameof(ExecutionTime),
OrderByType.Asc)] OrderByType.Asc)]
[SugarIndex($"index_{nameof(ExecutionTime)}_{nameof(UserId)}", nameof(TenantId), OrderByType.Asc, nameof(UserId), [SugarIndex($"index_{nameof(ExecutionTime)}_{nameof(UserId)}", nameof(TenantId), OrderByType.Asc, nameof(UserId),

@ -7,7 +7,7 @@ using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities; namespace NPin.Framework.AuditLogging.Domain.Entities;
[SugarTable("NPinEntityChange")] [SugarTable("SysEntityChange")]
[SugarIndex($"index_{nameof(AuditLogId)}", nameof(AuditLogId), OrderByType.Asc)] [SugarIndex($"index_{nameof(AuditLogId)}", nameof(AuditLogId), OrderByType.Asc)]
[SugarIndex($"index_{nameof(TenantId)}_{nameof(EntityId)}", nameof(TenantId), OrderByType.Asc, [SugarIndex($"index_{nameof(TenantId)}_{nameof(EntityId)}", nameof(TenantId), OrderByType.Asc,
nameof(EntityTypeFullName), OrderByType.Asc, nameof(EntityId), OrderByType.Asc)] nameof(EntityTypeFullName), OrderByType.Asc, nameof(EntityId), OrderByType.Asc)]

@ -7,7 +7,7 @@ using Volo.Abp.MultiTenancy;
namespace NPin.Framework.AuditLogging.Domain.Entities; namespace NPin.Framework.AuditLogging.Domain.Entities;
[SugarTable("NPinEntityPropertyChange")] [SugarTable("SysEntityPropertyChange")]
[SugarIndex($"index_{nameof(EntityChangeId)}", nameof(EntityChangeId), OrderByType.Asc)] [SugarIndex($"index_{nameof(EntityChangeId)}", nameof(EntityChangeId), OrderByType.Asc)]
public class EntityPropertyChangeEntity : Entity<Guid>, IMultiTenant public class EntityPropertyChangeEntity : Entity<Guid>, IMultiTenant
{ {

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

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

@ -10,6 +10,8 @@ namespace NPin.Framework.SettingManagement.Domain;
public class SettingManager : ISettingManager, ISingletonDependency public class SettingManager : ISettingManager, ISingletonDependency
{ {
private readonly Lazy<List<ISettingManagementProvider>> _lazyProviders; private readonly Lazy<List<ISettingManagementProvider>> _lazyProviders;
protected readonly IServiceProvider ServiceProvider;
protected ISettingDefinitionManager SettingDefinitionManager { get; } protected ISettingDefinitionManager SettingDefinitionManager { get; }
protected ISettingEncryptionService SettingEncryptionService { get; } protected ISettingEncryptionService SettingEncryptionService { get; }
protected List<ISettingManagementProvider> Providers => _lazyProviders.Value; protected List<ISettingManagementProvider> Providers => _lazyProviders.Value;
@ -21,18 +23,21 @@ public class SettingManager : ISettingManager, ISingletonDependency
ISettingEncryptionService settingEncryptionService, ISettingEncryptionService settingEncryptionService,
IOptions<SettingManagementOptions> options) IOptions<SettingManagementOptions> options)
{ {
ServiceProvider = serviceProvider;
SettingDefinitionManager = settingDefinitionManager; SettingDefinitionManager = settingDefinitionManager;
SettingEncryptionService = settingEncryptionService; SettingEncryptionService = settingEncryptionService;
Options = options.Value; Options = options.Value;
// TODO use IServiceScopeFactory and create a scope ? _lazyProviders = new Lazy<List<ISettingManagementProvider>>(CreateSettingManagementProviders, true);
_lazyProviders = new Lazy<List<ISettingManagementProvider>>( }
() => Options
.Providers protected virtual List<ISettingManagementProvider> CreateSettingManagementProviders()
.Select(c => serviceProvider.GetRequiredService(c) as ISettingManagementProvider) {
.ToList(), using var scope = ServiceProvider.CreateScope();
true
); return Options.Providers
.Select(p => (scope.ServiceProvider.GetRequiredService(p) as ISettingManagementProvider)!)
.ToList();
} }
public virtual Task<string?> GetOrNullAsync(string name, string providerName, string? providerKey, 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 IDistributedCache<SettingCacheItem> Cache { get; }
protected ISettingDefinitionManager SettingDefinitionManager { get; } protected ISettingDefinitionManager SettingDefinitionManager { get; }
protected ISettingRepository SettingRepository { get; } protected ISettingRepository SettingRepository { get; }
protected IGuidGenerator GuidGenerator { get; }
public SettingManagementStore(IDistributedCache<SettingCacheItem> cache, public SettingManagementStore(IDistributedCache<SettingCacheItem> cache,
ISettingDefinitionManager settingDefinitionManager, ISettingRepository settingRepository, ISettingDefinitionManager settingDefinitionManager, ISettingRepository settingRepository)
IGuidGenerator guidGenerator)
{ {
Cache = cache; Cache = cache;
SettingDefinitionManager = settingDefinitionManager; SettingDefinitionManager = settingDefinitionManager;
SettingRepository = settingRepository; SettingRepository = settingRepository;
GuidGenerator = guidGenerator;
} }
[UnitOfWork] [UnitOfWork]
@ -65,7 +62,7 @@ public class SettingManagementStore : ISettingManagementStore, ITransientDepende
var setting = await SettingRepository.FindAsync(name, providerName, providerKey); var setting = await SettingRepository.FindAsync(name, providerName, providerKey);
if (setting == null) if (setting == null)
{ {
setting = new SettingEntity(GuidGenerator.Create(), name, value, providerName, providerKey); setting = new SettingEntity(name, value, providerName, providerKey);
await SettingRepository.InsertAsync(setting); await SettingRepository.InsertAsync(setting);
} }
else else

@ -29,7 +29,17 @@ public class TenantService : NPinCrudAppService<TenantAggregateRoot, TenantGetOu
} }
/// <summary> /// <summary>
/// 多查 /// 租户单查
/// </summary>
/// <param name="id">唯一ID</param>
/// <returns></returns>
public override Task<TenantGetOutputDto> GetAsync(Guid id)
{
return base.GetAsync(id);
}
/// <summary>
/// 租户多查
/// </summary> /// </summary>
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <returns></returns>
@ -87,7 +97,6 @@ public class TenantService : NPinCrudAppService<TenantAggregateRoot, TenantGetOu
return await base.UpdateAsync(id, input); return await base.UpdateAsync(id, input);
} }
// 只是为了可以在swagger上展示?
/// <summary> /// <summary>
/// 租户删除 /// 租户删除
/// </summary> /// </summary>

@ -9,7 +9,7 @@ using Check = Volo.Abp.Check;
namespace NPin.Framework.TenantManagement.Domain; namespace NPin.Framework.TenantManagement.Domain;
[SugarTable("NPinTenant")] [SugarTable("SysTenant", "租户表")]
[MasterTenant] [MasterTenant]
public class TenantAggregateRoot : FullAuditedAggregateRoot<Guid>, IHasEntityVersion public class TenantAggregateRoot : FullAuditedAggregateRoot<Guid>, IHasEntityVersion
{ {

@ -5,9 +5,14 @@ public class ConfigConst
/// <summary> /// <summary>
/// 系统配置前缀 /// 系统配置前缀
/// </summary> /// </summary>
public const string SysConfigPrefix = "Sys"; public const string SysConfigPrefix = "sys";
public const string AliyunConfigKey = "Aliyun"; /// <summary>
public const string TencentConfigKey = "Tencent"; /// 短信配置相关
public const string SmsConfigKey = "Sms"; /// </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";
} }

@ -5,7 +5,7 @@ namespace NPin.Framework.Upms.Domain.Shared.Enums;
/// <summary> /// <summary>
/// 短信提供商枚举 /// 短信提供商枚举
/// </summary> /// </summary>
public enum SmsProviderEnum public enum SmsProviderTypeEnum
{ {
Aliyun, Aliyun,
Tencent Tencent

@ -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,4 +1,5 @@
using NPin.Framework.Upms.Domain.Shared.Enums; using Newtonsoft.Json;
using NPin.Framework.Upms.Domain.Shared.Enums;
namespace NPin.Framework.Upms.Domain.Shared.Model; namespace NPin.Framework.Upms.Domain.Shared.Model;
@ -10,22 +11,64 @@ public class SmsConfigModel
public bool Enabled { get; set; } = false; public bool Enabled { get; set; } = false;
/// <summary> /// <summary>
/// 设定集 /// 提供商列表
/// </summary> /// </summary>
public Dictionary<SmsTypeEnum, SmsSettings> Settings { get; set; } [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 class SmsSettings 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>
/// 是否启用 /// 短信提供商配置
/// </summary> /// </summary>
public bool Enabled { get; set; } = false; public class SmsProvider
{
/// <summary>
/// ID
/// </summary>
public Guid Id { get; set; }
/// <summary> /// <summary>
/// 服务提供商 /// 服务提供商
/// </summary> /// </summary>
public SmsProviderEnum Provider { get; set; } public SmsProviderTypeEnum ProviderType { get; set; }
/// <summary>
/// 访问密钥Key
/// </summary>
public string AccessKeyId { get; set; }
/// <summary>
/// 访问密钥
/// </summary>
public string AccessKeySecret { get; set; }
/// <summary> /// <summary>
/// 区域 /// 区域
@ -37,6 +80,19 @@ public class SmsSettings
/// </summary> /// </summary>
public string Endpoint { get; set; } 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>
/// 短信签名名称 /// 短信签名名称
/// </summary> /// </summary>
@ -46,6 +102,24 @@ public class SmsSettings
/// 短信模板Code /// 短信模板Code
/// </summary> /// </summary>
public string TemplateCode { get; set; } 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>
/// 过期时间,单位:秒 /// 过期时间,单位:秒

@ -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; }
}

@ -5,7 +5,7 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Authorization; namespace NPin.Framework.Upms.Domain.Authorization;
[SugarTable("Social", "第三方授权表")] [SugarTable("SysSocial", "第三方授权表")]
public class SocialAggregateRoot: AggregateRoot<Guid>, ISoftDelete, IHasCreationTime public class SocialAggregateRoot: AggregateRoot<Guid>, ISoftDelete, IHasCreationTime
{ {
[SugarColumn(IsPrimaryKey = true)] [SugarColumn(IsPrimaryKey = true)]

@ -5,7 +5,7 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities; namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("announcement", "系统公告表")] [SugarTable("SysAnnouncement", "系统公告表")]
public class AnnouncementEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled public class AnnouncementEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{ {
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; } [SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }

@ -6,7 +6,7 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities; namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("Config", "系统配置表")] [SugarTable("SysConfig", "系统配置表")]
[SugarIndex($"index_{nameof(Key)}", nameof(Key), OrderByType.Asc, true)] [SugarIndex($"index_{nameof(Key)}", nameof(Key), OrderByType.Asc, true)]
public class ConfigEntity : Entity<Guid>, IEnabled, IOrderNum, ISoftDelete, IAuditedObject public class ConfigEntity : Entity<Guid>, IEnabled, IOrderNum, ISoftDelete, IAuditedObject
{ {

@ -5,7 +5,7 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities; namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("File", "文件信息表")] [SugarTable("SysFile", "文件信息表")]
public class FileAggregateRoot : AggregateRoot<Guid>, IAuditedObject public class FileAggregateRoot : AggregateRoot<Guid>, IAuditedObject
{ {
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")] [SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]

@ -10,7 +10,8 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities; namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("LoginLog", "登录日志表")] [SugarTable("SysLoginLog", "登录日志表")]
[SugarIndex($"index_{nameof(LoginUser)}", nameof(LoginUser), OrderByType.Asc)]
public class LoginLogEntity : Entity<Guid>, ICreationAuditedObject public class LoginLogEntity : Entity<Guid>, ICreationAuditedObject
{ {
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; } [SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
@ -28,10 +29,10 @@ public class LoginLogEntity : Entity<Guid>, ICreationAuditedObject
public string? LoginLocation { get; set; } public string? LoginLocation { get; set; }
[SugarColumn(ColumnDescription = "Ipv4")] [SugarColumn(ColumnDescription = "Ipv4")]
public string? LoginIPv4 { get; set; } public string? LoginIpv4 { get; set; }
[SugarColumn(ColumnDescription = "Ipv6")] [SugarColumn(ColumnDescription = "Ipv6")]
public string? LoginIPv6 { get; set; } public string? LoginIpv6 { get; set; }
[SugarColumn(ColumnDescription = "浏览器")] [SugarColumn(ColumnDescription = "浏览器")]
public string? Browser { get; set; } public string? Browser { get; set; }
@ -71,8 +72,8 @@ public class LoginLogEntity : Entity<Guid>, ICreationAuditedObject
{ {
Browser = clientInfo.Device.Family, Browser = clientInfo.Device.Family,
Os = clientInfo.OS.ToString(), Os = clientInfo.OS.ToString(),
LoginIPv4 = ipv4AddrStr, LoginIpv4 = ipv4AddrStr,
LoginIPv6 = ipv6AddrStr, LoginIpv6 = ipv6AddrStr,
LoginLocation = $"{location.Country}-{location.Province}-{location.City}" LoginLocation = $"{location.Country}-{location.Province}-{location.City}"
}; };

@ -5,7 +5,7 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities; namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("Organization", "组织机构表")] [SugarTable("SysOrganization", "组织机构表")]
public class OrganizationEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled public class OrganizationEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{ {
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")] [SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]

@ -5,7 +5,7 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities; namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("Post", "岗位表")] [SugarTable("SysPost", "岗位表")]
public class PostEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled public class PostEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{ {
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")] [SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]

@ -6,7 +6,7 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities; namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("Role", "角色表")] [SugarTable("SysRole", "角色表")]
public class RoleEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled public class RoleEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IEnabled
{ {
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")] [SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]

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

@ -8,7 +8,8 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.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 public class UserEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IEnabled, IOrderNum
{ {
#region User #region User

@ -4,7 +4,7 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities; namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("UserMetadata", "用户元数据表")] [SugarTable("SysUserMetadata", "用户元数据表")]
public class UserMetaEntity : Entity<Guid>, IAuditedObject public class UserMetaEntity : Entity<Guid>, IAuditedObject
{ {
[SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; } [SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }

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

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities; namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("RelUserPost","用户-岗位 关系表")] [SugarTable("SysRelUserPost","用户-岗位 关系表")]
public class UserPostEntity: Entity<Guid> public class UserPostEntity: Entity<Guid>
{ {
[SugarColumn(IsPrimaryKey = true)] [SugarColumn(IsPrimaryKey = true)]

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.Entities; namespace NPin.Framework.Upms.Domain.Entities;
[SugarTable("RelUserRole", "用户-角色 关系表")] [SugarTable("SysRelUserRole", "用户-角色 关系表")]
public class UserRoleEntity: Entity<Guid> public class UserRoleEntity: Entity<Guid>
{ {
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")] [SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]

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

@ -3,6 +3,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\framework\NPin.Framework.Caching.FreeRedis\NPin.Framework.Caching.FreeRedis.csproj" /> <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="..\..\..\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" /> <ProjectReference Include="..\NPin.Framework.Upms.Domain.Shared\NPin.Framework.Upms.Domain.Shared.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -1,7 +1,10 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using NPin.Framework.Caching.FreeRedis; 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.Authorization;
using NPin.Framework.Upms.Domain.OperLog; using NPin.Framework.Upms.Domain.OperLog;
using NPin.Framework.Upms.Domain.Settings;
using NPin.Framework.Upms.Domain.Shared; using NPin.Framework.Upms.Domain.Shared;
using Volo.Abp.AspNetCore.SignalR; using Volo.Abp.AspNetCore.SignalR;
using Volo.Abp.Caching; using Volo.Abp.Caching;
@ -12,6 +15,8 @@ namespace NPin.Framework.Upms.Domain;
[DependsOn( [DependsOn(
typeof(NPinFrameworkUpmsDomainSharedModule), typeof(NPinFrameworkUpmsDomainSharedModule),
typeof(NPinFrameworkCachingFreeRedisModule), typeof(NPinFrameworkCachingFreeRedisModule),
// Settings
typeof(NPinFrameworkSettingManagementDomainModule),
// Abp // Abp
typeof(AbpAspNetCoreSignalRModule), typeof(AbpAspNetCoreSignalRModule),
typeof(AbpDddDomainModule), typeof(AbpDddDomainModule),
@ -28,5 +33,8 @@ public class NPinFrameworkUpmsDomainModule : AbpModule
opts.Filters.Add<PermissionGlobalAttribute>(); opts.Filters.Add<PermissionGlobalAttribute>();
opts.Filters.Add<OperLogGlobalAttribute>(); opts.Filters.Add<OperLogGlobalAttribute>();
}); });
// 添加Upms模块的设置提供者
Configure<SettingManagementOptions>(opt => { opt.Providers.Add<UpmsSettingManagementProvider>(); });
} }
} }

@ -3,20 +3,17 @@ using System.Net.Sockets;
using IPTools.Core; using IPTools.Core;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using NPin.Framework.Core.Extensions; using NPin.Framework.Core.Extensions;
using NPin.Framework.Upms.Domain.Entities;
using NPin.Framework.Upms.Domain.Shared.OperLog; using NPin.Framework.Upms.Domain.Shared.OperLog;
using SqlSugar; using SqlSugar;
using UAParser;
using Volo.Abp.Auditing; using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
namespace NPin.Framework.Upms.Domain.OperLog; namespace NPin.Framework.Upms.Domain.OperLog;
[SugarTable("OperationLog", "操作日志记录表")] [SugarTable("SysOperationLog", "操作日志记录表")]
public class OperationLogEntity : Entity<Guid>, ICreationAuditedObject public class OperationLogEntity : Entity<Guid>, ICreationAuditedObject
{ {
[SugarColumn(IsPrimaryKey = true)] [SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }
public override Guid Id { get; protected set; }
[SugarColumn(ColumnDescription = "日志标题")] [SugarColumn(ColumnDescription = "日志标题")]
public string? Title { get; set; } public string? Title { get; set; }
@ -76,6 +73,7 @@ public class OperationLogEntity: Entity<Guid>, ICreationAuditedObject
info = IpTool.Search(ipAddr.ToString()); info = IpTool.Search(ipAddr.ToString());
} }
} }
return new OperationLogEntity return new OperationLogEntity
{ {
OperIPv4 = ipv4AddrStr, OperIPv4 = ipv4AddrStr,

@ -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; using NPin.Framework.Upms.Domain.Shared.Enums;
using NPin.Framework.Upms.Domain.Shared.Model;
namespace NPin.Framework.Upms.Domain.Managers; namespace NPin.Framework.Upms.Domain.Sms;
/// <summary> /// <summary>
/// 短信接口 /// 统一短信接口
/// </summary> /// </summary>
public interface ISms public interface ISms
{ {
Task SendSmsAsync(SmsTypeEnum smsType, string phoneNumbers, object templateParam); /// <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));
}
}

@ -4,6 +4,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\framework\NPin.Framework.Mapster\NPin.Framework.Mapster.csproj" /> <ProjectReference Include="..\..\..\framework\NPin.Framework.Mapster\NPin.Framework.Mapster.csproj" />
<ProjectReference Include="..\..\..\framework\NPin.Framework.SqlSugarCore\NPin.Framework.SqlSugarCore.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" /> <ProjectReference Include="..\NPin.Framework.Upms.Domain\NPin.Framework.Upms.Domain.csproj" />
</ItemGroup> </ItemGroup>

@ -1,11 +1,14 @@
using NPin.Framework.Mapster; using NPin.Framework.Mapster;
using NPin.Framework.SettingManagement.SqlSugarCore;
using NPin.Framework.SqlSugarCore; using NPin.Framework.SqlSugarCore;
using NPin.Framework.Upms.Domain; using NPin.Framework.Upms.Domain;
namespace NPin.Framework.Upms.SqlSugarCore; namespace NPin.Framework.Upms.SqlSugarCore.Repositories;
[DependsOn( [DependsOn(
typeof(NPinFrameworkUpmsDomainModule), typeof(NPinFrameworkUpmsDomainModule),
// settings
typeof(NPinFrameworkSettingManagementSqlSugarCoreModule),
// framework // framework
typeof(NPinFrameworkMapsterModule), typeof(NPinFrameworkMapsterModule),
typeof(NPinFrameworkSqlSugarCoreModule) typeof(NPinFrameworkSqlSugarCoreModule)

@ -4,7 +4,7 @@ using NPin.Framework.Upms.Domain.Shared.Consts;
using SqlSugar; using SqlSugar;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace NPin.Framework.Upms.SqlSugarCore; namespace NPin.Framework.Upms.SqlSugarCore.Repositories;
public class NPinUpmsDbContext : SqlSugarDbContext public class NPinUpmsDbContext : SqlSugarDbContext
{ {

@ -5,7 +5,7 @@ using NPin.Framework.Upms.Domain.Entities;
using NPin.Framework.Upms.Domain.Repositories; using NPin.Framework.Upms.Domain.Repositories;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace NPin.Framework.Upms.SqlSugarCore; namespace NPin.Framework.Upms.SqlSugarCore.Repositories;
public class ConfigRepository : SqlSugarRepository<ConfigEntity, Guid>, IConfigRepository, ITransientDependency public class ConfigRepository : SqlSugarRepository<ConfigEntity, Guid>, IConfigRepository, ITransientDependency
{ {

@ -1,4 +1,4 @@
using NPin.Framework.Upms.SqlSugarCore; using NPin.Framework.Upms.SqlSugarCore.Repositories;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace NPin.SqlSugarCore; namespace NPin.SqlSugarCore;

@ -3,7 +3,7 @@ using NPin.Framework.AuditLogging.SqlSugarCore;
using NPin.Framework.Mapster; using NPin.Framework.Mapster;
using NPin.Framework.SqlSugarCore; using NPin.Framework.SqlSugarCore;
using NPin.Framework.TenantManagement.SqlSugarCore; using NPin.Framework.TenantManagement.SqlSugarCore;
using NPin.Framework.Upms.SqlSugarCore; using NPin.Framework.Upms.SqlSugarCore.Repositories;
namespace NPin.SqlSugarCore; namespace NPin.SqlSugarCore;

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

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

@ -9,7 +9,16 @@
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
}, },
"dotnetRunMessages": true, "dotnetRunMessages": true,
"applicationUrl": "http://localhost:19001" "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": { "App": {
"SelfUrl": "http://*:19001", "SelfUrl": "http://*:19000",
"CorsOrigins": "http://localhost:19001;http://localhost:18000" "CorsOrigins": "http://localhost:19000;http://localhost:18000"
}, },
// //
"DbList": [ "DbList": [

Loading…
Cancel
Save