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.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; namespace FlexJobApi.Core { /// /// 数据库工具 /// public static class DbUtils { /// /// 查询分页列表数据 /// /// /// /// /// /// /// /// public static async Task> ToPagedListAsync( 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); q = q.OrderBy(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 IQueryable OrderBy(this IQueryable q, List orders) { if (orders.IsNull()) return q; var entityType = typeof(T); int index = 0; var props = entityType.GetProperties(); foreach (var order in orders) { if (string.IsNullOrEmpty(order.Property)) continue; // 获取排序字段的属性信息 var propertyInfo = props.FirstOrDefault(it => it.Name.Equals(order.Property, StringComparison.OrdinalIgnoreCase)); if (propertyInfo == null) throw Oops.Oh(EnumErrorCodeType.s404, $"该排序字段{order.Property}"); // 创建表达式树 var parameter = Expression.Parameter(entityType, "x"); var propertyAccess = Expression.Property(parameter, propertyInfo); var lambda = Expression.Lambda(propertyAccess, parameter); string methodName; if (index == 0) { // 首次排序 methodName = order.Order == EnumPagedListOrder.Asc ? "OrderBy" : "OrderByDescending"; } else { // 二次及以后排序 methodName = order.Order == EnumPagedListOrder.Asc ? "ThenBy" : "ThenByDescending"; } // 调用相应的排序方法 var resultExpression = Expression.Call( typeof(Queryable), methodName, [entityType, propertyInfo.PropertyType], q.Expression, Expression.Quote(lambda) ); q = q.Provider.CreateQuery(resultExpression); index++; } return q; } /// /// 设置是否禁用 /// /// /// /// /// /// public static async Task SetIsDisable(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 SaveData(this TRequest request, Func, TEntity, TRequest, bool> checkExist = 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 (request.Id.HasValue) { var entity = await rep.AsQueryable().FirstOrDefaultAsync(it => it.Id == request.Id, cancellationToken); if (entity == null) throw Oops.Oh(EnumErrorCodeType.s404, $"该{summary ?? "信息"}"); if (checkExist != null && checkExist(rep.AsQueryable().AsNoTracking(), entity, request)) throw Oops.Oh(EnumErrorCodeType.s405, $"该{summary ?? "信息"}"); request.Adapt(entity); await rep.UpdateAsync(entity); return entity.Id; } else { var entity = new TEntity(); if (checkExist != null && checkExist(rep.AsQueryable().AsNoTracking(), entity, request)) throw Oops.Oh(EnumErrorCodeType.s405, $"该{summary ?? "信息"}"); request.Adapt(entity); await rep.InsertAsync(entity); return entity.Id; } } /// /// 生成实体 /// /// /// /// public static async Task BuildEntity(ModelBuilder modelBuilder, Type dbContextLocator = 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); } } 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); } } Console.WriteLine("数据库链接地址:" + App.Configuration.GetConnectionString("FlexJobApi")); } /// /// 数据变更事件 /// /// /// 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(); // 获取所有已更改的实体 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.CreatedUserInfoId)); if (prop != null && prop.CurrentValue == null) { prop.CurrentValue = logier?.UserInfoId; } // 赋值跟踪Id prop = entity.Property(nameof(CommonEntity.TraceId)); if (prop != null) { prop.CurrentValue = App.GetTraceId(); } } else { // 赋值修改日期 var prop = entity.Property(nameof(CommonEntity.UpdatedTime)); if (prop != null) { prop.CurrentValue = DateTimeOffset.Now; } // 赋值用户信息Id prop = entity.Property(nameof(CommonEntity.UpdatedUserInfoId)); if (prop != null) { prop.CurrentValue = logier?.UserInfoId; } // 赋值跟踪Id prop = entity.Property(nameof(CommonEntity.TraceId)); if (prop != null) { prop.CurrentValue = App.GetTraceId(); } // 软删除 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; } Db.GetRepository().InsertNow(new DbAuditLog { Id = IDGen.NextID(), TableName = entityType.Name, PrimaryKey = (Guid)entity.Property("Id").CurrentValue, NewValues = entity.State == EntityState.Added || entity.State == EntityState.Modified ? GetPropertyValuesAsJson(entity) : null, OldValues = entity.State == EntityState.Deleted || entity.State == EntityState.Modified ? GetPropertyValuesAsJson(entity, entity.State == EntityState.Modified) : null, Operate = entity.State == EntityState.Added ? EnumDbAuditOperate.Added : entity.State == EntityState.Modified ? EnumDbAuditOperate.Modified : EnumDbAuditOperate.Deleted, TraceId = App.GetTraceId(), CreatedTime = DateTime.Now, CreatedUserInfoId = logier?.UserInfoId, }); } } private static string GetPropertyValuesAsJson(EntityEntry entry, bool getOldValues = false) { var properties = entry.Properties .Where(p => getOldValues ? p.IsModified : true) .ToDictionary(p => p.Metadata.Name, p => getOldValues ? p.OriginalValue : p.CurrentValue); return JsonConvert.SerializeObject(properties); } } }