using Newtonsoft.Json.Linq; using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using VOL.Core.Configuration; using VOL.Core.DbContext; using VOL.Core.DBManager; using VOL.Core.Enums; using VOL.Core.Extensions; using VOL.Core.UserManager; using VOL.Core.Utilities; using VOL.Entity; using VOL.Entity.DomainModels; using VOL.Entity.SystemModels; namespace VOL.Core.BaseProvider { public static class ApplicationServiceBaseUpdateOrAddExtensions { private static MethodInfo ConvertDetailDataMethod { get { return typeof(ApplicationServiceBaseUpdateOrAddExtensions) .GetMethods(BindingFlags.NonPublic | BindingFlags.Static) .First(m => m.Name == nameof(ConvertDetailData)); } } public static (WebResponseContent res, TEntity entity) GetAddEntityData( this SaveModel saveDataModel, ServiceBase service) where TEntity : BaseEntity, new() where TRepository : IRepository { WebResponseContent baseWebResponse = new(); if (service.AddOnExecute != null) { baseWebResponse = service.AddOnExecute(saveDataModel); if (service.ResponseIsError) return (baseWebResponse, null); } if ((saveDataModel?.MainData?.Count ?? 0) == 0) return (baseWebResponse.Set(ResponseType.ParametersLack, false), null); saveDataModel.DetailData = saveDataModel.DetailData?.Where(x => x.Count > 0).ToList(); Type type = typeof(TEntity); saveDataModel.MainData .SetDataVersionDefaultValue(saveDataModel.DataVersionField) //数据版本号管理 .SetAuditDefaultValue()//审批字段默认值 .SetLogicDelValue() //设置默认创建人信息 .SetCreateDefaultVal(); //设置默认逻辑删除值 string validReslut = type.ValidateDicInEntity(saveDataModel.MainData, true, false, ApplicationServiceBaseConfig.UserIgnoreFields); if (!string.IsNullOrEmpty(validReslut)) return (baseWebResponse.Error(validReslut), null); //设置主键默认值 saveDataModel.MainData.SetPrimaryKeyDefaultValue(); //实体转换与租户设置 TEntity mainEntity = saveDataModel.MainData.DicToEntity(); baseWebResponse = ValidationEntityUnique(mainEntity, true); if (!baseWebResponse.Status) return (baseWebResponse, null); baseWebResponse = saveDataModel.ConvertDetailEntity(service, mainEntity, true); //if (CheckResponseResult()) return baseWebResponse; return (baseWebResponse, mainEntity); } public static (WebResponseContent res, TEntity entity) GetUpdateEntityData( this SaveModel saveDataModel, ServiceBase service) where TEntity : BaseEntity, new() where TRepository : IRepository { WebResponseContent baseWebResponse = new(); if (service.UpdateOnExecute != null) { baseWebResponse = service.UpdateOnExecute(saveDataModel); if (service.ResponseIsError) return (baseWebResponse, null); } if ((saveDataModel?.MainData?.Count ?? 0) == 0) return (baseWebResponse.Set(ResponseType.ParametersLack, false), null); saveDataModel.DetailData = saveDataModel.DetailData?.Where(x => x.Count > 0).ToList(); Type type = typeof(TEntity); saveDataModel.MainData.SetModifyDefaultVal(); //设置编辑人信息 string validReslut = type.ValidateDicInEntity( saveDataModel.MainData, true, false, ApplicationServiceBaseConfig.UserIgnoreFields, requireAllField: false); if (!string.IsNullOrEmpty(validReslut)) return (baseWebResponse.Error(validReslut), null); TEntity mainEntity = saveDataModel.MainData.DicToEntity(); baseWebResponse = ValidationEntityUnique(mainEntity, false); if (!baseWebResponse.Status) return (baseWebResponse, null); //检查版本号 baseWebResponse = service.ValidationDataVersion(mainEntity, saveDataModel); if (service.ResponseIsError) return (baseWebResponse, null); baseWebResponse = saveDataModel.ConvertDetailEntity(service, mainEntity, false); // if (service.CheckResponseResult()) return baseWebResponse; return (baseWebResponse, mainEntity); } public static WebResponseContent ConvertDetailEntity( this SaveModel saveModel, ServiceBase service, TEntity mainEntity, bool isAdd = true) where TEntity : BaseEntity, new() where TRepository : IRepository { service.MultipleTableEntity = new ApplicationServiceBaseMultipleTableEntity(); WebResponseContent webResponse = new(true); var detailTypes = typeof(TEntity).GetCustomAttribute().DetailTable; //1级表(主表) int lv = 1; if ((saveModel.DetailData != null && saveModel.DetailData.Count > 0) || saveModel.DelKeys?.Count > 0) { saveModel.DetailData ??= []; var generic = ConvertDetailDataMethod.MakeGenericMethod(typeof(TEntity), detailTypes[0]); webResponse = generic.Invoke(null, [mainEntity, saveModel, saveModel.DetailData, saveModel.DelKeys, service.MultipleTableEntity, isAdd, lv]) as WebResponseContent; } return webResponse; } /// /// 明细表校验与转换 /// /// /// /// /// /// private static WebResponseContent ConvertDetailData( TMain mainEntity, SaveModel saveModel, List> dic, List delKeys, ApplicationServiceBaseMultipleTableEntity multipleTableEntity, bool isAdd = true, int lv = 1) where TMain : class where TDetail : class { //二级子表如果是新建的对象,三级表不应该再调用添加 WebResponseContent webResponse = new(); Type detailType = typeof(TDetail); var subDetailTypes = detailType.GetCustomAttribute().DetailTable ?? []; var ignoreSubRowFields = subDetailTypes.Select(s => s.Name).ToList(); //获取主表主键验证 string foreignKey = typeof(TMain).GetForeignKey(detailType.Name); //isAdd && if (!string.IsNullOrEmpty(foreignKey)) { ignoreSubRowFields.Add(foreignKey); } string msg = detailType.ValidateDicInEntity(dic, true, false, ignoreSubRowFields.ToArray(),requireAllField:isAdd); if (!string.IsNullOrEmpty(msg)) { return webResponse.Error($"{detailType.GetEntityTableCnName()}:{msg}"); } List list = []; if (!multipleTableEntity.Data.TryGetValue(detailType, out EntityData entityData)) { entityData = new EntityData(); multipleTableEntity.Data[detailType] = entityData; } //删除的数据 entityData.DelKeys = delKeys; //三细明细表传的是null值 if (mainEntity==null) { return webResponse.OK(); } var mainKeyProperty = typeof(TMain).GetKeyProperty(); var mainKeyValue = mainKeyProperty.GetValue(mainEntity); foreach (var item in dic) { bool detailIsAdd = isAdd || item.IsTypeAdd(detailType); //新增数据 if (detailIsAdd) { item.SetLogicDelValue() .SetCreateDefaultVal() //设置自定义主表主键字段的值 .SetPrimaryKeyDefaultValue(foreignKey, mainKeyValue); } else { //记录编辑的字段 if (entityData.UpdateFields.Count == 0) { entityData.UpdateFields = [.. FilterUpdateFields(detailType, item.Select(s => s.Key))]; } //编辑数据 item.SetModifyDefaultVal(); } TDetail detailData = item.DicToEntity(); ; list.Add(detailData); //添加的数据 if (detailIsAdd) { entityData.AddList.Add(detailData); //二级主表为添加的数据时 if (isAdd && lv > 1) { entityData.InsertDb = false; } } else { //编辑的数据 entityData.UpdateList.Add(detailData); } } if (list?.Count>0) { //给主表写入明细表对象(二级明细表是添加的数据,三级明细表应该标记不手动写入数据库) var property = typeof(TMain).GetProperty(detailType.GetEntityTableName(false)); property?.SetValue(mainEntity, list); } return webResponse.OK(); } private static WebResponseContent ValidationEntityHasDetail(Type detailType, string table) { WebResponseContent webResponse = new(); if (detailType == null) { return webResponse.Error($"未找到明细表[{table}]配置,请重新配置主表的明细表,再点生成model"); } return webResponse.OK(); } private static (List> list, WebResponseContent res) ConvertObjectToDic(object value, Type type) { WebResponseContent webResponse = new(); List> list = new(); if (value==null) { return (list, webResponse.OK()); } string keyName = type.GetKeyName(); foreach (JObject jObject in (JArray)value) { Dictionary dic = new Dictionary(); foreach (var property in jObject.Properties()) { dic[property.Name] = property.Value.ToObject(); } string result = type.ValidateDicInEntity(dic, removeNotContains: false, removerKey: false,requireAllField:!dic.ContainsKey(keyName)); if (!string.IsNullOrEmpty(result)) { return (list, webResponse.Error($"{type.GetEntityTableCnName()}:{result}")); } list.Add(dic); } return (list, webResponse.OK()); } private static string[] FilterUpdateFields(Type entityType, IEnumerable rawFields) { var props = entityType.GetProperties(); var keyName = entityType.GetKeyName(); // 只更新:实体真实存在的普通字段(排除主键/集合导航/前端辅助字段) var safe = rawFields .Where(f => !string.IsNullOrWhiteSpace(f)) .Where(f => !f.Equals(keyName, StringComparison.OrdinalIgnoreCase)) .Where(f => !f.Equals("elementIndex", StringComparison.OrdinalIgnoreCase)) .Where(f => props.Any(p => p.Name == f && p.PropertyType.Name != "List`1")) .ToList(); return [.. safe]; } private static MethodInfo[] DetailTypedMethods => typeof(ApplicationServiceBaseUpdateOrAddExtensions).GetMethods(BindingFlags.NonPublic | BindingFlags.Static); public static WebResponseContent ValidationDataVersion( this ServiceBase service, TEntity entity, SaveModel saveModel) where TEntity : BaseEntity, new() where TRepository : IRepository { WebResponseContent webResponse = new(true); if (string.IsNullOrEmpty(saveModel.DataVersionField) || string.IsNullOrEmpty(saveModel.DataVersionValue)) return webResponse; var versionProperty = typeof(TEntity).GetProperty(saveModel.DataVersionField); if (versionProperty == null) return webResponse; var keyProperty = typeof(TEntity).GetKeyProperty(); object keyValue = keyProperty.GetValue(entity); var where = keyProperty.Name.CreateExpression(keyValue, LinqExpressionType.Equal); var selectExpression = saveModel.DataVersionField.GetExpression(); string dataVersionValue = service.repository.FindAsIQueryable(where).Select(selectExpression).FirstOrDefault(); if (string.IsNullOrEmpty(dataVersionValue)) return webResponse; if (dataVersionValue != saveModel.DataVersionValue) return webResponse.Error("数据已发生变化,请刷新页面后重新编辑"); string value = Guid.NewGuid().ToString(); versionProperty.SetValue(entity, value); saveModel.MainData[saveModel.DataVersionField] = value; return webResponse; } /// /// 获取更新前的原始数据 /// /// /// /// public static Expression> GetUpdateBeforeActionLogDbDataFilterExpression(this SaveModel saveDataModel) where TEntity : class { bool isActionLog = typeof(TEntity).IsTableActionLog(); if (!isActionLog) return null; var property = typeof(TEntity).GetKeyProperty(); object mainKeyVal = saveDataModel.MainData[property.Name]; Expression> expression = property.Name.CreateExpression(mainKeyVal.ToString(), LinqExpressionType.Equal); return expression; } /// /// 校验字段唯一 /// /// /// /// private static WebResponseContent ValidationEntityUnique(TEntity entity, bool isAdd) where TEntity : class, new() { WebResponseContent webResponse = new WebResponseContent(); var uniqueFields = TableColumnContext.Data.Where(x => x.TableName == typeof(TEntity).Name && x.IsUnique == 1).Select(s => new { s.ColumnName, s.ColumnCnName }).ToList(); if (uniqueFields.Count == 0) { return webResponse.OK(); } foreach (var item in uniqueFields) { var property = typeof(TEntity).GetProperty(item.ColumnName); if (property == null) continue; string value = property.GetValue(entity)?.ToString(); if (value == null) continue; List searches = [ new SearchParameters() { Name=item.ColumnName, Value=value }, ]; if (!isAdd) { var keyProperty = typeof(TEntity).GetKeyProperty(); searches.Add(new SearchParameters() { Name = keyProperty.Name, Value = keyProperty.GetValue(entity).ToString(), DisplayType = "!=" }); } var param = new PageDataOptions() { Filter = searches, Page = 1, Rows = 1 }; if (param.GetSearchQueryable().Any()) { return webResponse.Error($"字段[{item.ColumnCnName}]值[{value}]已存在"); } } return webResponse.OK(); } /// /// 明细表更新 /// /// /// /// 明细实体类型 public static void UpdateDetail(this EntityData entityData, BaseDbContext dbContext, Type detailType) { if (entityData.UpdateList == null || entityData.UpdateList.Count == 0) return; DetailTypedMethods.First(m => m.Name == nameof(UpdateDetailTyped) && m.IsGenericMethodDefinition) .MakeGenericMethod(detailType) .Invoke(null, [entityData, dbContext.SqlSugarClient]); } private static void UpdateDetailTyped(EntityData entityData, ISqlSugarClient sqlSugarClient) where T : class, new() { var list = entityData.UpdateList.Cast().ToList(); sqlSugarClient.Updateable(list).UpdateColumns(entityData.UpdateFields.ToArray()).ExecuteCommand(); } /// /// 明细表更新 /// public static async Task UpdateDetailAsync(this EntityData entityData, BaseDbContext dbContext, Type detailType) { if (entityData.UpdateList == null || entityData.UpdateList.Count == 0) return 0; return await (DetailTypedMethods.First(m => m.Name == nameof(UpdateDetailTypedAsync) && m.IsGenericMethodDefinition) .MakeGenericMethod(detailType) .Invoke(null, [entityData, dbContext.SqlSugarClient]) as Task); } private static Task UpdateDetailTypedAsync(EntityData entityData, ISqlSugarClient sqlSugarClient) where T : class, new() { var list = entityData.UpdateList.Cast().ToList(); return sqlSugarClient.Updateable(list).UpdateColumns(entityData.UpdateFields.ToArray()).ExecuteCommandAsync(); } /// /// 明细批量插入 /// public static void InsertDetail(this EntityData entityData, BaseDbContext dbContext, Type detailType) { if (entityData.AddList == null || entityData.AddList.Count == 0) return; DetailTypedMethods.First(m => m.Name == nameof(InsertDetailTyped) && m.IsGenericMethodDefinition) .MakeGenericMethod(detailType) .Invoke(null, [entityData, dbContext]); } /// /// 明细批量插入(异步) /// public static async Task InsertDetailAsync(this EntityData entityData, BaseDbContext dbContext, Type detailType) { if (entityData.AddList == null || entityData.AddList.Count == 0) return; await (DetailTypedMethods.First(m => m.Name == nameof(InsertDetailTypedAsync) && m.IsGenericMethodDefinition) .MakeGenericMethod(detailType) .Invoke(null, [entityData, dbContext]) as Task); } private static void InsertDetailTyped(EntityData entityData, BaseDbContext dbContext) where T : class, new() { var list = entityData.AddList.Cast().ToList(); bool isIdentit = IsIdentityType(typeof(T).GetKeyProperty()); //非自增直接保存 if (!isIdentit) { dbContext.SqlSugarClient.Insertable(list).ExecuteCommand(); } foreach (var entity in list) { //自增主键,InsertNavDetail调用写入数据库 dbContext.InsertNavDetail(entity, isIdentit); } } private static async Task InsertDetailTypedAsync(EntityData entityData, BaseDbContext dbContext) where T : class, new() { var list = entityData.AddList.Cast().ToList(); bool isIdentit = IsIdentityType(typeof(T).GetKeyProperty()); //非自增直接保存 if (!isIdentit) { await dbContext.SqlSugarClient.Insertable(list).ExecuteCommandAsync(); } foreach (var entity in list) { //自增主键,InsertNavDetail调用写入数据库 await dbContext.InsertNavDetailAsync(entity, isIdentit); } return true; } /// /// 新建时主从表一起写入 /// /// /// /// /// 是否保存主表数据 /// public static bool InsertNavDetail(this BaseDbContext dbContext, T mainEntity, bool insertMainData = true) where T : class, new() { if (insertMainData) { dbContext.SqlSugarClient.AddWithSetIdentity(mainEntity, true); } Type[] detailTypes = GetDetailTypes(); foreach (Type detailType in detailTypes) { GetInsertNavDetailMemberInfo(nameof(InsertNavDetailPair)) .MakeGenericMethod(typeof(T), detailType) .Invoke(null, [mainEntity, dbContext]); } return true; } private static MethodInfo GetInsertNavDetailMemberInfo(string name) { return typeof(ApplicationServiceBaseUpdateOrAddExtensions) .GetMethods(BindingFlags.NonPublic | BindingFlags.Static) .First(m => m.Name == name && m.IsGenericMethodDefinition); } public static async Task InsertNavDetailAsync(this BaseDbContext dbContext, T mainEntity, bool insertMainData = true) where T : class, new() { if (insertMainData) { dbContext.SqlSugarClient.AddWithSetIdentity(mainEntity, true); } Type[] detailTypes = GetDetailTypes(); foreach (Type detailType in detailTypes) { await (GetInsertNavDetailMemberInfo(nameof(InsertNavDetailPairAsync)) .MakeGenericMethod(typeof(T), detailType) .Invoke(null, [mainEntity, dbContext]) as Task); } return true; } private static async Task InsertNavDetailPairAsync(TMain mainEntity, BaseDbContext dbContext) where TMain : class where TDetail : class, new() { List list = GetDetails(mainEntity); if (list == null || list.Count == 0) return true; var sugarClient = dbContext.SqlSugarClient; PropertyInfo detailKeyProp = typeof(TDetail).GetKeyProperty(); if (IsIdentityType(detailKeyProp)) { foreach (TDetail row in list) { await sugarClient.Insertable(row).ExecuteReturnEntityAsync(); } } else { await sugarClient.Insertable(list).ExecuteCommandAsync(); } //三级明细表 if (!HasDetail()) return true; foreach (TDetail row in list) { await dbContext.InsertNavDetailAsync(row, false); } return true; } private static void InsertNavDetailPair(TMain mainEntity, BaseDbContext dbContext) where TMain : class where TDetail : class, new() { List list = GetDetails(mainEntity); if (list == null || list.Count == 0) return; var sugarClient = dbContext.SqlSugarClient; PropertyInfo detailKeyProp = typeof(TDetail).GetKeyProperty(); if (IsIdentityType(detailKeyProp)) { foreach (TDetail row in list) { sugarClient.Insertable(row).ExecuteReturnEntity(); } } else { sugarClient.Insertable(list).ExecuteCommand(); } //三级明细表 if (!HasDetail()) return; foreach (TDetail row in list) { dbContext.InsertNavDetail(row, false); } } private static List GetDetails(TMain mainEntity) { PropertyInfo detailProperty = typeof(TMain).GetProperty(typeof(TDetail).GetEntityTableName(false)); List list = detailProperty.GetValue(mainEntity) as List; if (list == null || list.Count == 0) return null; //主表主键 PropertyInfo mainKeyProp = typeof(TMain).GetKeyProperty(); //与主表关联主键字段 string foreignKey = typeof(TMain).GetForeignKey(typeof(TDetail).Name); //明细表与主表主键关联字段 PropertyInfo fkProp = typeof(TDetail).GetProperty(foreignKey); if (IsIdentityType(mainKeyProp)) { object mainKeyVal = mainKeyProp?.GetValue(mainEntity); foreach (TDetail row in list) { fkProp.SetValue(row, mainKeyVal.ChangeType(fkProp.PropertyType)); } } return list; } /// /// 是否自增 /// /// /// private static bool IsIdentityType(PropertyInfo property) { return property.PropertyType == typeof(int) || (property.PropertyType == typeof(long) && !AppSetting.EnableSnowFlakeID); } private static bool HasDetail() where T : class { return GetDetailTypes().Length > 0; } public static Type[] GetDetailTypes() where T : class { return GetDetailTypes(typeof(T)); } public static Type[] GetDetailTypes(this Type mainType) { return mainType.GetCustomAttribute()?.DetailTable ?? []; ; } } }