351 lines
14 KiB
C#
351 lines
14 KiB
C#
using MiaoYu.Repository.Admin.Entities.LowCode;
|
||
using MiaoYu.Repository.ChatAI.Admin.Entities;
|
||
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;
|
||
|
||
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl;
|
||
|
||
/// <summary>
|
||
/// 服务 Low_Code_TableService
|
||
/// </summary>
|
||
public class LowCodeTableService : ApplicationService<IRepository<LowCodeTable>>
|
||
{
|
||
private readonly IRepository<LowCodeTableInfo> _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;
|
||
|
||
public LowCodeTableService(
|
||
IRepository<LowCodeTable> defaultRepository,
|
||
LowCodeTableInfoService lowCodeTableInfoService,
|
||
IRepository<LowCodeTableInfo> lowCodeTableInfoRepository,
|
||
CoreIDatabaseTableService databaseTableService,
|
||
CoreICodeGenerationService codeGenerationService,
|
||
CoreDataSourceManager dataSourceManager,
|
||
CoreITableMetaConfigService tableMetaConfigService,
|
||
CoreITableSchemaCache tableSchemaCache,
|
||
CorePathResolver pathResolver) : base(defaultRepository)
|
||
{
|
||
_lowCodeTableInfoService = lowCodeTableInfoService;
|
||
_lowCodeTableInfoRepository = lowCodeTableInfoRepository;
|
||
_databaseTableService = databaseTableService;
|
||
_codeGenerationService = codeGenerationService;
|
||
_dataSourceManager = dataSourceManager;
|
||
_tableMetaConfigService = tableMetaConfigService;
|
||
_tableSchemaCache = tableSchemaCache;
|
||
_pathResolver = pathResolver;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取列表数据(从 Core 层查询表结构并合并配置)
|
||
/// </summary>
|
||
/// <param name="pagingSearchInput"></param>
|
||
/// <returns></returns>
|
||
public async Task<PagingView> FindListAsync(PagingSearchInput<LowCodeTable> 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<Dictionary<string, object?>>();
|
||
foreach (var t in pagedTablesList)
|
||
{
|
||
var id = await GetOrCreateTableId(t.TableName, t.DataBase);
|
||
pagedTables.Add(new Dictionary<string, object?>
|
||
{
|
||
["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
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取或创建表的 ID(用于兼容性)
|
||
/// </summary>
|
||
private async Task<Guid> 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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据id数组删除
|
||
/// </summary>
|
||
/// <param name="ids"></param>
|
||
/// <returns></returns>
|
||
public async Task DeleteListAsync(List<Guid> 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)
|
||
{
|
||
// 配置文件可能不存在,忽略异常
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 同步表(支持多数据源)
|
||
/// </summary>
|
||
public async Task SynchronizationAsync()
|
||
{
|
||
// 刷新表结构缓存(从数据库重新加载表结构)
|
||
_tableSchemaCache.RefreshCache();
|
||
|
||
var allTables = _databaseTableService.GetAllTableInfos();
|
||
var oldAllTables = await _defaultRepository.ToListAllAsync();
|
||
|
||
var insertList = new List<LowCodeTable>();
|
||
var updateList = new List<LowCodeTable>();
|
||
var ids = new List<Guid>();
|
||
|
||
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);
|
||
}
|
||
|
||
// 注意:这里不需要清除缓存,因为缓存在开始时已经刷新过了
|
||
// 清除缓存会导致下次查询重新加载,没有必要
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将下划线命名转换为 PascalCase
|
||
/// </summary>
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 变更数据
|
||
/// </summary>
|
||
/// <param name="lowCodeTables"></param>
|
||
/// <returns></returns>
|
||
public async Task ChangeAsync(List<LowCodeTable> lowCodeTables)
|
||
{
|
||
_databaseTableService.ClearAllTablesByCache();
|
||
var oldLowCodeTables =
|
||
await _defaultRepository.ToListAsync(w => lowCodeTables.Select(w => w.Id).Contains(w.Id));
|
||
var updateList = new List<LowCodeTable>();
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询表单数据
|
||
/// </summary>
|
||
/// <param name="id"></param>
|
||
/// <returns></returns>
|
||
public async Task<Dictionary<string, object>> FindFormAsync(Guid id)
|
||
{
|
||
var res = new Dictionary<string, object>();
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据 lowCodeTable 填充路径(支持多数据源)
|
||
/// </summary>
|
||
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!);
|
||
|
||
if (string.IsNullOrEmpty(lowCodeTable.ClientInfoPath))
|
||
lowCodeTable.ClientInfoPath = _pathResolver.ResolvePath(provider.Config.ClientInfoPathTemplate, provider.Config, lowCodeTable.TableName!);
|
||
|
||
if (string.IsNullOrEmpty(lowCodeTable.ClientServicePath))
|
||
lowCodeTable.ClientServicePath = _pathResolver.ResolvePath(provider.Config.ClientServicePathTemplate, provider.Config, lowCodeTable.TableName!);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存数据
|
||
/// </summary>
|
||
/// <param name="form"></param>
|
||
/// <returns></returns>
|
||
[Transactional]
|
||
public virtual async Task SaveFormAsync(LowCodeTable form)
|
||
{
|
||
await _defaultRepository.InsertOrUpdateAsync(form);
|
||
}
|
||
} |