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,84 +9,89 @@ using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Options;
namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
{
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 serviceProvider = services.BuildServiceProvider();
var mvcOptions = serviceProvider.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>();
var mvcSettings = mvcOptions.Value.ConventionalControllers.ConventionalControllerSettings.DistinctBy(x => x.RemoteServiceName);
var mvcOptions = services.GetPreConfigureActions<AbpAspNetCoreMvcOptions>().Configure();
var mvcSettings =
mvcOptions.ConventionalControllers.ConventionalControllerSettings.DistinctBy(x => x.RemoteServiceName);
services.AddAbpSwaggerGen(
options =>
{
if (action is not null)
options =>
{
action.Invoke(options);
}
// 配置分组,还需要去重,支持重写,如果外部传入后,将以外部为准
foreach (var setting in mvcSettings.OrderBy(x => x.RemoteServiceName))
{
if (!options.SwaggerGeneratorOptions.SwaggerDocs.ContainsKey(setting.RemoteServiceName))
if (action is not null)
{
options.SwaggerDoc(setting.RemoteServiceName, new OpenApiInfo { Title = setting.RemoteServiceName, Version = "v1" });
action.Invoke(options);
}
}
// 根据分组名称过滤 API 文档
options.DocInclusionPredicate((docName, apiDesc) =>
{
if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
// 配置分组,还需要去重,支持重写,如果外部传入后,将以外部为准
foreach (var setting in mvcSettings.OrderBy(x => x.RemoteServiceName))
{
var settingOrNull = mvcSettings.Where(x => x.Assembly == controllerActionDescriptor.ControllerTypeInfo.Assembly).FirstOrDefault();
if (settingOrNull is not null)
if (!options.SwaggerGeneratorOptions.SwaggerDocs.ContainsKey(setting.RemoteServiceName))
{
return docName == settingOrNull.RemoteServiceName;
options.SwaggerDoc(setting.RemoteServiceName,
new OpenApiInfo { Title = setting.RemoteServiceName, Version = "v1" });
}
}
return false;
});
options.CustomSchemaIds(type => type.FullName);
var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
if (basePath is not null)
{
foreach (var item in Directory.GetFiles(basePath, "*.xml"))
// 根据分组名称过滤 API 文档
options.DocInclusionPredicate((docName, apiDesc) =>
{
options.IncludeXmlComments(item, true);
if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{
var settingOrNull = mvcSettings
.Where(x => x.Assembly == controllerActionDescriptor.ControllerTypeInfo.Assembly)
.FirstOrDefault();
if (settingOrNull is not null)
{
return docName == settingOrNull.RemoteServiceName;
}
}
return false;
});
options.CustomSchemaIds(type => type.FullName);
var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
if (basePath is not null)
{
foreach (var item in Directory.GetFiles(basePath, "*.xml"))
{
options.IncludeXmlComments(item, true);
}
}
options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
{
Description = "直接输入Token即可",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
var scheme = new OpenApiSecurityScheme()
{
Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
};
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
[scheme] = new string[0]
});
options.OperationFilter<AddRequiredHeaderParameter>();
options.SchemaFilter<EnumSchemaFilter>();
}
);
options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
{
Description = "直接输入Token即可",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
var scheme = new OpenApiSecurityScheme()
{
Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
};
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
[scheme] = new string[0]
});
options.OperationFilter<AddRequiredHeaderParameter>();
options.SchemaFilter<EnumSchemaFilter>();
}
);
return services;
}
@@ -103,7 +108,6 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
/// </summary>
/// <param name="model"></param>
/// <param name="context"></param>
public void Apply(OpenApiSchema model, SchemaFilterContext context)
{
if (context.Type.IsEnum)
@@ -112,7 +116,7 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
model.Type = "string";
model.Format = null;
StringBuilder stringBuilder = new StringBuilder();
Enum.GetNames(context.Type)
.ToList()
@@ -121,9 +125,10 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
Enum e = (Enum)Enum.Parse(context.Type, name);
var descrptionOrNull = GetEnumDescription(e);
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);
return attributes.Length > 0 ? attributes[0].Description : null;
}
}
public class AddRequiredHeaderParameter : IOperationFilter
{
public static string HeaderKey { get; set; } = "__tenant";
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
@@ -150,8 +155,8 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
In = ParameterLocation.Header,
Required = false,
AllowEmptyValue = true,
Description="租户id或者租户名称可空为默认租户"
Description = "租户id或者租户名称可空为默认租户"
});
}
}
}
}

View File

@@ -14,7 +14,7 @@ namespace Yi.Framework.SqlSugarCore.Abstractions
/// SqlSugarDb
/// </summary>
ISqlSugarClient SqlSugarClient { get; }
/// <summary>
/// 数据库备份
/// </summary>

View File

@@ -12,6 +12,7 @@ using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
using Volo.Abp.Users;
using Yi.Framework.SqlSugarCore.Abstractions;
@@ -19,19 +20,23 @@ namespace Yi.Framework.SqlSugarCore;
public class DefaultSqlSugarDbContext : SqlSugarDbContext
{
protected DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
protected ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService<ICurrentUser>();
protected IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService<IGuidGenerator>();
protected ILoggerFactory Logger => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
protected IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>();
public IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService<IUnitOfWorkManager>();
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
protected IEntityChangeEventHelper EntityChangeEventHelper =>
LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance);
public DefaultSqlSugarDbContext(IAbpLazyServiceProvider lazyServiceProvider) : base(lazyServiceProvider)
{
}
protected override void CustomDataFilter(ISqlSugarClient sqlSugarClient)
{
if (IsSoftDeleteFilterEnabled)
@@ -120,7 +125,7 @@ public class DefaultSqlSugarDbContext : SqlSugarDbContext
}
//领域事件
//实体变更领域事件
switch (entityInfo.OperationType)
{
case DataFilterType.InsertByObject:
@@ -163,6 +168,14 @@ public class DefaultSqlSugarDbContext : SqlSugarDbContext
break;
}
//实体领域事件-所有操作类型
if (entityInfo.PropertyName == nameof(IEntity<object>.Id))
{
var eventReport = CreateEventReport(entityInfo.EntityValue);
PublishEntityEvents(eventReport);
}
}
public override void OnLogExecuting(string sql, SugarParameter[] pars)
@@ -204,4 +217,74 @@ public class DefaultSqlSugarDbContext : SqlSugarDbContext
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 IAbpLazyServiceProvider LazyServiceProvider { get; set; }
protected IAbpLazyServiceProvider LazyServiceProvider { get; }
public SqlSugarDbContext(IAbpLazyServiceProvider lazyServiceProvider)
{
this.LazyServiceProvider = lazyServiceProvider;
}
protected ISqlSugarClient SqlSugarClient { get;private set; }
public int ExecutionOrder => 0;

View File

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

View File

@@ -17,4 +17,4 @@ namespace Yi.Framework.SqlSugarCore.Uow
DbContext = dbContext;
}
}
}
}

View File

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

View File

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

View File

@@ -110,7 +110,9 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
)
.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>
{
Items = output,

View File

@@ -158,10 +158,11 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
//查询完主题之后,要过滤一下私有的主题信息
items.ApplyPermissionTypeFilter(CurrentUser.Id ?? Guid.Empty);
var levelCacheDic= await _bbsUserManager.GetLevelCacheMapAsync();
//等级、是否点赞赋值
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)
{
//默认fasle
@@ -212,7 +213,8 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
}
}, true)
.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;
}

View File

@@ -14,7 +14,8 @@ namespace Yi.Framework.Bbs.Domain.Managers
{
public ISqlSugarRepository<UserAggregateRoot> _userRepository;
public ISqlSugarRepository<BbsUserExtraInfoEntity> _bbsUserInfoRepository;
public Dictionary<int, LevelCacheItem> _levelCacheDic;
// public Dictionary<int, LevelCacheItem> _levelCacheDic;
private LevelManager _levelManager;
public BbsUserManager(ISqlSugarRepository<UserAggregateRoot> userRepository,
ISqlSugarRepository<BbsUserExtraInfoEntity> bbsUserInfoRepository,
@@ -23,7 +24,12 @@ namespace Yi.Framework.Bbs.Domain.Managers
{
_userRepository = userRepository;
_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)
@@ -44,7 +50,8 @@ namespace Yi.Framework.Bbs.Domain.Managers
}, true)
.FirstAsync(user => user.Id == userId);
userInfo.LevelName = _levelCacheDic[userInfo.Level].Name;
var levelCacheDic= await GetLevelCacheMapAsync();
userInfo.LevelName = levelCacheDic[userInfo.Level].Name;
return userInfo;
}
@@ -66,7 +73,8 @@ namespace Yi.Framework.Bbs.Domain.Managers
DiscussNumber = info.DiscussNumber
}, true)
.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>();
}

View File

@@ -355,13 +355,13 @@ namespace Yi.Framework.Rbac.Application.Services
{
//将后端菜单转换成前端路由,组件级别需要过滤
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")
{
//将后端菜单转换成前端路由,组件级别需要过滤
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;

View File

@@ -13,6 +13,7 @@ namespace Yi.Framework.Rbac.Application.Services.Monitor
{
private ILogger<OnlineService> _logger;
private IHubContext<OnlineHub> _hub;
public OnlineService(ILogger<OnlineService> logger, IHubContext<OnlineHub> hub)
{
_logger = logger;
@@ -26,18 +27,21 @@ namespace Yi.Framework.Rbac.Application.Services.Monitor
/// <returns></returns>
public Task<PagedResultDto<OnlineUserModel>> GetListAsync([FromQuery] OnlineUserModel online)
{
var data = OnlineHub.clientUsers;
IEnumerable<OnlineUserModel> dataWhere = data.AsEnumerable();
var data = OnlineHub.ClientUsersDic;
IEnumerable<OnlineUserModel> dataWhere = data.Values.AsEnumerable();
if (!string.IsNullOrEmpty(online.Ipaddr))
{
dataWhere = dataWhere.Where((u) => u.Ipaddr!.Contains(online.Ipaddr));
}
if (!string.IsNullOrEmpty(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,13 +54,14 @@ namespace Yi.Framework.Rbac.Application.Services.Monitor
[Route("online/{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", "你已被强制退出!");
return true;
}
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.SignalR;
using Microsoft.Extensions.Logging;
@@ -13,53 +14,54 @@ namespace Yi.Framework.Rbac.Application.SignalRHubs
//[Authorize]
public class OnlineHub : AbpHub
{
public static readonly List<OnlineUserModel> clientUsers = new();
private readonly static object objLock = new object();
public static ConcurrentDictionary<string, OnlineUserModel> ClientUsersDic { get; set; } = new();
private HttpContext? _httpContext;
private readonly HttpContext? _httpContext;
private ILogger<OnlineHub> _logger => LoggerFactory.CreateLogger<OnlineHub>();
public OnlineHub(IHttpContextAccessor httpContextAccessor)
{
_httpContext = httpContextAccessor?.HttpContext;
}
/// <summary>
/// 成功连接
/// </summary>
/// <returns></returns>
public override Task OnConnectedAsync()
{
lock (objLock)
if (_httpContext is null)
{
var name = CurrentUser.UserName;
var loginUser = new LoginLogAggregateRoot().GetInfoByHttpContext(_httpContext);
OnlineUserModel user = new(Context.ConnectionId)
{
Browser = loginUser?.Browser,
LoginLocation = loginUser?.LoginLocation,
Ipaddr = loginUser?.LoginIp,
LoginTime = DateTime.Now,
Os = loginUser?.Os,
UserName = name ?? "Null",
UserId = CurrentUser.Id ?? Guid.Empty
};
//已登录
if (CurrentUser.Id is not null)
{ //先移除之前的用户id一个用户只能一个
clientUsers.RemoveAll(u => u.UserId == CurrentUser.Id);
_logger.LogInformation($"{DateTime.Now}{name},{Context.ConnectionId}连接服务端success当前已连接{clientUsers.Count}个");
}
//全部移除之后,再进行添加
clientUsers.RemoveAll(u => u.ConnnectionId == Context.ConnectionId);
clientUsers.Add(user);
//当有人加入,向全部客户端发送当前总数
Clients.All.SendAsync("onlineNum", clientUsers.Count);
return Task.CompletedTask;
}
var name = CurrentUser.UserName;
var loginUser = new LoginLogAggregateRoot().GetInfoByHttpContext(_httpContext);
OnlineUserModel user = new(Context.ConnectionId)
{
Browser = loginUser?.Browser,
LoginLocation = loginUser?.LoginLocation,
Ipaddr = loginUser?.LoginIp,
LoginTime = DateTime.Now,
Os = loginUser?.Os,
UserName = name ?? "Null",
UserId = CurrentUser.Id ?? Guid.Empty
};
//已登录
if (CurrentUser.IsAuthenticated)
{
ClientUsersDic.RemoveAll(u => u.Value.UserId == CurrentUser.Id);
_logger.LogDebug(
$"{DateTime.Now}{name},{Context.ConnectionId}连接服务端success当前已连接{ClientUsersDic.Count}个");
}
ClientUsersDic.AddOrUpdate(Context.ConnectionId, user, (_, _) => user);
//当有人加入,向全部客户端发送当前总数
Clients.All.SendAsync("onlineNum", ClientUsersDic.Count);
return base.OnConnectedAsync();
}
@@ -69,22 +71,17 @@ namespace Yi.Framework.Rbac.Application.SignalRHubs
/// </summary>
/// <param name="exception"></param>
/// <returns></returns>
public override Task OnDisconnectedAsync(Exception exception)
public override Task OnDisconnectedAsync(Exception? exception)
{
lock (objLock)
//已登录
if (CurrentUser.IsAuthenticated)
{
//已登录
if (CurrentUser.Id is not null)
{
clientUsers.RemoveAll(u => u.UserId == CurrentUser.Id);
_logger.LogInformation($"用户{CurrentUser?.UserName}离开了,当前已连接{clientUsers.Count}个");
}
clientUsers.RemoveAll(u => u.ConnnectionId == Context.ConnectionId);
Clients.All.SendAsync("onlineNum", clientUsers.Count);
ClientUsersDic.RemoveAll(u => u.Value.UserId == CurrentUser.Id);
_logger.LogDebug($"用户{CurrentUser?.UserName}离开了,当前已连接{ClientUsersDic.Count}个");
}
ClientUsersDic.Remove(Context.ConnectionId, out _);
Clients.All.SendAsync("onlineNum", ClientUsersDic.Count);
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<MenuDto> Menus { get; set; } = new();
public List<string> RoleCodes { get; set; } = new();
public List<string> PermissionCodes { get; set; } = new();
public HashSet<string> RoleCodes { get; set; } = new();
public HashSet<string> PermissionCodes { get; set; } = new();
}
public class UserDto

View File

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

View File

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

View File

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

View File

@@ -8,5 +8,8 @@ namespace Yi.Abp.SqlSugarCore
{
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";
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)
{
var configuration = context.Services.GetConfiguration();
@@ -91,28 +115,7 @@ namespace Yi.Abp.Web
//配置错误处理显示详情
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格式留给后人铭记
// service.AddControllers().AddNewtonsoftJson(options =>
// {

View File

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

View File

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

View File

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