using Consul.Filtering; using Furion; using Furion.DatabaseAccessor; using Furion.DistributedIDGenerator; using Furion.FriendlyException; using Mapster; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Timers; namespace FlexJobApi.Core { /// /// 数据库工具 /// public static class DbUtils { /// /// 获取树形数据路径 /// /// /// /// /// public static async Task GetTreeDataPath( Guid? parentId, CancellationToken cancellationToken = default) where TEntity : CommonEntity, ITreeData, new() { var rep = Db.GetRepository(); if (parentId.HasValue) { var parent = await rep.AsQueryable().AsNoTracking() .Where(it => it.Id == parentId) .Select(it => new { it.Path, it.Code }) .FirstOrDefaultAsync(cancellationToken); var summary = typeof(TEntity).GetSummary(); if (parent == null) throw Oops.Oh(EnumErrorCodeType.s404, $"上级{summary}"); return $"{parent.Path}{parent.Code}/"; } else { return "/"; } } /// /// 更新树形数据下级路径 /// /// /// /// /// public static async Task UpdateTreeDataChildrenPath( string oldPath, string newPath, CancellationToken cancellationToken) where TEntity : CommonEntity, ITreeData, new() { var rep = Db.GetRepository(); var models = await rep.AsQueryable() .Where(it => it.Path.StartsWith(oldPath)) .ToListAsync(cancellationToken); if (models.IsNotNull()) { foreach (var model in models) { model.Path = model.Path.Replace(oldPath, newPath); } await rep.UpdateAsync(models); } } /// /// 查询选择器数据 /// /// /// /// /// /// /// /// /// /// public static async Task>> GetSelect( this SelectQuery request, Func getValue, Func getLabel, Func, IQueryable> query = null, CancellationToken cancellationToken = default) where TEntity : CommonEntity, new() { var rep = Db.GetRepository(); var q = rep.AsQueryable().AsNoTracking(); if (query != null) q = query(q); else q = q.OrderBy(it => it.Sort).ThenBy(it => it.CreatedTime); var models = await q .ProjectToType() .ToListAsync(cancellationToken); var options = new List>(); foreach (var model in models) { var option = new SelectOption(); option.Data = model; option.Value = getValue(model); option.Label = getLabel(model); options.Add(option); } return options; } /// /// 查询分页列表数据 /// /// /// /// /// /// /// /// public static async Task> GetPagedListAsync( this PagedListQueryPageModel page, Func, IQueryable> query = null, Expression> selector = null, CancellationToken cancellationToken = default) where TEntity : CommonEntity, new() where TItem : class, new() { var rep = Db.GetRepository(); var q = rep.AsQueryable().AsNoTracking(); if (query != null) q = query(q); else q = q.OrderBy(it => it.Sort).ThenBy(it => it.CreatedTime); if (page.OrderInput.IsNotNull()) q = q.CustomOrderBy(page.OrderInput); var s = selector == null ? q.ProjectToType() : q.Select(selector); var pagedList = await s.ToPagedListAsync(page.Page, page.Rows, cancellationToken); var result = new PagedListQueryResult(); result.PageModel = page.Adapt(); result.PageModel.TotalCount = pagedList.TotalCount; result.PageModel.TotalPage = pagedList.TotalPages; result.Data = pagedList.Items.ToList(); return result; } /// /// 查询分页列表数据 /// /// /// /// /// /// /// public static async Task GetPagedListAsync( this PagedListQueryPageModel page, IQueryable q, CancellationToken cancellationToken = default) where TItem : class, new() where TResult : PagedListQueryResult, new() { if (page.OrderInput.IsNotNull()) q = q.CustomOrderBy(page.OrderInput); var pagedList = await q.ToPagedListAsync(page.Page, page.Rows, cancellationToken); var result = new TResult(); result.PageModel = page.Adapt(); result.PageModel.TotalCount = pagedList.TotalCount; result.PageModel.TotalPage = pagedList.TotalPages; result.Data = pagedList.Items.ToList(); return result; } /// /// 查询分页列表数据 /// /// /// /// /// /// public static Task> GetPagedListAsync( this PagedListQueryPageModel page, IQueryable q, CancellationToken cancellationToken = default) where TItem : class, new() { return GetPagedListAsync, TItem>(page, q, cancellationToken); } public static IOrderedQueryable CustomOrderBy(this IQueryable q, List orders) { ParameterExpression parameter = Expression.Parameter(typeof(T), "p"); PagedListQueryPageModelOrderInput orderInput = orders[0]; IOrderedQueryable orderedQueryable = (orderInput.Order == EnumPagedListOrder.Asc) ? OrderBy(q, orderInput.Property, parameter) : OrderByDescending(q, orderInput.Property, parameter); for (int i = 1; i < orders.Count; i++) { PagedListQueryPageModelOrderInput orderInput2 = orders[i]; orderedQueryable = (orderInput2.Order == EnumPagedListOrder.Asc) ? ThenBy(orderedQueryable, orderInput2.Property, parameter) : ThenByDescending(orderedQueryable, orderInput2.Property, parameter); } return orderedQueryable; } private static IOrderedQueryable Ordering(IQueryable source, ParameterExpression parameter, string propertyName, string methodName) { Type typeFromHandle = typeof(T); MemberExpression memberExpression = Expression.PropertyOrField(parameter, propertyName); LambdaExpression expression = Expression.Lambda(memberExpression, parameter); MethodCallExpression expression2 = Expression.Call(typeof(Queryable), methodName, [typeFromHandle, memberExpression.Type], source.Expression, Expression.Quote(expression)); return (IOrderedQueryable)source.Provider.CreateQuery(expression2); } public static IOrderedQueryable OrderBy(IQueryable source, string propertyName, ParameterExpression parameter) { return Ordering(source, parameter, propertyName, "OrderBy"); } public static IOrderedQueryable OrderByDescending(IQueryable source, string propertyName, ParameterExpression parameter) { return Ordering(source, parameter, propertyName, "OrderByDescending"); } public static IOrderedQueryable ThenBy(IOrderedQueryable source, string propertyName, ParameterExpression parameter) { return Ordering(source, parameter, propertyName, "ThenBy"); } public static IOrderedQueryable ThenByDescending(IOrderedQueryable source, string propertyName, ParameterExpression parameter) { return Ordering(source, parameter, propertyName, "ThenByDescending"); } public static async Task GetDetail( this Guid id, CancellationToken cancellationToken = default) where TEntity : CommonEntity, new() { var rep = Db.GetRepository(); return await rep.AsQueryable().AsNoTracking() .Where(it => it.Id == id) .GetDetail(cancellationToken); } public static async Task GetDetail( this IQueryable q, CancellationToken cancellationToken = default) { var model = await q .ProjectToType() .FirstOrDefaultAsync(cancellationToken); if (model == null) { var summary = await typeof(TEntity).GetSummary(); throw Oops.Oh(EnumErrorCodeType.s404, $"{summary ?? "信息"}"); } return model; } /// /// 设置是否禁用 /// /// /// /// /// /// public static async Task SetIsDisabled( this SetIsDisabledCommand request, Func, IQueryable> query = null, CancellationToken cancellationToken = default) where TEntity : CommonEntity, IIsDisabled, new() { var rep = Db.GetRepository(); var q = rep.AsQueryable(); if (query != null) q = query(q); var entities = await q .Where(it => request.Ids.Contains(it.Id) && it.IsDisabled != request.IsDisabled) .ToListAsync(); foreach (var entity in entities) { entity.IsDisabled = request.IsDisabled; } return entities.Count; } /// /// 删除数据 /// /// /// /// /// /// public static async Task DeleteData( this DeleteDataCommand request, Func, IQueryable> query = null, CancellationToken cancellationToken = default) where TEntity : CommonEntity, new() { var rep = Db.GetRepository(); var q = rep.AsQueryable(); if (query != null) q = query(q); var entities = await q .Where(it => request.Ids.Contains(it.Id)) .ToListAsync(cancellationToken); return entities.Any() ? await rep.DeleteNowAsync(entities, cancellationToken) : 0; } public static async Task UpdateData( this IQueryable q, TRequest request, Action update = null, CancellationToken cancellationToken = default) where TEntity : CommonEntity, new() { var rep = Db.GetRepository(); var entity = await q.FirstOrDefaultAsync(); if (entity == null) { var summary = await typeof(TEntity).GetSummary(); throw Oops.Oh(EnumErrorCodeType.s404, $"{summary ?? "信息"}"); } if (update != null) update(entity); else request.Adapt(entity); await rep.UpdateAsync(entity); return entity.Id; } /// /// 保存数据 /// /// /// /// /// /// /// /// /// public static async Task SaveData( this TRequest request, Func, IQueryable> query = null, Expression> checkExist = null, Action update = null, CancellationToken cancellationToken = default) where TEntity : CommonEntity, new() where TRequest : SaveDataCommand, new() { var xmlDoc = await XmlDocUtils.GetXmlDocAsync(); var summary = await typeof(TEntity).GetSummary(xmlDoc); var rep = Db.GetRepository(); if (checkExist != null && await rep.AsQueryable().AsNoTracking().AnyAsync(checkExist)) throw Oops.Oh(EnumErrorCodeType.s405, $"{summary ?? "信息"}"); if (request.Id.HasValue) { var q = rep.AsQueryable(); if (query != null) q = query(q); var entity = await q.FirstOrDefaultAsync(it => it.Id == request.Id, cancellationToken); if (entity == null) throw Oops.Oh(EnumErrorCodeType.s404, $"{summary ?? "信息"}"); if (update != null) update(entity); else request.Adapt(entity); await rep.UpdateAsync(entity); return entity.Id; } else { var entity = new TEntity(); if (update != null) update(entity); else request.Adapt(entity); await rep.InsertAsync(entity); return entity.Id; } } /// /// 生成实体 /// /// /// /// public static async Task BuildEntity(ModelBuilder modelBuilder, Type dbContextLocator = null) { if (App.GetService() != null) { var xmlDoc = await XmlDocUtils.GetXmlDocAsync(); foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { Console.WriteLine($"正在生成表:{entityType.Name}"); // 获取实体类的XML注释,并设置为表注释 var entityBuilder = modelBuilder.Entity(entityType.ClrType); string typeComment = (await entityType.ClrType.GetXmlDocMemberAsync(xmlDoc))?.Summary; if (!string.IsNullOrEmpty(typeComment)) { Console.WriteLine($"正在生成表注释:{entityType.Name}-{typeComment}"); entityBuilder.ToTable(it => it.HasComment(typeComment)); } // 获取实体属性的XML注释,并设置为列注释 var properties = entityType.ClrType.GetProperties() .Where(p => p.CanRead && p.CanWrite && !typeof(System.Collections.ICollection).IsAssignableFrom(p.PropertyType) && (p.PropertyType.IsClass ? p.PropertyType.FullName.Contains("System.") : true)); foreach (var property in properties) { string propComment = (await property.GetXmlDocMemberAsync(xmlDoc))?.Summary; if (!string.IsNullOrEmpty(propComment)) { Console.WriteLine($"正在生成属性注释:{property.Name}-{propComment}"); entityBuilder.Property(property.Name).HasComment(propComment); } } } } foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (typeof(CommonEntity).IsAssignableFrom(entityType.ClrType)) { // 构建筛选条件:IsDeleted == false var parameter = Expression.Parameter(entityType.ClrType, "e"); var property = Expression.Property(parameter, "IsDeleted"); var constant = Expression.Constant(false); var equal = Expression.Equal(property, constant); var lambda = Expression.Lambda(equal, parameter); // 添加全局筛选器 modelBuilder.Entity(entityType.ClrType).HasQueryFilter(lambda); } } } /// /// 数据变更事件 /// /// /// public static void SavingChangesEvent(DbContextEventData eventData, InterceptionResult result) { // 获取当前事件对应上下文 var dbContext = eventData.Context; // 强制重新检查一边实体更改信息 // dbContext.ChangeTracker.DetectChanges(); // 获取所有更改,删除,新增的实体 var dbAuditLogIgnoreType = typeof(IDbAuditLogIgnore); var physicalDeletionType = typeof(IPhysicalDeletion); var entities = dbContext.ChangeTracker.Entries() .Where(u => u.GetType() != typeof(DbAuditLog) && (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added)) .ToList(); // 通过请求中获取当前操作人 var logier = JwtUtils.GetCurrentLogier(); var traceId = App.GetTraceId(); // 获取所有已更改的实体 foreach (var entity in entities) { // 获取实体类型 var entityType = entity.Entity.GetType(); if (entity.State == EntityState.Added) { // 赋值创建日期 var prop = entity.Property(nameof(CommonEntity.CreatedTime)); if (prop != null && prop.CurrentValue?.ToDateTime() == null) { prop.CurrentValue = DateTimeOffset.Now; } // 生成Id prop = entity.Property(nameof(CommonEntity.Id)); var defaultValue = Activator.CreateInstance(prop.Metadata.ClrType); if (prop != null && (prop.CurrentValue == null || prop.CurrentValue.Equals(defaultValue))) { prop.CurrentValue = IDGen.NextID(); } // 赋值用户信息Id prop = entity.Property(nameof(CommonEntity.CreatedUserId)); if (prop != null && prop.CurrentValue == null) { prop.CurrentValue = logier?.Id; } // 赋值企业Id prop = entity.Property(nameof(CommonEntity.CreatedEnterpriseId)); if (prop != null && prop.CurrentValue == null) { prop.CurrentValue = logier?.EnterpriseId; } // 赋值跟踪Id prop = entity.Property(nameof(CommonEntity.TraceId)); if (prop != null && prop.CurrentValue == null && traceId.IsNotNull()) { prop.CurrentValue = traceId; } } else { // 赋值修改日期 var prop = entity.Property(nameof(CommonEntity.UpdatedTime)); if (prop != null) { prop.CurrentValue = DateTimeOffset.Now; } // 赋值用户信息Id prop = entity.Property(nameof(CommonEntity.UpdatedUserId)); if (prop != null) { prop.CurrentValue = logier?.Id; } // 赋值跟踪Id prop = entity.Property(nameof(CommonEntity.TraceId)); if (prop != null && traceId.IsNotNull()) { prop.CurrentValue = traceId; } // 软删除 if (entity.State == EntityState.Deleted && !physicalDeletionType.IsAssignableFrom(entityType)) { entity.State = EntityState.Modified; prop = entity.Property(nameof(CommonEntity.IsDeleted)); if (prop != null) { prop.CurrentValue = true; } } } if (dbAuditLogIgnoreType.IsAssignableFrom(entityType)) { continue; } var log = new DbAuditLog { Id = IDGen.NextID(), TableName = entityType.Name, PrimaryKey = (Guid)entity.Property("Id").CurrentValue, TraceId = App.GetTraceId(), CreatedTime = DateTime.Now, CreatedUserId = logier?.Id, CreatedEnterpriseId = logier?.EnterpriseId }; log.Operate = entity.State == EntityState.Added ? EnumDbAuditOperate.Added : entity.State == EntityState.Modified ? EnumDbAuditOperate.Modified : EnumDbAuditOperate.Deleted; if (entity.Property(nameof(CommonEntity.IsDeleted)).CurrentValue is bool isDeleted && isDeleted == true) { log.Operate = EnumDbAuditOperate.Deleted; } log.NewValues = log.Operate == EnumDbAuditOperate.Deleted ? null : JsonConvert.SerializeObject(entity.Properties .Where(p => log.Operate == EnumDbAuditOperate.Modified ? p.IsModified : true) .ToDictionary(p => p.Metadata.Name, p => p.CurrentValue)); log.OldValues = log.Operate == EnumDbAuditOperate.Added ? null : JsonConvert.SerializeObject(entity.Properties .Where(p => log.Operate == EnumDbAuditOperate.Modified ? p.IsModified : true) .ToDictionary(p => p.Metadata.Name, p => p.OriginalValue)); Db.GetRepository().InsertNow(log); } } } }