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,
menuPath = targetTable.MenuPath,
routerPath = targetTable.RouterPath,
isCover = targetTable.IsCover ?? false
};
res["menu"] = targetTable.MenuPath ?? $"views/apps/{targetTable.TableName}s/Index.vue";
res["router"] = targetTable.RouterPath ?? $"/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);
}
}