This commit is contained in:
zpc 2025-11-08 04:30:38 +08:00
parent 7cbea2a3ef
commit 9f5f837a4b
33 changed files with 2891 additions and 1972 deletions

View File

@ -11,6 +11,7 @@ namespace MiaoYu.Api.Admin;
[ImportStartupModule<
CoreQuartzStartup,
CoreRazorStartup,
CoreCodeGeneratorStartup,
CoreRedisStartup,
CoreIdentityStartup,
AdminRepositoryStartup,

View File

@ -1,83 +0,0 @@
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions;
/// <summary>
/// 数据源配置
/// </summary>
public class DataSourceConfig
{
/// <summary>
/// 数据库标识Admin, MiaoYuChat, LiveForum
/// </summary>
public string DatabaseKey { get; set; } = string.Empty;
/// <summary>
/// 显示名称
/// </summary>
public string DisplayName { get; set; } = string.Empty;
/// <summary>
/// 实体项目命名空间
/// </summary>
public string EntityNamespace { get; set; } = string.Empty;
/// <summary>
/// 实体类路径模板(支持占位符:{RootPath}, {Namespace}, {EntityName}, {EntityNamePlural}, {TableName}
/// </summary>
public string ModelPathTemplate { get; set; } = string.Empty;
/// <summary>
/// 服务层路径模板
/// </summary>
public string ServicePathTemplate { get; set; } = string.Empty;
/// <summary>
/// 控制器路径模板
/// </summary>
public string ControllerPathTemplate { get; set; } = string.Empty;
/// <summary>
/// 前端Index页面路径模板
/// </summary>
public string ClientIndexPathTemplate { get; set; } = string.Empty;
/// <summary>
/// 前端Info页面路径模板
/// </summary>
public string ClientInfoPathTemplate { get; set; } = string.Empty;
/// <summary>
/// 前端Service路径模板
/// </summary>
public string ClientServicePathTemplate { get; set; } = string.Empty;
/// <summary>
/// 代码生成模板目录
/// </summary>
public string TemplatePath { get; set; } = string.Empty;
/// <summary>
/// 实体类命名规则(保持原名 or 驼峰转换)
/// </summary>
public EntityNamingStrategy NamingStrategy { get; set; }
/// <summary>
/// 排序权重(数字越小越靠前)
/// </summary>
public int Order { get; set; }
/// <summary>
/// 是否启用实体类名前缀(用于避免多数据源同名表冲突)
/// </summary>
public bool EnableEntityPrefix { get; set; } = false;
/// <summary>
/// 实体类名前缀Chat、Forum
/// </summary>
public string EntityPrefix { get; set; } = string.Empty;
/// <summary>
/// 是否使用复数形式的路径(如 /Users/ vs /User/
/// </summary>
public bool UsesPluralPath { get; set; } = true;
}

View File

@ -1,23 +0,0 @@
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions;
/// <summary>
/// 数据源常量
/// </summary>
public static class DataSourceConstants
{
/// <summary>
/// 后台管理系统数据库
/// </summary>
public const string Admin = "Admin";
/// <summary>
/// 喵语AI聊天数据库
/// </summary>
public const string MiaoYuChat = "MiaoYuChat";
/// <summary>
/// 直播论坛数据库(预留)
/// </summary>
public const string LiveForum = "LiveForum";
}

View File

@ -1,18 +0,0 @@
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions;
/// <summary>
/// 实体命名策略
/// </summary>
public enum EntityNamingStrategy
{
/// <summary>
/// 保持数据库表名原样
/// </summary>
KeepOriginal = 0,
/// <summary>
/// 转换为驼峰命名(去除前缀下划线)
/// </summary>
ToPascalCase = 1
}

View File

@ -1,25 +0,0 @@
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions;
/// <summary>
/// 数据源提供者接口
/// </summary>
public interface IDataSourceProvider
{
/// <summary>
/// 数据源配置
/// </summary>
DataSourceConfig Config { get; }
/// <summary>
/// 获取该数据源的所有表信息
/// </summary>
/// <returns>表信息列表</returns>
List<DbTableInfo> GetTables();
/// <summary>
/// 获取DbContext用于获取FreeSql实例
/// </summary>
/// <returns>数据库上下文</returns>
DbContext GetDbContext();
}

View File

@ -1,44 +0,0 @@
using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions;
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core;
/// <summary>
/// 数据源扩展方法
/// </summary>
public static class DataSourceExtensions
{
/// <summary>
/// 从 Schema 中提取数据库标识
/// </summary>
/// <param name="schema">Schema字符串</param>
/// <returns>数据库标识</returns>
public static string ExtractDatabaseKey(this string schema)
{
if (string.IsNullOrWhiteSpace(schema))
return DataSourceConstants.Admin;
if (schema.Contains("."))
{
var parts = schema.Split('.');
return parts.Length > 1 ? parts[1] : DataSourceConstants.Admin;
}
return DataSourceConstants.Admin;
}
/// <summary>
/// 清理 Schema移除数据库标识
/// </summary>
/// <param name="schema">Schema字符串</param>
/// <returns>清理后的Schema</returns>
public static string CleanSchema(this string schema)
{
if (string.IsNullOrWhiteSpace(schema))
return schema;
return schema.Contains(".")
? schema.Split('.')[0]
: schema;
}
}

View File

@ -1,70 +0,0 @@
using HZY.Framework.DependencyInjection.Attributes;
using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions;
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core;
/// <summary>
/// 数据源管理器
/// </summary>
[Component]
public class DataSourceManager : IScopedDependency
{
private readonly IEnumerable<IDataSourceProvider> _providers;
/// <summary>
/// 构造函数通过依赖注入自动收集所有IDataSourceProvider实现
/// </summary>
/// <param name="providers">所有数据源提供者</param>
public DataSourceManager(IEnumerable<IDataSourceProvider> providers)
{
_providers = providers.OrderBy(p => p.Config.Order);
}
/// <summary>
/// 获取所有数据源提供者
/// </summary>
/// <returns>数据源提供者集合</returns>
public IEnumerable<IDataSourceProvider> GetAllProviders() => _providers;
/// <summary>
/// 根据数据库标识获取数据源提供者
/// </summary>
/// <param name="databaseKey">数据库标识Admin, MiaoYuChat</param>
/// <returns>数据源提供者如果未找到返回null</returns>
public IDataSourceProvider? GetProvider(string databaseKey)
{
if (string.IsNullOrWhiteSpace(databaseKey))
return null;
return _providers.FirstOrDefault(p =>
p.Config.DatabaseKey.Equals(databaseKey, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// 获取所有数据源的表信息
/// </summary>
/// <returns>所有表信息列表</returns>
public List<DbTableInfo> GetAllTables()
{
var allTables = new List<DbTableInfo>();
foreach (var provider in _providers)
{
try
{
var tables = provider.GetTables();
if (tables != null && tables.Count > 0)
{
allTables.AddRange(tables);
}
}
catch (Exception ex)
{
LogUtil.Log.Warning($"获取数据源 {provider.Config.DatabaseKey} 的表信息失败: {ex.Message}");
}
}
return allTables;
}
}

View File

@ -1,67 +0,0 @@
using HZY.Framework.DependencyInjection.Attributes;
using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions;
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core;
/// <summary>
/// 路径解析器
/// </summary>
[Component]
public class PathResolver : IScopedDependency
{
private readonly IWebHostEnvironment _environment;
public PathResolver(IWebHostEnvironment environment)
{
_environment = environment;
}
/// <summary>
/// 解析路径模板
/// </summary>
/// <param name="template">路径模板(支持占位符)</param>
/// <param name="config">数据源配置</param>
/// <param name="tableName">表名</param>
/// <returns>解析后的完整路径</returns>
public string ResolvePath(string template, DataSourceConfig config, string tableName)
{
if (string.IsNullOrWhiteSpace(template))
return string.Empty;
var rootPath = _environment.ContentRootPath
.Replace("\\" + _environment.ApplicationName, "");
var entityName = GetEntityName(tableName, config);
var entityNamePlural = config.UsesPluralPath ? entityName + "s" : entityName;
return template
.Replace("{RootPath}", rootPath)
.Replace("{AppPath}", _environment.ContentRootPath)
.Replace("{Namespace}", config.EntityNamespace)
.Replace("{EntityName}", entityName)
.Replace("{EntityNamePlural}", entityNamePlural)
.Replace("{TableName}", tableName);
}
/// <summary>
/// 根据命名策略和配置获取实体名
/// </summary>
/// <param name="tableName">表名</param>
/// <param name="config">数据源配置</param>
/// <returns>实体名</returns>
public string GetEntityName(string tableName, DataSourceConfig config)
{
var baseName = config.NamingStrategy == EntityNamingStrategy.ToPascalCase
? tableName.ToLineConvertHump()
: tableName;
// 应用前缀(如果启用)
if (config.EnableEntityPrefix && !string.IsNullOrWhiteSpace(config.EntityPrefix))
{
return config.EntityPrefix + baseName;
}
return baseName;
}
}

View File

@ -1,124 +0,0 @@
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode;
/// <summary>
/// 代码生成服务
/// </summary>
public interface ICodeGenerationService : IScopedDependency
{
/// <summary>
/// 生成上下文集合
/// </summary>
/// <returns></returns>
PagingView GetGenContextDtos(int page, int size, GenFormDto search);
/// <summary>
/// 获取表字段集合
/// </summary>
/// <param name="tableName"></param>
/// <param name="databaseKey">数据库标识(可选)</param>
/// <returns></returns>
GenDbTableDto GetGenContextDtoByTableName(string tableName, string? databaseKey = null);
/// <summary>
/// 根据 lowCodeTable 填充路径
/// </summary>
/// <param name="lowCodeTable"></param>
/// <returns></returns>
LowCodeTable FillPathByLowCodeTable(LowCodeTable lowCodeTable);
/// <summary>
/// 获取代码生成上下文
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
GenDbTableDto GetGenContextDto(GenFormDto genFormDto);
/// <summary>
/// 生成 model
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task<string> GenModelAsync(GenFormDto genFormDto);
/// <summary>
/// 生成 service
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task<string> GenServiceAsync(GenFormDto genFormDto);
/// <summary>
/// 生成 controller
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task<string> GenControllerAsync(GenFormDto genFormDto);
/// <summary>
/// 生成 serviceJs
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task<string> GenServiceJsAsync(GenFormDto genFormDto);
/// <summary>
/// 生成 Index
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task<string> GenIndexAsync(GenFormDto genFormDto);
/// <summary>
/// 生成 Info
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task<string> GenInfoAsync(GenFormDto genFormDto);
/// <summary>
/// 获取代码
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task<string> GetCodeByTypeAndTableNameAsync(GenFormDto genFormDto);
/// <summary>
/// 下载
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task<(byte[] codeBytes, string contentType, string fileName)> DownloadAsync(GenFormDto genFormDto);
/// <summary>
/// 根据类型下载类型下所有的代码
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task<(byte[] codeBytes, string contentType, string fileName)> DownloadAllAsync(GenFormDto genFormDto);
/// <summary>
/// 创建所有代码文件
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task<bool> CreateAllCodeFilesAsync(GenFormDto genFormDto);
/// <summary>
/// 创建数据字典文件 excel
/// </summary>
/// <returns></returns>
(byte[] excel, string dataBase) CreateDataDictionary();
/// <summary>
/// 代码生成自动导入项目
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
Task AutoImprotProjectAsync(GenFormDto genFormDto);
/// <summary>
/// 获取所有数据库列表
/// </summary>
/// <returns></returns>
List<DataSourceDto> GetAllDataSources();
}

View File

@ -1,700 +0,0 @@
using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions;
using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core;
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl;
/// <summary>
/// 代码生成服务
/// </summary>
public class CodeGenerationService : ICodeGenerationService
{
private readonly string _webRootPath;
private readonly string templateRootPath = "/wwwroot/code_generation/template/";
private readonly string templateRootPath_v4 = "/wwwroot/code_generation/templatev4/";
private readonly string codesRootPath = "/code_generation/codes";
private readonly string zipRootPath = "/code_generation/zip";
//domain 模板文件
private readonly string templateModel = "tempModel.cshtml";
private readonly string templateService = "tempService.cshtml";
private readonly string templateController = "tempController.cshtml";
private readonly string templateServiceJs = "tempClientService.cshtml";
private readonly string templateIndex = "tempClientIndex.cshtml";
private readonly string templateInfo = "tempClientInfo.cshtml";
// 客户端名称
private readonly string projectClientName = "admin-client";
private readonly IDatabaseTableService _databaseTableService;
private readonly IRazorViewRender _razorViewRender;
private readonly DataSourceManager _dataSourceManager;
private readonly PathResolver _pathResolver;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="databaseTableService">数据库表服务</param>
/// <param name="razorViewRender">Razor视图渲染器</param>
/// <param name="webHostEnvironment">Web宿主环境</param>
/// <param name="dataSourceManager">数据源管理器</param>
/// <param name="pathResolver">路径解析器</param>
public CodeGenerationService(IDatabaseTableService databaseTableService,
IRazorViewRender razorViewRender,
IWebHostEnvironment webHostEnvironment,
DataSourceManager dataSourceManager,
PathResolver pathResolver)
{
_databaseTableService = databaseTableService;
_razorViewRender = razorViewRender;
_webRootPath = webHostEnvironment.WebRootPath;
_dataSourceManager = dataSourceManager;
_pathResolver = pathResolver;
}
/// <summary>
/// 生成上下文集合
/// </summary>
/// <returns></returns>
public PagingView GetGenContextDtos(int page, int size, GenFormDto search)
{
var PagingView = new PagingView(page, size);
var result = new List<Dictionary<string, object>>();
var query = _databaseTableService.GetAllTablesByCache()
.WhereIf(!string.IsNullOrWhiteSpace(search.TableName), w => w.TableName.Contains(search.TableName))
.WhereIf(!string.IsNullOrWhiteSpace(search.DataBase), w => w.DataBase == search.DataBase);
var tables = query
.Skip((page - 1) * size)
.Take(size)
.ToList();
foreach (var item in tables)
{
var dic = new Dictionary<string, object>();
dic.Add(nameof(item.TableName), item.TableName);
dic.Add(nameof(item.Remark), item.Remark);
dic.Add(nameof(item.DataBase), item.DataBase);
result.Add(dic);
}
PagingView.Total = query.LongCount();
PagingView.Page = page;
PagingView.Size = size;
PagingView.DataSource = result;
PagingView.PageCount = PagingView.Total / size;
return PagingView;
}
/// <summary>
/// 获取所有表集合信息
/// </summary>
/// <param name="tableName">表名</param>
/// <param name="databaseKey">数据库标识(可选)</param>
/// <returns></returns>
public GenDbTableDto GetGenContextDtoByTableName(string tableName, string? databaseKey = null)
{
var query = _databaseTableService.GetAllTables().AsEnumerable();
// 如果指定了数据库,则精确匹配
if (!string.IsNullOrWhiteSpace(databaseKey))
{
query = query.Where(w => w.TableName == tableName && w.DataBase == databaseKey);
}
else
{
query = query.Where(w => w.TableName == tableName);
}
var genDbTableDto = query.FirstOrDefault();
if (genDbTableDto != null)
{
FillPathByLowCodeTable(genDbTableDto);
}
return genDbTableDto;
}
/// <summary>
/// 根据 lowCodeTable 填充路径(支持多数据源)
/// </summary>
/// <param name="lowCodeTable">低代码表配置</param>
/// <returns>填充路径后的低代码表配置</returns>
public LowCodeTable FillPathByLowCodeTable(LowCodeTable lowCodeTable)
{
var provider = _dataSourceManager.GetProvider(lowCodeTable.DataBase ?? DataSourceConstants.Admin);
if (provider == null)
{
LogUtil.Log.Warning($"未找到数据源 {lowCodeTable.DataBase}使用默认Admin配置");
provider = _dataSourceManager.GetProvider(DataSourceConstants.Admin);
}
var config = provider!.Config;
if (string.IsNullOrWhiteSpace(lowCodeTable.ModelPath))
{
lowCodeTable.ModelPath = _pathResolver.ResolvePath(
config.ModelPathTemplate, config, lowCodeTable.TableName);
}
if (string.IsNullOrWhiteSpace(lowCodeTable.ServicePath))
{
lowCodeTable.ServicePath = _pathResolver.ResolvePath(
config.ServicePathTemplate, config, lowCodeTable.TableName);
}
if (string.IsNullOrWhiteSpace(lowCodeTable.ControllerPath))
{
lowCodeTable.ControllerPath = _pathResolver.ResolvePath(
config.ControllerPathTemplate, config, lowCodeTable.TableName);
}
if (string.IsNullOrWhiteSpace(lowCodeTable.ClientIndexPath))
{
lowCodeTable.ClientIndexPath = _pathResolver.ResolvePath(
config.ClientIndexPathTemplate, config, lowCodeTable.TableName);
}
if (string.IsNullOrWhiteSpace(lowCodeTable.ClientInfoPath))
{
lowCodeTable.ClientInfoPath = _pathResolver.ResolvePath(
config.ClientInfoPathTemplate, config, lowCodeTable.TableName);
}
if (string.IsNullOrWhiteSpace(lowCodeTable.ClientServicePath))
{
lowCodeTable.ClientServicePath = _pathResolver.ResolvePath(
config.ClientServicePathTemplate, config, lowCodeTable.TableName);
}
return lowCodeTable;
}
/// <summary>
/// 获取代码生成上下文
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public GenDbTableDto GetGenContextDto(GenFormDto genFormDto)
{
var tableName = genFormDto.TableName;
var tableInfo = GetGenContextDtoByTableName(tableName, genFormDto.DataBase);
if (tableInfo == null) return null;
tableInfo.Namespace = Tools.GetNamespacePrefix<CodeGenerationService>();
return tableInfo;
}
/// <summary>
/// 生成model支持多数据源
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task<string> GenModelAsync(GenFormDto genFormDto)
{
var context = GetGenContextDto(genFormDto);
var provider = _dataSourceManager.GetProvider(context.DataBase ?? DataSourceConstants.Admin);
var templatePath = provider?.Config.TemplatePath ?? templateRootPath;
return ClearSymbol(await _razorViewRender.RenderAsync(templatePath + templateModel, context));
}
/// <summary>
/// 生成service支持多数据源
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task<string> GenServiceAsync(GenFormDto genFormDto)
{
var context = GetGenContextDto(genFormDto);
var provider = _dataSourceManager.GetProvider(context.DataBase ?? DataSourceConstants.Admin);
var templatePath = provider?.Config.TemplatePath ?? templateRootPath;
return ClearSymbol(await _razorViewRender.RenderAsync(templatePath + templateService, context));
}
/// <summary>
/// 生成controller支持多数据源
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task<string> GenControllerAsync(GenFormDto genFormDto)
{
var context = GetGenContextDto(genFormDto);
var provider = _dataSourceManager.GetProvider(context.DataBase ?? DataSourceConstants.Admin);
var templatePath = provider?.Config.TemplatePath ?? templateRootPath;
return ClearSymbol(await _razorViewRender.RenderAsync(templatePath + templateController, context));
}
/// <summary>
/// 生成service js支持多数据源
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task<string> GenServiceJsAsync(GenFormDto genFormDto)
{
var context = GetGenContextDto(genFormDto);
var provider = _dataSourceManager.GetProvider(context.DataBase ?? DataSourceConstants.Admin);
var templatePath = provider?.Config.TemplatePath ?? templateRootPath;
return ClearSymbol(await _razorViewRender.RenderAsync(templatePath + templateServiceJs, context));
}
/// <summary>
/// 生成 index vue支持多数据源
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task<string> GenIndexAsync(GenFormDto genFormDto)
{
var context = GetGenContextDto(genFormDto);
var provider = _dataSourceManager.GetProvider(context.DataBase ?? DataSourceConstants.Admin);
var templatePath = provider?.Config.TemplatePath ?? templateRootPath;
return ClearSymbol(await _razorViewRender.RenderAsync(templatePath + templateIndex, context));
}
/// <summary>
/// 生成 info vue支持多数据源
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task<string> GenInfoAsync(GenFormDto genFormDto)
{
var context = GetGenContextDto(genFormDto);
var provider = _dataSourceManager.GetProvider(context.DataBase ?? DataSourceConstants.Admin);
var templatePath = provider?.Config.TemplatePath ?? templateRootPath;
return ClearSymbol(await _razorViewRender.RenderAsync(templatePath + templateInfo, context));
}
/// <summary>
/// 获取代码
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task<string> GetCodeByTypeAndTableNameAsync(GenFormDto genFormDto)
{
return genFormDto.Type switch
{
"MiaoYu.Models" => await GenModelAsync(genFormDto),
//"MiaoYu.Repository.DbSet" => await this.CreateRepositoryDbSetAsync(),
"MiaoYu.Services.Admin" => await GenServiceAsync(genFormDto),
"MiaoYu.Controllers.Admin" => await GenControllerAsync(genFormDto),
"Client.Index" => await GenIndexAsync(genFormDto),
"Client.Info" => await GenInfoAsync(genFormDto),
"Client.Service" => await GenServiceJsAsync(genFormDto),
_ => string.Empty
};
}
/// <summary>
/// 创建所有代码文件
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task<bool> CreateAllCodeFilesAsync(GenFormDto genFormDto)
{
var tables = _databaseTableService.GetAllTablesByCache();
foreach (var item in tables)
{
genFormDto.TableName = item.TableName;
genFormDto.DataBase = item.DataBase;
await CreateCodeFilesAsync(genFormDto);
await Task.Delay(25);
}
return true;
}
/// <summary>
/// 获取下载代码信息
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task<(byte[] codeBytes, string contentType, string fileName)> DownloadAsync(GenFormDto genFormDto)
{
var fileName = FindCodeFileClassName(genFormDto);
var contentType = Tools.GetFileContentType[".cs"];
if (fileName == "Index.vue" || fileName == "Info.vue")
{
contentType = Tools.GetFileContentType[".txt"];
}
return (Encoding.UTF8.GetBytes(await GetCodeByTypeAndTableNameAsync(genFormDto)), contentType, fileName);
}
/// <summary>
/// 根据类型下载所有代码
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task<(byte[] codeBytes, string contentType, string fileName)> DownloadAllAsync(GenFormDto genFormDto)
{
var isViews = genFormDto.Type == "Client.Index" || genFormDto.Type == "Client.Info";
var success = await CreateAllCodeFilesAsync(genFormDto);
if (!success) LogUtil.Log.Warning("无法下载,代码创建失败!");
string path;
string zipPath;
if (isViews)
{
path = $"{_webRootPath}{codesRootPath}/pages";
zipPath = $"{_webRootPath}{zipRootPath}";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
if (!Directory.Exists(zipPath))
{
Directory.CreateDirectory(zipPath);
}
zipPath += "/pages.zip";
}
else
{
path = $"{_webRootPath}{codesRootPath}/{genFormDto.Type}";
zipPath = $"{_webRootPath}{zipRootPath}";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
if (!Directory.Exists(zipPath))
{
Directory.CreateDirectory(zipPath);
}
zipPath += $"/{genFormDto.Type}.zip";
}
//开始压缩
var zip = new Zip(path, zipPath);
var bytes = await File.ReadAllBytesAsync(zipPath);
//删除文件
if (File.Exists(zipPath)) File.Delete(zipPath);
if (Directory.Exists(path)) Directory.Delete(path, true);
return (bytes, Tools.GetFileContentType[".zip"], $"{(isViews ? "pages" : genFormDto.Type)}.zip");
}
/// <summary>
/// 创建数据库字典文件
/// </summary>
/// <returns></returns>
public (byte[] excel, string dataBase) CreateDataDictionary()
{
var tables = _databaseTableService.GetAllTablesByCache();
var workbook = new XSSFWorkbook();
var dataBaseName = _databaseTableService.GetDatabaseName();
foreach (var item in tables)
{
var sheet = workbook.CreateSheet(item.TableName + (string.IsNullOrWhiteSpace(item.Remark) ? "" : "_" + item.Remark));
var i = 0;
#region
var rowTitle = sheet.CreateRow(i);
rowTitle.CreateCell(0).SetCellValue("表空间");
sheet.SetColumnWidth(0, 20 * 256);
rowTitle.CreateCell(1).SetCellValue("表名");
sheet.SetColumnWidth(1, 20 * 256);
rowTitle.CreateCell(2).SetCellValue("表描述");
sheet.SetColumnWidth(2, 20 * 256);
rowTitle.CreateCell(3).SetCellValue("字段");
sheet.SetColumnWidth(3, 20 * 256);
rowTitle.CreateCell(4).SetCellValue("字段描述");
sheet.SetColumnWidth(4, 20 * 256);
rowTitle.CreateCell(5).SetCellValue("是否主键");
sheet.SetColumnWidth(5, 20 * 256);
rowTitle.CreateCell(6).SetCellValue("是否自增");
sheet.SetColumnWidth(6, 20 * 256);
rowTitle.CreateCell(7).SetCellValue("可否为 Null");
sheet.SetColumnWidth(7, 20 * 256);
rowTitle.CreateCell(8).SetCellValue("数据库类型");
sheet.SetColumnWidth(8, 20 * 256);
rowTitle.CreateCell(9).SetCellValue("C#类型");
sheet.SetColumnWidth(9, 20 * 256);
rowTitle.CreateCell(10).SetCellValue("数据长度");
sheet.SetColumnWidth(10, 20 * 256);
#endregion
//组装数据
foreach (var tableInfo in item.TableInfos)
{
i++;
var index = item.TableInfos.IndexOf(tableInfo);
var row = sheet.CreateRow(i);
//row.CreateCell(0).SetCellValue(item.Schema);
row.CreateCell(1).SetCellValue(item.TableName);
row.CreateCell(2).SetCellValue(item.Remark);
row.CreateCell(3).SetCellValue(tableInfo.ColumnName);
row.CreateCell(4).SetCellValue(tableInfo.Describe);
row.CreateCell(5).SetCellValue(tableInfo.IsPrimary ? "是" : "否");
row.CreateCell(6).SetCellValue(tableInfo.IsIdentity ? "是" : "否");
row.CreateCell(7).SetCellValue(tableInfo.IsNullable ? "是" : "否");
row.CreateCell(8).SetCellValue(tableInfo.DatabaseColumnType);
row.CreateCell(9).SetCellValue(tableInfo.CsType);
row.CreateCell(10).SetCellValue(tableInfo.MaxLength ?? 0);
}
}
//填充byte
using var ms = new MemoryStream();
workbook.Write(ms);
return (ms.ToArray(), dataBaseName);
}
/// <summary>
/// 自动导入文件到项目
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
public async Task AutoImprotProjectAsync(GenFormDto genFormDto)
{
var context = GetGenContextDto(genFormDto);
if (context == null)
{
MessageBox.Show("找不到此数据表!");
return;
}
//获取表路径信息
var tables = _databaseTableService.GetAllTablesByCache();
var tableInfo = tables.FirstOrDefault(w =>
w.TableName == genFormDto.TableName &&
w.DataBase == genFormDto.DataBase);
var fileTyps = Enum.GetValues<FileTypeEnum>();
foreach (var fileType in fileTyps)
{
var (filePath, oldName, replaceName) = GetFileAbsolutelyPath(
genFormDto.TableName, fileType, genFormDto.DataBase);
await SaveToFileAsync(genFormDto.TableName, fileType, filePath, oldName, replaceName);
}
}
/// <summary>
/// 获取所有数据库列表
/// </summary>
/// <returns></returns>
public List<DataSourceDto> GetAllDataSources()
{
var providers = _dataSourceManager.GetAllProviders()
.OrderBy(p => p.Config.Order)
.ToList();
return providers.Select(p => new DataSourceDto
{
Key = p.Config.DatabaseKey,
DisplayName = p.Config.DisplayName
}).ToList();
}
#region
/// <summary>
/// 清除多余符号
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
private string ClearSymbol(StringBuilder code)
{
return code
.ToString()
.Replace("<pre>", "")
.Replace("</pre>", "")
.Trim();
}
/// <summary>
/// 创建代码文件
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
private async Task<string> CreateCodeFilesAsync(GenFormDto genFormDto)
{
var path = $"{_webRootPath}{codesRootPath}";
if (genFormDto.Type == "Client.Index" || genFormDto.Type == "Client.Info")
{
path += $"/pages"; //
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += $"/{genFormDto.TableName}";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
//Index
var codeString = await GenIndexAsync(genFormDto);
await File.WriteAllTextAsync($"{path}/Index.vue", codeString, Encoding.UTF8);
//Info
codeString = await GenInfoAsync(genFormDto);
await File.WriteAllTextAsync($"{path}/Info.vue", codeString, Encoding.UTF8);
return path;
}
//
path = $"{_webRootPath}{codesRootPath}/{genFormDto.Type}";
var className = FindCodeFileClassName(genFormDto);
var code = await GetCodeByTypeAndTableNameAsync(genFormDto);
//
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
await File.WriteAllTextAsync($"{path}/{className}", code, Encoding.UTF8);
return path;
}
/// <summary>
/// 获取代码文件名称
/// </summary>
/// <param name="genFormDto"></param>
/// <returns></returns>
private string FindCodeFileClassName(GenFormDto genFormDto)
{
var provider = _dataSourceManager.GetProvider(genFormDto.DataBase ?? DataSourceConstants.Admin);
var entityName = _pathResolver.GetEntityName(genFormDto.TableName, provider.Config);
return genFormDto.Type switch
{
"MiaoYu.Models" => $"{entityName}.cs",
"MiaoYu.Services.Admin" => $"{entityName}Service.cs",
"MiaoYu.Controllers.Admin" => $"{entityName}Controller.cs",
"Client.Index" => $"Index.vue",
"Client.Info" => $"Info.vue",
"Client.Service" => $"{entityName}Service.ts",
_ => string.Empty
};
}
/// <summary>
/// 获取要生成文件的绝对路径
/// </summary>
/// <param name="tableName"></param>
/// <param name="type"></param>
/// <param name="databaseKey">数据库标识(可选)</param>
/// <returns></returns>
private (string, string, string) GetFileAbsolutelyPath(string tableName, FileTypeEnum type, string? databaseKey = null)
{
var replaceName = string.Empty;
var oldFileName = string.Empty;
var dto = new GenFormDto() { TableName = tableName, Type = GetEnumDescription(type), DataBase = databaseKey };
var provider = _dataSourceManager.GetProvider(databaseKey ?? DataSourceConstants.Admin);
var entityName = _pathResolver.GetEntityName(tableName, provider.Config);
var entityNameWithPath = provider.Config.UsesPluralPath ? entityName + "s" : entityName;
var fileName = FindCodeFileClassName(dto);
var path = string.Empty;
//获取表路径信息
var tableInfo = GetGenContextDtoByTableName(tableName, databaseKey);
switch (type)
{
case FileTypeEnum.Model:
path = provider.Config.UsesPluralPath
? $"{tableInfo.ModelPath}/{entityNameWithPath}"
: tableInfo.ModelPath;
break;
case FileTypeEnum.Service:
path = provider.Config.UsesPluralPath
? $"{tableInfo.ServicePath}/{entityNameWithPath}"
: tableInfo.ServicePath;
break;
case FileTypeEnum.Controller:
path = provider.Config.UsesPluralPath
? $"{tableInfo.ControllerPath}/{entityNameWithPath}"
: tableInfo.ControllerPath;
break;
case FileTypeEnum.ClientIndex:
path = tableInfo.ClientIndexPath + $"/{tableName}s";
break;
case FileTypeEnum.ClientInfo:
path = tableInfo.ClientInfoPath + $"/{tableName}s";
break;
case FileTypeEnum.ClientService:
path = tableInfo.ClientServicePath + $"/{tableName}s";
break;
}
var fileDirPath = Path.Combine(path);
if (!Directory.Exists(fileDirPath))
{
Directory.CreateDirectory(fileDirPath);
}
// 组合成完整路劲
var filePath = $"{fileDirPath}/{fileName}";
// 判断是否覆盖文件
if (!(tableInfo.IsCover ?? false))
{
// 如果文件已存在 加尾缀 重新创建文件夹
if (File.Exists(filePath))
{
oldFileName = FindCodeFileClassName(dto);
replaceName = $"{oldFileName}{DateTime.Now.ToString("yyyyMMddHHmmss")}";
filePath = $"{fileDirPath}/{replaceName}";
}
}
return (filePath, oldFileName, replaceName);
}
/// <summary>
/// 保存到文件
/// </summary>
/// <param name="tableName"></param>
/// <param name="type"></param>
/// <param name="filePath"></param>
/// <param name="oldName"></param>
/// <param name="replaceName"></param>
/// <returns></returns>
private async Task SaveToFileAsync(string tableName, FileTypeEnum type, string filePath, string oldName, string replaceName)
{
var dto = new GenFormDto() { TableName = tableName, Type = GetEnumDescription(type) };
var codeString = await GetCodeByTypeAndTableNameAsync(dto);
if (!string.IsNullOrWhiteSpace(replaceName) && !string.IsNullOrWhiteSpace(oldName))
{
if (type == FileTypeEnum.Model || type == FileTypeEnum.Service || type == FileTypeEnum.Controller)
{
codeString = codeString.Replace(oldName, replaceName);
}
}
await Task.Delay(500);
await File.WriteAllTextAsync(filePath, codeString, Encoding.UTF8);
}
/// <summary>
/// 获取枚举上的描述特性
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private string GetEnumDescription(FileTypeEnum type)
{
return type.GetType().GetField(Enum.GetName(type)).GetCustomAttribute<DescriptionAttribute>().Description;
}
#endregion
}
public enum FileTypeEnum
{
[Description("MiaoYu.Models")]
Model,
[Description("MiaoYu.Services.Admin")]
Service,
[Description("MiaoYu.Controllers.Admin")]
Controller,
[Description("Client.Index")]
ClientIndex,
[Description("Client.Info")]
ClientInfo,
[Description("Client.Service")]
ClientService
}

View File

@ -1,98 +0,0 @@
using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core;
using MiaoYu.Repository.ChatAI.Admin.Entities;
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl;
/// <summary>
/// 数据库表服务
/// </summary>
public class DatabaseTableService : IDatabaseTableService
{
private readonly string TableInfoKey = "TableInfo:GenDbTableDto";
private readonly int CacheTime = 12;
private readonly IMemoryCache _memoryCache;
private readonly IRepository<LowCodeTable> _lowCodeTableRepository;
private readonly IRepository<LowCodeTableInfo> _lowCodeTableInfoRepository;
private readonly IRepository<T_Image_Config> _imageConfig;
private readonly DataSourceManager _dataSourceManager;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="memoryCache">内存缓存</param>
/// <param name="lowCodeTableRepository">低代码表仓储</param>
/// <param name="lowCodeTableInfoRepository">低代码表字段仓储</param>
/// <param name="imageConfig">图片配置仓储</param>
/// <param name="dataSourceManager">数据源管理器</param>
public DatabaseTableService(
IMemoryCache memoryCache,
IRepository<LowCodeTable> lowCodeTableRepository,
IRepository<LowCodeTableInfo> lowCodeTableInfoRepository,
IRepository<T_Image_Config> imageConfig,
DataSourceManager dataSourceManager
)
{
_memoryCache = memoryCache;
_lowCodeTableRepository = lowCodeTableRepository;
_lowCodeTableInfoRepository = lowCodeTableInfoRepository;
_imageConfig = imageConfig;
_dataSourceManager = dataSourceManager;
}
/// <summary>
/// 获取所有的表 包含表下面的列(支持多数据源)
/// </summary>
/// <returns>所有表信息列表</returns>
public virtual List<DbTableInfo> GetAllTableInfos()
{
return _dataSourceManager.GetAllTables();
}
/// <summary>
/// 获取所有的表 包含表下面的列
/// </summary>
/// <returns></returns>
public virtual List<GenDbTableDto> GetAllTables()
{
var tables = _lowCodeTableRepository.ToListAll();
var tableColumns = _lowCodeTableInfoRepository.ToListAll();
var result = new List<GenDbTableDto>();
foreach (var item in tables)
{
var table = item.MapTo<LowCodeTable, GenDbTableDto>();
table.TableInfos = tableColumns.Where(w => w.Low_Code_TableId == item.Id).ToList();
result.Add(table);
}
_memoryCache.Set(TableInfoKey, result, DateTime.Now.AddHours(CacheTime));
return result;
}
/// <summary>
/// 获取表信息根据缓存
/// </summary>
/// <returns></returns>
public List<GenDbTableDto> GetAllTablesByCache() => _memoryCache.Get<List<GenDbTableDto>>(TableInfoKey) ?? GetAllTables();
/// <summary>
/// 清空所有表缓存信息
/// </summary>
/// <returns></returns>
public bool ClearAllTablesByCache()
{
_memoryCache.Remove(TableInfoKey);
return true;
}
/// <summary>
/// 获取数据库名称
/// </summary>
/// <returns></returns>
public string? GetDatabaseName() => _lowCodeTableRepository.GetContext()?.Database.GetDbConnection().Database;
}

View File

@ -1,4 +1,9 @@
using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core;
using MiaoYu.Repository.Admin.Entities.LowCode;
using CoreIDatabaseTableService = MiaoYu.Core.CodeGenerator.Services.IDatabaseTableService;
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;
@ -7,62 +12,102 @@ namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl;
/// </summary>
public class LowCodeTableInfoService : ApplicationService<IRepository<LowCodeTableInfo>>
{
private readonly IDatabaseTableService _databaseTableService;
private readonly CoreIDatabaseTableService _databaseTableService;
private readonly IRepository<LowCodeTable> _lowCodeTableRepository;
private readonly CoreITableMetaConfigService _tableMetaConfigService;
public LowCodeTableInfoService(
IDatabaseTableService databaseTableService,
CoreIDatabaseTableService databaseTableService,
IRepository<LowCodeTableInfo> defaultRepository,
IRepository<LowCodeTable> lowCodeTableRepository) : base(defaultRepository)
IRepository<LowCodeTable> lowCodeTableRepository,
CoreITableMetaConfigService tableMetaConfigService) : base(defaultRepository)
{
_databaseTableService = databaseTableService;
_lowCodeTableRepository = lowCodeTableRepository;
_tableMetaConfigService = tableMetaConfigService;
}
/// <summary>
/// 获取列表数据
/// 获取列表数据(从 Core 层查询表结构并合并配置)
/// </summary>
/// <param name="pagingSearchInput"></param>
/// <returns></returns>
public async Task<PagingView> FindListAsync(PagingSearchInput<LowCodeTableInfo> pagingSearchInput)
{
var query = _defaultRepository.SelectNoTracking
.WhereIf(pagingSearchInput.Search.Low_Code_TableId != Guid.Empty, w => w.Low_Code_TableId == pagingSearchInput.Search.Low_Code_TableId)
.WhereIf(!string.IsNullOrWhiteSpace(pagingSearchInput.Search.ColumnName), w => w.ColumnName.Contains(pagingSearchInput.Search.ColumnName))
.WhereIf(!string.IsNullOrWhiteSpace(pagingSearchInput.Search.Describe), w => w.Describe.Contains(pagingSearchInput.Search.Describe))
.OrderBy(w => w.Position)
.ThenByDescending(w => w.CreationTime)
.Select(w => new
{
w.Id,
w.IsPrimary,
w.IsIdentity,
w.IsNullable,
w.Position,
w.ColumnName,
w.Describe,
w.DatabaseColumnType,
w.CsType,
w.CsField,
w.DisplayName,
w.Low_Code_TableId,
w.LastModificationTime,
w.CreationTime,
IsTableColumnShow = w.IsTableColumnShow ?? true,
w.IsTableSelect,
w.IsImageId,
OrderById = w.OrderById ?? 10
})
;
var result = await _defaultRepository.AsPagingViewAsync(query, pagingSearchInput);
//覆盖值
result
.FormatValue(query, w => w.CreationTime, (oldValue) => oldValue.ToString("yyyy-MM-dd"))
.FormatValue(query, w => w.LastModificationTime, (oldValue) => oldValue?.ToString("yyyy-MM-dd"))
;
return result;
// ✅ 从 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,
["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>
@ -89,10 +134,7 @@ public class LowCodeTableInfoService : ApplicationService<IRepository<LowCodeTab
// 修复:同时匹配表名和数据库标识
var tableInfo = allTables.Find(w =>
{
var dbKey = w.Schema.ExtractDatabaseKey();
return w.Name == table.TableName && dbKey == table.DataBase;
});
w.Name == table.TableName && w.DataBase == table.DataBase);
//查询出当前表所有的字段
var tableColumns = await _defaultRepository.ToListAsync(w => w.Low_Code_TableId == table.Id);
@ -115,8 +157,8 @@ public class LowCodeTableInfoService : ApplicationService<IRepository<LowCodeTab
model.Position = item.Position;
model.Low_Code_TableId = table.Id;
model.ColumnName = item.Name;
model.DatabaseColumnType = item.DbTypeTextFull;
model.CsType = item.CsType.Name;
model.DatabaseColumnType = item.DbType;
model.CsType = item.CsType;
model.CsField = item.Name;
model.MaxLength = item.MaxLength;
//model.IsImageId = item.IsImageId;
@ -143,11 +185,11 @@ public class LowCodeTableInfoService : ApplicationService<IRepository<LowCodeTab
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.DbTypeTextFull;
model.CsType = item.CsType.Name;
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))
@ -175,14 +217,104 @@ public class LowCodeTableInfoService : ApplicationService<IRepository<LowCodeTab
}
/// <summary>
/// 变更数据
/// 变更数据(保存到配置文件)
/// </summary>
/// <param name="lowCodeTableInfos"></param>
/// <returns></returns>
public Task ChangeAsync(List<LowCodeTableInfo> lowCodeTableInfos)
public async Task ChangeAsync(List<LowCodeTableInfo> lowCodeTableInfos)
{
// 清除缓存
_databaseTableService.ClearAllTablesByCache();
return _defaultRepository.UpdateRangeAsync(lowCodeTableInfos);
// 按表分组保存
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
};
}
}
}
await _tableMetaConfigService.SaveConfigAsync(tableDto.DataBase!, tableDto.TableName!, config);
}

View File

@ -1,6 +1,12 @@
using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core;
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;
@ -11,66 +17,116 @@ public class LowCodeTableService : ApplicationService<IRepository<LowCodeTable>>
{
private readonly IRepository<LowCodeTableInfo> _lowCodeTableInfoRepository;
private readonly LowCodeTableInfoService _lowCodeTableInfoService;
private readonly IDatabaseTableService _databaseTableService;
private readonly ICodeGenerationService _codeGenerationService;
private readonly DataSourceManager _dataSourceManager;
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,
IDatabaseTableService databaseTableService,
ICodeGenerationService codeGenerationService,
DataSourceManager dataSourceManager) : base(defaultRepository)
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)
{
var query = _defaultRepository.SelectNoTracking
.WhereIf(!string.IsNullOrWhiteSpace(pagingSearchInput.Search.TableName),
w => w.TableName.Contains(pagingSearchInput.Search.TableName))
.WhereIf(!string.IsNullOrWhiteSpace(pagingSearchInput.Search.EntityName),
w => w.EntityName.Contains(pagingSearchInput.Search.EntityName))
.WhereIf(!string.IsNullOrWhiteSpace(pagingSearchInput.Search.DisplayName),
w => w.DisplayName.Contains(pagingSearchInput.Search.DisplayName))
.OrderByDescending(w => w.CreationTime)
.ThenByDescending(w => w.LastModificationTime)
.Select(w => new
{
w.Id,
w.TableName,
w.DisplayName,
w.EntityName,
w.Remark,
w.LastModificationTime,
w.CreationTime,
w.DataBase,
})
;
//获取一下数据用于缓存
_databaseTableService.GetAllTables();
var result = await _defaultRepository.AsPagingViewAsync(query, pagingSearchInput);
//覆盖值
result
.FormatValue(query, w => w.CreationTime, (oldValue) => oldValue.ToString("yyyy-MM-dd"))
.FormatValue(query, w => w.LastModificationTime, (oldValue) => oldValue?.ToString("yyyy-MM-dd"))
;
return result;
// ✅ 从 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>
@ -80,12 +136,32 @@ public class LowCodeTableService : ApplicationService<IRepository<LowCodeTable>>
/// <returns></returns>
public async Task DeleteListAsync(List<Guid> ids)
{
// 查询要删除的表信息,用于删除配置文件
var tablesToDelete = await _defaultRepository.Select.Where(w => ids.Contains(w.Id)).ToListAsync();
_databaseTableService.ClearAllTablesByCache();
//删除子表
//await this._lowCodeTableInfoRepository.DeleteBulkAsync(w => ids.Contains(w.Low_Code_TableId));
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>
@ -93,6 +169,9 @@ public class LowCodeTableService : ApplicationService<IRepository<LowCodeTable>>
/// </summary>
public async Task SynchronizationAsync()
{
// 刷新表结构缓存(从数据库重新加载表结构)
_tableSchemaCache.RefreshCache();
var allTables = _databaseTableService.GetAllTableInfos();
var oldAllTables = await _defaultRepository.ToListAllAsync();
@ -102,12 +181,12 @@ public class LowCodeTableService : ApplicationService<IRepository<LowCodeTable>>
foreach (var item in allTables)
{
var table = oldAllTables.Find(w => w.TableName == item.Name);
// 修复:同时匹配表名和数据库标识,避免多数据源同名表冲突
var table = oldAllTables.Find(w => w.TableName == item.Name && w.DataBase == item.DataBase);
var id = Guid.NewGuid();
// 使用扩展方法提取数据库标识
var databaseKey = item.Schema.ExtractDatabaseKey();
var provider = _dataSourceManager.GetProvider(databaseKey);
// 获取数据源提供者
var provider = _dataSourceManager.GetProvider(item.DataBase ?? "Admin");
if (table == null)
{
@ -116,12 +195,12 @@ public class LowCodeTableService : ApplicationService<IRepository<LowCodeTable>>
Id = id,
DisplayName = item.Comment,
TableName = item.Name,
DataBase = databaseKey,
Schema = item.Schema.CleanSchema(),
DataBase = item.DataBase,
Schema = item.Schema,
// 根据命名策略生成实体名
EntityName = provider?.Config.NamingStrategy == Abstractions.EntityNamingStrategy.KeepOriginal
EntityName = provider?.Config.NamingStrategy == CoreEntityNamingStrategy.KeepOriginal
? item.Name
: item.Name.ToLineConvertHump()
: ConvertToPascalCase(item.Name)
};
insertList.Add(lowCodeTable);
@ -129,11 +208,11 @@ public class LowCodeTableService : ApplicationService<IRepository<LowCodeTable>>
else
{
id = table.Id;
table.DataBase = databaseKey;
table.Schema = item.Schema.CleanSchema();
table.EntityName = provider?.Config.NamingStrategy == Abstractions.EntityNamingStrategy.KeepOriginal
table.DataBase = item.DataBase;
table.Schema = item.Schema;
table.EntityName = provider?.Config.NamingStrategy == CoreEntityNamingStrategy.KeepOriginal
? item.Name
: item.Name.ToLineConvertHump();
: ConvertToPascalCase(item.Name);
updateList.Add(table);
}
@ -151,12 +230,40 @@ public class LowCodeTableService : ApplicationService<IRepository<LowCodeTable>>
await _defaultRepository.UpdateRangeAsync(updateList);
}
// 同步列信息
foreach (var item in ids)
{
await _lowCodeTableInfoService.SynchronizationColumnByTableIdAsync(item, true);
}
_databaseTableService.ClearAllTablesByCache();
// 注意:这里不需要清除缓存,因为缓存在开始时已经刷新过了
// 清除缓存会导致下次查询重新加载,没有必要
}
/// <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>
@ -192,7 +299,7 @@ public class LowCodeTableService : ApplicationService<IRepository<LowCodeTable>>
var res = new Dictionary<string, object>();
var form = (await _defaultRepository.FindByIdAsync(id)).NullSafe();
_codeGenerationService.FillPathByLowCodeTable(form);
FillPathByLowCodeTable(form);
res[nameof(id)] = id == Guid.Empty ? "" : id;
res[nameof(form)] = form;
@ -201,6 +308,36 @@ public class LowCodeTableService : ApplicationService<IRepository<LowCodeTable>>
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>

View File

@ -1,5 +1,8 @@
using HZY.Framework.DependencyInjection.Attributes;
using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions;
using MiaoYu.Core.CodeGenerator.Abstractions;
using CoreDbTableInfo = MiaoYu.Core.CodeGenerator.Models.DbTableInfo;
using CoreDbColumnInfo = MiaoYu.Core.CodeGenerator.Models.DbColumnInfo;
using MiaoYu.Repository.Admin.Entities.LowCode;
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Providers;
@ -35,11 +38,42 @@ public class AdminDataSourceProvider : IDataSourceProvider, IScopedDependency
UsesPluralPath = true
};
public List<DbTableInfo> GetTables()
public List<CoreDbTableInfo> GetTables()
{
return _repository.UnitOfWork.FreeSqlOrm.DbFirst.GetTablesByDatabase();
var freeSqlTables = _repository.UnitOfWork.FreeSqlOrm.DbFirst.GetTablesByDatabase();
return ConvertToDbTableInfoList(freeSqlTables);
}
public DbContext GetDbContext() => _repository.GetContext()!;
public object GetDbContext() => _repository.GetContext()!;
private List<CoreDbTableInfo> ConvertToDbTableInfoList(List<FreeSql.DatabaseModel.DbTableInfo> freeSqlTables)
{
var result = new List<CoreDbTableInfo>();
foreach (var table in freeSqlTables)
{
var dbTableInfo = new CoreDbTableInfo
{
DataBase = Config.DatabaseKey,
Schema = table.Schema,
Name = table.Name,
Type = table.Type.ToString(),
Comment = table.Comment,
Columns = table.Columns?.Select(c => new CoreDbColumnInfo
{
Name = c.Name,
Comment = c.Comment,
IsPrimary = c.IsPrimary,
IsIdentity = c.IsIdentity,
IsNullable = c.IsNullable,
Position = c.Position,
DbType = c.DbTypeTextFull,
CsType = c.CsType?.Name,
MaxLength = c.MaxLength
}).ToList()
};
result.Add(dbTableInfo);
}
return result;
}
}

View File

@ -1,5 +1,7 @@
using HZY.Framework.DependencyInjection.Attributes;
using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions;
using MiaoYu.Core.CodeGenerator.Abstractions;
using CoreDbTableInfo = MiaoYu.Core.CodeGenerator.Models.DbTableInfo;
using CoreDbColumnInfo = MiaoYu.Core.CodeGenerator.Models.DbColumnInfo;
using MiaoYu.Repository.ChatAI.Admin.Entities;
namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Providers;
@ -36,14 +38,42 @@ public class MiaoYuChatDataSourceProvider : IDataSourceProvider, IScopedDependen
UsesPluralPath = false
};
public List<DbTableInfo> GetTables()
public List<CoreDbTableInfo> GetTables()
{
var tables = _repository.UnitOfWork.FreeSqlOrm.DbFirst.GetTablesByDatabase();
// 标记数据源来源
tables.ForEach(t => t.Schema = t.Schema + "." + Config.DatabaseKey);
return tables;
var freeSqlTables = _repository.UnitOfWork.FreeSqlOrm.DbFirst.GetTablesByDatabase();
return ConvertToDbTableInfoList(freeSqlTables);
}
public DbContext GetDbContext() => _repository.GetContext()!;
public object GetDbContext() => _repository.GetContext()!;
private List<CoreDbTableInfo> ConvertToDbTableInfoList(List<FreeSql.DatabaseModel.DbTableInfo> freeSqlTables)
{
var result = new List<CoreDbTableInfo>();
foreach (var table in freeSqlTables)
{
var dbTableInfo = new CoreDbTableInfo
{
DataBase = Config.DatabaseKey,
Schema = table.Schema,
Name = table.Name,
Type = table.Type.ToString(),
Comment = table.Comment,
Columns = table.Columns?.Select(c => new CoreDbColumnInfo
{
Name = c.Name,
Comment = c.Comment,
IsPrimary = c.IsPrimary,
IsIdentity = c.IsIdentity,
IsNullable = c.IsNullable,
Position = c.Position,
DbType = c.DbTypeTextFull,
CsType = c.CsType?.Name,
MaxLength = c.MaxLength
}).ToList()
};
result.Add(dbTableInfo);
}
return result;
}
}

View File

@ -1,12 +1,17 @@
using CoreICodeGenerationService = MiaoYu.Core.CodeGenerator.Services.ICodeGenerationService;
using CoreDataSourceDto = MiaoYu.Core.CodeGenerator.Models.DataSourceDto;
using CorePagingView = MiaoYu.Core.CodeGenerator.Models.PagingView;
using CoreGenFormDto = MiaoYu.Core.CodeGenerator.Models.GenFormDto;
namespace MiaoYu.Api.Admin.Controllers.DevelopmentTools;
/// <summary>
/// 代码生成器控制器
/// </summary>
[ControllerDescriptor(MenuId = "31")]
public class CodeGenerationController : AdminControllerBase<ICodeGenerationService>
public class CodeGenerationController : AdminControllerBase<CoreICodeGenerationService>
{
public CodeGenerationController(ICodeGenerationService defaultService) : base(defaultService)
public CodeGenerationController(CoreICodeGenerationService defaultService) : base(defaultService)
{
@ -18,7 +23,7 @@ public class CodeGenerationController : AdminControllerBase<ICodeGenerationServi
/// <returns></returns>
[ActionDescriptor(DisplayName = "获取数据库列表")]
[HttpGet]
public List<DataSourceDto> GetDatabasesAsync()
public List<CoreDataSourceDto> GetDatabasesAsync()
{
return _defaultService.GetAllDataSources();
}
@ -32,7 +37,7 @@ public class CodeGenerationController : AdminControllerBase<ICodeGenerationServi
/// <returns></returns>
[ActionDescriptor(DisplayName = "查询列表")]
[HttpPost("{size}/{page}")]
public PagingView FindListAsync([FromRoute] int size, [FromRoute] int page, [FromBody] GenFormDto search)
public CorePagingView FindListAsync([FromRoute] int size, [FromRoute] int page, [FromBody] CoreGenFormDto search)
{
return _defaultService.GetGenContextDtos(page, size, search);
}
@ -44,7 +49,7 @@ public class CodeGenerationController : AdminControllerBase<ICodeGenerationServi
/// <returns></returns>
[ActionDescriptor(DisplayName = "获取代码")]
[HttpPost]
public async Task<dynamic> GetCodeAsync([FromBody] GenFormDto genFormDto)
public async Task<dynamic> GetCodeAsync([FromBody] CoreGenFormDto genFormDto)
{
var code = await _defaultService.GetCodeByTypeAndTableNameAsync(genFormDto);
@ -85,7 +90,7 @@ public class CodeGenerationController : AdminControllerBase<ICodeGenerationServi
/// <returns></returns>
[ActionDescriptor(DisplayName = "下载当前代码")]
[HttpPost]
public async Task DownloadAsync([FromBody] GenFormDto genFormDto)
public async Task DownloadAsync([FromBody] CoreGenFormDto genFormDto)
{
var (codeBytes, contentType, fileName) = await _defaultService.DownloadAsync(genFormDto);
HttpContext.DownLoadFile(codeBytes, contentType, fileName);
@ -98,7 +103,7 @@ public class CodeGenerationController : AdminControllerBase<ICodeGenerationServi
/// <returns></returns>
[ActionDescriptor(DisplayName = "创建代码文件")]
[HttpPost]
public async Task DownloadAllAsync([FromBody] GenFormDto genFormDto)
public async Task DownloadAllAsync([FromBody] CoreGenFormDto genFormDto)
{
var (codeBytes, contentType, fileName) = await _defaultService.DownloadAllAsync(genFormDto);
HttpContext.DownLoadFile(codeBytes, contentType, fileName);
@ -126,7 +131,7 @@ public class CodeGenerationController : AdminControllerBase<ICodeGenerationServi
/// <returns></returns>
[ActionDescriptor(DisplayName = "生成代码并自动导入项目")]
[HttpPost]
public async Task<bool> AutoImprotProjectAsync([FromBody] GenFormDto genFormDto)
public async Task<bool> AutoImprotProjectAsync([FromBody] CoreGenFormDto genFormDto)
{
await _defaultService.AutoImprotProjectAsync(genFormDto);
return true;

View File

@ -19,6 +19,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MiaoYu.Core.CodeGenerator\MiaoYu.Core.CodeGenerator.csproj" />
<ProjectReference Include="..\MiaoYu.Core.Cos\MiaoYu.Core.Cos.csproj" />
<ProjectReference Include="..\MiaoYu.Core.Identity\MiaoYu.Core.Identity.csproj" />
<ProjectReference Include="..\MiaoYu.Core.Quartz\MiaoYu.Core.Quartz.csproj" />

View File

@ -1384,561 +1384,6 @@
</summary>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig">
<summary>
数据源配置
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.DatabaseKey">
<summary>
数据库标识Admin, MiaoYuChat, LiveForum
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.DisplayName">
<summary>
显示名称
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.EntityNamespace">
<summary>
实体项目命名空间
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.ModelPathTemplate">
<summary>
实体类路径模板(支持占位符:{RootPath}, {Namespace}, {EntityName}, {EntityNamePlural}, {TableName}
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.ServicePathTemplate">
<summary>
服务层路径模板
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.ControllerPathTemplate">
<summary>
控制器路径模板
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.ClientIndexPathTemplate">
<summary>
前端Index页面路径模板
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.ClientInfoPathTemplate">
<summary>
前端Info页面路径模板
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.ClientServicePathTemplate">
<summary>
前端Service路径模板
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.TemplatePath">
<summary>
代码生成模板目录
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.NamingStrategy">
<summary>
实体类命名规则(保持原名 or 驼峰转换)
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.Order">
<summary>
排序权重(数字越小越靠前)
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.EnableEntityPrefix">
<summary>
是否启用实体类名前缀(用于避免多数据源同名表冲突)
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.EntityPrefix">
<summary>
实体类名前缀Chat、Forum
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig.UsesPluralPath">
<summary>
是否使用复数形式的路径(如 /Users/ vs /User/
</summary>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConstants">
<summary>
数据源常量
</summary>
</member>
<member name="F:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConstants.Admin">
<summary>
后台管理系统数据库
</summary>
</member>
<member name="F:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConstants.MiaoYuChat">
<summary>
喵语AI聊天数据库
</summary>
</member>
<member name="F:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConstants.LiveForum">
<summary>
直播论坛数据库(预留)
</summary>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.EntityNamingStrategy">
<summary>
实体命名策略
</summary>
</member>
<member name="F:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.EntityNamingStrategy.KeepOriginal">
<summary>
保持数据库表名原样
</summary>
</member>
<member name="F:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.EntityNamingStrategy.ToPascalCase">
<summary>
转换为驼峰命名(去除前缀下划线)
</summary>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.IDataSourceProvider">
<summary>
数据源提供者接口
</summary>
</member>
<member name="P:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.IDataSourceProvider.Config">
<summary>
数据源配置
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.IDataSourceProvider.GetTables">
<summary>
获取该数据源的所有表信息
</summary>
<returns>表信息列表</returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.IDataSourceProvider.GetDbContext">
<summary>
获取DbContext用于获取FreeSql实例
</summary>
<returns>数据库上下文</returns>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.DataSourceExtensions">
<summary>
数据源扩展方法
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.DataSourceExtensions.ExtractDatabaseKey(System.String)">
<summary>
从 Schema 中提取数据库标识
</summary>
<param name="schema">Schema字符串</param>
<returns>数据库标识</returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.DataSourceExtensions.CleanSchema(System.String)">
<summary>
清理 Schema移除数据库标识
</summary>
<param name="schema">Schema字符串</param>
<returns>清理后的Schema</returns>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.DataSourceManager">
<summary>
数据源管理器
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.DataSourceManager.#ctor(System.Collections.Generic.IEnumerable{MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.IDataSourceProvider})">
<summary>
构造函数通过依赖注入自动收集所有IDataSourceProvider实现
</summary>
<param name="providers">所有数据源提供者</param>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.DataSourceManager.GetAllProviders">
<summary>
获取所有数据源提供者
</summary>
<returns>数据源提供者集合</returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.DataSourceManager.GetProvider(System.String)">
<summary>
根据数据库标识获取数据源提供者
</summary>
<param name="databaseKey">数据库标识Admin, MiaoYuChat</param>
<returns>数据源提供者如果未找到返回null</returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.DataSourceManager.GetAllTables">
<summary>
获取所有数据源的表信息
</summary>
<returns>所有表信息列表</returns>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.PathResolver">
<summary>
路径解析器
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.PathResolver.ResolvePath(System.String,MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig,System.String)">
<summary>
解析路径模板
</summary>
<param name="template">路径模板(支持占位符)</param>
<param name="config">数据源配置</param>
<param name="tableName">表名</param>
<returns>解析后的完整路径</returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.PathResolver.GetEntityName(System.String,MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions.DataSourceConfig)">
<summary>
根据命名策略和配置获取实体名
</summary>
<param name="tableName">表名</param>
<param name="config">数据源配置</param>
<returns>实体名</returns>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService">
<summary>
代码生成服务
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GetGenContextDtos(System.Int32,System.Int32,MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成上下文集合
</summary>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GetGenContextDtoByTableName(System.String,System.String)">
<summary>
获取表字段集合
</summary>
<param name="tableName"></param>
<param name="databaseKey">数据库标识(可选)</param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.FillPathByLowCodeTable(MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTable)">
<summary>
根据 lowCodeTable 填充路径
</summary>
<param name="lowCodeTable"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GetGenContextDto(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
获取代码生成上下文
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GenModelAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成 model
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GenServiceAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成 service
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GenControllerAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成 controller
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GenServiceJsAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成 serviceJs
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GenIndexAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成 Index
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GenInfoAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成 Info
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GetCodeByTypeAndTableNameAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
获取代码
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.DownloadAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
下载
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.DownloadAllAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
根据类型下载类型下所有的代码
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.CreateAllCodeFilesAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
创建所有代码文件
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.CreateDataDictionary">
<summary>
创建数据字典文件 excel
</summary>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.AutoImprotProjectAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
代码生成自动导入项目
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.ICodeGenerationService.GetAllDataSources">
<summary>
获取所有数据库列表
</summary>
<returns></returns>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService">
<summary>
代码生成服务
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.#ctor(MiaoYu.Shared.Admin.ApplicationServices.IDatabaseTableService,MiaoYu.Core.Razor.Services.IRazorViewRender,Microsoft.AspNetCore.Hosting.IWebHostEnvironment,MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.DataSourceManager,MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.PathResolver)">
<summary>
构造函数
</summary>
<param name="databaseTableService">数据库表服务</param>
<param name="razorViewRender">Razor视图渲染器</param>
<param name="webHostEnvironment">Web宿主环境</param>
<param name="dataSourceManager">数据源管理器</param>
<param name="pathResolver">路径解析器</param>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GetGenContextDtos(System.Int32,System.Int32,MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成上下文集合
</summary>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GetGenContextDtoByTableName(System.String,System.String)">
<summary>
获取所有表集合信息
</summary>
<param name="tableName">表名</param>
<param name="databaseKey">数据库标识(可选)</param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.FillPathByLowCodeTable(MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTable)">
<summary>
根据 lowCodeTable 填充路径(支持多数据源)
</summary>
<param name="lowCodeTable">低代码表配置</param>
<returns>填充路径后的低代码表配置</returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GetGenContextDto(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
获取代码生成上下文
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GenModelAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成model支持多数据源
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GenServiceAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成service支持多数据源
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GenControllerAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成controller支持多数据源
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GenServiceJsAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成service js支持多数据源
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GenIndexAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成 index vue支持多数据源
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GenInfoAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
生成 info vue支持多数据源
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GetCodeByTypeAndTableNameAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
获取代码
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.CreateAllCodeFilesAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
创建所有代码文件
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.DownloadAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
获取下载代码信息
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.DownloadAllAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
根据类型下载所有代码
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.CreateDataDictionary">
<summary>
创建数据库字典文件
</summary>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.AutoImprotProjectAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
自动导入文件到项目
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GetAllDataSources">
<summary>
获取所有数据库列表
</summary>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.ClearSymbol(System.Text.StringBuilder)">
<summary>
清除多余符号
</summary>
<param name="code"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.CreateCodeFilesAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
创建代码文件
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.FindCodeFileClassName(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<summary>
获取代码文件名称
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GetFileAbsolutelyPath(System.String,MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.FileTypeEnum,System.String)">
<summary>
获取要生成文件的绝对路径
</summary>
<param name="tableName"></param>
<param name="type"></param>
<param name="databaseKey">数据库标识(可选)</param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.SaveToFileAsync(System.String,MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.FileTypeEnum,System.String,System.String,System.String)">
<summary>
保存到文件
</summary>
<param name="tableName"></param>
<param name="type"></param>
<param name="filePath"></param>
<param name="oldName"></param>
<param name="replaceName"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.CodeGenerationService.GetEnumDescription(MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.FileTypeEnum)">
<summary>
获取枚举上的描述特性
</summary>
<param name="type"></param>
<returns></returns>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.DatabaseTableService">
<summary>
数据库表服务
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.DatabaseTableService.#ctor(Microsoft.Extensions.Caching.Memory.IMemoryCache,HZY.Framework.Repository.EntityFramework.IRepository{MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTable},HZY.Framework.Repository.EntityFramework.IRepository{MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTableInfo},HZY.Framework.Repository.EntityFramework.IRepository{MiaoYu.Repository.ChatAI.Admin.Entities.T_Image_Config},MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core.DataSourceManager)">
<summary>
构造函数
</summary>
<param name="memoryCache">内存缓存</param>
<param name="lowCodeTableRepository">低代码表仓储</param>
<param name="lowCodeTableInfoRepository">低代码表字段仓储</param>
<param name="imageConfig">图片配置仓储</param>
<param name="dataSourceManager">数据源管理器</param>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.DatabaseTableService.GetAllTableInfos">
<summary>
获取所有的表 包含表下面的列(支持多数据源)
</summary>
<returns>所有表信息列表</returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.DatabaseTableService.GetAllTables">
<summary>
获取所有的表 包含表下面的列
</summary>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.DatabaseTableService.GetAllTablesByCache">
<summary>
获取表信息根据缓存
</summary>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.DatabaseTableService.ClearAllTablesByCache">
<summary>
清空所有表缓存信息
</summary>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.DatabaseTableService.GetDatabaseName">
<summary>
获取数据库名称
</summary>
<returns></returns>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableInfoService">
<summary>
服务 Low_Code_Table_InfoService
@ -1946,7 +1391,7 @@
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableInfoService.FindListAsync(MiaoYu.Shared.Admin.Models.PagingViews.PagingSearchInput{MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTableInfo})">
<summary>
获取列表数据
获取列表数据(从 Core 层查询表结构并合并配置)
</summary>
<param name="pagingSearchInput"></param>
<returns></returns>
@ -1968,11 +1413,16 @@
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableInfoService.ChangeAsync(System.Collections.Generic.List{MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTableInfo})">
<summary>
变更数据
变更数据(保存到配置文件)
</summary>
<param name="lowCodeTableInfos"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableInfoService.SaveTableMetaConfigAsync(MiaoYu.Core.CodeGenerator.Models.GenDbTableDto)">
<summary>
保存表元信息到配置文件
</summary>
</member>
<member name="T:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableService">
<summary>
服务 Low_Code_TableService
@ -1980,11 +1430,16 @@
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableService.FindListAsync(MiaoYu.Shared.Admin.Models.PagingViews.PagingSearchInput{MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTable})">
<summary>
获取列表数据
获取列表数据(从 Core 层查询表结构并合并配置)
</summary>
<param name="pagingSearchInput"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableService.GetOrCreateTableId(System.String,System.String)">
<summary>
获取或创建表的 ID用于兼容性
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableService.DeleteListAsync(System.Collections.Generic.List{System.Guid})">
<summary>
根据id数组删除
@ -1997,6 +1452,11 @@
同步表(支持多数据源)
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableService.ConvertToPascalCase(System.String)">
<summary>
将下划线命名转换为 PascalCase
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableService.ChangeAsync(System.Collections.Generic.List{MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTable})">
<summary>
变更数据
@ -2011,6 +1471,11 @@
<param name="id"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableService.FillPathByLowCodeTable(MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTable)">
<summary>
根据 lowCodeTable 填充路径(支持多数据源)
</summary>
</member>
<member name="M:MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Impl.LowCodeTableService.SaveFormAsync(MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTable)">
<summary>
保存数据
@ -3859,7 +3324,7 @@
</summary>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.Controllers.DevelopmentTools.CodeGenerationController.FindListAsync(System.Int32,System.Int32,MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<member name="M:MiaoYu.Api.Admin.Controllers.DevelopmentTools.CodeGenerationController.FindListAsync(System.Int32,System.Int32,MiaoYu.Core.CodeGenerator.Models.GenFormDto)">
<summary>
获取列表
</summary>
@ -3868,21 +3333,21 @@
<param name="search"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.Controllers.DevelopmentTools.CodeGenerationController.GetCodeAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<member name="M:MiaoYu.Api.Admin.Controllers.DevelopmentTools.CodeGenerationController.GetCodeAsync(MiaoYu.Core.CodeGenerator.Models.GenFormDto)">
<summary>
获取代码 根据 表名 和 类型
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.Controllers.DevelopmentTools.CodeGenerationController.DownloadAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<member name="M:MiaoYu.Api.Admin.Controllers.DevelopmentTools.CodeGenerationController.DownloadAsync(MiaoYu.Core.CodeGenerator.Models.GenFormDto)">
<summary>
下载当前代码
</summary>
<param name="genFormDto"></param>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.Controllers.DevelopmentTools.CodeGenerationController.DownloadAllAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<member name="M:MiaoYu.Api.Admin.Controllers.DevelopmentTools.CodeGenerationController.DownloadAllAsync(MiaoYu.Core.CodeGenerator.Models.GenFormDto)">
<summary>
创建代码文件
</summary>
@ -3895,7 +3360,7 @@
</summary>
<returns></returns>
</member>
<member name="M:MiaoYu.Api.Admin.Controllers.DevelopmentTools.CodeGenerationController.AutoImprotProjectAsync(MiaoYu.Api.Admin.Models.Dtos.DevelopmentTool.GenFormDto)">
<member name="M:MiaoYu.Api.Admin.Controllers.DevelopmentTools.CodeGenerationController.AutoImprotProjectAsync(MiaoYu.Core.CodeGenerator.Models.GenFormDto)">
<summary>
生成代码并自动导入项目
</summary>
@ -4902,7 +4367,7 @@
版本
</summary>
</member>
<member name="M:AspNetCoreGeneratedDocument.wwwroot_code_generation_templatev4_tempModel.GetTypeNew(MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTableInfo)">
<member name="M:AspNetCoreGeneratedDocument.wwwroot_code_generation_templatev4_tempModel.GetTypeNew(MiaoYu.Core.CodeGenerator.Models.LowCodeTableInfo)">
<summary>
获取类型 根据 appTableInfo
</summary>
@ -4915,7 +4380,7 @@
</summary>
<returns></returns>
</member>
<member name="M:AspNetCoreGeneratedDocument.wwwroot_code_generation_template_tempModel.GetTypeNew(MiaoYu.Repository.Admin.Entities.LowCode.LowCodeTableInfo)">
<member name="M:AspNetCoreGeneratedDocument.wwwroot_code_generation_template_tempModel.GetTypeNew(MiaoYu.Core.CodeGenerator.Models.LowCodeTableInfo)">
<summary>
获取类型 根据 appTableInfo
</summary>

View File

@ -30,6 +30,7 @@ global using MiaoYu.Core.Quartz.Models;
global using MiaoYu.Core.Quartz.Services;
global using MiaoYu.Core.Razor;
global using MiaoYu.Core.Razor.Services;
global using MiaoYu.Core.CodeGenerator;
global using MiaoYu.Core.Redis;
global using MiaoYu.Core.Swagger;
global using MiaoYu.Core.Zips;
@ -59,7 +60,10 @@ global using MiaoYu.Shared.Admin.ApplicationServices;
global using MiaoYu.Shared.Admin.Extensions;
global using MiaoYu.Shared.Admin.Filters;
global using MiaoYu.Shared.Admin.Models.Consts;
global using MiaoYu.Shared.Admin.Models.LowCodes;
// ❌ 已废弃,重构到 Core 层
// global using MiaoYu.Shared.Admin.Models.LowCodes;
// ⚠️ 不能全局引入 Core.CodeGenerator.Models会与 Repository 层的实体类型冲突
// 需要时使用类型别名CoreGenDbTableDto, CoreLowCodeTable 等
global using MiaoYu.Shared.Admin.Models.PagingViews;
global using MiaoYu.Shared.Filters;
global using MiaoYu.Shared.Upload;

View File

@ -0,0 +1,3 @@
@* 代码生成器模型命名空间 *@
@using MiaoYu.Core.CodeGenerator.Models

View File

@ -25,9 +25,9 @@
@functions
{
//获取类型 根据 appTableInfo
string GetType(DbColumnInfo appTableInfo)
string GetType(LowCodeTableInfo appTableInfo)
{
switch (appTableInfo.DbTypeText)
switch (appTableInfo.DatabaseColumnType)
{
case "uniqueidentifier":
return appTableInfo.IsPrimary ? "Guid" : "Guid?";
@ -42,7 +42,7 @@
case "decimal":
return appTableInfo.IsNullable ? "decimal?" : "decimal";
default:
return appTableInfo.IsNullable ? appTableInfo.CsType.Name + "?" : appTableInfo.CsType.Name;
return appTableInfo.IsNullable ? appTableInfo.CsType + "?" : appTableInfo.CsType;
}
}

View File

@ -0,0 +1,3 @@
@* 代码生成器模型命名空间 *@
@using MiaoYu.Core.CodeGenerator.Models

View File

@ -1,4 +1,4 @@
@model GenDbTableDto
@model GenDbTableDto
@{
var className = Model.EntityName;
var classNameRemark = Model.DisplayName;
@ -25,9 +25,9 @@
@functions
{
//获取类型 根据 appTableInfo
string GetType(DbColumnInfo appTableInfo)
string GetType(LowCodeTableInfo appTableInfo)
{
switch (appTableInfo.DbTypeText)
switch (appTableInfo.DatabaseColumnType)
{
case "uniqueidentifier":
return appTableInfo.IsPrimary ? "Guid" : "Guid?";
@ -42,7 +42,7 @@
case "decimal":
return appTableInfo.IsNullable ? "decimal?" : "decimal";
default:
return appTableInfo.IsNullable ? appTableInfo.CsType.Name + "?" : appTableInfo.CsType.Name;
return appTableInfo.IsNullable ? appTableInfo.CsType + "?" : appTableInfo.CsType;
}
}

View File

@ -20,4 +20,3 @@ public static class DataSourceConstants
/// </summary>
public const string LiveForum = "LiveForum";
}

View File

@ -0,0 +1,30 @@
namespace MiaoYu.Core.CodeGenerator;
/// <summary>
/// 代码生成器模块启动器
/// </summary>
public class CoreCodeGeneratorStartup : StartupModule<CoreCodeGeneratorStartup>
{
/// <summary>
/// 配置服务
/// </summary>
/// <param name="webApplicationBuilder"></param>
public override void ConfigureServices(WebApplicationBuilder webApplicationBuilder)
{
var services = webApplicationBuilder.Services;
// 服务已通过 [Component] 和 IScopedDependency 自动注册
// 无需手动注册
}
/// <summary>
/// 配置中间件
/// </summary>
/// <param name="webApplication"></param>
public override void Configure(WebApplication webApplication)
{
// 无需配置中间件
}
}

View File

@ -141,6 +141,23 @@
</summary>
<returns>数据库上下文</returns>
</member>
<member name="T:MiaoYu.Core.CodeGenerator.CoreCodeGeneratorStartup">
<summary>
代码生成器模块启动器
</summary>
</member>
<member name="M:MiaoYu.Core.CodeGenerator.CoreCodeGeneratorStartup.ConfigureServices(Microsoft.AspNetCore.Builder.WebApplicationBuilder)">
<summary>
配置服务
</summary>
<param name="webApplicationBuilder"></param>
</member>
<member name="M:MiaoYu.Core.CodeGenerator.CoreCodeGeneratorStartup.Configure(Microsoft.AspNetCore.Builder.WebApplication)">
<summary>
配置中间件
</summary>
<param name="webApplication"></param>
</member>
<member name="T:MiaoYu.Core.CodeGenerator.Core.DataSourceExtensions">
<summary>
数据源扩展方法
@ -681,7 +698,7 @@
获取所有表集合信息
</summary>
<param name="tableName">表名</param>
<param name="databaseKey">数据库标识(可选</param>
<param name="databaseKey">数据库标识(强烈建议传入,避免多数据源同名表冲突</param>
<returns></returns>
</member>
<member name="M:MiaoYu.Core.CodeGenerator.Services.CodeGenerationService.FillPathByLowCodeTable(MiaoYu.Core.CodeGenerator.Models.LowCodeTable)">
@ -817,7 +834,7 @@
<param name="databaseKey">数据库标识(可选)</param>
<returns></returns>
</member>
<member name="M:MiaoYu.Core.CodeGenerator.Services.CodeGenerationService.SaveToFileAsync(System.String,MiaoYu.Core.CodeGenerator.Services.FileTypeEnum,System.String,System.String,System.String)">
<member name="M:MiaoYu.Core.CodeGenerator.Services.CodeGenerationService.SaveToFileAsync(System.String,MiaoYu.Core.CodeGenerator.Services.FileTypeEnum,System.String,System.String,System.String,System.String)">
<summary>
保存到文件
</summary>
@ -826,6 +843,7 @@
<param name="filePath"></param>
<param name="oldName"></param>
<param name="replaceName"></param>
<param name="databaseKey">数据库标识(可选)</param>
<returns></returns>
</member>
<member name="M:MiaoYu.Core.CodeGenerator.Services.CodeGenerationService.GetEnumDescription(MiaoYu.Core.CodeGenerator.Services.FileTypeEnum)">
@ -846,12 +864,14 @@
数据库表服务
</summary>
</member>
<member name="M:MiaoYu.Core.CodeGenerator.Services.DatabaseTableService.#ctor(MiaoYu.Core.CodeGenerator.Services.ITableSchemaCache,MiaoYu.Core.CodeGenerator.Services.ITableMetaConfigService,Microsoft.Extensions.Logging.ILogger{MiaoYu.Core.CodeGenerator.Services.DatabaseTableService})">
<member name="M:MiaoYu.Core.CodeGenerator.Services.DatabaseTableService.#ctor(MiaoYu.Core.CodeGenerator.Services.ITableSchemaCache,MiaoYu.Core.CodeGenerator.Services.ITableMetaConfigService,MiaoYu.Core.CodeGenerator.Core.DataSourceManager,MiaoYu.Core.CodeGenerator.Core.PathResolver,Microsoft.Extensions.Logging.ILogger{MiaoYu.Core.CodeGenerator.Services.DatabaseTableService})">
<summary>
构造函数
</summary>
<param name="tableSchemaCache">表结构缓存</param>
<param name="tableMetaConfigService">表元信息配置服务</param>
<param name="dataSourceManager">数据源管理器</param>
<param name="pathResolver">路径解析器</param>
<param name="logger">日志</param>
</member>
<member name="M:MiaoYu.Core.CodeGenerator.Services.DatabaseTableService.GetAllTableInfos">

View File

@ -6,7 +6,8 @@ namespace MiaoYu.Core.CodeGenerator.Services;
/// <summary>
/// 代码生成服务
/// </summary>
public class CodeGenerationService : ICodeGenerationService
[Component]
public class CodeGenerationService : ICodeGenerationService, IScopedDependency
{
private readonly string _webRootPath;
private readonly string templateRootPath = "/wwwroot/code_generation/template/";
@ -103,7 +104,7 @@ public class CodeGenerationService : ICodeGenerationService
/// 获取所有表集合信息
/// </summary>
/// <param name="tableName">表名</param>
/// <param name="databaseKey">数据库标识(可选</param>
/// <param name="databaseKey">数据库标识(强烈建议传入,避免多数据源同名表冲突</param>
/// <returns></returns>
public GenDbTableDto GetGenContextDtoByTableName(string tableName, string? databaseKey = null)
{
@ -116,7 +117,17 @@ public class CodeGenerationService : ICodeGenerationService
}
else
{
// 警告:如果多个数据源有同名表,将返回第一个匹配的表
// 建议:始终传入 databaseKey 参数以避免歧义
query = query.Where(w => w.TableName == tableName);
// 记录警告日志
var matchedTables = query.ToList();
if (matchedTables.Count > 1)
{
var databases = string.Join(", ", matchedTables.Select(t => t.DataBase));
LogUtil.Log.Warning($"表 '{tableName}' 在多个数据源中存在: [{databases}]。未指定 databaseKey将使用第一个匹配项。");
}
}
var genDbTableDto = query.FirstOrDefault();
@ -670,10 +681,11 @@ public class CodeGenerationService : ICodeGenerationService
/// <param name="filePath"></param>
/// <param name="oldName"></param>
/// <param name="replaceName"></param>
/// <param name="databaseKey">数据库标识(可选)</param>
/// <returns></returns>
private async Task SaveToFileAsync(string tableName, FileTypeEnum type, string filePath, string oldName, string replaceName)
private async Task SaveToFileAsync(string tableName, FileTypeEnum type, string filePath, string oldName, string replaceName, string? databaseKey = null)
{
var dto = new GenFormDto() { TableName = tableName, Type = GetEnumDescription(type) };
var dto = new GenFormDto() { TableName = tableName, Type = GetEnumDescription(type), DataBase = databaseKey };
var codeString = await GetCodeByTypeAndTableNameAsync(dto);
if (!string.IsNullOrWhiteSpace(replaceName) && !string.IsNullOrWhiteSpace(oldName))
{

View File

@ -3,10 +3,13 @@ namespace MiaoYu.Core.CodeGenerator.Services;
/// <summary>
/// 数据库表服务
/// </summary>
public class DatabaseTableService : IDatabaseTableService
[Component]
public class DatabaseTableService : IDatabaseTableService, IScopedDependency
{
private readonly ITableSchemaCache _tableSchemaCache;
private readonly ITableMetaConfigService _tableMetaConfigService;
private readonly DataSourceManager _dataSourceManager;
private readonly PathResolver _pathResolver;
private readonly ILogger<DatabaseTableService> _logger;
/// <summary>
@ -14,14 +17,20 @@ public class DatabaseTableService : IDatabaseTableService
/// </summary>
/// <param name="tableSchemaCache">表结构缓存</param>
/// <param name="tableMetaConfigService">表元信息配置服务</param>
/// <param name="dataSourceManager">数据源管理器</param>
/// <param name="pathResolver">路径解析器</param>
/// <param name="logger">日志</param>
public DatabaseTableService(
ITableSchemaCache tableSchemaCache,
ITableMetaConfigService tableMetaConfigService,
DataSourceManager dataSourceManager,
PathResolver pathResolver,
ILogger<DatabaseTableService> logger)
{
_tableSchemaCache = tableSchemaCache;
_tableMetaConfigService = tableMetaConfigService;
_dataSourceManager = dataSourceManager;
_pathResolver = pathResolver;
_logger = logger;
}
@ -91,6 +100,12 @@ public class DatabaseTableService : IDatabaseTableService
/// </summary>
private GenDbTableDto MergeTableWithConfig(DbTableInfo schemaTable)
{
// 获取数据源配置以应用命名策略
var provider = _dataSourceManager.GetProvider(schemaTable.DataBase ?? "");
var defaultEntityName = provider != null
? _pathResolver.GetEntityName(schemaTable.Name ?? "", provider.Config)
: schemaTable.Name;
var genTable = new GenDbTableDto
{
TableName = schemaTable.Name,
@ -98,6 +113,9 @@ public class DatabaseTableService : IDatabaseTableService
Schema = schemaTable.Schema,
Type = schemaTable.Type,
Remark = schemaTable.Comment,
// 提供默认的实体名和显示名(应用命名策略)
EntityName = defaultEntityName,
DisplayName = schemaTable.Comment ?? schemaTable.Name,
TableInfos = schemaTable.Columns?.Select(c => new LowCodeTableInfo
{
ColumnName = c.Name,
@ -118,10 +136,13 @@ public class DatabaseTableService : IDatabaseTableService
var config = _tableMetaConfigService.LoadConfig(schemaTable.DataBase, schemaTable.Name);
if (config != null)
{
// 使用配置文件中的元信息覆盖
genTable.DisplayName = config.DisplayName;
genTable.EntityName = config.EntityName;
genTable.Remark = config.Remark ?? genTable.Remark;
// 使用配置文件中的元信息覆盖(如果配置文件中有值才覆盖)
if (!string.IsNullOrEmpty(config.DisplayName))
genTable.DisplayName = config.DisplayName;
if (!string.IsNullOrEmpty(config.EntityName))
genTable.EntityName = config.EntityName;
if (!string.IsNullOrEmpty(config.Remark))
genTable.Remark = config.Remark;
genTable.ModelPath = config.ModelPath;
genTable.ServicePath = config.ServicePath;
genTable.ControllerPath = config.ControllerPath;

View File

@ -6,7 +6,7 @@ namespace MiaoYu.Core.CodeGenerator.Services;
/// 表元信息配置服务实现
/// </summary>
[Component]
public class TableMetaConfigService : ITableMetaConfigService
public class TableMetaConfigService : ITableMetaConfigService, IScopedDependency
{
private readonly IWebHostEnvironment _environment;
private readonly ILogger<TableMetaConfigService> _logger;

View File

@ -4,7 +4,7 @@ namespace MiaoYu.Core.CodeGenerator.Services;
/// 表结构缓存服务实现
/// </summary>
[Component]
public class TableSchemaCache : ITableSchemaCache
public class TableSchemaCache : ITableSchemaCache, IScopedDependency
{
private readonly IMemoryCache _memoryCache;
private readonly DataSourceManager _dataSourceManager;

View File

@ -10,7 +10,10 @@ global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Caching.Memory;
global using Microsoft.Extensions.Logging;
global using Microsoft.AspNetCore.Hosting;
global using Microsoft.AspNetCore.Builder;
global using NPOI.XSSF.UserModel;
global using HZY.Framework.Core;
global using HZY.Framework.Core.AspNetCore;
global using HZY.Framework.DependencyInjection;
global using HZY.Framework.DependencyInjection.Attributes;
global using MiaoYu.Core.Logs;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,885 @@
# 代码生成器架构与流程文档
## 一、整体架构
### 1.1 架构设计原则
```
┌─────────────────────────────────────────────────────────────┐
│ 代码生成器核心架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ API层 (MiaoYu.Api.Admin) │
│ ├── Controllers (对外接口) │
│ ├── Providers (数据源提供者实现) │
│ │ ├── AdminDataSourceProvider │
│ │ ├── MiaoYuChatDataSourceProvider │
│ │ └── LiveForumDataSourceProvider (待实现) │
│ └── Services (业务逻辑) │
│ ├── LowCodeTableService (表列表管理) │
│ └── LowCodeTableInfoService (列配置管理) │
│ │
│ ───────────────────────────────────────────────────── │
│ │
│ 核心层 (MiaoYu.Core.CodeGenerator) │
│ ├── Abstractions (抽象接口) │
│ │ ├── IDataSourceProvider (数据源提供者接口) │
│ │ ├── DataSourceConfig (数据源配置) │
│ │ └── EntityNamingStrategy (命名策略枚举) │
│ ├── Core (核心逻辑) │
│ │ ├── DataSourceManager (数据源管理器) │
│ │ └── PathResolver (路径解析器) │
│ ├── Services (核心服务) │
│ │ ├── CodeGenerationService (代码生成服务) │
│ │ ├── DatabaseTableService (数据库表服务) │
│ │ ├── TableMetaConfigService (表元信息配置服务) │
│ │ └── TableSchemaCache (表结构缓存) │
│ └── Models (数据模型) │
│ ├── GenDbTableDto (统一的表模型) │
│ ├── LowCodeTableInfo (列信息模型) │
│ ├── DbTableInfo (数据库表信息) │
│ └── TableMetaConfig (表元信息配置) │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 1.2 核心设计模式
1. **Provider 模式**: 支持多数据源扩展
2. **策略模式**: 命名策略可配置PascalCase、KeepOriginal
3. **模板方法模式**: 统一的代码生成流程
4. **缓存模式**: 表结构缓存,提升性能
5. **配置驱动**: 路径、命名等通过配置文件管理
---
## 二、核心流程详解
### 2.1 启动初始化流程
```
应用启动
注册数据源提供者 (AdminDataSourceProvider, MiaoYuChatDataSourceProvider)
DataSourceManager 收集所有 IDataSourceProvider 实例
TableSchemaCache 从所有数据源加载表结构
缓存有效期20分钟
```
**关键代码位置**:
- `MiaoYu.Core.CodeGenerator/CoreCodeGeneratorStartup.cs` - 核心模块注册
- `MiaoYu.Core.CodeGenerator/Core/DataSourceManager.cs` - 数据源管理
- `MiaoYu.Core.CodeGenerator/Services/TableSchemaCache.cs` - 缓存服务
---
### 2.2 获取表列表流程
```
前端请求 GET /api/v1/admin/LowCodeTable/list?dataBase=Admin
LowCodeTableService.FindListAsync()
调用 DatabaseTableService.GetAllTables()
├─→ TableSchemaCache.GetAllTables() (从缓存获取原始表信息)
│ ↓
│ 遍历所有 DataSourceProvider 获取表结构
│ ↓
│ 返回 List<DbTableInfo>
遍历每个 DbTableInfo调用 MergeTableWithConfig()
├─→ 获取数据源配置 (DataSourceConfig)
├─→ 应用命名策略生成默认 EntityName
│ 例如: "song_likes" → "SongLikes" (ToPascalCase)
├─→ 加载 TableMetaConfig (如果存在)
│ 路径: configs/{dataBase}/{tableName}.json
├─→ 合并配置:配置文件中的值覆盖默认值
└─→ 返回 GenDbTableDto (统一的表模型)
过滤 (dataBase, tableName 等条件)
分页返回
```
**数据转换链**:
```
DbTableInfo (数据库原始信息)
+ DataSourceConfig (数据源配置)
+ TableMetaConfig (用户自定义配置,可选)
= GenDbTableDto (最终统一模型)
```
**关键文件**:
- `MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableService.cs`
- `MiaoYu.Core.CodeGenerator/Services/DatabaseTableService.cs`
---
### 2.3 修改列配置流程
```
前端请求 PUT /api/v1/admin/LowCodeTableInfo/change
LowCodeTableInfoService.ChangeAsync(List<LowCodeTableInfo>)
清除缓存: DatabaseTableService.ClearAllTablesByCache()
按表分组处理
对每个表:
├─→ 获取 LowCodeTable (从数据库,用于获取 tableName 和 dataBase)
├─→ 从 DatabaseTableService.GetAllTables() 获取最新的表信息
├─→ 查找目标表
├─→ 更新列配置 (DisplayName, Describe, CsField, IsTableSelect 等)
└─→ 调用 SaveTableMetaConfigAsync()
TableMetaConfigService.SaveConfigAsync()
保存到: configs/{dataBase}/{tableName}.json
文件内容示例:
{
"DisplayName": "歌曲点赞记录",
"EntityName": "SongLike",
"Remark": "记录用户对歌曲的点赞",
"Columns": {
"SongId": {
"DisplayName": "歌曲ID",
"Describe": "被点赞的歌曲ID",
"CsField": "SongId",
...
}
}
}
```
**配置文件结构**:
```json
{
"DisplayName": "显示名称",
"EntityName": "实体类名",
"Remark": "备注说明",
"ModelPath": "模型路径",
"ServicePath": "服务路径",
"ControllerPath": "控制器路径",
"ClientIndexPath": "前端索引页路径",
"ClientInfoPath": "前端详情页路径",
"ClientServicePath": "前端服务路径",
"IsCover": true,
"Columns": {
"ColumnName1": {
"DisplayName": "列显示名",
"Describe": "列描述",
"CsField": "C#字段名",
"IsTableSelect": true,
"IsImageId": false,
"IsTableColumnShow": true
}
}
}
```
**关键文件**:
- `MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableInfoService.cs`
- `MiaoYu.Core.CodeGenerator/Services/TableMetaConfigService.cs`
---
### 2.4 代码生成流程
```
前端请求 POST /api/v1/admin/CodeGeneration/getCode
参数: { tableName: "song_likes", fileTypeEnum: 1 (Model) }
CodeGenerationController.GetCodeAsync()
CoreICodeGenerationService.GetCodeAsync()
CodeGenerationService.GetCodeAsync()
1. 获取表信息
├─→ GetGenContextDtoByTableName("song_likes")
├─→ DatabaseTableService.GetAllTables()
└─→ 找到对应的 GenDbTableDto
2. 确定数据源和配置
├─→ DataSourceManager.GetProvider(dataBase)
└─→ 获取 DataSourceConfig (包含模板路径、命名策略等)
3. 解析路径
├─→ PathResolver.ResolvePath(template, config, tableName)
├─→ 替换占位符:
│ {RootPath} → 项目根路径
│ {AppPath} → 应用路径
│ {EntityName} → SongLike (应用命名策略)
│ {EntityNamePlural} → SongLikes
│ {TableName} → song_likes
└─→ 生成完整路径
4. 渲染模板
├─→ 根据 fileTypeEnum 选择模板文件
│ FileTypeEnum.Model → tempModel.cshtml
│ FileTypeEnum.Service → tempService.cshtml
│ FileTypeEnum.Controller → tempController.cshtml
│ 等等
├─→ 加载 Razor 模板
├─→ 传入 Model: GenDbTableDto
└─→ RazorViewRender.RenderToStringAsync()
5. 返回生成的代码
├─→ ModelCode: "public class SongLike { ... }"
├─→ FilePath: "D:\\...\\Entities\\Apps\\SongLikes"
└─→ FileName: "SongLike.cs"
```
**模板渲染过程**:
```
tempModel.cshtml (Razor模板)
@model GenDbTableDto
访问模型属性:
@Model.EntityName → "SongLike"
@Model.DisplayName → "歌曲点赞记录"
@Model.TableName → "song_likes"
@Model.TableInfos → List<LowCodeTableInfo>
遍历列信息:
@foreach (var item in tableInfos)
{
public @GetType(item) @item.ColumnName { get; set; }
}
生成最终代码
```
**关键文件**:
- `MiaoYu.Api.Admin/Controllers/DevelopmentTools/CodeGenerationController.cs`
- `MiaoYu.Core.CodeGenerator/Services/CodeGenerationService.cs`
- `MiaoYu.Core.CodeGenerator/Core/PathResolver.cs`
- `MiaoYu.Api.Admin/wwwroot/code_generation/template/*.cshtml`
---
## 三、关键组件详解
### 3.1 DataSourceManager (数据源管理器)
**职责**:
- 收集所有 `IDataSourceProvider` 实现
- 提供按 key 查询特定数据源的能力
- 聚合所有数据源的表信息
**核心方法**:
```csharp
public IDataSourceProvider? GetProvider(string key)
public List<DbTableInfo> GetAllTables()
```
**使用场景**:
- 初始化时收集所有数据源
- 生成代码时根据 dataBase 获取对应的 Provider 和配置
---
### 3.2 DatabaseTableService (数据库表服务)
**职责**:
- 管理表结构信息
- 合并数据库 schema 和用户配置
- 提供统一的表数据模型
**核心方法**:
```csharp
// 获取所有表(已合并配置)
public List<GenDbTableDto> GetAllTables()
// 合并表结构和配置文件信息
private GenDbTableDto MergeTableWithConfig(DbTableInfo schemaTable)
```
**合并逻辑**:
1. 创建 `GenDbTableDto`,设置基础信息
2. 应用命名策略生成默认 `EntityName`
3. 从配置文件加载 `TableMetaConfig`
4. 用配置文件中的值覆盖默认值(如果配置存在且有值)
5. 合并列级配置
---
### 3.3 TableMetaConfigService (表元信息配置服务)
**职责**:
- 加载和保存表的元信息配置
- 管理配置文件JSON格式
**配置文件路径规则**:
```
configs/{dataBase}/{tableName}.json
```
**核心方法**:
```csharp
public TableMetaConfig? LoadConfig(string dataBase, string tableName)
public void SaveConfigAsync(string dataBase, string tableName, TableMetaConfig config)
```
**使用场景**:
- 用户修改列配置后保存
- 加载表信息时读取用户自定义配置
---
### 3.4 PathResolver (路径解析器)
**职责**:
- 解析路径模板中的占位符
- 应用命名策略转换表名为实体名
**支持的占位符**:
- `{RootPath}`: 项目根路径
- `{AppPath}`: 应用路径
- `{Namespace}`: 命名空间
- `{EntityName}`: 实体名(单数,应用命名策略)
- `{EntityNamePlural}`: 实体名(复数)
- `{TableName}`: 原始表名
**命名策略**:
```csharp
public enum EntityNamingStrategy
{
KeepOriginal, // 保持原样
ToPascalCase // 转为 PascalCase (song_likes → SongLikes)
}
```
**转换规则**:
```csharp
private static string ConvertToPascalCase(string input)
{
// "song_likes" → ["song", "likes"]
var words = input.Split('_', '-');
// 首字母大写: ["Song", "Likes"]
foreach (var word in words)
{
result.Append(char.ToUpper(word[0]));
result.Append(word.Substring(1).ToLower());
}
// 结果: "SongLikes"
return result.ToString();
}
```
---
### 3.5 TableSchemaCache (表结构缓存)
**职责**:
- 缓存所有数据源的表结构信息
- 减少数据库查询次数
- 提供缓存刷新机制
**缓存策略**:
```csharp
private const int CacheDurationMinutes = 20;
```
**核心方法**:
```csharp
public List<DbTableInfo> GetAllTables()
public void ClearCache()
```
**缓存刷新时机**:
- 应用启动时自动加载
- 缓存过期20分钟
- 用户手动清除(修改列配置后)
---
## 四、数据源配置详解
### 4.1 DataSourceConfig 配置项
```csharp
public class DataSourceConfig
{
// 数据库标识键
public string DatabaseKey { get; set; }
// 显示名称
public string DisplayName { get; set; }
// 实体命名空间
public string EntityNamespace { get; set; }
// 路径模板(支持占位符)
public string ModelPathTemplate { get; set; }
public string ServicePathTemplate { get; set; }
public string ControllerPathTemplate { get; set; }
public string ClientIndexPathTemplate { get; set; }
public string ClientInfoPathTemplate { get; set; }
public string ClientServicePathTemplate { get; set; }
// 模板路径
public string TemplatePath { get; set; }
// 命名策略
public EntityNamingStrategy NamingStrategy { get; set; }
// 实体前缀配置
public bool EnableEntityPrefix { get; set; }
public string EntityPrefix { get; set; }
// 路径是否使用复数形式
public bool UsesPluralPath { get; set; }
// 显示顺序
public int Order { get; set; }
}
```
### 4.2 示例Admin 数据源配置
```csharp
public DataSourceConfig Config => new DataSourceConfig
{
DatabaseKey = "Admin",
DisplayName = "后台管理系统",
EntityNamespace = "MiaoYu.Repository.Admin.Entities",
// 模型路径: D:\...\MiaoYu.Repository.Admin\Entities\Apps\SongLikes
ModelPathTemplate = "{RootPath}\\{Namespace}\\Entities\\Apps\\{EntityNamePlural}",
// 服务路径: D:\...\MiaoYu.Api.Admin\ApplicationServices\Apps\SongLikes
ServicePathTemplate = "{AppPath}\\ApplicationServices\\Apps\\{EntityNamePlural}",
// 控制器路径
ControllerPathTemplate = "{AppPath}\\Controllers\\Apps\\{EntityNamePlural}",
// 前端路径
ClientIndexPathTemplate = "{RootPath}\\admin-client\\src\\views\\apps\\{TableName}s",
ClientInfoPathTemplate = "{RootPath}\\admin-client\\src\\views\\apps\\{TableName}s",
ClientServicePathTemplate = "{RootPath}\\admin-client\\src\\services\\apps\\{TableName}s",
// 模板路径
TemplatePath = "/wwwroot/code_generation/template/",
// 命名策略:转为 PascalCase
NamingStrategy = EntityNamingStrategy.ToPascalCase,
// 不使用前缀
EnableEntityPrefix = false,
EntityPrefix = "",
// 路径使用复数
UsesPluralPath = true,
Order = 1
};
```
---
## 五、Razor 模板详解
### 5.1 模板文件结构
```
wwwroot/code_generation/
├── template/ # 模板 V1
│ ├── _ViewImports.cshtml # Razor 视图导入(重要!)
│ ├── tempModel.cshtml # 实体模型模板
│ ├── tempService.cshtml # 服务模板
│ ├── tempController.cshtml # 控制器模板
│ ├── tempIndex.cshtml # 前端索引页模板
│ ├── tempInfo.cshtml # 前端详情页模板
│ └── tempService_admin.cshtml # 前端服务模板
└── templatev4/ # 模板 V4新版本
├── _ViewImports.cshtml
├── tempModel.cshtml
└── ...
```
### 5.2 _ViewImports.cshtml 的重要性
**作用**: 定义 Razor 模板的全局 using 指令
```cshtml
@* 代码生成器模型命名空间 *@
@using MiaoYu.Core.CodeGenerator.Models
```
**为什么需要**:
1. 解决类型冲突:`GenDbTableDto` 在多个命名空间存在
2. 明确指定模板使用 Core 层的模型
3. 避免 Razor 编译时的类型歧义错误
**历史问题回顾**:
```
错误: The model item is of type 'MiaoYu.Core.CodeGenerator.Models.GenDbTableDto',
but this ViewDataDictionary requires 'MiaoYu.Shared.Admin.Models.LowCodes.GenDbTableDto'
原因: 缺少 _ViewImports.cshtmlRazor 引擎使用了错误的类型
解决: 创建 _ViewImports.cshtml 明确指定使用 Core 层的模型
```
### 5.3 模板示例tempModel.cshtml
```cshtml
@model GenDbTableDto
@{
var className = Model.EntityName; // "SongLike"
var classNameRemark = Model.DisplayName; // "歌曲点赞记录"
// 需要忽略的字段
var ignores = new string[] {
"Id", "CreationTime", "CreatorUserId",
"LastModificationTime", "LastModifierUserId",
"DeletionTime", "DeleterUserId",
"IsDeleted", "TenantId"
};
// 过滤并排序列
var tableInfos = Model.TableInfos
.Where(w => !ignores.Contains(w.ColumnName))
.OrderBy(w => w.Position)
.ToList();
}
@functions
{
// 根据列信息获取 C# 类型
string GetType(LowCodeTableInfo appTableInfo)
{
switch (appTableInfo.DatabaseColumnType)
{
case "uniqueidentifier":
return appTableInfo.IsNullable ? "Guid?" : "Guid";
case "int":
return appTableInfo.IsNullable ? "int?" : "int";
case "bigint":
return appTableInfo.IsNullable ? "long?" : "long";
case "datetime":
return appTableInfo.IsNullable ? "DateTime?" : "DateTime";
// ... 更多类型映射
default:
// 使用数据库提供的 C# 类型
return appTableInfo.IsNullable
? appTableInfo.CsType + "?"
: appTableInfo.CsType;
}
}
}
<pre>
namespace @Model.EntityNamespace.Entities.Apps;
/// <summary>
/// @classNameRemark
/// </summary>
[EntityDescription(FieldIgnored = true)]
[Table("@Model.TableName")]
public class @className : DefaultEntityV4
{
@foreach (var item in tableInfos)
{
<text>
/// <summary>
/// @(item.DisplayName ?? item.ColumnName) => 备注: @(item.Describe ?? item.ColumnName)
/// </summary>
public @GetType(item) @item.ColumnName { get; set; }
</text>
}
}
</pre>
```
---
## 六、常见问题与解决方案
### 6.1 生成的类名为空
**现象**:
```csharp
[Table("")]
public class : DefaultEntityV4
```
**原因**:
- `GenDbTableDto.EntityName``TableName` 为 null
- `DatabaseTableService` 未提供默认值
**解决方案**:
- 在 `MergeTableWithConfig` 中为 `EntityName` 提供默认值
- 使用 `PathResolver.GetEntityName()` 应用命名策略
- 从 `schemaTable.Name` 转换为 PascalCase
### 6.2 Razor 模板类型错误
**现象**:
```
InvalidOperationException: The model item is of type 'X' but requires type 'Y'
```
**原因**:
- 缺少 `_ViewImports.cshtml`
- 类型在多个命名空间存在,导致歧义
**解决方案**:
- 创建 `_ViewImports.cshtml`
- 明确指定: `@using MiaoYu.Core.CodeGenerator.Models`
### 6.3 属性访问错误
**现象**:
```
'DbColumnInfo' does not contain a definition for 'DbTypeText'
'string' does not contain a definition for 'Name'
```
**原因**:
- 模板中使用了不存在的属性名
- 属性类型已更改(如 `CsType``Type` 改为 `string`
**解决方案**:
- 检查模型定义,使用正确的属性名
- `DbTypeText``DatabaseColumnType`
- `CsType.Name``CsType` (因为已是字符串)
### 6.4 数据库过滤不生效
**现象**:
- 选择 "Admin" 数据库,仍显示所有数据库的表
**原因**:
- `LowCodeTableService.FindListAsync()` 未应用 `dataBase` 过滤
**解决方案**:
```csharp
if (!string.IsNullOrWhiteSpace(pagingSearchInput.Search.DataBase))
{
filteredTables = filteredTables.Where(t =>
t.DataBase == pagingSearchInput.Search.DataBase);
}
```
---
## 七、扩展指南
### 7.1 添加新的数据源
**步骤**:
1. **创建 Provider 实现**:
```csharp
[Component]
public class LiveForumDataSourceProvider : IDataSourceProvider, IScopedDependency
{
private readonly IRepository<SomeEntity> _repository;
public LiveForumDataSourceProvider(IRepository<SomeEntity> repository)
{
_repository = repository;
}
public DataSourceConfig Config => new DataSourceConfig
{
DatabaseKey = DataSourceConstants.LiveForum,
DisplayName = "直播论坛",
EntityNamespace = "MiaoYu.Repository.LiveForum.Entities",
ModelPathTemplate = "{RootPath}\\{Namespace}\\Entities\\{EntityNamePlural}",
// ... 其他配置
NamingStrategy = EntityNamingStrategy.ToPascalCase,
Order = 3
};
public List<DbTableInfo> GetTables()
{
var freeSqlTables = _repository.UnitOfWork.FreeSqlOrm
.DbFirst.GetTablesByDatabase();
return ConvertToDbTableInfoList(freeSqlTables);
}
public object GetDbContext() => _repository.GetContext()!;
private List<DbTableInfo> ConvertToDbTableInfoList(/* ... */)
{
// 转换逻辑
}
}
```
2. **添加常量**:
```csharp
// MiaoYu.Core.CodeGenerator/Abstractions/DataSourceConstants.cs
public static class DataSourceConstants
{
public const string Admin = "Admin";
public const string MiaoYuChat = "MiaoYuChat";
public const string LiveForum = "LiveForum"; // 新增
}
```
3. **自动注册**:
- 使用 `[Component]` 特性,框架会自动注册
- `DataSourceManager` 会自动发现并收集
### 7.2 添加新的命名策略
**步骤**:
1. **扩展枚举**:
```csharp
public enum EntityNamingStrategy
{
KeepOriginal,
ToPascalCase,
ToSnakeCase // 新增: PascalCase → snake_case
}
```
2. **实现转换逻辑**:
```csharp
// PathResolver.cs
public string GetEntityName(string tableName, DataSourceConfig config)
{
var baseName = config.NamingStrategy switch
{
EntityNamingStrategy.ToPascalCase => ConvertToPascalCase(tableName),
EntityNamingStrategy.ToSnakeCase => ConvertToSnakeCase(tableName),
_ => tableName
};
// ...
}
private static string ConvertToSnakeCase(string input)
{
// 实现 PascalCase → snake_case 转换
}
```
### 7.3 自定义模板
**步骤**:
1. **创建新模板**: 复制现有模板并修改
2. **定义模板路径**: 在 `DataSourceConfig` 中配置
3. **添加文件类型**: 扩展 `FileTypeEnum`
4. **实现生成逻辑**: 在 `CodeGenerationService` 中添加对应处理
---
## 八、性能优化建议
### 8.1 缓存策略
1. **表结构缓存**: 20 分钟有效期,避免频繁查询数据库
2. **配置文件缓存**: 考虑使用 `MemoryCache` 缓存 JSON 配置
3. **模板编译缓存**: Razor 引擎已内置缓存
### 8.2 并发处理
1. **读写分离**: 读取表结构和生成代码不影响数据库写入
2. **异步处理**: 所有 IO 操作使用 `async/await`
3. **并行生成**: 批量生成时可使用 `Parallel.ForEach`
---
## 九、总结
### 9.1 核心优势
1. **多数据源支持**: 轻松扩展新数据源
2. **配置驱动**: 灵活的路径和命名策略
3. **模板化**: Razor 模板,易于定制
4. **文件化配置**: JSON 配置文件,支持版本控制
5. **缓存优化**: 减少数据库查询,提升性能
### 9.2 设计亮点
1. **Provider 模式**: 实现数据源的插件化
2. **双层合并**: 数据库 schema + 用户配置
3. **命名策略**: 智能转换表名为类名
4. **统一模型**: `GenDbTableDto` 作为中间层
5. **路径解析**: 占位符系统,灵活配置
### 9.3 后续改进方向
1. **LiveForum 数据源**: 补充第三个数据源实现
2. **模板管理**: 支持在线编辑和版本管理
3. **增量生成**: 只生成变更的部分
4. **代码预览**: 生成前预览代码
5. **批量操作**: 支持批量表生成
---
## 十、参考资料
### 10.1 核心文件清单
**API 层**:
- `MiaoYu.Api.Admin/Controllers/DevelopmentTools/CodeGenerationController.cs`
- `MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableService.cs`
- `MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableInfoService.cs`
- `MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/AdminDataSourceProvider.cs`
- `MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/MiaoYuChatDataSourceProvider.cs`
**核心层**:
- `MiaoYu.Core.CodeGenerator/CoreCodeGeneratorStartup.cs`
- `MiaoYu.Core.CodeGenerator/Core/DataSourceManager.cs`
- `MiaoYu.Core.CodeGenerator/Core/PathResolver.cs`
- `MiaoYu.Core.CodeGenerator/Services/CodeGenerationService.cs`
- `MiaoYu.Core.CodeGenerator/Services/DatabaseTableService.cs`
- `MiaoYu.Core.CodeGenerator/Services/TableMetaConfigService.cs`
- `MiaoYu.Core.CodeGenerator/Services/TableSchemaCache.cs`
**模板**:
- `MiaoYu.Api.Admin/wwwroot/code_generation/template/_ViewImports.cshtml`
- `MiaoYu.Api.Admin/wwwroot/code_generation/template/tempModel.cshtml`
### 10.2 配置文件示例
**表元信息配置**: `configs/Admin/song_likes.json`
```json
{
"DisplayName": "歌曲点赞记录",
"EntityName": "SongLike",
"Remark": "记录用户对歌曲的点赞行为",
"IsCover": true,
"Columns": {
"SongId": {
"DisplayName": "歌曲ID",
"Describe": "被点赞的歌曲标识",
"CsField": "SongId",
"IsTableSelect": true,
"IsTableColumnShow": true
}
}
}
```
---
**文档版本**: v1.0
**最后更新**: 2025-11-07
**维护者**: AI Assistant