using MiaoYu.Repository.Admin.Entities.LowCode; using MiaoYu.Shared.Admin.Models.LowCodes; using CoreIDatabaseTableService = MiaoYu.Core.CodeGenerator.Services.IDatabaseTableService; using CoreLowCodeTableInfo = MiaoYu.Core.CodeGenerator.Models.LowCodeTableInfo; using CoreITableMetaConfigService = MiaoYu.Core.CodeGenerator.Services.ITableMetaConfigService; using CoreGenDbTableDto = MiaoYu.Core.CodeGenerator.Models.GenDbTableDto; using CoreTableMetaConfig = MiaoYu.Core.CodeGenerator.Models.TableMetaConfig; using CoreColumnMetaConfig = MiaoYu.Core.CodeGenerator.Models.ColumnMetaConfig; namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl; /// /// 服务 Low_Code_Table_InfoService /// public class LowCodeTableInfoService : ApplicationService> { private readonly CoreIDatabaseTableService _databaseTableService; private readonly IRepository _lowCodeTableRepository; private readonly CoreITableMetaConfigService _tableMetaConfigService; private readonly ILogger? _logger; public LowCodeTableInfoService( CoreIDatabaseTableService databaseTableService, IRepository defaultRepository, IRepository lowCodeTableRepository, CoreITableMetaConfigService tableMetaConfigService, ILogger? logger = null) : base(defaultRepository) { _databaseTableService = databaseTableService; _lowCodeTableRepository = lowCodeTableRepository; _tableMetaConfigService = tableMetaConfigService; _logger = logger; } /// /// 根据表名和数据库标识获取列列表(推荐使用) /// /// 查询参数,包含表名和数据库标识 /// public Task FindListByTableAsync(TableColumnQueryInput input) { // 参数验证 if (string.IsNullOrWhiteSpace(input.TableName) || string.IsNullOrWhiteSpace(input.DataBase)) { return Task.FromResult(new PagingView(input.Page, input.Size)); } // 从 Core 层获取所有表信息(已包含配置合并) var allTables = _databaseTableService.GetAllTables(); // 直接使用表名和数据库标识查找目标表 var targetTable = allTables.FirstOrDefault(t => t.TableName == input.TableName && t.DataBase == input.DataBase); if (targetTable == null || targetTable.TableInfos == null) { return Task.FromResult(new PagingView(input.Page, input.Size)); } // 应用筛选条件 var columns = targetTable.TableInfos.AsEnumerable(); if (!string.IsNullOrWhiteSpace(input.Search?.ColumnName)) { columns = columns.Where(w => w.ColumnName != null && w.ColumnName.Contains(input.Search.ColumnName)); } if (!string.IsNullOrWhiteSpace(input.Search?.Describe)) { columns = columns.Where(w => w.Describe != null && w.Describe.Contains(input.Search.Describe)); } // 排序和分页 var orderedColumns = columns .OrderBy(w => w.Position) .Skip((input.Page - 1) * input.Size) .Take(input.Size) .Select(w => new Dictionary { ["id"] = Guid.NewGuid(), // 临时 ID,因为是从内存查询 ["isPrimary"] = w.IsPrimary, ["isIdentity"] = w.IsIdentity, ["isNullable"] = w.IsNullable, ["position"] = w.Position, ["columnName"] = w.ColumnName, ["describe"] = w.Describe, ["databaseColumnType"] = w.DatabaseColumnType, ["csType"] = w.CsType, ["csField"] = w.CsField, ["displayName"] = w.DisplayName, ["tableName"] = input.TableName, ["dataBase"] = input.DataBase, ["lastModificationTime"] = DateTime.Now.ToString("yyyy-MM-dd"), ["creationTime"] = DateTime.Now.ToString("yyyy-MM-dd"), ["isTableColumnShow"] = w.IsTableColumnShow ?? true, ["isTableSelect"] = w.IsTableSelect, ["isImageId"] = w.IsImageId, ["orderById"] = w.OrderById ?? 10, ["width"] = w.Width, ["maxLength"] = w.MaxLength }) .ToList(); return Task.FromResult(new PagingView(input.Page, input.Size) { Total = columns.Count(), DataSource = orderedColumns, PageCount = (columns.Count() + input.Size - 1) / input.Size }); } /// /// 获取列表数据(从 Core 层查询表结构并合并配置) /// /// /// [Obsolete("请使用 FindListByTableAsync 方法,该方法使用 TableName + DataBase 作为查询参数")] public async Task FindListAsync(PagingSearchInput pagingSearchInput) { // ✅ 从 Core 层获取所有表信息(已包含配置合并) var allTables = _databaseTableService.GetAllTables(); // 根据 Low_Code_TableId 查找对应的表名和数据库 string? targetTableName = null; string? targetDatabase = null; if (pagingSearchInput.Search.Low_Code_TableId != Guid.Empty) { var lowCodeTable = await _lowCodeTableRepository.FindAsync(w => w.Id == pagingSearchInput.Search.Low_Code_TableId); if (lowCodeTable != null) { targetTableName = lowCodeTable.TableName; targetDatabase = lowCodeTable.DataBase; } } // 筛选目标表 var targetTable = allTables.FirstOrDefault(t => t.TableName == targetTableName && t.DataBase == targetDatabase); if (targetTable == null || targetTable.TableInfos == null) { return new PagingView(pagingSearchInput.Page, pagingSearchInput.Size); } // 应用筛选条件 var columns = targetTable.TableInfos.AsEnumerable(); if (!string.IsNullOrWhiteSpace(pagingSearchInput.Search.ColumnName)) { columns = columns.Where(w => w.ColumnName != null && w.ColumnName.Contains(pagingSearchInput.Search.ColumnName)); } if (!string.IsNullOrWhiteSpace(pagingSearchInput.Search.Describe)) { columns = columns.Where(w => w.Describe != null && w.Describe.Contains(pagingSearchInput.Search.Describe)); } // 排序和分页 var orderedColumns = columns .OrderBy(w => w.Position) .Skip((pagingSearchInput.Page - 1) * pagingSearchInput.Size) .Take(pagingSearchInput.Size) .Select(w => new Dictionary { ["id"] = Guid.NewGuid(), // 临时 ID,因为是从内存查询 ["isPrimary"] = w.IsPrimary, ["isIdentity"] = w.IsIdentity, ["isNullable"] = w.IsNullable, ["position"] = w.Position, ["columnName"] = w.ColumnName, ["describe"] = w.Describe, ["databaseColumnType"] = w.DatabaseColumnType, ["csType"] = w.CsType, ["csField"] = w.CsField, ["displayName"] = w.DisplayName, ["low_Code_TableId"] = pagingSearchInput.Search.Low_Code_TableId, ["lastModificationTime"] = DateTime.Now.ToString("yyyy-MM-dd"), ["creationTime"] = DateTime.Now.ToString("yyyy-MM-dd"), ["isTableColumnShow"] = w.IsTableColumnShow ?? true, ["isTableSelect"] = w.IsTableSelect, ["isImageId"] = w.IsImageId, ["orderById"] = w.OrderById ?? 10, ["width"] = w.Width, ["maxLength"] = w.MaxLength }) .ToList(); return new PagingView(pagingSearchInput.Page, pagingSearchInput.Size) { Total = columns.Count(), DataSource = orderedColumns, PageCount = (columns.Count() + pagingSearchInput.Size - 1) / pagingSearchInput.Size }; } /// /// 根据id数组删除 /// /// /// public Task DeleteListAsync(List ids) { _databaseTableService.ClearAllTablesByCache(); return _defaultRepository.DeleteByIdsAsync(ids); } /// /// 根据表名和数据库标识同步字段(推荐使用) /// /// 同步参数,包含表名和数据库标识 /// /// 参数验证失败或表不存在时抛出 public Task SynchronizationByTableAsync(TableSyncInput input) { // 参数验证 if (input == null) throw new ArgumentException("参数不能为空", nameof(input)); if (string.IsNullOrWhiteSpace(input.TableName)) throw new ArgumentException("表名不能为空", nameof(input)); if (string.IsNullOrWhiteSpace(input.DataBase)) throw new ArgumentException("数据库标识不能为空", nameof(input)); // 清除缓存并重新加载 _databaseTableService.ClearAllTablesByCache(); _databaseTableService.RefreshCache(); // 验证表是否存在 var allTables = _databaseTableService.GetAllTables(); var targetTable = allTables.FirstOrDefault(t => t.TableName == input.TableName && t.DataBase == input.DataBase); if (targetTable == null) throw new ArgumentException($"指定的表不存在:表名={input.TableName}, 数据库={input.DataBase}", nameof(input)); return Task.FromResult(true); } /// /// 同步表 /// /// /// /// [Obsolete("请使用 SynchronizationByTableAsync 方法,该方法使用 TableName + DataBase 作为查询参数")] public async Task SynchronizationColumnByTableIdAsync(Guid tableId, bool isTableSync = false) { var allTables = _databaseTableService.GetAllTableInfos(); var table = await _lowCodeTableRepository.FindAsync(w => w.Id == tableId); // 修复:同时匹配表名和数据库标识 var tableInfo = allTables.Find(w => w.Name == table.TableName && w.DataBase == table.DataBase); //查询出当前表所有的字段 var tableColumns = await _defaultRepository.ToListAsync(w => w.Low_Code_TableId == table.Id); //操作集合 var list = new List(); if (isTableSync) { if (tableColumns != null && tableColumns.Count == 0) { foreach (var item in tableInfo.Columns) { // if (tableColumns.Any(w => w.ColumnName == item.Name)) continue; var model = new LowCodeTableInfo(); model.IsPrimary = item.IsPrimary; model.IsIdentity = item.IsIdentity; model.IsNullable = item.IsNullable; model.Position = item.Position; model.Low_Code_TableId = table.Id; model.ColumnName = item.Name; model.DatabaseColumnType = item.DbType; model.CsType = item.CsType; model.CsField = item.Name; model.MaxLength = item.MaxLength; //model.IsImageId = item.IsImageId; //model.IsImageId = item.IsImageId; //model.IsImageId = item.IsImageId; if (!string.IsNullOrWhiteSpace(item.Comment)) { model.Describe = item.Comment; model.DisplayName = item.Comment; } list.Add(model); } } } else { foreach (var item in tableInfo.Columns) { // if (tableColumns.Any(w => w.ColumnName == item.Name)) continue; var model = new LowCodeTableInfo(); model.IsPrimary = item.IsPrimary; model.IsIdentity = item.IsIdentity; model.IsNullable = item.IsNullable; model.Position = item.Position; model.Low_Code_TableId = table.Id; model.ColumnName = item.Name; model.DatabaseColumnType = item.DbType; model.CsType = item.CsType; model.CsField = item.Name; model.MaxLength = item.MaxLength; if (!string.IsNullOrWhiteSpace(item.Comment)) { model.Describe = item.Comment; model.DisplayName = item.Comment; } var oldColumns = tableColumns.FirstOrDefault(w => w.ColumnName == item.Name); if (oldColumns!=null) { model.OrderById = oldColumns.OrderById; model.Describe = oldColumns.Describe; model.IsImageId = oldColumns.IsImageId; model.IsTableColumnShow = oldColumns.IsTableColumnShow; model.IsTableSelect = oldColumns.IsTableSelect; model.Width = oldColumns.Width; }; list.Add(model); } } await _defaultRepository.DeleteAsync(w => w.Low_Code_TableId == table.Id); await _defaultRepository.InsertRangeAsync(list); _databaseTableService.ClearAllTablesByCache(); } /// /// 根据表名和数据库标识变更列配置(推荐使用) /// /// 修改参数,包含表名、数据库标识和列配置列表 /// 保存成功返回 true /// 参数验证失败或表不存在时抛出 public async Task ChangeByTableAsync(TableColumnChangeInput input) { // 参数验证 if (input == null) throw new ArgumentException("参数不能为空", nameof(input)); if (string.IsNullOrWhiteSpace(input.TableName)) throw new ArgumentException("表名不能为空", nameof(input)); if (string.IsNullOrWhiteSpace(input.DataBase)) throw new ArgumentException("数据库标识不能为空", nameof(input)); if (input.Columns == null || input.Columns.Count == 0) throw new ArgumentException("列配置列表不能为空", nameof(input)); // 验证所有列的 ColumnName 不为空 var invalidColumns = input.Columns.Where(c => string.IsNullOrWhiteSpace(c.ColumnName)).ToList(); if (invalidColumns.Count > 0) throw new ArgumentException("列配置中存在空的列名", nameof(input)); // 清除缓存 _databaseTableService.ClearAllTablesByCache(); // 从内存缓存中查找表 var allTables = _databaseTableService.GetAllTables(); var targetTable = allTables.FirstOrDefault(t => t.TableName == input.TableName && t.DataBase == input.DataBase); if (targetTable == null) throw new ArgumentException($"指定的表不存在:表名={input.TableName}, 数据库={input.DataBase}", nameof(input)); if (targetTable.TableInfos == null) throw new ArgumentException("表列信息为空", nameof(input)); // 更新用户修改的列配置 var updatedCount = 0; foreach (var changedColumn in input.Columns) { var existingColumn = targetTable.TableInfos.FirstOrDefault(c => c.ColumnName == changedColumn.ColumnName); if (existingColumn == null) { // 列不存在,记录日志但继续处理其他列 _logger?.LogWarning($"列不存在,跳过更新:表名={input.TableName}, 列名={changedColumn.ColumnName}"); continue; } // 更新可配置字段 // 注意:由于 JsonExtensionData 只能捕获未定义的属性,而我们的字段都已定义, // 所以无法通过 WasFieldProvided 检测。我们采用以下策略: // 1. 前端总是传递所有字段(包括 null) // 2. 我们直接更新所有字段(字符串 null = 清空,可空值类型 null = 设置为 null) // 字符串字段:直接更新(null = 清空,有值 = 更新) existingColumn.DisplayName = changedColumn.DisplayName; existingColumn.Describe = changedColumn.Describe; existingColumn.CsField = changedColumn.CsField; existingColumn.Width = changedColumn.Width; // 可空值类型:直接更新(null = 设置为 null,有值 = 设置为该值) existingColumn.IsTableColumnShow = changedColumn.IsTableColumnShow; existingColumn.IsTableSelect = changedColumn.IsTableSelect; existingColumn.IsImageId = changedColumn.IsImageId; existingColumn.OrderById = changedColumn.OrderById; updatedCount++; } if (updatedCount == 0) { throw new ArgumentException("没有成功更新任何列配置,请检查列名是否正确", nameof(input)); } // 保存到配置文件 await SaveTableMetaConfigAsync(targetTable); // 清除缓存,确保下次查询时重新加载并合并最新的配置文件 _databaseTableService.ClearAllTablesByCache(); return true; } /// /// 变更数据(保存到配置文件) /// /// /// [Obsolete("请使用 ChangeByTableAsync 方法,该方法使用 TableName + DataBase 作为查询参数")] public async Task ChangeAsync(List lowCodeTableInfos) { // 清除缓存 _databaseTableService.ClearAllTablesByCache(); // 按表分组保存 var groupedByTable = lowCodeTableInfos.GroupBy(c => c.Low_Code_TableId); foreach (var group in groupedByTable) { var tableId = group.Key; var lowCodeTable = await _lowCodeTableRepository.FindAsync(w => w.Id == tableId); if (lowCodeTable == null || string.IsNullOrEmpty(lowCodeTable.DataBase) || string.IsNullOrEmpty(lowCodeTable.TableName)) continue; // 获取完整的表信息(包含所有列) var allTables = _databaseTableService.GetAllTables(); var targetTable = allTables.FirstOrDefault(t => t.TableName == lowCodeTable.TableName && t.DataBase == lowCodeTable.DataBase); if (targetTable == null) continue; // 更新用户修改的列配置 foreach (var changedColumn in group) { var existingColumn = targetTable.TableInfos?.FirstOrDefault(c => c.ColumnName == changedColumn.ColumnName); if (existingColumn != null) { existingColumn.DisplayName = changedColumn.DisplayName; existingColumn.Describe = changedColumn.Describe; existingColumn.CsField = changedColumn.CsField; existingColumn.IsTableSelect = changedColumn.IsTableSelect; existingColumn.IsImageId = changedColumn.IsImageId; existingColumn.IsTableColumnShow = changedColumn.IsTableColumnShow; existingColumn.OrderById = changedColumn.OrderById; existingColumn.Width = changedColumn.Width; } } // 保存到配置文件 await SaveTableMetaConfigAsync(targetTable); } // 注意:不再更新数据库表,改为更新配置文件 // await _defaultRepository.UpdateRangeAsync(lowCodeTableInfos); } /// /// 保存表元信息到配置文件 /// private async Task SaveTableMetaConfigAsync(CoreGenDbTableDto tableDto) { if (string.IsNullOrEmpty(tableDto.DataBase) || string.IsNullOrEmpty(tableDto.TableName)) return; var config = new CoreTableMetaConfig { DisplayName = tableDto.DisplayName, EntityName = tableDto.EntityName, Remark = tableDto.Remark, ModelPath = tableDto.ModelPath, ServicePath = tableDto.ServicePath, ControllerPath = tableDto.ControllerPath, ClientIndexPath = tableDto.ClientIndexPath, ClientInfoPath = tableDto.ClientInfoPath, ClientServicePath = tableDto.ClientServicePath, IsCover = tableDto.IsCover ?? false, Columns = new Dictionary() }; // 保存列配置 if (tableDto.TableInfos != null) { foreach (var column in tableDto.TableInfos) { if (!string.IsNullOrEmpty(column.ColumnName)) { config.Columns[column.ColumnName] = new CoreColumnMetaConfig { DisplayName = column.DisplayName, Describe = column.Describe, CsField = column.CsField, IsTableSelect = column.IsTableSelect, IsImageId = column.IsImageId, IsTableColumnShow = column.IsTableColumnShow, Width = column.Width, OrderById = column.OrderById }; } } } await _tableMetaConfigService.SaveConfigAsync(tableDto.DataBase!, tableDto.TableName!, config); } }