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);
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 async Task> GetPagedListAsync(
this PagedListQueryPageModel page,
IQueryable q,
CancellationToken cancellationToken = default)
where TItem : class, new()
{
q = q.OrderBy(page.OrderInput);
var pagedList = await q.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 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 SaveData(
this TRequest request,
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 entity = await rep.AsQueryable().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.CreatedUserInfoId));
if (prop != null && prop.CurrentValue == null)
{
prop.CurrentValue = logier?.UserInfoId;
}
// 赋值跟踪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.UpdatedUserInfoId));
if (prop != null)
{
prop.CurrentValue = logier?.UserInfoId;
}
// 赋值跟踪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;
}
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);
}
}
}