Compare commits

...

11 Commits

Author SHA1 Message Date
橙子
5dfaf75440 feat: 新增实体领域事件 2024-12-10 22:04:46 +08:00
橙子
6be5398114 perf: 优化等级处理 2024-12-08 03:43:04 +08:00
chenchun
3932b24fda fix: 修复审计日志无工作单元 2024-12-02 10:11:25 +08:00
橙子
356938d6d3 pref: 优化db工作单元 2024-11-30 23:45:19 +08:00
chenchun
1090907178 perf: 优化动态api启动 2024-11-29 18:01:54 +08:00
chenchun
da2f7073f9 style: 优化abp-cli提示 2024-11-29 15:25:16 +08:00
chenchun
f656ec32c1 perf: 优化在线hub 2024-11-29 14:58:10 +08:00
chenchun
1cc0ef916f feat:去除SqlSugarDbContextFactory的缓存 2024-11-20 16:11:33 +08:00
chenchun
5d793344cd fix: 修复移除属性注入 2024-11-20 13:43:35 +08:00
橙子
bcaca0b782 Merge branch 'refs/heads/multipleDbContext' into abp
# Conflicts:
#	Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarCoreExtensions.cs
2024-11-19 21:44:57 +08:00
橙子
5cee7319c6 update Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlsugarCoreExtensions.cs.
Signed-off-by: 橙子 <454313500@qq.com>
2024-11-19 13:43:25 +00:00
23 changed files with 311 additions and 183 deletions

View File

@@ -9,18 +9,20 @@ using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Options;
namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
{ {
public static class SwaggerAddExtensions public static class SwaggerAddExtensions
{ {
public static IServiceCollection AddYiSwaggerGen<Program>(this IServiceCollection services, Action<SwaggerGenOptions>? action=null) public static IServiceCollection AddYiSwaggerGen<Program>(this IServiceCollection services,
Action<SwaggerGenOptions>? action = null)
{ {
var mvcOptions = services.GetPreConfigureActions<AbpAspNetCoreMvcOptions>().Configure();
var serviceProvider = services.BuildServiceProvider(); var mvcSettings =
var mvcOptions = serviceProvider.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>(); mvcOptions.ConventionalControllers.ConventionalControllerSettings.DistinctBy(x => x.RemoteServiceName);
var mvcSettings = mvcOptions.Value.ConventionalControllers.ConventionalControllerSettings.DistinctBy(x => x.RemoteServiceName);
services.AddAbpSwaggerGen( services.AddAbpSwaggerGen(
@@ -36,7 +38,8 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
{ {
if (!options.SwaggerGeneratorOptions.SwaggerDocs.ContainsKey(setting.RemoteServiceName)) if (!options.SwaggerGeneratorOptions.SwaggerDocs.ContainsKey(setting.RemoteServiceName))
{ {
options.SwaggerDoc(setting.RemoteServiceName, new OpenApiInfo { Title = setting.RemoteServiceName, Version = "v1" }); options.SwaggerDoc(setting.RemoteServiceName,
new OpenApiInfo { Title = setting.RemoteServiceName, Version = "v1" });
} }
} }
@@ -45,12 +48,15 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
{ {
if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor) if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{ {
var settingOrNull = mvcSettings.Where(x => x.Assembly == controllerActionDescriptor.ControllerTypeInfo.Assembly).FirstOrDefault(); var settingOrNull = mvcSettings
.Where(x => x.Assembly == controllerActionDescriptor.ControllerTypeInfo.Assembly)
.FirstOrDefault();
if (settingOrNull is not null) if (settingOrNull is not null)
{ {
return docName == settingOrNull.RemoteServiceName; return docName == settingOrNull.RemoteServiceName;
} }
} }
return false; return false;
}); });
@@ -87,7 +93,6 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
); );
return services; return services;
} }
} }
@@ -103,7 +108,6 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
/// </summary> /// </summary>
/// <param name="model"></param> /// <param name="model"></param>
/// <param name="context"></param> /// <param name="context"></param>
public void Apply(OpenApiSchema model, SchemaFilterContext context) public void Apply(OpenApiSchema model, SchemaFilterContext context)
{ {
if (context.Type.IsEnum) if (context.Type.IsEnum)
@@ -121,7 +125,8 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
Enum e = (Enum)Enum.Parse(context.Type, name); Enum e = (Enum)Enum.Parse(context.Type, name);
var descrptionOrNull = GetEnumDescription(e); var descrptionOrNull = GetEnumDescription(e);
model.Enum.Add(new OpenApiString(name)); model.Enum.Add(new OpenApiString(name));
stringBuilder.Append($"【枚举:{name}{(descrptionOrNull is null ? string.Empty : $"({descrptionOrNull})")}={Convert.ToInt64(Enum.Parse(context.Type, name))}】<br />"); stringBuilder.Append(
$"【枚举:{name}{(descrptionOrNull is null ? string.Empty : $"({descrptionOrNull})")}={Convert.ToInt64(Enum.Parse(context.Type, name))}】<br />");
}); });
model.Description = stringBuilder.ToString(); model.Description = stringBuilder.ToString();
} }
@@ -133,13 +138,13 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false); var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : null; return attributes.Length > 0 ? attributes[0].Description : null;
} }
} }
public class AddRequiredHeaderParameter : IOperationFilter public class AddRequiredHeaderParameter : IOperationFilter
{ {
public static string HeaderKey { get; set; } = "__tenant"; public static string HeaderKey { get; set; } = "__tenant";
public void Apply(OpenApiOperation operation, OperationFilterContext context) public void Apply(OpenApiOperation operation, OperationFilterContext context)
{ {
if (operation.Parameters == null) if (operation.Parameters == null)

View File

@@ -12,6 +12,7 @@ using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
using Volo.Abp.Users; using Volo.Abp.Users;
using Yi.Framework.SqlSugarCore.Abstractions; using Yi.Framework.SqlSugarCore.Abstractions;
@@ -19,19 +20,23 @@ namespace Yi.Framework.SqlSugarCore;
public class DefaultSqlSugarDbContext : SqlSugarDbContext public class DefaultSqlSugarDbContext : SqlSugarDbContext
{ {
protected DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value; protected DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
protected ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>(); protected ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>();
protected IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>(); protected IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>();
protected ILoggerFactory Logger => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>(); protected ILoggerFactory Logger => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>(); protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
protected IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>(); protected IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
public IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService<IUnitOfWorkManager>();
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false; protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false; protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
protected IEntityChangeEventHelper EntityChangeEventHelper => protected IEntityChangeEventHelper EntityChangeEventHelper =>
LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance); LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance);
public DefaultSqlSugarDbContext(IAbpLazyServiceProvider lazyServiceProvider) : base(lazyServiceProvider)
{
}
protected override void CustomDataFilter(ISqlSugarClient sqlSugarClient) protected override void CustomDataFilter(ISqlSugarClient sqlSugarClient)
{ {
if (IsSoftDeleteFilterEnabled) if (IsSoftDeleteFilterEnabled)
@@ -120,7 +125,7 @@ public class DefaultSqlSugarDbContext : SqlSugarDbContext
} }
//领域事件 //实体变更领域事件
switch (entityInfo.OperationType) switch (entityInfo.OperationType)
{ {
case DataFilterType.InsertByObject: case DataFilterType.InsertByObject:
@@ -163,6 +168,14 @@ public class DefaultSqlSugarDbContext : SqlSugarDbContext
break; break;
} }
//实体领域事件-所有操作类型
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
var eventReport = CreateEventReport(entityInfo.EntityValue);
PublishEntityEvents(eventReport);
}
} }
public override void OnLogExecuting(string sql, SugarParameter[] pars) public override void OnLogExecuting(string sql, SugarParameter[] pars)
@@ -204,4 +217,74 @@ public class DefaultSqlSugarDbContext : SqlSugarDbContext
entityColumnInfo.IsPrimarykey = true; entityColumnInfo.IsPrimarykey = true;
} }
} }
/// <summary>
/// 创建领域事件报告
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
protected virtual EntityEventReport? CreateEventReport(object entity)
{
var eventReport = new EntityEventReport();
//判断是否为领域事件-聚合根
var generatesDomainEventsEntity = entity as IGeneratesDomainEvents;
if (generatesDomainEventsEntity == null)
{
return eventReport;
}
var localEvents = generatesDomainEventsEntity.GetLocalEvents()?.ToArray();
if (localEvents != null && localEvents.Any())
{
eventReport.DomainEvents.AddRange(
localEvents.Select(
eventRecord => new DomainEventEntry(
entity,
eventRecord.EventData,
eventRecord.EventOrder
)
)
);
generatesDomainEventsEntity.ClearLocalEvents();
}
var distributedEvents = generatesDomainEventsEntity.GetDistributedEvents()?.ToArray();
if (distributedEvents != null && distributedEvents.Any())
{
eventReport.DistributedEvents.AddRange(
distributedEvents.Select(
eventRecord => new DomainEventEntry(
entity,
eventRecord.EventData,
eventRecord.EventOrder)
)
);
generatesDomainEventsEntity.ClearDistributedEvents();
}
return eventReport;
}
/// <summary>
/// 发布领域事件
/// </summary>
/// <param name="changeReport"></param>
private void PublishEntityEvents(EntityEventReport changeReport)
{
foreach (var localEvent in changeReport.DomainEvents)
{
UnitOfWorkManager.Current?.AddOrReplaceLocalEvent(
new UnitOfWorkEventRecord(localEvent.EventData.GetType(), localEvent.EventData, localEvent.EventOrder)
);
}
foreach (var distributedEvent in changeReport.DistributedEvents)
{
UnitOfWorkManager.Current?.AddOrReplaceDistributedEvent(
new UnitOfWorkEventRecord(distributedEvent.EventData.GetType(), distributedEvent.EventData, distributedEvent.EventOrder)
);
}
}
} }

View File

@@ -7,8 +7,14 @@ namespace Yi.Framework.SqlSugarCore;
public abstract class SqlSugarDbContext : ISqlSugarDbContextDependencies public abstract class SqlSugarDbContext : ISqlSugarDbContextDependencies
{ {
//属性注入 protected IAbpLazyServiceProvider LazyServiceProvider { get; }
public IAbpLazyServiceProvider LazyServiceProvider { get; set; }
public SqlSugarDbContext(IAbpLazyServiceProvider lazyServiceProvider)
{
this.LazyServiceProvider = lazyServiceProvider;
}
protected ISqlSugarClient SqlSugarClient { get;private set; } protected ISqlSugarClient SqlSugarClient { get;private set; }
public int ExecutionOrder => 0; public int ExecutionOrder => 0;

View File

@@ -38,13 +38,17 @@ namespace Yi.Framework.SqlSugarCore
var connectionString = GetCurrentConnectionString(); var connectionString = GetCurrentConnectionString();
//获取连接配置操作,需要进行缓存 var connectionConfig =BuildConnectionConfig(action: options =>
var connectionConfig = ConnectionConfigCache.GetOrAdd(connectionString, (_) =>
BuildConnectionConfig(action: options =>
{ {
options.ConnectionString = connectionString; options.ConnectionString = connectionString;
options.DbType = GetCurrentDbType(); options.DbType = GetCurrentDbType();
})); });
// var connectionConfig = ConnectionConfigCache.GetOrAdd(connectionString, (_) =>
// BuildConnectionConfig(action: options =>
// {
// options.ConnectionString = connectionString;
// options.DbType = GetCurrentDbType();
// }));
SqlSugarClient = new SqlSugarClient(connectionConfig); SqlSugarClient = new SqlSugarClient(connectionConfig);
//生命周期以下都可以直接使用sqlsugardb了 //生命周期以下都可以直接使用sqlsugardb了
@@ -247,7 +251,6 @@ namespace Yi.Framework.SqlSugarCore
return null; return null;
} }
public virtual void BackupDataBase() public virtual void BackupDataBase()
{ {
string directoryName = "database_backup"; string directoryName = "database_backup";

View File

@@ -36,7 +36,6 @@ namespace Yi.Framework.SqlSugarCore.Uow
Logger = NullLogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>>.Instance; Logger = NullLogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>>.Instance;
} }
//private static object _databaseApiLock = new object();
public virtual async Task<TDbContext> GetDbContextAsync() public virtual async Task<TDbContext> GetDbContextAsync()
{ {
@@ -48,19 +47,16 @@ namespace Yi.Framework.SqlSugarCore.Uow
var unitOfWork = UnitOfWorkManager.Current; var unitOfWork = UnitOfWorkManager.Current;
if (unitOfWork == null /*|| unitOfWork.Options.IsTransactional == false*/) if (unitOfWork == null )
{ {
var dbContext = (TDbContext)ServiceProvider.GetRequiredService<ISqlSugarDbContext>(); //var dbContext = (TDbContext)ServiceProvider.GetRequiredService<ISqlSugarDbContext>();
//提高体验,取消工作单元强制性
//throw new AbpException("A DbContext can only be created inside a unit of work!");
//如果不启用工作单元创建一个新的db不开启事务即可 //如果不启用工作单元创建一个新的db不开启事务即可
return dbContext; //return dbContext;
//2024-11-30改回强制性使用工作单元否则容易造成歧义
throw new AbpException("DbContext 只能在工作单元内工作当前DbContext没有工作单元如需创建新线程并发操作请手动创建工作单元");
} }
//尝试当前工作单元获取db //尝试当前工作单元获取db
var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey); var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);

View File

@@ -53,10 +53,10 @@ public class AuditingStore : IAuditingStore, ITransientDependency
protected virtual async Task SaveLogAsync(AuditLogInfo auditInfo) protected virtual async Task SaveLogAsync(AuditLogInfo auditInfo)
{ {
Logger.LogDebug("Yi-请求追踪:" + JsonHelper.ObjToStr(auditInfo, "yyyy-MM-dd HH:mm:ss")); Logger.LogDebug("Yi-请求追踪:" + JsonHelper.ObjToStr(auditInfo, "yyyy-MM-dd HH:mm:ss"));
// using (var uow = UnitOfWorkManager.Begin(true,isTransactional:false)) using (var uow = UnitOfWorkManager.Begin())
// { {
await AuditLogRepository.InsertAsync(await Converter.ConvertAsync(auditInfo)); await AuditLogRepository.InsertAsync(await Converter.ConvertAsync(auditInfo));
// await uow.CompleteAsync(); await uow.CompleteAsync();
// } }
} }
} }

View File

@@ -110,7 +110,9 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
) )
.ToPageListAsync(pageIndex, input.MaxResultCount, total); .ToPageListAsync(pageIndex, input.MaxResultCount, total);
output.ForEach(x => { x.LevelName = _bbsUserManager._levelCacheDic[x.Level].Name; }); var levelCache = await _bbsUserManager.GetLevelCacheMapAsync();
output.ForEach(x => { x.LevelName = levelCache[x.Level].Name; });
return new PagedResultDto<MoneyTopUserDto> return new PagedResultDto<MoneyTopUserDto>
{ {
Items = output, Items = output,

View File

@@ -158,10 +158,11 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
//查询完主题之后,要过滤一下私有的主题信息 //查询完主题之后,要过滤一下私有的主题信息
items.ApplyPermissionTypeFilter(CurrentUser.Id ?? Guid.Empty); items.ApplyPermissionTypeFilter(CurrentUser.Id ?? Guid.Empty);
var levelCacheDic= await _bbsUserManager.GetLevelCacheMapAsync();
//等级、是否点赞赋值 //等级、是否点赞赋值
items?.ForEach(x => items?.ForEach(x =>
{ {
x.User.LevelName = _bbsUserManager._levelCacheDic[x.User.Level].Name; x.User.LevelName = levelCacheDic[x.User.Level].Name;
if (CurrentUser.Id is not null) if (CurrentUser.Id is not null)
{ {
//默认fasle //默认fasle
@@ -212,7 +213,8 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
} }
}, true) }, true)
.ToListAsync(); .ToListAsync();
output?.ForEach(x => x.User.LevelName = _bbsUserManager._levelCacheDic[x.User.Level].Name); var levelCacheDic= await _bbsUserManager.GetLevelCacheMapAsync();
output?.ForEach(x => x.User.LevelName = levelCacheDic[x.User.Level].Name);
return output; return output;
} }

View File

@@ -14,7 +14,8 @@ namespace Yi.Framework.Bbs.Domain.Managers
{ {
public ISqlSugarRepository<UserAggregateRoot> _userRepository; public ISqlSugarRepository<UserAggregateRoot> _userRepository;
public ISqlSugarRepository<BbsUserExtraInfoEntity> _bbsUserInfoRepository; public ISqlSugarRepository<BbsUserExtraInfoEntity> _bbsUserInfoRepository;
public Dictionary<int, LevelCacheItem> _levelCacheDic; // public Dictionary<int, LevelCacheItem> _levelCacheDic;
private LevelManager _levelManager;
public BbsUserManager(ISqlSugarRepository<UserAggregateRoot> userRepository, public BbsUserManager(ISqlSugarRepository<UserAggregateRoot> userRepository,
ISqlSugarRepository<BbsUserExtraInfoEntity> bbsUserInfoRepository, ISqlSugarRepository<BbsUserExtraInfoEntity> bbsUserInfoRepository,
@@ -23,7 +24,12 @@ namespace Yi.Framework.Bbs.Domain.Managers
{ {
_userRepository = userRepository; _userRepository = userRepository;
_bbsUserInfoRepository = bbsUserInfoRepository; _bbsUserInfoRepository = bbsUserInfoRepository;
_levelCacheDic = levelManager.GetCacheMapAsync().Result; _levelManager = levelManager;
}
public async Task<Dictionary<int, LevelCacheItem>> GetLevelCacheMapAsync()
{
return await _levelManager.GetCacheMapAsync();
} }
public async Task<BbsUserInfoDto?> GetBbsUserInfoAsync(Guid userId) public async Task<BbsUserInfoDto?> GetBbsUserInfoAsync(Guid userId)
@@ -44,7 +50,8 @@ namespace Yi.Framework.Bbs.Domain.Managers
}, true) }, true)
.FirstAsync(user => user.Id == userId); .FirstAsync(user => user.Id == userId);
userInfo.LevelName = _levelCacheDic[userInfo.Level].Name; var levelCacheDic= await GetLevelCacheMapAsync();
userInfo.LevelName = levelCacheDic[userInfo.Level].Name;
return userInfo; return userInfo;
} }
@@ -66,7 +73,8 @@ namespace Yi.Framework.Bbs.Domain.Managers
DiscussNumber = info.DiscussNumber DiscussNumber = info.DiscussNumber
}, true) }, true)
.ToListAsync(); .ToListAsync();
userInfos?.ForEach(userInfo => userInfo.LevelName = _levelCacheDic[userInfo.Level].Name); var levelCacheDic= await GetLevelCacheMapAsync();
userInfos?.ForEach(userInfo => userInfo.LevelName =levelCacheDic[userInfo.Level].Name);
return userInfos ?? new List<BbsUserInfoDto>(); return userInfos ?? new List<BbsUserInfoDto>();
} }

View File

@@ -355,13 +355,13 @@ namespace Yi.Framework.Rbac.Application.Services
{ {
//将后端菜单转换成前端路由,组件级别需要过滤 //将后端菜单转换成前端路由,组件级别需要过滤
output = output =
ObjectMapper.Map<List<MenuDto>, List<MenuAggregateRoot>>(menus).Vue3RuoYiRouterBuild(); ObjectMapper.Map<List<MenuDto>, List<MenuAggregateRoot>>(menus.Where(x=>x.MenuSource==MenuSourceEnum.Ruoyi).ToList()).Vue3RuoYiRouterBuild();
} }
else if (routerType == "pure") else if (routerType == "pure")
{ {
//将后端菜单转换成前端路由,组件级别需要过滤 //将后端菜单转换成前端路由,组件级别需要过滤
output = output =
ObjectMapper.Map<List<MenuDto>, List<MenuAggregateRoot>>(menus).Vue3PureRouterBuild(); ObjectMapper.Map<List<MenuDto>, List<MenuAggregateRoot>>(menus.Where(x=>x.MenuSource==MenuSourceEnum.Pure).ToList()).Vue3PureRouterBuild();
} }
return output; return output;

View File

@@ -13,6 +13,7 @@ namespace Yi.Framework.Rbac.Application.Services.Monitor
{ {
private ILogger<OnlineService> _logger; private ILogger<OnlineService> _logger;
private IHubContext<OnlineHub> _hub; private IHubContext<OnlineHub> _hub;
public OnlineService(ILogger<OnlineService> logger, IHubContext<OnlineHub> hub) public OnlineService(ILogger<OnlineService> logger, IHubContext<OnlineHub> hub)
{ {
_logger = logger; _logger = logger;
@@ -26,18 +27,21 @@ namespace Yi.Framework.Rbac.Application.Services.Monitor
/// <returns></returns> /// <returns></returns>
public Task<PagedResultDto<OnlineUserModel>> GetListAsync([FromQuery] OnlineUserModel online) public Task<PagedResultDto<OnlineUserModel>> GetListAsync([FromQuery] OnlineUserModel online)
{ {
var data = OnlineHub.clientUsers; var data = OnlineHub.ClientUsersDic;
IEnumerable<OnlineUserModel> dataWhere = data.AsEnumerable(); IEnumerable<OnlineUserModel> dataWhere = data.Values.AsEnumerable();
if (!string.IsNullOrEmpty(online.Ipaddr)) if (!string.IsNullOrEmpty(online.Ipaddr))
{ {
dataWhere = dataWhere.Where((u) => u.Ipaddr!.Contains(online.Ipaddr)); dataWhere = dataWhere.Where((u) => u.Ipaddr!.Contains(online.Ipaddr));
} }
if (!string.IsNullOrEmpty(online.UserName)) if (!string.IsNullOrEmpty(online.UserName))
{ {
dataWhere = dataWhere.Where((u) => u.UserName!.Contains(online.UserName)); dataWhere = dataWhere.Where((u) => u.UserName!.Contains(online.UserName));
} }
return Task.FromResult(new PagedResultDto<OnlineUserModel>() { TotalCount = data.Count, Items = dataWhere.ToList() });
return Task.FromResult(new PagedResultDto<OnlineUserModel>()
{ TotalCount = data.Count, Items = dataWhere.ToList() });
} }
@@ -50,12 +54,13 @@ namespace Yi.Framework.Rbac.Application.Services.Monitor
[Route("online/{connnectionId}")] [Route("online/{connnectionId}")]
public async Task<bool> ForceOut(string connnectionId) public async Task<bool> ForceOut(string connnectionId)
{ {
if (OnlineHub.clientUsers.Exists(u => u.ConnnectionId == connnectionId)) if (OnlineHub.ClientUsersDic.ContainsKey(connnectionId))
{ {
//前端接受到这个事件后,触发前端自动退出 //前端接受到这个事件后,触发前端自动退出
await _hub.Clients.Client(connnectionId).SendAsync("forceOut", "你已被强制退出!"); await _hub.Clients.Client(connnectionId).SendAsync("forceOut", "你已被强制退出!");
return true; return true;
} }
return false; return false;
} }
} }

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Authorization; using System.Collections.Concurrent;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -13,26 +14,27 @@ namespace Yi.Framework.Rbac.Application.SignalRHubs
//[Authorize] //[Authorize]
public class OnlineHub : AbpHub public class OnlineHub : AbpHub
{ {
public static readonly List<OnlineUserModel> clientUsers = new(); public static ConcurrentDictionary<string, OnlineUserModel> ClientUsersDic { get; set; } = new();
private readonly static object objLock = new object();
private HttpContext? _httpContext; private readonly HttpContext? _httpContext;
private ILogger<OnlineHub> _logger => LoggerFactory.CreateLogger<OnlineHub>(); private ILogger<OnlineHub> _logger => LoggerFactory.CreateLogger<OnlineHub>();
public OnlineHub(IHttpContextAccessor httpContextAccessor) public OnlineHub(IHttpContextAccessor httpContextAccessor)
{ {
_httpContext = httpContextAccessor?.HttpContext; _httpContext = httpContextAccessor?.HttpContext;
} }
/// <summary> /// <summary>
/// 成功连接 /// 成功连接
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public override Task OnConnectedAsync() public override Task OnConnectedAsync()
{ {
lock (objLock) if (_httpContext is null)
{ {
return Task.CompletedTask;
}
var name = CurrentUser.UserName; var name = CurrentUser.UserName;
var loginUser = new LoginLogAggregateRoot().GetInfoByHttpContext(_httpContext); var loginUser = new LoginLogAggregateRoot().GetInfoByHttpContext(_httpContext);
@@ -48,18 +50,18 @@ namespace Yi.Framework.Rbac.Application.SignalRHubs
}; };
//已登录 //已登录
if (CurrentUser.Id is not null) if (CurrentUser.IsAuthenticated)
{ //先移除之前的用户id一个用户只能一个 {
clientUsers.RemoveAll(u => u.UserId == CurrentUser.Id); ClientUsersDic.RemoveAll(u => u.Value.UserId == CurrentUser.Id);
_logger.LogInformation($"{DateTime.Now}{name},{Context.ConnectionId}连接服务端success当前已连接{clientUsers.Count}个"); _logger.LogDebug(
$"{DateTime.Now}{name},{Context.ConnectionId}连接服务端success当前已连接{ClientUsersDic.Count}个");
} }
//全部移除之后,再进行添加
clientUsers.RemoveAll(u => u.ConnnectionId == Context.ConnectionId);
clientUsers.Add(user); ClientUsersDic.AddOrUpdate(Context.ConnectionId, user, (_, _) => user);
//当有人加入,向全部客户端发送当前总数 //当有人加入,向全部客户端发送当前总数
Clients.All.SendAsync("onlineNum", clientUsers.Count); Clients.All.SendAsync("onlineNum", ClientUsersDic.Count);
}
return base.OnConnectedAsync(); return base.OnConnectedAsync();
} }
@@ -69,22 +71,17 @@ namespace Yi.Framework.Rbac.Application.SignalRHubs
/// </summary> /// </summary>
/// <param name="exception"></param> /// <param name="exception"></param>
/// <returns></returns> /// <returns></returns>
public override Task OnDisconnectedAsync(Exception exception) public override Task OnDisconnectedAsync(Exception? exception)
{
lock (objLock)
{ {
//已登录 //已登录
if (CurrentUser.Id is not null) if (CurrentUser.IsAuthenticated)
{ {
clientUsers.RemoveAll(u => u.UserId == CurrentUser.Id); ClientUsersDic.RemoveAll(u => u.Value.UserId == CurrentUser.Id);
_logger.LogInformation($"用户{CurrentUser?.UserName}离开了,当前已连接{clientUsers.Count}个"); _logger.LogDebug($"用户{CurrentUser?.UserName}离开了,当前已连接{ClientUsersDic.Count}个");
}
clientUsers.RemoveAll(u => u.ConnnectionId == Context.ConnectionId);
Clients.All.SendAsync("onlineNum", clientUsers.Count);
} }
ClientUsersDic.Remove(Context.ConnectionId, out _);
Clients.All.SendAsync("onlineNum", ClientUsersDic.Count);
return base.OnDisconnectedAsync(exception); return base.OnDisconnectedAsync(exception);
} }
} }
} }

View File

@@ -9,8 +9,8 @@ namespace Yi.Framework.Rbac.Domain.Shared.Dtos
public HashSet<RoleDto> Roles { get; set; } = new(); public HashSet<RoleDto> Roles { get; set; } = new();
public HashSet<MenuDto> Menus { get; set; } = new(); public HashSet<MenuDto> Menus { get; set; } = new();
public List<string> RoleCodes { get; set; } = new(); public HashSet<string> RoleCodes { get; set; } = new();
public List<string> PermissionCodes { get; set; } = new(); public HashSet<string> PermissionCodes { get; set; } = new();
} }
public class UserDto public class UserDto

View File

@@ -217,8 +217,8 @@ namespace Yi.Framework.Rbac.Domain.Managers
} }
else else
{ {
dto.PermissionCodes?.ForEach(per => AddToClaim(claims, TokenTypeConst.Permission, per)); dto.PermissionCodes?.ToList()?.ForEach(per => AddToClaim(claims, TokenTypeConst.Permission, per));
dto.RoleCodes?.ForEach(role => AddToClaim(claims, AbpClaimTypes.Role, role)); dto.RoleCodes?.ToList()?.ForEach(role => AddToClaim(claims, AbpClaimTypes.Role, role));
} }
return claims; return claims;

View File

@@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using SqlSugar; using SqlSugar;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Users; using Volo.Abp.Users;
using Yi.Framework.Rbac.Domain.Authorization; using Yi.Framework.Rbac.Domain.Authorization;
using Yi.Framework.Rbac.Domain.Entities; using Yi.Framework.Rbac.Domain.Entities;
@@ -24,6 +25,9 @@ namespace Yi.Framework.Rbac.SqlSugarCore
} }
public YiRbacDbContext(IAbpLazyServiceProvider lazyServiceProvider) : base(lazyServiceProvider)
{
}
/// <summary> /// <summary>
/// 数据权限过滤 /// 数据权限过滤
/// </summary> /// </summary>
@@ -86,5 +90,6 @@ namespace Yi.Framework.Rbac.SqlSugarCore
sqlSugarClient.QueryFilter.AddTableFilter(expUser.ToExpression()); sqlSugarClient.QueryFilter.AddTableFilter(expUser.ToExpression());
sqlSugarClient.QueryFilter.AddTableFilter(expRole.ToExpression()); sqlSugarClient.QueryFilter.AddTableFilter(expRole.ToExpression());
} }
} }
} }

View File

@@ -67,7 +67,8 @@ namespace Yi.Abp.Application.Services
public async Task GetUowAsync() public async Task GetUowAsync()
{ {
//魔改 //魔改
// 用户体验优先,万金油模式,支持高并发。支持单、多线程并发安全,支持多线程工作单元,支持多线程无工作单元,支持。。。 // 用户体验优先,万金油模式,支持高并发。支持单、多线程并发安全,支持多线程工作单元,支持。。。
// 不支持多线程无工作单元应由工作单元统一管理来自abp工作单元设计
// 请注意如果requiresNew: true只有在没有工作单元内使用嵌套子工作单元默认值false即可 // 请注意如果requiresNew: true只有在没有工作单元内使用嵌套子工作单元默认值false即可
// 自动在各个情况处理db客户端最优解之一 // 自动在各个情况处理db客户端最优解之一
int i = 3; int i = 3;
@@ -78,7 +79,8 @@ namespace Yi.Abp.Application.Services
{ {
tasks.Add(Task.Run(async () => tasks.Add(Task.Run(async () =>
{ {
await sqlSugarRepository.InsertAsync(new BannerAggregateRoot { Name = "插入2" }); //以下操作是错误的不允许在新线程中直接操作db所有db操作应放在工作单元内应由工作单元统一管理-来自abp工作单元设计
//await sqlSugarRepository.InsertAsync(new BannerAggregateRoot { Name = "插入2" });
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: true)) using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: true))
{ {
await sqlSugarRepository.InsertAsync(new BannerAggregateRoot { Name = "插入1" }); await sqlSugarRepository.InsertAsync(new BannerAggregateRoot { Name = "插入1" });
@@ -173,6 +175,5 @@ namespace Yi.Abp.Application.Services
return result ?? string.Empty; return result ?? string.Empty;
} }
} }
} }

View File

@@ -7,6 +7,9 @@ using Yi.Framework.SqlSugarCore;
namespace Yi.Abp.SqlSugarCore namespace Yi.Abp.SqlSugarCore
{ {
public class YiDbContext : SqlSugarDbContext public class YiDbContext : SqlSugarDbContext
{
public YiDbContext(IAbpLazyServiceProvider lazyServiceProvider) : base(lazyServiceProvider)
{ {
} }
} }
}

View File

@@ -66,6 +66,30 @@ namespace Yi.Abp.Web
{ {
private const string DefaultCorsPolicyName = "Default"; private const string DefaultCorsPolicyName = "Default";
public override void PreConfigureServices(ServiceConfigurationContext context)
{
//动态Api-改进在pre中配置启动更快
PreConfigure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(YiAbpApplicationModule).Assembly,
options => options.RemoteServiceName = "default");
options.ConventionalControllers.Create(typeof(YiFrameworkRbacApplicationModule).Assembly,
options => options.RemoteServiceName = "rbac");
options.ConventionalControllers.Create(typeof(YiFrameworkBbsApplicationModule).Assembly,
options => options.RemoteServiceName = "bbs");
options.ConventionalControllers.Create(typeof(YiFrameworkChatHubApplicationModule).Assembly,
options => options.RemoteServiceName = "chat-hub");
options.ConventionalControllers.Create(
typeof(YiFrameworkTenantManagementApplicationModule).Assembly,
options => options.RemoteServiceName = "tenant-management");
options.ConventionalControllers.Create(typeof(YiFrameworkCodeGenApplicationModule).Assembly,
options => options.RemoteServiceName = "code-gen");
//统一前缀
options.ConventionalControllers.ConventionalControllerSettings.ForEach(x => x.RootPath = "api/app");
});
}
public override Task ConfigureServicesAsync(ServiceConfigurationContext context) public override Task ConfigureServicesAsync(ServiceConfigurationContext context)
{ {
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
@@ -92,27 +116,6 @@ namespace Yi.Abp.Web
//配置错误处理显示详情 //配置错误处理显示详情
Configure<AbpExceptionHandlingOptions>(options => { options.SendExceptionsDetailsToClients = true; }); Configure<AbpExceptionHandlingOptions>(options => { options.SendExceptionsDetailsToClients = true; });
//动态Api
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(YiAbpApplicationModule).Assembly,
options => options.RemoteServiceName = "default");
options.ConventionalControllers.Create(typeof(YiFrameworkRbacApplicationModule).Assembly,
options => options.RemoteServiceName = "rbac");
options.ConventionalControllers.Create(typeof(YiFrameworkBbsApplicationModule).Assembly,
options => options.RemoteServiceName = "bbs");
options.ConventionalControllers.Create(typeof(YiFrameworkChatHubApplicationModule).Assembly,
options => options.RemoteServiceName = "chat-hub");
options.ConventionalControllers.Create(
typeof(YiFrameworkTenantManagementApplicationModule).Assembly,
options => options.RemoteServiceName = "tenant-management");
options.ConventionalControllers.Create(typeof(YiFrameworkCodeGenApplicationModule).Assembly,
options => options.RemoteServiceName = "code-gen");
//统一前缀
options.ConventionalControllers.ConventionalControllerSettings.ForEach(x => x.RootPath = "api/app");
});
//【NewtonsoftJson严重问题逆天】设置api格式留给后人铭记 //【NewtonsoftJson严重问题逆天】设置api格式留给后人铭记
// service.AddControllers().AddNewtonsoftJson(options => // service.AddControllers().AddNewtonsoftJson(options =>
// { // {

View File

@@ -20,6 +20,7 @@ namespace Yi.Abp.Tool.Commands
{ {
application.OnExecute(() => application.OnExecute(() =>
{ {
Console.WriteLine("正在克隆,请耐心等待");
StartCmd($"git clone {CloneAddress}"); StartCmd($"git clone {CloneAddress}");
return 0; return 0;
}); });

View File

@@ -36,6 +36,9 @@ namespace Yi.Abp.Tool.Commands
var soureOption = application.Option("-s|--soure", "模板来源gitee模板库分支名称: 默认值`default`", var soureOption = application.Option("-s|--soure", "模板来源gitee模板库分支名称: 默认值`default`",
CommandOptionType.SingleValue); CommandOptionType.SingleValue);
var dbmsOption = application.Option("-dbms|--dataBaseMs", "数据库类型,支持目前主流数据库",
CommandOptionType.SingleValue);
var moduleNameArgument = application.Argument("moduleName", "模块名", (_) => { }); var moduleNameArgument = application.Argument("moduleName", "模块名", (_) => { });
//子命令new list //子命令new list
@@ -58,6 +61,11 @@ namespace Yi.Abp.Tool.Commands
application.OnExecute(() => application.OnExecute(() =>
{ {
if (dbmsOption.HasValue())
{
Console.WriteLine($"检测到使用数据库类型-{dbmsOption.Value()}请在生成后只需在配置文件中更改DbConnOptions:Url及DbType即可支持目前主流数据库20+");
}
var path = string.Empty; var path = string.Empty;
if (pathOption.HasValue()) if (pathOption.HasValue())
{ {

View File

@@ -5,7 +5,7 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Version>2.0.4</Version> <Version>2.0.5</Version>
<Authors>橙子老哥</Authors> <Authors>橙子老哥</Authors>
<Description>yi-framework框架配套工具</Description> <Description>yi-framework框架配套工具</Description>
<PackageProjectUrl>https://ccnetcore.com</PackageProjectUrl> <PackageProjectUrl>https://ccnetcore.com</PackageProjectUrl>