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
|
{
|
/// <summary>
|
/// 数据库工具
|
/// </summary>
|
public static class DbUtils
|
{
|
/// <summary>
|
/// 删除数据
|
/// </summary>
|
/// <typeparam name="TEntity"></typeparam>
|
/// <param name="request"></param>
|
/// <param name="query"></param>
|
/// <param name="cancellationToken"></param>
|
/// <returns></returns>
|
public static async Task<int> DeleteData<TEntity>(this DeleteDataCommand request, Func<IQueryable<TEntity>, IQueryable<TEntity>> query = null, CancellationToken cancellationToken = default)
|
where TEntity : CommonEntity, new()
|
{
|
var rep = Db.GetRepository<TEntity>();
|
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;
|
}
|
|
/// <summary>
|
/// 保存数据
|
/// </summary>
|
/// <typeparam name="TEntity"></typeparam>
|
/// <typeparam name="TRequest"></typeparam>
|
/// <param name="request"></param>
|
/// <param name="checkExist"></param>
|
/// <param name="cancellationToken"></param>
|
/// <returns></returns>
|
public static async Task<Guid> SaveData<TEntity, TRequest>(this TRequest request, Func<IQueryable<TEntity>, 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<TEntity>();
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 生成实体
|
/// </summary>
|
/// <param name="modelBuilder"></param>
|
/// <param name="dbContextLocator"></param>
|
/// <returns></returns>
|
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"));
|
}
|
|
/// <summary>
|
/// 数据变更事件
|
/// </summary>
|
/// <param name="eventData"></param>
|
/// <param name="result"></param>
|
public static void SavingChangesEvent(DbContextEventData eventData, InterceptionResult<int> 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<DbAuditLog, LogDbContextLocator>().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);
|
}
|
}
|
}
|