using MiaoYu.Repository.Admin.Entities.LowCode; using MiaoYu.Repository.ChatAI.Admin.Entities; using MiaoYu.Shared.Admin.Models.LowCodes; using CoreIDatabaseTableService = MiaoYu.Core.CodeGenerator.Services.IDatabaseTableService; using CoreICodeGenerationService = MiaoYu.Core.CodeGenerator.Services.ICodeGenerationService; using CoreDataSourceManager = MiaoYu.Core.CodeGenerator.Core.DataSourceManager; using CoreITableMetaConfigService = MiaoYu.Core.CodeGenerator.Services.ITableMetaConfigService; using CoreITableSchemaCache = MiaoYu.Core.CodeGenerator.Services.ITableSchemaCache; using CoreEntityNamingStrategy = MiaoYu.Core.CodeGenerator.Abstractions.EntityNamingStrategy; using CorePathResolver = MiaoYu.Core.CodeGenerator.Core.PathResolver; 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_TableService /// public class LowCodeTableService : ApplicationService> { private readonly IRepository _lowCodeTableInfoRepository; private readonly LowCodeTableInfoService _lowCodeTableInfoService; private readonly CoreIDatabaseTableService _databaseTableService; private readonly CoreICodeGenerationService _codeGenerationService; private readonly CoreDataSourceManager _dataSourceManager; private readonly CoreITableMetaConfigService _tableMetaConfigService; private readonly CoreITableSchemaCache _tableSchemaCache; private readonly CorePathResolver _pathResolver; private readonly ILogger? _logger; public LowCodeTableService( IRepository defaultRepository, LowCodeTableInfoService lowCodeTableInfoService, IRepository lowCodeTableInfoRepository, CoreIDatabaseTableService databaseTableService, CoreICodeGenerationService codeGenerationService, CoreDataSourceManager dataSourceManager, CoreITableMetaConfigService tableMetaConfigService, CoreITableSchemaCache tableSchemaCache, CorePathResolver pathResolver, ILogger? logger = null) : base(defaultRepository) { _lowCodeTableInfoService = lowCodeTableInfoService; _lowCodeTableInfoRepository = lowCodeTableInfoRepository; _databaseTableService = databaseTableService; _codeGenerationService = codeGenerationService; _dataSourceManager = dataSourceManager; _tableMetaConfigService = tableMetaConfigService; _tableSchemaCache = tableSchemaCache; _pathResolver = pathResolver; _logger = logger; } /// /// 获取列表数据(从 Core 层查询表结构并合并配置) /// /// /// public async Task FindListAsync(PagingSearchInput pagingSearchInput) { // ✅ 从 Core 层获取所有表信息(已包含配置合并) var allTables = _databaseTableService.GetAllTables(); // 应用筛选条件 var filteredTables = allTables.AsEnumerable(); // ✅ 数据库筛选条件(重要:支持多数据源筛选) if (!string.IsNullOrWhiteSpace(pagingSearchInput.Search.DataBase)) { filteredTables = filteredTables.Where(t => t.DataBase == pagingSearchInput.Search.DataBase); } if (!string.IsNullOrWhiteSpace(pagingSearchInput.Search.TableName)) { filteredTables = filteredTables.Where(t => t.TableName != null && t.TableName.Contains(pagingSearchInput.Search.TableName)); } if (!string.IsNullOrWhiteSpace(pagingSearchInput.Search.EntityName)) { filteredTables = filteredTables.Where(t => t.EntityName != null && t.EntityName.Contains(pagingSearchInput.Search.EntityName)); } if (!string.IsNullOrWhiteSpace(pagingSearchInput.Search.DisplayName)) { filteredTables = filteredTables.Where(t => t.DisplayName != null && t.DisplayName.Contains(pagingSearchInput.Search.DisplayName)); } // 排序和分页 var pagedTablesList = filteredTables .Skip((pagingSearchInput.Page - 1) * pagingSearchInput.Size) .Take(pagingSearchInput.Size) .ToList(); // 异步获取 ID 并构建结果 var pagedTables = new List>(); foreach (var t in pagedTablesList) { var id = await GetOrCreateTableId(t.TableName, t.DataBase); pagedTables.Add(new Dictionary { ["id"] = id, ["tableName"] = t.TableName, ["displayName"] = t.DisplayName, ["entityName"] = t.EntityName, ["remark"] = t.Remark, ["lastModificationTime"] = DateTime.Now.ToString("yyyy-MM-dd"), ["creationTime"] = DateTime.Now.ToString("yyyy-MM-dd"), ["dataBase"] = t.DataBase }); } return new PagingView(pagingSearchInput.Page, pagingSearchInput.Size) { Total = filteredTables.Count(), DataSource = pagedTables, PageCount = (filteredTables.Count() + pagingSearchInput.Size - 1) / pagingSearchInput.Size }; } /// /// 获取或创建表的 ID(用于兼容性) /// private async Task GetOrCreateTableId(string? tableName, string? dataBase) { if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(dataBase)) return Guid.NewGuid(); var existingTable = await _defaultRepository.FindAsync(t => t.TableName == tableName && t.DataBase == dataBase); return existingTable?.Id ?? Guid.NewGuid(); } /// /// 根据id数组删除 /// /// /// public async Task DeleteListAsync(List ids) { // 查询要删除的表信息,用于删除配置文件 var tablesToDelete = await _defaultRepository.Select.Where(w => ids.Contains(w.Id)).ToListAsync(); _databaseTableService.ClearAllTablesByCache(); //删除子表 await _lowCodeTableInfoRepository.DeleteAsync(w => ids.Contains(w.Low_Code_TableId)); //删除主表 await _defaultRepository.DeleteByIdsAsync(ids); // 删除对应的配置文件 foreach (var table in tablesToDelete) { if (!string.IsNullOrEmpty(table.DataBase) && !string.IsNullOrEmpty(table.TableName)) { try { await _tableMetaConfigService.DeleteConfigAsync(table.DataBase, table.TableName); } catch (Exception) { // 配置文件可能不存在,忽略异常 } } } } /// /// 同步表(支持多数据源) /// public async Task SynchronizationAsync() { // 刷新表结构缓存(从数据库重新加载表结构) _tableSchemaCache.RefreshCache(); var allTables = _databaseTableService.GetAllTableInfos(); var oldAllTables = await _defaultRepository.ToListAllAsync(); var insertList = new List(); var updateList = new List(); var ids = new List(); foreach (var item in allTables) { // 修复:同时匹配表名和数据库标识,避免多数据源同名表冲突 var table = oldAllTables.Find(w => w.TableName == item.Name && w.DataBase == item.DataBase); var id = Guid.NewGuid(); // 获取数据源提供者 var provider = _dataSourceManager.GetProvider(item.DataBase ?? "Admin"); if (table == null) { var lowCodeTable = new LowCodeTable { Id = id, DisplayName = item.Comment, TableName = item.Name, DataBase = item.DataBase, Schema = item.Schema, // 根据命名策略生成实体名 EntityName = provider?.Config.NamingStrategy == CoreEntityNamingStrategy.KeepOriginal ? item.Name : ConvertToPascalCase(item.Name) }; insertList.Add(lowCodeTable); } else { id = table.Id; table.DataBase = item.DataBase; table.Schema = item.Schema; table.EntityName = provider?.Config.NamingStrategy == CoreEntityNamingStrategy.KeepOriginal ? item.Name : ConvertToPascalCase(item.Name); updateList.Add(table); } ids.Add(id); } if (insertList.Count > 0) { await _defaultRepository.InsertRangeAsync(insertList); } if (updateList.Count > 0) { await _defaultRepository.UpdateRangeAsync(updateList); } // 同步列信息 foreach (var item in ids) { await _lowCodeTableInfoService.SynchronizationColumnByTableIdAsync(item, true); } // 注意:这里不需要清除缓存,因为缓存在开始时已经刷新过了 // 清除缓存会导致下次查询重新加载,没有必要 } /// /// 将下划线命名转换为 PascalCase /// private static string ConvertToPascalCase(string input) { if (string.IsNullOrEmpty(input)) return input; var words = input.Split(new[] { '_', '-' }, StringSplitOptions.RemoveEmptyEntries); var result = new System.Text.StringBuilder(); foreach (var word in words) { if (word.Length > 0) { result.Append(char.ToUpper(word[0])); if (word.Length > 1) { result.Append(word.Substring(1).ToLower()); } } } return result.ToString(); } /// /// 根据表名和数据库标识变更表配置(推荐使用) /// /// 变更参数,包含表配置列表 /// 保存成功返回 true /// 参数验证失败或表不存在时抛出 public async Task ChangeByTableAsync(TableChangeInput input) { // 参数验证 if (input == null) throw new ArgumentException("参数不能为空", nameof(input)); if (input.Tables == null || input.Tables.Count == 0) throw new ArgumentException("表配置列表不能为空", nameof(input)); // 清除缓存 _databaseTableService.ClearAllTablesByCache(); // 从内存缓存获取所有表 var allTables = _databaseTableService.GetAllTables(); var updatedCount = 0; foreach (var tableItem in input.Tables) { // 验证参数 if (string.IsNullOrWhiteSpace(tableItem.TableName)) throw new ArgumentException("表名不能为空", nameof(input)); if (string.IsNullOrWhiteSpace(tableItem.DataBase)) throw new ArgumentException("数据库标识不能为空", nameof(input)); // 查找目标表 var targetTable = allTables.FirstOrDefault(t => t.TableName == tableItem.TableName && t.DataBase == tableItem.DataBase); if (targetTable == null) { _logger?.LogWarning($"表不存在,跳过更新:表名={tableItem.TableName}, 数据库={tableItem.DataBase}"); continue; } // 更新表配置(只更新传递的字段) if (tableItem.DisplayName != null) targetTable.DisplayName = tableItem.DisplayName; if (tableItem.EntityName != null) targetTable.EntityName = tableItem.EntityName; if (tableItem.Remark != null) targetTable.Remark = tableItem.Remark; // 保存到配置文件 await SaveTableMetaConfigAsync(targetTable); updatedCount++; } if (updatedCount == 0) { throw new ArgumentException("没有成功更新任何表配置,请检查表名和数据库标识是否正确", nameof(input)); } // 清除缓存,确保下次查询时重新加载并合并最新的配置文件 _databaseTableService.ClearAllTablesByCache(); return true; } /// /// 变更数据 /// /// /// [Obsolete("请使用 ChangeByTableAsync 方法,该方法使用 TableName + DataBase 作为查询参数")] public async Task ChangeAsync(List lowCodeTables) { _databaseTableService.ClearAllTablesByCache(); var oldLowCodeTables = await _defaultRepository.ToListAsync(w => lowCodeTables.Select(w => w.Id).Contains(w.Id)); var updateList = new List(); foreach (var item in lowCodeTables) { var lowCodeTable = oldLowCodeTables.Find(w => w.Id == item.Id); lowCodeTable.DisplayName = item.DisplayName; lowCodeTable.EntityName = item.EntityName; lowCodeTable.Remark = item.Remark; updateList.Add(lowCodeTable); } await _defaultRepository.UpdateRangeAsync(updateList); } /// /// 根据表名和数据库标识查询表单数据(推荐使用) /// /// 查询参数,包含表名和数据库标识 /// 表单数据字典 /// 参数验证失败或表不存在时抛出 public Task> FindFormByTableAsync(TableFormQueryInput 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)); // 从内存缓存获取表信息 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)); // 填充路径(使用 CodeGenerationService 的方法) _codeGenerationService.FillPathByLowCodeTable(targetTable); var res = new Dictionary(); res["id"] = Guid.NewGuid().ToString(); // 临时ID res["form"] = new { tableName = targetTable.TableName, dataBase = targetTable.DataBase, displayName = targetTable.DisplayName, entityName = targetTable.EntityName, remark = targetTable.Remark, modelPath = targetTable.ModelPath, servicePath = targetTable.ServicePath, controllerPath = targetTable.ControllerPath, clientIndexPath = targetTable.ClientIndexPath, clientInfoPath = targetTable.ClientInfoPath, clientServicePath = targetTable.ClientServicePath, isCover = targetTable.IsCover ?? false }; res["menu"] = $"views/apps/{targetTable.TableName}s/Index.vue"; res["router"] = $"/apps/{targetTable.TableName?.ToLower()}s"; return Task.FromResult(res); } /// /// 查询表单数据 /// /// /// [Obsolete("请使用 FindFormByTableAsync 方法,该方法使用 TableName + DataBase 作为查询参数")] public async Task> FindFormAsync(Guid id) { var res = new Dictionary(); var form = (await _defaultRepository.FindByIdAsync(id)).NullSafe(); FillPathByLowCodeTable(form); res[nameof(id)] = id == Guid.Empty ? "" : id; res[nameof(form)] = form; res["menu"] = $"views/apps/{form.TableName}s/Index.vue"; res["router"] = $"/apps/{form.TableName.ToLower()}s"; return res; } /// /// 根据 lowCodeTable 填充路径(支持多数据源) /// private void FillPathByLowCodeTable(LowCodeTable lowCodeTable) { var provider = _dataSourceManager.GetProvider(lowCodeTable.DataBase ?? "Admin"); if (provider == null) { return; } if (string.IsNullOrEmpty(lowCodeTable.ModelPath)) lowCodeTable.ModelPath = _pathResolver.ResolvePath(provider.Config.ModelPathTemplate, provider.Config, lowCodeTable.TableName!); if (string.IsNullOrEmpty(lowCodeTable.ServicePath)) lowCodeTable.ServicePath = _pathResolver.ResolvePath(provider.Config.ServicePathTemplate, provider.Config, lowCodeTable.TableName!); if (string.IsNullOrEmpty(lowCodeTable.ControllerPath)) lowCodeTable.ControllerPath = _pathResolver.ResolvePath(provider.Config.ControllerPathTemplate, provider.Config, lowCodeTable.TableName!); if (string.IsNullOrEmpty(lowCodeTable.ClientIndexPath)) lowCodeTable.ClientIndexPath = _pathResolver.ResolvePath(provider.Config.ClientIndexPathTemplate, provider.Config, lowCodeTable.TableName?.ToLower()!); if (string.IsNullOrEmpty(lowCodeTable.ClientInfoPath)) lowCodeTable.ClientInfoPath = _pathResolver.ResolvePath(provider.Config.ClientInfoPathTemplate, provider.Config, lowCodeTable.TableName?.ToLower()!); if (string.IsNullOrEmpty(lowCodeTable.ClientServicePath)) lowCodeTable.ClientServicePath = _pathResolver.ResolvePath(provider.Config.ClientServicePathTemplate, provider.Config, lowCodeTable.TableName?.ToLower()!); } /// /// 根据表名和数据库标识保存表单数据(推荐使用) /// /// 保存参数,包含表名、数据库标识和配置信息 /// 保存成功返回 true /// 参数验证失败或表不存在时抛出 public async Task SaveFormByTableAsync(TableFormSaveInput 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(); // 从内存缓存获取表信息 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 (input.DisplayName != null) targetTable.DisplayName = input.DisplayName; if (input.EntityName != null) targetTable.EntityName = input.EntityName; if (input.Remark != null) targetTable.Remark = input.Remark; if (input.ModelPath != null) targetTable.ModelPath = input.ModelPath; if (input.ServicePath != null) targetTable.ServicePath = input.ServicePath; if (input.ControllerPath != null) targetTable.ControllerPath = input.ControllerPath; if (input.ClientIndexPath != null) targetTable.ClientIndexPath = input.ClientIndexPath; if (input.ClientInfoPath != null) targetTable.ClientInfoPath = input.ClientInfoPath; if (input.ClientServicePath != null) targetTable.ClientServicePath = input.ClientServicePath; if (input.IsCover.HasValue) targetTable.IsCover = input.IsCover; // 保存到配置文件 await SaveTableMetaConfigAsync(targetTable); // 清除缓存,确保下次查询时重新加载并合并最新的配置文件 _databaseTableService.ClearAllTablesByCache(); return true; } /// /// 保存数据 /// /// /// [Transactional] [Obsolete("请使用 SaveFormByTableAsync 方法,该方法使用 TableName + DataBase 作为查询参数")] public virtual async Task SaveFormAsync(LowCodeTable form) { await _defaultRepository.InsertOrUpdateAsync(form); } /// /// 保存表元信息到配置文件 /// 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); } }