HuanMengAdmin/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableInfoService.cs
2025-11-08 15:00:24 +08:00

530 lines
22 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <summary>
/// 服务 Low_Code_Table_InfoService
/// </summary>
public class LowCodeTableInfoService : ApplicationService<IRepository<LowCodeTableInfo>>
{
private readonly CoreIDatabaseTableService _databaseTableService;
private readonly IRepository<LowCodeTable> _lowCodeTableRepository;
private readonly CoreITableMetaConfigService _tableMetaConfigService;
private readonly ILogger<LowCodeTableInfoService>? _logger;
public LowCodeTableInfoService(
CoreIDatabaseTableService databaseTableService,
IRepository<LowCodeTableInfo> defaultRepository,
IRepository<LowCodeTable> lowCodeTableRepository,
CoreITableMetaConfigService tableMetaConfigService,
ILogger<LowCodeTableInfoService>? logger = null) : base(defaultRepository)
{
_databaseTableService = databaseTableService;
_lowCodeTableRepository = lowCodeTableRepository;
_tableMetaConfigService = tableMetaConfigService;
_logger = logger;
}
/// <summary>
/// 根据表名和数据库标识获取列列表(推荐使用)
/// </summary>
/// <param name="input">查询参数,包含表名和数据库标识</param>
/// <returns></returns>
public Task<PagingView> 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<string, object?>
{
["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
});
}
/// <summary>
/// 获取列表数据(从 Core 层查询表结构并合并配置)
/// </summary>
/// <param name="pagingSearchInput"></param>
/// <returns></returns>
[Obsolete("请使用 FindListByTableAsync 方法,该方法使用 TableName + DataBase 作为查询参数")]
public async Task<PagingView> FindListAsync(PagingSearchInput<LowCodeTableInfo> 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<string, object?>
{
["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
};
}
/// <summary>
/// 根据id数组删除
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
public Task DeleteListAsync(List<Guid> ids)
{
_databaseTableService.ClearAllTablesByCache();
return _defaultRepository.DeleteByIdsAsync(ids);
}
/// <summary>
/// 根据表名和数据库标识同步字段(推荐使用)
/// </summary>
/// <param name="input">同步参数,包含表名和数据库标识</param>
/// <returns></returns>
/// <exception cref="ArgumentException">参数验证失败或表不存在时抛出</exception>
public Task<bool> 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);
}
/// <summary>
/// 同步表
/// </summary>
/// <param name="tableId"></param>
/// <param name="isTableSync"></param>
/// <returns></returns>
[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<LowCodeTableInfo>();
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();
}
/// <summary>
/// 根据表名和数据库标识变更列配置(推荐使用)
/// </summary>
/// <param name="input">修改参数,包含表名、数据库标识和列配置列表</param>
/// <returns>保存成功返回 true</returns>
/// <exception cref="ArgumentException">参数验证失败或表不存在时抛出</exception>
public async Task<bool> 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;
}
/// <summary>
/// 变更数据(保存到配置文件)
/// </summary>
/// <param name="lowCodeTableInfos"></param>
/// <returns></returns>
[Obsolete("请使用 ChangeByTableAsync 方法,该方法使用 TableName + DataBase 作为查询参数")]
public async Task ChangeAsync(List<LowCodeTableInfo> 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);
}
/// <summary>
/// 保存表元信息到配置文件
/// </summary>
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<string, CoreColumnMetaConfig>()
};
// 保存列配置
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);
}
}