From 9f5f837a4baacef2b321ef064f751f1f75a9cd81 Mon Sep 17 00:00:00 2001 From: zpc Date: Sat, 8 Nov 2025 04:30:38 +0800 Subject: [PATCH] 12 --- .../MiaoYu.Api.Admin/ApiAdminStartup.cs | 1 + .../LowCode/Abstractions/DataSourceConfig.cs | 83 - .../Abstractions/DataSourceConstants.cs | 23 - .../Abstractions/EntityNamingStrategy.cs | 18 - .../Abstractions/IDataSourceProvider.cs | 25 - .../LowCode/Core/DataSourceExtensions.cs | 44 - .../LowCode/Core/DataSourceManager.cs | 70 - .../LowCode/Core/PathResolver.cs | 67 - .../LowCode/ICodeGenerationService.cs | 124 -- .../LowCode/Impl/CodeGenerationService.cs | 700 --------- .../LowCode/Impl/DatabaseTableService.cs | 98 -- .../LowCode/Impl/LowCodeTableInfoService.cs | 244 ++- .../LowCode/Impl/LowCodeTableService.cs | 251 ++- .../Providers/AdminDataSourceProvider.cs | 42 +- .../Providers/MiaoYuChatDataSourceProvider.cs | 44 +- .../CodeGenerationController.cs | 21 +- .../MiaoYu.Api.Admin/MiaoYu.Api.Admin.csproj | 1 + .../MiaoYu.Api.Admin/MiaoYu.Api.Admin.xml | 595 +------ admin-server/MiaoYu.Api.Admin/Usings.cs | 6 +- .../template/_ViewImports.cshtml | 3 + .../code_generation/template/tempModel.cshtml | 6 +- .../templatev4/_ViewImports.cshtml | 3 + .../templatev4/tempModel.cshtml | 8 +- .../Abstractions/DataSourceConstants.cs | 1 - .../CoreCodeGeneratorStartup.cs | 30 + .../MiaoYu.Core.CodeGenerator.xml | 26 +- .../Services/CodeGenerationService.cs | 20 +- .../Services/DatabaseTableService.cs | 31 +- .../Services/TableMetaConfigService.cs | 2 +- .../Services/TableSchemaCache.cs | 2 +- .../MiaoYu.Core.CodeGenerator/Usings.cs | 3 + admin-server/代码生成器架构说明.md | 1386 +++++++++++++++++ admin-server/代码生成器流程文档.md | 885 +++++++++++ 33 files changed, 2891 insertions(+), 1972 deletions(-) delete mode 100644 admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/DataSourceConfig.cs delete mode 100644 admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/DataSourceConstants.cs delete mode 100644 admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/EntityNamingStrategy.cs delete mode 100644 admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/IDataSourceProvider.cs delete mode 100644 admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/DataSourceExtensions.cs delete mode 100644 admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/DataSourceManager.cs delete mode 100644 admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/PathResolver.cs delete mode 100644 admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/ICodeGenerationService.cs delete mode 100644 admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/CodeGenerationService.cs delete mode 100644 admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/DatabaseTableService.cs create mode 100644 admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/template/_ViewImports.cshtml create mode 100644 admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/templatev4/_ViewImports.cshtml create mode 100644 admin-server/MiaoYu.Core.CodeGenerator/CoreCodeGeneratorStartup.cs create mode 100644 admin-server/代码生成器架构说明.md create mode 100644 admin-server/代码生成器流程文档.md diff --git a/admin-server/MiaoYu.Api.Admin/ApiAdminStartup.cs b/admin-server/MiaoYu.Api.Admin/ApiAdminStartup.cs index 130cf7c..0e75b58 100644 --- a/admin-server/MiaoYu.Api.Admin/ApiAdminStartup.cs +++ b/admin-server/MiaoYu.Api.Admin/ApiAdminStartup.cs @@ -11,6 +11,7 @@ namespace MiaoYu.Api.Admin; [ImportStartupModule< CoreQuartzStartup, CoreRazorStartup, + CoreCodeGeneratorStartup, CoreRedisStartup, CoreIdentityStartup, AdminRepositoryStartup, diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/DataSourceConfig.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/DataSourceConfig.cs deleted file mode 100644 index 8b30f1e..0000000 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/DataSourceConfig.cs +++ /dev/null @@ -1,83 +0,0 @@ -namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions; - -/// -/// 数据源配置 -/// -public class DataSourceConfig -{ - /// - /// 数据库标识(如:Admin, MiaoYuChat, LiveForum) - /// - public string DatabaseKey { get; set; } = string.Empty; - - /// - /// 显示名称 - /// - public string DisplayName { get; set; } = string.Empty; - - /// - /// 实体项目命名空间 - /// - public string EntityNamespace { get; set; } = string.Empty; - - /// - /// 实体类路径模板(支持占位符:{RootPath}, {Namespace}, {EntityName}, {EntityNamePlural}, {TableName}) - /// - public string ModelPathTemplate { get; set; } = string.Empty; - - /// - /// 服务层路径模板 - /// - public string ServicePathTemplate { get; set; } = string.Empty; - - /// - /// 控制器路径模板 - /// - public string ControllerPathTemplate { get; set; } = string.Empty; - - /// - /// 前端Index页面路径模板 - /// - public string ClientIndexPathTemplate { get; set; } = string.Empty; - - /// - /// 前端Info页面路径模板 - /// - public string ClientInfoPathTemplate { get; set; } = string.Empty; - - /// - /// 前端Service路径模板 - /// - public string ClientServicePathTemplate { get; set; } = string.Empty; - - /// - /// 代码生成模板目录 - /// - public string TemplatePath { get; set; } = string.Empty; - - /// - /// 实体类命名规则(保持原名 or 驼峰转换) - /// - public EntityNamingStrategy NamingStrategy { get; set; } - - /// - /// 排序权重(数字越小越靠前) - /// - public int Order { get; set; } - - /// - /// 是否启用实体类名前缀(用于避免多数据源同名表冲突) - /// - public bool EnableEntityPrefix { get; set; } = false; - - /// - /// 实体类名前缀(如:Chat、Forum) - /// - public string EntityPrefix { get; set; } = string.Empty; - - /// - /// 是否使用复数形式的路径(如 /Users/ vs /User/) - /// - public bool UsesPluralPath { get; set; } = true; -} - diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/DataSourceConstants.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/DataSourceConstants.cs deleted file mode 100644 index f0a42ee..0000000 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/DataSourceConstants.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions; - -/// -/// 数据源常量 -/// -public static class DataSourceConstants -{ - /// - /// 后台管理系统数据库 - /// - public const string Admin = "Admin"; - - /// - /// 喵语AI聊天数据库 - /// - public const string MiaoYuChat = "MiaoYuChat"; - - /// - /// 直播论坛数据库(预留) - /// - public const string LiveForum = "LiveForum"; -} - diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/EntityNamingStrategy.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/EntityNamingStrategy.cs deleted file mode 100644 index a0e332c..0000000 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/EntityNamingStrategy.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions; - -/// -/// 实体命名策略 -/// -public enum EntityNamingStrategy -{ - /// - /// 保持数据库表名原样 - /// - KeepOriginal = 0, - - /// - /// 转换为驼峰命名(去除前缀下划线) - /// - ToPascalCase = 1 -} - diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/IDataSourceProvider.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/IDataSourceProvider.cs deleted file mode 100644 index a23e144..0000000 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Abstractions/IDataSourceProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions; - -/// -/// 数据源提供者接口 -/// -public interface IDataSourceProvider -{ - /// - /// 数据源配置 - /// - DataSourceConfig Config { get; } - - /// - /// 获取该数据源的所有表信息 - /// - /// 表信息列表 - List GetTables(); - - /// - /// 获取DbContext(用于获取FreeSql实例) - /// - /// 数据库上下文 - DbContext GetDbContext(); -} - diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/DataSourceExtensions.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/DataSourceExtensions.cs deleted file mode 100644 index c60ef9a..0000000 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/DataSourceExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -using MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Abstractions; - -namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Core; - -/// -/// 数据源扩展方法 -/// -public static class DataSourceExtensions -{ - /// - /// 从 Schema 中提取数据库标识 - /// - /// Schema字符串 - /// 数据库标识 - 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; - } - - /// - /// 清理 Schema(移除数据库标识) - /// - /// Schema字符串 - /// 清理后的Schema - public static string CleanSchema(this string schema) - { - if (string.IsNullOrWhiteSpace(schema)) - return schema; - - return schema.Contains(".") - ? schema.Split('.')[0] - : schema; - } -} - diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/DataSourceManager.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/DataSourceManager.cs deleted file mode 100644 index d10cea7..0000000 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/DataSourceManager.cs +++ /dev/null @@ -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; - -/// -/// 数据源管理器 -/// -[Component] -public class DataSourceManager : IScopedDependency -{ - private readonly IEnumerable _providers; - - /// - /// 构造函数(通过依赖注入自动收集所有IDataSourceProvider实现) - /// - /// 所有数据源提供者 - public DataSourceManager(IEnumerable providers) - { - _providers = providers.OrderBy(p => p.Config.Order); - } - - /// - /// 获取所有数据源提供者 - /// - /// 数据源提供者集合 - public IEnumerable GetAllProviders() => _providers; - - /// - /// 根据数据库标识获取数据源提供者 - /// - /// 数据库标识(如:Admin, MiaoYuChat) - /// 数据源提供者,如果未找到返回null - public IDataSourceProvider? GetProvider(string databaseKey) - { - if (string.IsNullOrWhiteSpace(databaseKey)) - return null; - - return _providers.FirstOrDefault(p => - p.Config.DatabaseKey.Equals(databaseKey, StringComparison.OrdinalIgnoreCase)); - } - - /// - /// 获取所有数据源的表信息 - /// - /// 所有表信息列表 - public List GetAllTables() - { - var allTables = new List(); - - 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; - } -} - diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/PathResolver.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/PathResolver.cs deleted file mode 100644 index 3be9a6a..0000000 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Core/PathResolver.cs +++ /dev/null @@ -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; - -/// -/// 路径解析器 -/// -[Component] -public class PathResolver : IScopedDependency -{ - private readonly IWebHostEnvironment _environment; - - public PathResolver(IWebHostEnvironment environment) - { - _environment = environment; - } - - /// - /// 解析路径模板 - /// - /// 路径模板(支持占位符) - /// 数据源配置 - /// 表名 - /// 解析后的完整路径 - 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); - } - - /// - /// 根据命名策略和配置获取实体名 - /// - /// 表名 - /// 数据源配置 - /// 实体名 - 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; - } -} - diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/ICodeGenerationService.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/ICodeGenerationService.cs deleted file mode 100644 index f293491..0000000 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/ICodeGenerationService.cs +++ /dev/null @@ -1,124 +0,0 @@ -namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode; - -/// -/// 代码生成服务 -/// -public interface ICodeGenerationService : IScopedDependency -{ - /// - /// 生成上下文集合 - /// - /// - PagingView GetGenContextDtos(int page, int size, GenFormDto search); - - /// - /// 获取表字段集合 - /// - /// - /// 数据库标识(可选) - /// - GenDbTableDto GetGenContextDtoByTableName(string tableName, string? databaseKey = null); - - /// - /// 根据 lowCodeTable 填充路径 - /// - /// - /// - LowCodeTable FillPathByLowCodeTable(LowCodeTable lowCodeTable); - - /// - /// 获取代码生成上下文 - /// - /// - /// - GenDbTableDto GetGenContextDto(GenFormDto genFormDto); - - /// - /// 生成 model - /// - /// - /// - Task GenModelAsync(GenFormDto genFormDto); - - /// - /// 生成 service - /// - /// - /// - Task GenServiceAsync(GenFormDto genFormDto); - - /// - /// 生成 controller - /// - /// - /// - Task GenControllerAsync(GenFormDto genFormDto); - - /// - /// 生成 serviceJs - /// - /// - /// - Task GenServiceJsAsync(GenFormDto genFormDto); - - /// - /// 生成 Index - /// - /// - /// - Task GenIndexAsync(GenFormDto genFormDto); - - /// - /// 生成 Info - /// - /// - /// - Task GenInfoAsync(GenFormDto genFormDto); - - /// - /// 获取代码 - /// - /// - /// - Task GetCodeByTypeAndTableNameAsync(GenFormDto genFormDto); - - /// - /// 下载 - /// - /// - /// - Task<(byte[] codeBytes, string contentType, string fileName)> DownloadAsync(GenFormDto genFormDto); - - /// - /// 根据类型下载类型下所有的代码 - /// - /// - /// - Task<(byte[] codeBytes, string contentType, string fileName)> DownloadAllAsync(GenFormDto genFormDto); - - /// - /// 创建所有代码文件 - /// - /// - /// - Task CreateAllCodeFilesAsync(GenFormDto genFormDto); - - /// - /// 创建数据字典文件 excel - /// - /// - (byte[] excel, string dataBase) CreateDataDictionary(); - - /// - /// 代码生成自动导入项目 - /// - /// - /// - Task AutoImprotProjectAsync(GenFormDto genFormDto); - - /// - /// 获取所有数据库列表 - /// - /// - List GetAllDataSources(); -} diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/CodeGenerationService.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/CodeGenerationService.cs deleted file mode 100644 index d9021e4..0000000 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/CodeGenerationService.cs +++ /dev/null @@ -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; - -/// -/// 代码生成服务 -/// -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; - - /// - /// 构造函数 - /// - /// 数据库表服务 - /// Razor视图渲染器 - /// Web宿主环境 - /// 数据源管理器 - /// 路径解析器 - public CodeGenerationService(IDatabaseTableService databaseTableService, - IRazorViewRender razorViewRender, - IWebHostEnvironment webHostEnvironment, - DataSourceManager dataSourceManager, - PathResolver pathResolver) - { - _databaseTableService = databaseTableService; - _razorViewRender = razorViewRender; - _webRootPath = webHostEnvironment.WebRootPath; - _dataSourceManager = dataSourceManager; - _pathResolver = pathResolver; - } - - /// - /// 生成上下文集合 - /// - /// - public PagingView GetGenContextDtos(int page, int size, GenFormDto search) - { - var PagingView = new PagingView(page, size); - var result = new List>(); - - 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(); - 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; - } - - /// - /// 获取所有表集合信息 - /// - /// 表名 - /// 数据库标识(可选) - /// - 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; - } - - /// - /// 根据 lowCodeTable 填充路径(支持多数据源) - /// - /// 低代码表配置 - /// 填充路径后的低代码表配置 - 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; - } - - /// - /// 获取代码生成上下文 - /// - /// - /// - public GenDbTableDto GetGenContextDto(GenFormDto genFormDto) - { - var tableName = genFormDto.TableName; - var tableInfo = GetGenContextDtoByTableName(tableName, genFormDto.DataBase); - - if (tableInfo == null) return null; - tableInfo.Namespace = Tools.GetNamespacePrefix(); - return tableInfo; - } - - /// - /// 生成model(支持多数据源) - /// - /// - /// - public async Task 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)); - } - - /// - /// 生成service(支持多数据源) - /// - /// - /// - public async Task 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)); - } - - /// - /// 生成controller(支持多数据源) - /// - /// - /// - public async Task 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)); - } - - /// - /// 生成service js(支持多数据源) - /// - /// - /// - public async Task 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)); - } - - /// - /// 生成 index vue(支持多数据源) - /// - /// - /// - public async Task 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)); - } - - /// - /// 生成 info vue(支持多数据源) - /// - /// - /// - public async Task 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)); - } - - - /// - /// 获取代码 - /// - /// - /// - public async Task 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 - }; - } - - /// - /// 创建所有代码文件 - /// - /// - /// - public async Task 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; - } - - /// - /// 获取下载代码信息 - /// - /// - /// - 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); - } - - /// - /// 根据类型下载所有代码 - /// - /// - /// - 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"); - } - - /// - /// 创建数据库字典文件 - /// - /// - 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); - } - - /// - /// 自动导入文件到项目 - /// - /// - /// - 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(); - - foreach (var fileType in fileTyps) - { - var (filePath, oldName, replaceName) = GetFileAbsolutelyPath( - genFormDto.TableName, fileType, genFormDto.DataBase); - await SaveToFileAsync(genFormDto.TableName, fileType, filePath, oldName, replaceName); - } - } - - /// - /// 获取所有数据库列表 - /// - /// - public List 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 私有方法 - - /// - /// 清除多余符号 - /// - /// - /// - private string ClearSymbol(StringBuilder code) - { - return code - .ToString() - .Replace("
", "")
-                .Replace("
", "") - .Trim(); - } - - /// - /// 创建代码文件 - /// - /// - /// - private async Task 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; - } - - /// - /// 获取代码文件名称 - /// - /// - /// - 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 - }; - } - - /// - /// 获取要生成文件的绝对路径 - /// - /// - /// - /// 数据库标识(可选) - /// - 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); - } - - /// - /// 保存到文件 - /// - /// - /// - /// - /// - /// - /// - 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); - } - - /// - /// 获取枚举上的描述特性 - /// - /// - /// - private string GetEnumDescription(FileTypeEnum type) - { - return type.GetType().GetField(Enum.GetName(type)).GetCustomAttribute().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 - -} diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/DatabaseTableService.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/DatabaseTableService.cs deleted file mode 100644 index d9d388b..0000000 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/DatabaseTableService.cs +++ /dev/null @@ -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; - -/// -/// 数据库表服务 -/// -public class DatabaseTableService : IDatabaseTableService -{ - private readonly string TableInfoKey = "TableInfo:GenDbTableDto"; - private readonly int CacheTime = 12; - - private readonly IMemoryCache _memoryCache; - private readonly IRepository _lowCodeTableRepository; - private readonly IRepository _lowCodeTableInfoRepository; - - private readonly IRepository _imageConfig; - private readonly DataSourceManager _dataSourceManager; - - /// - /// 构造函数 - /// - /// 内存缓存 - /// 低代码表仓储 - /// 低代码表字段仓储 - /// 图片配置仓储 - /// 数据源管理器 - public DatabaseTableService( - IMemoryCache memoryCache, - IRepository lowCodeTableRepository, - IRepository lowCodeTableInfoRepository, - IRepository imageConfig, - DataSourceManager dataSourceManager - ) - { - _memoryCache = memoryCache; - _lowCodeTableRepository = lowCodeTableRepository; - _lowCodeTableInfoRepository = lowCodeTableInfoRepository; - _imageConfig = imageConfig; - _dataSourceManager = dataSourceManager; - } - - /// - /// 获取所有的表 包含表下面的列(支持多数据源) - /// - /// 所有表信息列表 - public virtual List GetAllTableInfos() - { - return _dataSourceManager.GetAllTables(); - } - - /// - /// 获取所有的表 包含表下面的列 - /// - /// - public virtual List GetAllTables() - { - var tables = _lowCodeTableRepository.ToListAll(); - var tableColumns = _lowCodeTableInfoRepository.ToListAll(); - - var result = new List(); - foreach (var item in tables) - { - var table = item.MapTo(); - 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; - } - - /// - /// 获取表信息根据缓存 - /// - /// - public List GetAllTablesByCache() => _memoryCache.Get>(TableInfoKey) ?? GetAllTables(); - - /// - /// 清空所有表缓存信息 - /// - /// - public bool ClearAllTablesByCache() - { - _memoryCache.Remove(TableInfoKey); - return true; - } - - /// - /// 获取数据库名称 - /// - /// - public string? GetDatabaseName() => _lowCodeTableRepository.GetContext()?.Database.GetDbConnection().Database; - - -} diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableInfoService.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableInfoService.cs index 282bedf..3c98d55 100644 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableInfoService.cs +++ b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableInfoService.cs @@ -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; /// public class LowCodeTableInfoService : ApplicationService> { - private readonly IDatabaseTableService _databaseTableService; + private readonly CoreIDatabaseTableService _databaseTableService; private readonly IRepository _lowCodeTableRepository; + private readonly CoreITableMetaConfigService _tableMetaConfigService; public LowCodeTableInfoService( - IDatabaseTableService databaseTableService, + CoreIDatabaseTableService databaseTableService, IRepository defaultRepository, - IRepository lowCodeTableRepository) : base(defaultRepository) + IRepository lowCodeTableRepository, + CoreITableMetaConfigService tableMetaConfigService) : base(defaultRepository) { _databaseTableService = databaseTableService; _lowCodeTableRepository = lowCodeTableRepository; + _tableMetaConfigService = tableMetaConfigService; } /// - /// 获取列表数据 + /// 获取列表数据(从 Core 层查询表结构并合并配置) /// /// /// public async Task FindListAsync(PagingSearchInput 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 + { + ["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 + }; } /// @@ -89,10 +134,7 @@ public class LowCodeTableInfoService : ApplicationService - { - 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 - /// 变更数据 + /// 变更数据(保存到配置文件) /// /// /// - public Task ChangeAsync(List lowCodeTableInfos) + public async Task ChangeAsync(List 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); + } + + /// + /// 保存表元信息到配置文件 + /// + private async Task SaveTableMetaConfigAsync(CoreGenDbTableDto tableDto) + { + if (string.IsNullOrEmpty(tableDto.DataBase) || string.IsNullOrEmpty(tableDto.TableName)) + return; + + var config = new CoreTableMetaConfig + { + DisplayName = tableDto.DisplayName, + EntityName = tableDto.EntityName, + Remark = tableDto.Remark, + ModelPath = tableDto.ModelPath, + ServicePath = tableDto.ServicePath, + ControllerPath = tableDto.ControllerPath, + ClientIndexPath = tableDto.ClientIndexPath, + ClientInfoPath = tableDto.ClientInfoPath, + ClientServicePath = tableDto.ClientServicePath, + IsCover = tableDto.IsCover ?? false, + Columns = new Dictionary() + }; + + // 保存列配置 + if (tableDto.TableInfos != null) + { + foreach (var column in tableDto.TableInfos) + { + if (!string.IsNullOrEmpty(column.ColumnName)) + { + config.Columns[column.ColumnName] = new CoreColumnMetaConfig + { + DisplayName = column.DisplayName, + Describe = column.Describe, + CsField = column.CsField, + IsTableSelect = column.IsTableSelect, + IsImageId = column.IsImageId, + IsTableColumnShow = column.IsTableColumnShow + }; + } + } + } + + await _tableMetaConfigService.SaveConfigAsync(tableDto.DataBase!, tableDto.TableName!, config); } diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableService.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableService.cs index 177b0d6..ec7e03c 100644 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableService.cs +++ b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Impl/LowCodeTableService.cs @@ -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> { private readonly IRepository _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 defaultRepository, LowCodeTableInfoService lowCodeTableInfoService, IRepository 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; } /// - /// 获取列表数据 + /// 获取列表数据(从 Core 层查询表结构并合并配置) /// /// /// public async Task FindListAsync(PagingSearchInput 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>(); + foreach (var t in pagedTablesList) + { + var id = await GetOrCreateTableId(t.TableName, t.DataBase); + pagedTables.Add(new Dictionary + { + ["id"] = id, + ["tableName"] = t.TableName, + ["displayName"] = t.DisplayName, + ["entityName"] = t.EntityName, + ["remark"] = t.Remark, + ["lastModificationTime"] = DateTime.Now.ToString("yyyy-MM-dd"), + ["creationTime"] = DateTime.Now.ToString("yyyy-MM-dd"), + ["dataBase"] = t.DataBase + }); + } + + return new PagingView(pagingSearchInput.Page, pagingSearchInput.Size) + { + Total = filteredTables.Count(), + DataSource = pagedTables, + PageCount = (filteredTables.Count() + pagingSearchInput.Size - 1) / pagingSearchInput.Size + }; + } + + /// + /// 获取或创建表的 ID(用于兼容性) + /// + private async Task GetOrCreateTableId(string? tableName, string? dataBase) + { + if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(dataBase)) + return Guid.NewGuid(); + + var existingTable = await _defaultRepository.FindAsync(t => + t.TableName == tableName && t.DataBase == dataBase); + + return existingTable?.Id ?? Guid.NewGuid(); } /// @@ -80,12 +136,32 @@ public class LowCodeTableService : ApplicationService> /// public async Task DeleteListAsync(List 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) + { + // 配置文件可能不存在,忽略异常 + } + } + } } /// @@ -93,6 +169,9 @@ public class LowCodeTableService : ApplicationService> /// public async Task SynchronizationAsync() { + // 刷新表结构缓存(从数据库重新加载表结构) + _tableSchemaCache.RefreshCache(); + var allTables = _databaseTableService.GetAllTableInfos(); var oldAllTables = await _defaultRepository.ToListAllAsync(); @@ -102,12 +181,12 @@ public class LowCodeTableService : ApplicationService> 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> 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> 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> await _defaultRepository.UpdateRangeAsync(updateList); } + // 同步列信息 foreach (var item in ids) { await _lowCodeTableInfoService.SynchronizationColumnByTableIdAsync(item, true); } - _databaseTableService.ClearAllTablesByCache(); + // 注意:这里不需要清除缓存,因为缓存在开始时已经刷新过了 + // 清除缓存会导致下次查询重新加载,没有必要 + } + + /// + /// 将下划线命名转换为 PascalCase + /// + private static string ConvertToPascalCase(string input) + { + if (string.IsNullOrEmpty(input)) + return input; + + var words = input.Split(new[] { '_', '-' }, StringSplitOptions.RemoveEmptyEntries); + var result = new System.Text.StringBuilder(); + + foreach (var word in words) + { + if (word.Length > 0) + { + result.Append(char.ToUpper(word[0])); + if (word.Length > 1) + { + result.Append(word.Substring(1).ToLower()); + } + } + } + + return result.ToString(); } /// @@ -192,7 +299,7 @@ public class LowCodeTableService : ApplicationService> var res = new Dictionary(); 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> return res; } + /// + /// 根据 lowCodeTable 填充路径(支持多数据源) + /// + private void FillPathByLowCodeTable(LowCodeTable lowCodeTable) + { + var provider = _dataSourceManager.GetProvider(lowCodeTable.DataBase ?? "Admin"); + if (provider == null) + { + return; + } + + if (string.IsNullOrEmpty(lowCodeTable.ModelPath)) + lowCodeTable.ModelPath = _pathResolver.ResolvePath(provider.Config.ModelPathTemplate, provider.Config, lowCodeTable.TableName!); + + if (string.IsNullOrEmpty(lowCodeTable.ServicePath)) + lowCodeTable.ServicePath = _pathResolver.ResolvePath(provider.Config.ServicePathTemplate, provider.Config, lowCodeTable.TableName!); + + if (string.IsNullOrEmpty(lowCodeTable.ControllerPath)) + lowCodeTable.ControllerPath = _pathResolver.ResolvePath(provider.Config.ControllerPathTemplate, provider.Config, lowCodeTable.TableName!); + + if (string.IsNullOrEmpty(lowCodeTable.ClientIndexPath)) + lowCodeTable.ClientIndexPath = _pathResolver.ResolvePath(provider.Config.ClientIndexPathTemplate, provider.Config, lowCodeTable.TableName!); + + 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!); + } + /// /// 保存数据 /// diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/AdminDataSourceProvider.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/AdminDataSourceProvider.cs index 70ac37a..7976a35 100644 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/AdminDataSourceProvider.cs +++ b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/AdminDataSourceProvider.cs @@ -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 GetTables() + public List 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 ConvertToDbTableInfoList(List freeSqlTables) + { + var result = new List(); + 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; + } } diff --git a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/MiaoYuChatDataSourceProvider.cs b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/MiaoYuChatDataSourceProvider.cs index 2bf40c8..db73620 100644 --- a/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/MiaoYuChatDataSourceProvider.cs +++ b/admin-server/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/MiaoYuChatDataSourceProvider.cs @@ -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 GetTables() + public List 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 ConvertToDbTableInfoList(List freeSqlTables) + { + var result = new List(); + 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; + } } diff --git a/admin-server/MiaoYu.Api.Admin/Controllers/DevelopmentTools/CodeGenerationController.cs b/admin-server/MiaoYu.Api.Admin/Controllers/DevelopmentTools/CodeGenerationController.cs index 40eb6fc..8b83e1e 100644 --- a/admin-server/MiaoYu.Api.Admin/Controllers/DevelopmentTools/CodeGenerationController.cs +++ b/admin-server/MiaoYu.Api.Admin/Controllers/DevelopmentTools/CodeGenerationController.cs @@ -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; /// /// 代码生成器控制器 /// [ControllerDescriptor(MenuId = "31")] -public class CodeGenerationController : AdminControllerBase +public class CodeGenerationController : AdminControllerBase { - public CodeGenerationController(ICodeGenerationService defaultService) : base(defaultService) + public CodeGenerationController(CoreICodeGenerationService defaultService) : base(defaultService) { @@ -18,7 +23,7 @@ public class CodeGenerationController : AdminControllerBase [ActionDescriptor(DisplayName = "获取数据库列表")] [HttpGet] - public List GetDatabasesAsync() + public List GetDatabasesAsync() { return _defaultService.GetAllDataSources(); } @@ -32,7 +37,7 @@ public class CodeGenerationController : AdminControllerBase [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 [ActionDescriptor(DisplayName = "获取代码")] [HttpPost] - public async Task GetCodeAsync([FromBody] GenFormDto genFormDto) + public async Task GetCodeAsync([FromBody] CoreGenFormDto genFormDto) { var code = await _defaultService.GetCodeByTypeAndTableNameAsync(genFormDto); @@ -85,7 +90,7 @@ public class CodeGenerationController : AdminControllerBase [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 [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 [ActionDescriptor(DisplayName = "生成代码并自动导入项目")] [HttpPost] - public async Task AutoImprotProjectAsync([FromBody] GenFormDto genFormDto) + public async Task AutoImprotProjectAsync([FromBody] CoreGenFormDto genFormDto) { await _defaultService.AutoImprotProjectAsync(genFormDto); return true; diff --git a/admin-server/MiaoYu.Api.Admin/MiaoYu.Api.Admin.csproj b/admin-server/MiaoYu.Api.Admin/MiaoYu.Api.Admin.csproj index 381f37d..0b17774 100644 --- a/admin-server/MiaoYu.Api.Admin/MiaoYu.Api.Admin.csproj +++ b/admin-server/MiaoYu.Api.Admin/MiaoYu.Api.Admin.csproj @@ -19,6 +19,7 @@ + diff --git a/admin-server/MiaoYu.Api.Admin/MiaoYu.Api.Admin.xml b/admin-server/MiaoYu.Api.Admin/MiaoYu.Api.Admin.xml index 30ed990..2ff2296 100644 --- a/admin-server/MiaoYu.Api.Admin/MiaoYu.Api.Admin.xml +++ b/admin-server/MiaoYu.Api.Admin/MiaoYu.Api.Admin.xml @@ -1384,561 +1384,6 @@ - - - 数据源配置 - - - - - 数据库标识(如:Admin, MiaoYuChat, LiveForum) - - - - - 显示名称 - - - - - 实体项目命名空间 - - - - - 实体类路径模板(支持占位符:{RootPath}, {Namespace}, {EntityName}, {EntityNamePlural}, {TableName}) - - - - - 服务层路径模板 - - - - - 控制器路径模板 - - - - - 前端Index页面路径模板 - - - - - 前端Info页面路径模板 - - - - - 前端Service路径模板 - - - - - 代码生成模板目录 - - - - - 实体类命名规则(保持原名 or 驼峰转换) - - - - - 排序权重(数字越小越靠前) - - - - - 是否启用实体类名前缀(用于避免多数据源同名表冲突) - - - - - 实体类名前缀(如:Chat、Forum) - - - - - 是否使用复数形式的路径(如 /Users/ vs /User/) - - - - - 数据源常量 - - - - - 后台管理系统数据库 - - - - - 喵语AI聊天数据库 - - - - - 直播论坛数据库(预留) - - - - - 实体命名策略 - - - - - 保持数据库表名原样 - - - - - 转换为驼峰命名(去除前缀下划线) - - - - - 数据源提供者接口 - - - - - 数据源配置 - - - - - 获取该数据源的所有表信息 - - 表信息列表 - - - - 获取DbContext(用于获取FreeSql实例) - - 数据库上下文 - - - - 数据源扩展方法 - - - - - 从 Schema 中提取数据库标识 - - Schema字符串 - 数据库标识 - - - - 清理 Schema(移除数据库标识) - - Schema字符串 - 清理后的Schema - - - - 数据源管理器 - - - - - 构造函数(通过依赖注入自动收集所有IDataSourceProvider实现) - - 所有数据源提供者 - - - - 获取所有数据源提供者 - - 数据源提供者集合 - - - - 根据数据库标识获取数据源提供者 - - 数据库标识(如:Admin, MiaoYuChat) - 数据源提供者,如果未找到返回null - - - - 获取所有数据源的表信息 - - 所有表信息列表 - - - - 路径解析器 - - - - - 解析路径模板 - - 路径模板(支持占位符) - 数据源配置 - 表名 - 解析后的完整路径 - - - - 根据命名策略和配置获取实体名 - - 表名 - 数据源配置 - 实体名 - - - - 代码生成服务 - - - - - 生成上下文集合 - - - - - - 获取表字段集合 - - - 数据库标识(可选) - - - - - 根据 lowCodeTable 填充路径 - - - - - - - 获取代码生成上下文 - - - - - - - 生成 model - - - - - - - 生成 service - - - - - - - 生成 controller - - - - - - - 生成 serviceJs - - - - - - - 生成 Index - - - - - - - 生成 Info - - - - - - - 获取代码 - - - - - - - 下载 - - - - - - - 根据类型下载类型下所有的代码 - - - - - - - 创建所有代码文件 - - - - - - - 创建数据字典文件 excel - - - - - - 代码生成自动导入项目 - - - - - - - 获取所有数据库列表 - - - - - - 代码生成服务 - - - - - 构造函数 - - 数据库表服务 - Razor视图渲染器 - Web宿主环境 - 数据源管理器 - 路径解析器 - - - - 生成上下文集合 - - - - - - 获取所有表集合信息 - - 表名 - 数据库标识(可选) - - - - - 根据 lowCodeTable 填充路径(支持多数据源) - - 低代码表配置 - 填充路径后的低代码表配置 - - - - 获取代码生成上下文 - - - - - - - 生成model(支持多数据源) - - - - - - - 生成service(支持多数据源) - - - - - - - 生成controller(支持多数据源) - - - - - - - 生成service js(支持多数据源) - - - - - - - 生成 index vue(支持多数据源) - - - - - - - 生成 info vue(支持多数据源) - - - - - - - 获取代码 - - - - - - - 创建所有代码文件 - - - - - - - 获取下载代码信息 - - - - - - - 根据类型下载所有代码 - - - - - - - 创建数据库字典文件 - - - - - - 自动导入文件到项目 - - - - - - - 获取所有数据库列表 - - - - - - 清除多余符号 - - - - - - - 创建代码文件 - - - - - - - 获取代码文件名称 - - - - - - - 获取要生成文件的绝对路径 - - - - 数据库标识(可选) - - - - - 保存到文件 - - - - - - - - - - - 获取枚举上的描述特性 - - - - - - - 数据库表服务 - - - - - 构造函数 - - 内存缓存 - 低代码表仓储 - 低代码表字段仓储 - 图片配置仓储 - 数据源管理器 - - - - 获取所有的表 包含表下面的列(支持多数据源) - - 所有表信息列表 - - - - 获取所有的表 包含表下面的列 - - - - - - 获取表信息根据缓存 - - - - - - 清空所有表缓存信息 - - - - - - 获取数据库名称 - - - 服务 Low_Code_Table_InfoService @@ -1946,7 +1391,7 @@ - 获取列表数据 + 获取列表数据(从 Core 层查询表结构并合并配置) @@ -1968,11 +1413,16 @@ - 变更数据 + 变更数据(保存到配置文件) + + + 保存表元信息到配置文件 + + 服务 Low_Code_TableService @@ -1980,11 +1430,16 @@ - 获取列表数据 + 获取列表数据(从 Core 层查询表结构并合并配置) + + + 获取或创建表的 ID(用于兼容性) + + 根据id数组删除 @@ -1997,6 +1452,11 @@ 同步表(支持多数据源) + + + 将下划线命名转换为 PascalCase + + 变更数据 @@ -2011,6 +1471,11 @@ + + + 根据 lowCodeTable 填充路径(支持多数据源) + + 保存数据 @@ -3859,7 +3324,7 @@ - + 获取列表 @@ -3868,21 +3333,21 @@ - + 获取代码 根据 表名 和 类型 - + 下载当前代码 - + 创建代码文件 @@ -3895,7 +3360,7 @@ - + 生成代码并自动导入项目 @@ -4902,7 +4367,7 @@ 版本 - + 获取类型 根据 appTableInfo @@ -4915,7 +4380,7 @@ - + 获取类型 根据 appTableInfo diff --git a/admin-server/MiaoYu.Api.Admin/Usings.cs b/admin-server/MiaoYu.Api.Admin/Usings.cs index e2179a3..db32c09 100644 --- a/admin-server/MiaoYu.Api.Admin/Usings.cs +++ b/admin-server/MiaoYu.Api.Admin/Usings.cs @@ -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; diff --git a/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/template/_ViewImports.cshtml b/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/template/_ViewImports.cshtml new file mode 100644 index 0000000..b35efec --- /dev/null +++ b/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/template/_ViewImports.cshtml @@ -0,0 +1,3 @@ +@* 代码生成器模型命名空间 *@ +@using MiaoYu.Core.CodeGenerator.Models + diff --git a/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/template/tempModel.cshtml b/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/template/tempModel.cshtml index a90e8db..45f7686 100644 --- a/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/template/tempModel.cshtml +++ b/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/template/tempModel.cshtml @@ -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; } } diff --git a/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/templatev4/_ViewImports.cshtml b/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/templatev4/_ViewImports.cshtml new file mode 100644 index 0000000..b35efec --- /dev/null +++ b/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/templatev4/_ViewImports.cshtml @@ -0,0 +1,3 @@ +@* 代码生成器模型命名空间 *@ +@using MiaoYu.Core.CodeGenerator.Models + diff --git a/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/templatev4/tempModel.cshtml b/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/templatev4/tempModel.cshtml index 6169f8b..e4ba173 100644 --- a/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/templatev4/tempModel.cshtml +++ b/admin-server/MiaoYu.Api.Admin/wwwroot/code_generation/templatev4/tempModel.cshtml @@ -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; } } diff --git a/admin-server/MiaoYu.Core.CodeGenerator/Abstractions/DataSourceConstants.cs b/admin-server/MiaoYu.Core.CodeGenerator/Abstractions/DataSourceConstants.cs index 69bb837..274fed3 100644 --- a/admin-server/MiaoYu.Core.CodeGenerator/Abstractions/DataSourceConstants.cs +++ b/admin-server/MiaoYu.Core.CodeGenerator/Abstractions/DataSourceConstants.cs @@ -20,4 +20,3 @@ public static class DataSourceConstants /// public const string LiveForum = "LiveForum"; } - diff --git a/admin-server/MiaoYu.Core.CodeGenerator/CoreCodeGeneratorStartup.cs b/admin-server/MiaoYu.Core.CodeGenerator/CoreCodeGeneratorStartup.cs new file mode 100644 index 0000000..bb26a0f --- /dev/null +++ b/admin-server/MiaoYu.Core.CodeGenerator/CoreCodeGeneratorStartup.cs @@ -0,0 +1,30 @@ +namespace MiaoYu.Core.CodeGenerator; + +/// +/// 代码生成器模块启动器 +/// +public class CoreCodeGeneratorStartup : StartupModule +{ + /// + /// 配置服务 + /// + /// + public override void ConfigureServices(WebApplicationBuilder webApplicationBuilder) + { + var services = webApplicationBuilder.Services; + + // 服务已通过 [Component] 和 IScopedDependency 自动注册 + // 无需手动注册 + } + + /// + /// 配置中间件 + /// + /// + public override void Configure(WebApplication webApplication) + { + // 无需配置中间件 + } +} + + diff --git a/admin-server/MiaoYu.Core.CodeGenerator/MiaoYu.Core.CodeGenerator.xml b/admin-server/MiaoYu.Core.CodeGenerator/MiaoYu.Core.CodeGenerator.xml index cfab24e..85f9ea4 100644 --- a/admin-server/MiaoYu.Core.CodeGenerator/MiaoYu.Core.CodeGenerator.xml +++ b/admin-server/MiaoYu.Core.CodeGenerator/MiaoYu.Core.CodeGenerator.xml @@ -141,6 +141,23 @@ 数据库上下文 + + + 代码生成器模块启动器 + + + + + 配置服务 + + + + + + 配置中间件 + + + 数据源扩展方法 @@ -681,7 +698,7 @@ 获取所有表集合信息 表名 - 数据库标识(可选) + 数据库标识(强烈建议传入,避免多数据源同名表冲突) @@ -817,7 +834,7 @@ 数据库标识(可选) - + 保存到文件 @@ -826,6 +843,7 @@ + 数据库标识(可选) @@ -846,12 +864,14 @@ 数据库表服务 - + 构造函数 表结构缓存 表元信息配置服务 + 数据源管理器 + 路径解析器 日志 diff --git a/admin-server/MiaoYu.Core.CodeGenerator/Services/CodeGenerationService.cs b/admin-server/MiaoYu.Core.CodeGenerator/Services/CodeGenerationService.cs index f898723..87d5066 100644 --- a/admin-server/MiaoYu.Core.CodeGenerator/Services/CodeGenerationService.cs +++ b/admin-server/MiaoYu.Core.CodeGenerator/Services/CodeGenerationService.cs @@ -6,7 +6,8 @@ namespace MiaoYu.Core.CodeGenerator.Services; /// /// 代码生成服务 /// -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 /// 获取所有表集合信息 /// /// 表名 - /// 数据库标识(可选) + /// 数据库标识(强烈建议传入,避免多数据源同名表冲突) /// 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 /// /// /// + /// 数据库标识(可选) /// - 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)) { diff --git a/admin-server/MiaoYu.Core.CodeGenerator/Services/DatabaseTableService.cs b/admin-server/MiaoYu.Core.CodeGenerator/Services/DatabaseTableService.cs index d11acad..344483c 100644 --- a/admin-server/MiaoYu.Core.CodeGenerator/Services/DatabaseTableService.cs +++ b/admin-server/MiaoYu.Core.CodeGenerator/Services/DatabaseTableService.cs @@ -3,10 +3,13 @@ namespace MiaoYu.Core.CodeGenerator.Services; /// /// 数据库表服务 /// -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 _logger; /// @@ -14,14 +17,20 @@ public class DatabaseTableService : IDatabaseTableService /// /// 表结构缓存 /// 表元信息配置服务 + /// 数据源管理器 + /// 路径解析器 /// 日志 public DatabaseTableService( ITableSchemaCache tableSchemaCache, ITableMetaConfigService tableMetaConfigService, + DataSourceManager dataSourceManager, + PathResolver pathResolver, ILogger logger) { _tableSchemaCache = tableSchemaCache; _tableMetaConfigService = tableMetaConfigService; + _dataSourceManager = dataSourceManager; + _pathResolver = pathResolver; _logger = logger; } @@ -91,6 +100,12 @@ public class DatabaseTableService : IDatabaseTableService /// 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; diff --git a/admin-server/MiaoYu.Core.CodeGenerator/Services/TableMetaConfigService.cs b/admin-server/MiaoYu.Core.CodeGenerator/Services/TableMetaConfigService.cs index f4dbf0b..c287e1b 100644 --- a/admin-server/MiaoYu.Core.CodeGenerator/Services/TableMetaConfigService.cs +++ b/admin-server/MiaoYu.Core.CodeGenerator/Services/TableMetaConfigService.cs @@ -6,7 +6,7 @@ namespace MiaoYu.Core.CodeGenerator.Services; /// 表元信息配置服务实现 /// [Component] -public class TableMetaConfigService : ITableMetaConfigService +public class TableMetaConfigService : ITableMetaConfigService, IScopedDependency { private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; diff --git a/admin-server/MiaoYu.Core.CodeGenerator/Services/TableSchemaCache.cs b/admin-server/MiaoYu.Core.CodeGenerator/Services/TableSchemaCache.cs index 2e84687..a956aa2 100644 --- a/admin-server/MiaoYu.Core.CodeGenerator/Services/TableSchemaCache.cs +++ b/admin-server/MiaoYu.Core.CodeGenerator/Services/TableSchemaCache.cs @@ -4,7 +4,7 @@ namespace MiaoYu.Core.CodeGenerator.Services; /// 表结构缓存服务实现 /// [Component] -public class TableSchemaCache : ITableSchemaCache +public class TableSchemaCache : ITableSchemaCache, IScopedDependency { private readonly IMemoryCache _memoryCache; private readonly DataSourceManager _dataSourceManager; diff --git a/admin-server/MiaoYu.Core.CodeGenerator/Usings.cs b/admin-server/MiaoYu.Core.CodeGenerator/Usings.cs index 2ef57c1..7e3ed6b 100644 --- a/admin-server/MiaoYu.Core.CodeGenerator/Usings.cs +++ b/admin-server/MiaoYu.Core.CodeGenerator/Usings.cs @@ -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; diff --git a/admin-server/代码生成器架构说明.md b/admin-server/代码生成器架构说明.md new file mode 100644 index 0000000..ae2ac14 --- /dev/null +++ b/admin-server/代码生成器架构说明.md @@ -0,0 +1,1386 @@ +# 幻梦管理系统 - 动态代码生成器架构文档 + +> **版本:** v2.0 +> **更新时间:** 2024年11月 +> **架构类型:** 多数据源 + 配置驱动 + 模板化生成 + +--- + +## 📋 目录 + +- [一、系统概述](#一系统概述) +- [二、整体架构](#二整体架构) +- [三、核心组件](#三核心组件) +- [四、完整流程详解](#四完整流程详解) +- [五、配置说明](#五配置说明) +- [六、扩展指南](#六扩展指南) +- [七、常见问题](#七常见问题) + +--- + +## 一、系统概述 + +### 1.1 功能简介 + +动态代码生成器是一个**低代码开发工具**,支持从数据库表结构自动生成后端和前端代码,包括: + +- ✅ **后端代码** + - Entity Models(实体类) + - Service 服务层 + - Controller 控制器 + +- ✅ **前端代码** + - Index.vue(列表页面) + - Info.vue(详情页面) + - Service.ts(API 服务) + +### 1.2 核心特性 + +| 特性 | 说明 | +|------|------| +| **多数据源支持** | 支持 Admin、MiaoYuChat、LiveForum 等多个数据库 | +| **配置驱动** | 路径、命名规则、模板都可配置 | +| **智能缓存** | 表结构缓存 20 分钟,减少数据库查询 | +| **模板化** | 基于 Razor 模板引擎,灵活定制 | +| **配置持久化** | JSON 文件存储用户自定义配置 | +| **自动导入** | 一键生成并导入到项目指定目录 | + +### 1.3 技术栈 + +- **.NET 8.0** - 核心框架 +- **FreeSql** - ORM,读取数据库表结构 +- **Razor** - 模板引擎 +- **MemoryCache** - 表结构缓存 +- **JSON** - 配置文件存储 + +--- + +## 二、整体架构 + +### 2.1 架构图 + +``` +┌────────────────────────────────────────────────────────────────────┐ +│ 前端用户操作 │ +│ 1. 选择数据库(Admin/MiaoYuChat/LiveForum) │ +│ 2. 选择表(T_User, SysUser 等) │ +│ 3. 选择生成类型(Model/Service/Controller/Client) │ +└────────────────────────────────────────────────────────────────────┘ + ↓ +┌────────────────────────────────────────────────────────────────────┐ +│ API 层 - CodeGenerationController │ +│ · GetDatabasesAsync() - 获取数据库列表 │ +│ · FindListAsync() - 获取表列表 │ +│ · GetCodeAsync() - 生成代码(预览) │ +│ · DownloadAsync() - 下载单个代码文件 │ +│ · DownloadAllAsync() - 下载所有表的代码(ZIP) │ +│ · AutoImprotProjectAsync() - 自动导入到项目 │ +└────────────────────────────────────────────────────────────────────┘ + ↓ +┌────────────────────────────────────────────────────────────────────┐ +│ Core 层 - CodeGenerationService │ +│ 【核心服务】负责代码生成的所有逻辑 │ +└────────────────────────────────────────────────────────────────────┘ + ↓ + ┌───────────────────────┼───────────────────────┐ + ↓ ↓ ↓ +┌──────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ DataSource │ │ DatabaseTable │ │ TableMeta │ +│ Manager │ │ Service │ │ ConfigService │ +│ 数据源管理 │ │ 表结构查询 │ │ 配置持久化 │ +└──────────────┘ └──────────────────┘ └─────────────────┘ + ↓ ↓ ↓ +┌──────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ Providers │ │ TableSchema │ │ JSON 配置文件 │ +│ (Admin,Chat) │ │ Cache │ │ (自定义配置) │ +└──────────────┘ └──────────────────┘ └─────────────────┘ + ↓ ↓ +┌──────────────┐ ┌──────────────────┐ +│ FreeSql │ │ MemoryCache │ +│ DbFirst API │ │ (20分钟缓存) │ +└──────────────┘ └──────────────────┘ + ↓ +┌──────────────┐ +│ 数据库 │ +│ (表结构) │ +└──────────────┘ +``` + +### 2.2 分层说明 + +| 层级 | 项目 | 职责 | +|------|------|------| +| **API 层** | MiaoYu.Api.Admin | 提供 RESTful API,接收前端请求 | +| **Core 层** | MiaoYu.Core.CodeGenerator | 核心业务逻辑,代码生成引擎 | +| **Repository 层** | MiaoYu.Repository.* | 数据访问,提供 DbContext | +| **Shared 层** | MiaoYu.Shared.* | 共享模型和 DTO | + +--- + +## 三、核心组件 + +### 3.1 组件清单 + +| 组件 | 类型 | 位置 | 职责 | +|------|------|------|------| +| **CodeGenerationController** | Controller | API 层 | API 入口,路由请求 | +| **CodeGenerationService** | Service | Core 层 | 代码生成核心逻辑 | +| **DataSourceManager** | Manager | Core 层 | 管理所有数据源提供者 | +| **IDataSourceProvider** | Interface | Core 层 | 数据源接口定义 | +| **AdminDataSourceProvider** | Provider | API 层 | Admin 数据库提供者 | +| **MiaoYuChatDataSourceProvider** | Provider | API 层 | MiaoYuChat 数据库提供者 | +| **DatabaseTableService** | Service | Core 层 | 查询表结构并合并配置 | +| **TableSchemaCache** | Cache | Core 层 | 表结构缓存(20分钟) | +| **TableMetaConfigService** | Service | Core 层 | JSON 配置文件读写 | +| **PathResolver** | Utility | Core 层 | 路径模板解析 | +| **IRazorViewRender** | Service | Core.Razor | Razor 模板引擎 | + +### 3.2 数据源提供者(Provider) + +#### 3.2.1 接口定义 + +```csharp +public interface IDataSourceProvider +{ + /// + /// 数据源配置 + /// + DataSourceConfig Config { get; } + + /// + /// 获取该数据源的所有表信息 + /// + List GetTables(); + + /// + /// 获取DbContext(用于获取FreeSql实例) + /// + object GetDbContext(); +} +``` + +#### 3.2.2 已实现的提供者 + +| Provider | 数据库 | 命名策略 | 路径模式 | 模板 | +|----------|--------|----------|----------|------| +| **AdminDataSourceProvider** | Admin | ToPascalCase
`sys_user` → `SysUser` | 复数形式
`/Users/` | template/ | +| **MiaoYuChatDataSourceProvider** | MiaoYuChat | KeepOriginal
`T_User` → `T_User` | 扁平结构
`/Apps/` | templatev4/ | +| **LiveForumDataSourceProvider** | LiveForum | 待实现 | 待实现 | 待实现 | + +#### 3.2.3 配置对比 + +```csharp +// Admin 配置 +new DataSourceConfig +{ + DatabaseKey = "Admin", + DisplayName = "后台管理系统", + NamingStrategy = EntityNamingStrategy.ToPascalCase, + UsesPluralPath = true, // 使用复数路径 + TemplatePath = "/wwwroot/code_generation/template/", + ModelPathTemplate = "{RootPath}\\{Namespace}\\Entities\\Apps\\{EntityNamePlural}", + Order = 1 +}; + +// MiaoYuChat 配置 +new DataSourceConfig +{ + DatabaseKey = "MiaoYuChat", + DisplayName = "喵语AI聊天", + NamingStrategy = EntityNamingStrategy.KeepOriginal, + UsesPluralPath = false, // 扁平路径 + TemplatePath = "/wwwroot/code_generation/templatev4/", + ModelPathTemplate = "{RootPath}\\{Namespace}\\Entities\\Apps", + Order = 2 +}; +``` + +### 3.3 缓存机制 + +```csharp +public class TableSchemaCache : ITableSchemaCache +{ + private const string CACHE_KEY = "CodeGenerator:AllTables"; + private static readonly TimeSpan CACHE_DURATION = TimeSpan.FromMinutes(20); + + public List GetAllTables() + { + return _memoryCache.GetOrCreate(CACHE_KEY, entry => + { + entry.SlidingExpiration = CACHE_DURATION; // 滑动过期 + return _dataSourceManager.GetAllTables(); + }); + } +} +``` + +**缓存特性:** +- ⏱️ **过期策略**:滑动过期,20 分钟无访问自动清除 +- 🔄 **手动刷新**:调用 `RefreshCache()` 方法 +- 🗑️ **手动清除**:调用 `ClearCache()` 方法 +- 📊 **缓存键**:`CodeGenerator:AllTables` + +--- + +## 四、完整流程详解 + +### 4.1 流程 1:获取数据库列表 + +**前端请求:** +```http +GET /api/CodeGeneration/databases +``` + +**处理流程:** +``` +Controller.GetDatabasesAsync() + ↓ +CodeGenerationService.GetAllDataSources() + ↓ +DataSourceManager.GetAllProviders() + ↓ +自动收集所有实现 IDataSourceProvider 的类 +``` + +**返回数据:** +```json +[ + { + "key": "Admin", + "displayName": "后台管理系统" + }, + { + "key": "MiaoYuChat", + "displayName": "喵语AI聊天" + }, + { + "key": "LiveForum", + "displayName": "直播论坛系统" + } +] +``` + +--- + +### 4.2 流程 2:获取表列表(带筛选) + +**前端请求:** +```http +POST /api/CodeGeneration/50/1 +Content-Type: application/json + +{ + "tableName": "User", + "dataBase": "MiaoYuChat" +} +``` + +**处理流程:** + +``` +1️⃣ 获取缓存或查询数据库 + DatabaseTableService.GetAllTablesByCache() + ↓ + TableSchemaCache.GetAllTables() + ↓ + [缓存命中] → 直接返回 + [缓存未命中] → 查询数据库 + ↓ + DataSourceManager.GetAllTables() + ↓ + 遍历所有 Provider: + - AdminDataSourceProvider.GetTables() + → FreeSql.DbFirst.GetTablesByDatabase() + - MiaoYuChatDataSourceProvider.GetTables() + → FreeSql.DbFirst.GetTablesByDatabase() + ↓ + 标记每个表的 DataBase 字段 + +2️⃣ 合并配置文件 + DatabaseTableService.MergeTableWithConfig() + ↓ + 对每个表: + 读取: CodeGenConfig/{DataBase}/{TableName}.json + 合并: DisplayName, EntityName, 列配置等 + +3️⃣ 应用筛选条件 + - WHERE DataBase = "MiaoYuChat" ✅ + - AND TableName LIKE "%User%" ✅ + +4️⃣ 分页处理 + - Skip((page-1) * size) + - Take(size) +``` + +**返回数据:** +```json +{ + "total": 5, + "page": 1, + "size": 50, + "pageCount": 1, + "dataSource": [ + { + "tableName": "T_User", + "dataBase": "MiaoYuChat", + "entityName": "T_User", + "displayName": "用户表", + "remark": "系统用户信息" + }, + { + "tableName": "T_User_Currency", + "dataBase": "MiaoYuChat", + "entityName": "T_User_Currency", + "displayName": "用户货币", + "remark": "用户虚拟货币" + } + ] +} +``` + +--- + +### 4.3 流程 3:生成代码(核心) + +**前端请求:** +```http +POST /api/CodeGeneration/GetCodeAsync +Content-Type: application/json + +{ + "tableName": "T_User", + "dataBase": "MiaoYuChat", + "type": "MiaoYu.Models" +} +``` + +**处理流程:** + +``` +1️⃣ 路由到生成方法 + GetCodeByTypeAndTableNameAsync(genFormDto) + ↓ + 根据 Type 分支: + - "MiaoYu.Models" → GenModelAsync() + - "MiaoYu.Services.Admin" → GenServiceAsync() + - "MiaoYu.Controllers.Admin" → GenControllerAsync() + - "Client.Index" → GenIndexAsync() + - "Client.Info" → GenInfoAsync() + - "Client.Service" → GenServiceJsAsync() + +2️⃣ 构建生成上下文(以 GenModelAsync 为例) + GetGenContextDto(genFormDto) + ↓ + A. 获取表信息 + GetGenContextDtoByTableName("T_User", "MiaoYuChat") + ↓ + 从缓存/数据库查询表结构 + + B. 获取数据源配置 + DataSourceManager.GetProvider("MiaoYuChat") + ↓ + 找到 MiaoYuChatDataSourceProvider + 读取其 Config + + C. 解析路径模板 + PathResolver.ResolvePath(config.ModelPathTemplate, config, "T_User") + ↓ + 替换占位符: + {RootPath} → "D:\CodeManage\HuanMengAdmin\HuanMengAdmin" + {Namespace} → "MiaoYu.Repository.ChatAI.Admin" + {EntityName} → "T_User" + {TableName} → "T_User" + + D. 构建完整上下文 + GenDbTableDto { + TableName: "T_User", + DataBase: "MiaoYuChat", + EntityName: "T_User", + DisplayName: "用户表", + ModelPath: "D:\...\Entities\Apps", + Namespace: "MiaoYu.Repository.ChatAI.Admin", + TableInfos: [ + { + ColumnName: "Id", + CsType: "Guid", + IsPrimary: true, + Describe: "主键ID", + ... + }, + ... + ] + } + +3️⃣ Razor 模板渲染 + _razorViewRender.RenderAsync(templatePath + "tempModel.cshtml", context) + ↓ + 模板文件: /wwwroot/code_generation/templatev4/tempModel.cshtml + ↓ + Razor 处理: + @model GenDbTableDto + + namespace @Model.Namespace.Entities + { + /// + /// @Model.DisplayName + /// + public class @Model.EntityName + { + @foreach(var col in Model.TableInfos) + { + /// + /// @col.Describe + /// + public @col.CsType @col.ColumnName { get; set; } + } + } + } + +4️⃣ 清理和返回 + ClearSymbol(code) // 移除
 标签等
+       ↓
+   返回生成的代码字符串
+```
+
+**返回数据(示例):**
+```csharp
+namespace MiaoYu.Repository.ChatAI.Admin.Entities
+{
+    /// 
+    /// 用户表
+    /// 
+    public class T_User
+    {
+        /// 
+        /// 主键ID
+        /// 
+        [Key]
+        public Guid Id { get; set; }
+        
+        /// 
+        /// 用户名
+        /// 
+        public string UserName { get; set; }
+        
+        /// 
+        /// 手机号
+        /// 
+        public string? Phone { get; set; }
+        
+        // ... 其他字段
+    }
+}
+```
+
+---
+
+### 4.4 流程 4:自动导入项目
+
+**前端请求:**
+```http
+POST /api/CodeGeneration/AutoImprotProjectAsync
+Content-Type: application/json
+
+{
+  "tableName": "T_User",
+  "dataBase": "MiaoYuChat"
+}
+```
+
+**处理流程:**
+
+```
+1️⃣ 遍历所有文件类型
+   FileTypeEnum[] = {
+       Model,          // .cs 实体类
+       Service,        // .cs 服务类
+       Controller,     // .cs 控制器
+       ClientIndex,    // .vue 列表页面
+       ClientInfo,     // .vue 详情页面
+       ClientService   // .ts 前端服务
+   }
+
+2️⃣ 对每个文件类型执行:
+
+   A. 计算目标路径
+      GetFileAbsolutelyPath(tableName, fileType, dataBase)
+          ↓
+      根据 Provider 配置解析:
+          Model: "D:\...\Entities\Apps\T_User.cs"
+          Service: "D:\...\ApplicationServices\Apps\MiaoYuChat\T_UserService.cs"
+          Controller: "D:\...\Controllers\Apps\MiaoYuChat\T_UserController.cs"
+          ClientIndex: "D:\...\admin-client\src\views\apps\T_Users\Index.vue"
+          
+   B. 检查文件是否存在
+      if (File.Exists(filePath) && !table.IsCover)
+          → 生成带时间戳的文件名:T_User20241107123045.cs
+          
+   C. 生成代码
+      SaveToFileAsync(tableName, fileType, filePath)
+          ↓
+      调用对应生成方法获取代码
+      
+   D. 写入文件
+      Directory.CreateDirectory(dirPath)  // 确保目录存在
+      File.WriteAllTextAsync(filePath, code, UTF8)
+
+3️⃣ 保存配置文件
+   SaveTableMetaConfigAsync(tableDto)
+       ↓
+   创建: CodeGenConfig/MiaoYuChat/T_User.json
+   {
+       "displayName": "用户表",
+       "entityName": "T_User",
+       "modelPath": "D:\\...\\Entities\\Apps",
+       "isCover": false,
+       "columns": {
+           "Id": {
+               "displayName": "用户ID",
+               "isTableColumnShow": true
+           }
+       }
+   }
+
+4️⃣ 完成
+   返回 true,所有文件已生成并导入
+```
+
+**生成的文件结构:**
+```
+MiaoYu.Repository.ChatAI.Admin/
+└── Entities/
+    └── Apps/
+        └── T_User.cs  ✅
+
+MiaoYu.Api.Admin/
+├── ApplicationServices/
+│   └── Apps/
+│       └── MiaoYuChat/
+│           └── T_UserService.cs  ✅
+└── Controllers/
+    └── Apps/
+        └── MiaoYuChat/
+            └── T_UserController.cs  ✅
+
+admin-client/
+└── src/
+    ├── views/
+    │   └── apps/
+    │       └── T_Users/
+    │           ├── Index.vue  ✅
+    │           └── Info.vue   ✅
+    └── services/
+        └── apps/
+            └── T_Users/
+                └── T_UserService.ts  ✅
+
+CodeGenConfig/
+└── MiaoYuChat/
+    └── T_User.json  ✅
+```
+
+---
+
+## 五、配置说明
+
+### 5.1 数据源配置
+
+#### 5.1.1 配置模型
+
+```csharp
+public class DataSourceConfig
+{
+    /// 
+    /// 数据库标识(如:Admin, MiaoYuChat)
+    /// 
+    public string DatabaseKey { get; set; }
+
+    /// 
+    /// 显示名称
+    /// 
+    public string DisplayName { get; set; }
+
+    /// 
+    /// 实体项目命名空间
+    /// 
+    public string EntityNamespace { get; set; }
+
+    /// 
+    /// 实体类路径模板
+    /// 支持占位符:{RootPath}, {AppPath}, {Namespace}, 
+    ///            {EntityName}, {EntityNamePlural}, {TableName}
+    /// 
+    public string ModelPathTemplate { get; set; }
+
+    /// 
+    /// 服务层路径模板
+    /// 
+    public string ServicePathTemplate { get; set; }
+
+    /// 
+    /// 控制器路径模板
+    /// 
+    public string ControllerPathTemplate { get; set; }
+
+    /// 
+    /// 代码生成模板目录
+    /// 
+    public string TemplatePath { get; set; }
+
+    /// 
+    /// 实体类命名规则(保持原名 or 驼峰转换)
+    /// 
+    public EntityNamingStrategy NamingStrategy { get; set; }
+
+    /// 
+    /// 是否使用复数形式的路径
+    /// 
+    public bool UsesPluralPath { get; set; }
+
+    /// 
+    /// 排序权重(数字越小越靠前)
+    /// 
+    public int Order { get; set; }
+}
+```
+
+#### 5.1.2 路径占位符
+
+| 占位符 | 说明 | 示例 |
+|--------|------|------|
+| `{RootPath}` | 解决方案根目录 | `D:\Projects\HuanMengAdmin` |
+| `{AppPath}` | 当前应用根目录 | `D:\Projects\HuanMengAdmin\admin-server\MiaoYu.Api.Admin` |
+| `{Namespace}` | 实体命名空间 | `MiaoYu.Repository.ChatAI.Admin` |
+| `{EntityName}` | 实体名称(单数) | `User` |
+| `{EntityNamePlural}` | 实体名称(复数) | `Users` |
+| `{TableName}` | 表名(原始) | `T_User` |
+
+#### 5.1.3 命名策略
+
+```csharp
+public enum EntityNamingStrategy
+{
+    /// 
+    /// 保持数据库表名原样
+    /// 
+    KeepOriginal,
+    
+    /// 
+    /// 转换为驼峰命名(去除下划线前缀)
+    /// sys_user → SysUser
+    /// t_image_config → TImageConfig
+    /// 
+    ToPascalCase
+}
+```
+
+**转换示例:**
+```
+KeepOriginal 模式:
+  T_User → T_User
+  sys_user → sys_user
+  
+ToPascalCase 模式:
+  T_User → TUser
+  sys_user → SysUser
+  t_image_config → TImageConfig
+```
+
+### 5.2 表元信息配置
+
+#### 5.2.1 配置文件位置
+
+```
+CodeGenConfig/
+├── Admin/
+│   ├── SysUser.json
+│   ├── SysRole.json
+│   └── ...
+├── MiaoYuChat/
+│   ├── T_User.json
+│   ├── T_Character.json
+│   └── ...
+└── LiveForum/
+    └── ...
+```
+
+#### 5.2.2 配置文件结构
+
+```json
+{
+  "displayName": "用户表",
+  "entityName": "T_User",
+  "remark": "系统用户信息表",
+  "modelPath": "D:\\Projects\\HuanMengAdmin\\MiaoYu.Repository.ChatAI.Admin\\Entities\\Apps",
+  "servicePath": "D:\\Projects\\HuanMengAdmin\\admin-server\\MiaoYu.Api.Admin\\ApplicationServices\\Apps\\MiaoYuChat",
+  "controllerPath": "D:\\Projects\\HuanMengAdmin\\admin-server\\MiaoYu.Api.Admin\\Controllers\\Apps\\MiaoYuChat",
+  "clientIndexPath": "D:\\Projects\\HuanMengAdmin\\admin-client\\src\\views\\apps\\T_Users",
+  "clientInfoPath": "D:\\Projects\\HuanMengAdmin\\admin-client\\src\\views\\apps\\T_Users",
+  "clientServicePath": "D:\\Projects\\HuanMengAdmin\\admin-client\\src\\services\\apps\\T_Users",
+  "isCover": false,
+  "columns": {
+    "Id": {
+      "displayName": "用户ID",
+      "describe": "主键ID",
+      "csField": "Id",
+      "isTableSelect": false,
+      "isImageId": false,
+      "isTableColumnShow": true
+    },
+    "UserName": {
+      "displayName": "用户名",
+      "describe": "登录用户名",
+      "csField": "UserName",
+      "isTableSelect": true,
+      "isImageId": false,
+      "isTableColumnShow": true
+    },
+    "Avatar": {
+      "displayName": "头像",
+      "describe": "用户头像URL",
+      "csField": "Avatar",
+      "isTableSelect": false,
+      "isImageId": true,
+      "isTableColumnShow": true
+    }
+  }
+}
+```
+
+#### 5.2.3 配置字段说明
+
+**表级配置:**
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| displayName | string | 表的显示名称 |
+| entityName | string | 实体类名称 |
+| remark | string | 表备注说明 |
+| modelPath | string | 实体类生成路径 |
+| servicePath | string | 服务类生成路径 |
+| controllerPath | string | 控制器生成路径 |
+| clientIndexPath | string | 前端列表页路径 |
+| clientInfoPath | string | 前端详情页路径 |
+| clientServicePath | string | 前端服务文件路径 |
+| isCover | boolean | 是否覆盖已存在文件 |
+
+**列级配置:**
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| displayName | string | 列的显示名称 |
+| describe | string | 列的描述说明 |
+| csField | string | C# 字段名 |
+| isTableSelect | boolean | 是否作为查询条件 |
+| isImageId | boolean | 是否是图片ID字段 |
+| isTableColumnShow | boolean | 是否在表格中显示 |
+
+---
+
+## 六、扩展指南
+
+### 6.1 新增数据源
+
+#### 步骤 1:添加常量定义
+
+```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";
+    
+    // 🆕 新增
+    public const string YourNewDatabase = "YourNewDatabase";
+}
+```
+
+#### 步骤 2:创建 Provider 实现
+
+```csharp
+// MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/Providers/YourNewDataSourceProvider.cs
+
+using HZY.Framework.DependencyInjection.Attributes;
+using MiaoYu.Core.CodeGenerator.Abstractions;
+
+namespace MiaoYu.Api.Admin.ApplicationServices.DevelopmentTools.LowCode.Providers;
+
+/// 
+/// 新数据源提供者
+/// 
+[Component]
+public class YourNewDataSourceProvider : IDataSourceProvider, IScopedDependency
+{
+    private readonly IRepository _repository;
+
+    public YourNewDataSourceProvider(IRepository repository)
+    {
+        _repository = repository;
+    }
+
+    public DataSourceConfig Config => new DataSourceConfig
+    {
+        DatabaseKey = DataSourceConstants.YourNewDatabase,
+        DisplayName = "新数据库",
+        EntityNamespace = typeof(YourRepositoryStartup).Namespace!,
+        
+        // 路径模板配置
+        ModelPathTemplate = "{RootPath}\\{Namespace}\\Entities\\Apps\\{EntityNamePlural}",
+        ServicePathTemplate = "{AppPath}\\ApplicationServices\\Apps\\YourPath",
+        ControllerPathTemplate = "{AppPath}\\Controllers\\Apps\\YourPath",
+        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/",
+        
+        // 命名策略
+        NamingStrategy = EntityNamingStrategy.ToPascalCase,
+        
+        // 路径模式
+        UsesPluralPath = true,
+        
+        // 排序
+        Order = 3,
+        
+        // 前缀配置(可选)
+        EnableEntityPrefix = false,
+        EntityPrefix = ""
+    };
+
+    public List GetTables()
+    {
+        var freeSqlTables = _repository.UnitOfWork.FreeSqlOrm.DbFirst.GetTablesByDatabase();
+        return ConvertToDbTableInfoList(freeSqlTables);
+    }
+
+    public object GetDbContext() => _repository.GetContext()!;
+
+    private List ConvertToDbTableInfoList(List freeSqlTables)
+    {
+        var result = new List();
+        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;
+    }
+}
+```
+
+#### 步骤 3:自动注册
+
+由于 Provider 实现了 `IScopedDependency`,依赖注入系统会自动注册。
+
+`DataSourceManager` 会在构造函数中自动收集所有 Provider 实例。
+
+**无需手动配置,立即生效!** ✅
+
+### 6.2 自定义代码模板
+
+#### 6.2.1 模板文件位置
+
+```
+MiaoYu.Api.Admin/wwwroot/code_generation/
+├── template/           # Admin 数据源使用
+│   ├── tempModel.cshtml
+│   ├── tempService.cshtml
+│   ├── tempController.cshtml
+│   ├── tempClientIndex.cshtml
+│   ├── tempClientInfo.cshtml
+│   └── tempClientService.cshtml
+└── templatev4/         # MiaoYuChat 数据源使用
+    ├── tempModel.cshtml
+    ├── tempService.cshtml
+    └── ...
+```
+
+#### 6.2.2 模板变量
+
+```razor
+@model GenDbTableDto
+
+可用属性:
+- @Model.TableName           // 表名
+- @Model.DataBase            // 数据库标识
+- @Model.EntityName          // 实体名
+- @Model.DisplayName         // 显示名称
+- @Model.Remark              // 备注
+- @Model.Namespace           // 命名空间
+- @Model.ModelPath           // 实体路径
+- @Model.ServicePath         // 服务路径
+- @Model.ControllerPath      // 控制器路径
+- @Model.TableInfos          // 列集合
+
+列属性(TableInfo):
+- @col.ColumnName            // 列名
+- @col.CsType                // C# 类型
+- @col.DisplayName           // 显示名称
+- @col.Describe              // 描述
+- @col.IsPrimary             // 是否主键
+- @col.IsIdentity            // 是否自增
+- @col.IsNullable            // 是否可空
+- @col.DatabaseColumnType    // 数据库类型
+- @col.MaxLength             // 最大长度
+```
+
+#### 6.2.3 模板示例
+
+**Entity 模板:**
+```razor
+@model GenDbTableDto
+using System.ComponentModel.DataAnnotations;
+
+namespace @Model.Namespace.Entities
+{
+    /// 
+    /// @Model.DisplayName
+    /// 
+    public class @Model.EntityName
+    {
+        @foreach(var col in Model.TableInfos)
+        {
+        /// 
+        /// @col.Describe
+        /// 
+        @if(col.IsPrimary)
+        {
+        [Key]
+        }
+        @if(!col.IsNullable && col.CsType == "string")
+        {
+        [Required]
+        }
+        public @col.CsType@(col.IsNullable && col.CsType != "string" ? "?" : "") @col.ColumnName { get; set; }
+        
+        }
+    }
+}
+```
+
+### 6.3 修改路径规则
+
+编辑对应 Provider 的 `Config` 配置:
+
+```csharp
+public DataSourceConfig Config => new DataSourceConfig
+{
+    // 修改模板路径
+    ModelPathTemplate = "{RootPath}\\{Namespace}\\Models\\{EntityName}",
+    
+    // 修改服务路径
+    ServicePathTemplate = "{AppPath}\\Services\\{EntityName}",
+    
+    // 添加自定义占位符(需扩展 PathResolver)
+    // ...
+};
+```
+
+---
+
+## 七、常见问题
+
+### 7.1 缓存问题
+
+**Q:修改了数据库表结构,为什么代码生成器看不到变化?**
+
+A:表结构缓存了 20 分钟。解决方法:
+1. 等待 20 分钟自动过期
+2. 调用 `ClearCache()` 清除缓存
+3. 在低代码平台点击"同步表"按钮
+
+**Q:如何禁用缓存?**
+
+A:修改 `TableSchemaCache.cs`:
+```csharp
+private static readonly TimeSpan CACHE_DURATION = TimeSpan.FromSeconds(0);  // 禁用缓存
+```
+
+### 7.2 路径问题
+
+**Q:生成的文件路径不正确怎么办?**
+
+A:检查以下几点:
+1. Provider 的 `EntityNamespace` 是否正确
+2. 路径模板中的占位符是否正确
+3. `PathResolver` 的 `RootPath` 计算逻辑
+
+**Q:如何修改生成路径?**
+
+A:编辑对应 Provider 的 `Config.ModelPathTemplate` 等配置。
+
+### 7.3 命名问题
+
+**Q:为什么有的表名是驼峰,有的保持原样?**
+
+A:这由 `NamingStrategy` 决定:
+- `ToPascalCase`:`sys_user` → `SysUser`
+- `KeepOriginal`:`T_User` → `T_User`
+
+**Q:如何统一命名规则?**
+
+A:修改对应 Provider 的 `Config.NamingStrategy`。
+
+### 7.4 模板问题
+
+**Q:如何自定义生成的代码格式?**
+
+A:编辑 `/wwwroot/code_generation/template/` 或 `templatev4/` 下的 `.cshtml` 文件。
+
+**Q:不同数据源如何使用不同模板?**
+
+A:在 Provider 的 `Config.TemplatePath` 中指定不同的模板目录。
+
+### 7.5 配置文件问题
+
+**Q:配置文件在哪里?**
+
+A:`CodeGenConfig/{DatabaseKey}/{TableName}.json`
+
+**Q:配置文件什么时候生成?**
+
+A:
+1. 调用 `AutoImprotProjectAsync` 自动导入时
+2. 手动调用 `SaveTableMetaConfigAsync` 时
+
+**Q:如何批量修改配置?**
+
+A:直接编辑对应的 JSON 文件,或者通过低代码平台的配置界面。
+
+### 7.6 多数据源问题
+
+**Q:同名表如何区分?**
+
+A:通过 `DataBase` 字段区分:
+- `{ tableName: "User", dataBase: "Admin" }`
+- `{ tableName: "User", dataBase: "MiaoYuChat" }`
+
+**Q:如何新增数据源?**
+
+A:参考 [6.1 新增数据源](#61-新增数据源)。
+
+### 7.7 性能问题
+
+**Q:查询表列表很慢怎么办?**
+
+A:
+1. 检查缓存是否生效(应该只有第一次慢)
+2. 检查数据库连接是否正常
+3. 考虑减少表的数量或优化数据库查询
+
+**Q:生成代码很慢怎么办?**
+
+A:
+1. 检查 Razor 模板是否过于复杂
+2. 考虑异步批量生成
+3. 优化模板逻辑
+
+---
+
+## 八、API 接口文档
+
+### 8.1 获取数据库列表
+
+```http
+GET /api/CodeGeneration/databases
+```
+
+**响应示例:**
+```json
+[
+  { "key": "Admin", "displayName": "后台管理系统" },
+  { "key": "MiaoYuChat", "displayName": "喵语AI聊天" }
+]
+```
+
+### 8.2 获取表列表
+
+```http
+POST /api/CodeGeneration/{size}/{page}
+Content-Type: application/json
+
+{
+  "tableName": "User",
+  "dataBase": "MiaoYuChat"
+}
+```
+
+**响应示例:**
+```json
+{
+  "total": 5,
+  "page": 1,
+  "size": 50,
+  "dataSource": [
+    {
+      "tableName": "T_User",
+      "dataBase": "MiaoYuChat",
+      "remark": "用户表"
+    }
+  ]
+}
+```
+
+### 8.3 生成代码
+
+```http
+POST /api/CodeGeneration/GetCodeAsync
+Content-Type: application/json
+
+{
+  "tableName": "T_User",
+  "dataBase": "MiaoYuChat",
+  "type": "MiaoYu.Models"
+}
+```
+
+**type 可选值:**
+- `MiaoYu.Models` - 实体类
+- `MiaoYu.Services.Admin` - 服务类
+- `MiaoYu.Controllers.Admin` - 控制器
+- `Client.Index` - 前端列表页
+- `Client.Info` - 前端详情页
+- `Client.Service` - 前端服务
+
+### 8.4 下载代码
+
+```http
+POST /api/CodeGeneration/DownloadAsync
+Content-Type: application/json
+
+{
+  "tableName": "T_User",
+  "dataBase": "MiaoYuChat",
+  "type": "MiaoYu.Models"
+}
+```
+
+### 8.5 下载所有代码
+
+```http
+POST /api/CodeGeneration/DownloadAllAsync
+Content-Type: application/json
+
+{
+  "type": "MiaoYu.Models"
+}
+```
+
+### 8.6 自动导入项目
+
+```http
+POST /api/CodeGeneration/AutoImprotProjectAsync
+Content-Type: application/json
+
+{
+  "tableName": "T_User",
+  "dataBase": "MiaoYuChat"
+}
+```
+
+---
+
+## 九、最佳实践
+
+### 9.1 使用建议
+
+1. **先同步表** - 在生成代码前,先在低代码平台同步表结构
+2. **配置路径** - 根据项目结构配置合适的路径模板
+3. **自定义模板** - 根据团队规范自定义代码模板
+4. **备份配置** - 定期备份 `CodeGenConfig/` 目录
+5. **版本控制** - 将配置文件纳入 Git 管理
+
+### 9.2 命名规范
+
+- **表名**:使用有意义的前缀(如 `T_`, `Sys_`)
+- **列名**:使用驼峰命名或下划线分隔
+- **实体类**:使用 PascalCase(如 `User`, `OrderItem`)
+- **服务类**:使用 `XXXService` 格式
+- **控制器**:使用 `XXXController` 格式
+
+### 9.3 性能优化
+
+1. **合理使用缓存** - 保持 20 分钟缓存,减少数据库查询
+2. **按需生成** - 不要一次性生成所有表的代码
+3. **异步处理** - 大批量生成时使用异步方法
+4. **压缩打包** - 批量下载时使用 ZIP 格式
+
+### 9.4 安全建议
+
+1. **权限控制** - 代码生成接口应限制管理员访问
+2. **路径验证** - 防止路径穿越攻击
+3. **文件覆盖** - 默认不覆盖已存在文件
+4. **备份重要文件** - 生成前备份可能被覆盖的文件
+
+---
+
+## 十、更新日志
+
+### v2.0 (2024-11-07)
+- ✅ 重构为多数据源架构
+- ✅ 支持 Provider 模式
+- ✅ 配置文件持久化(JSON)
+- ✅ 表结构缓存优化
+- ✅ 路径模板支持占位符
+- ✅ 命名策略可配置
+
+### v1.0 (之前版本)
+- ✅ 基础代码生成功能
+- ✅ 单数据源支持
+- ✅ Razor 模板引擎
+- ✅ 自动导入项目
+
+---
+
+## 十一、技术支持
+
+- **文档位置**:`/代码生成器架构说明.md`
+- **配置文件**:`/CodeGenConfig/`
+- **模板文件**:`/MiaoYu.Api.Admin/wwwroot/code_generation/`
+- **架构文档**:`/MiaoYu.Api.Admin/ApplicationServices/DevelopmentTools/LowCode/README.md`
+
+---
+
+## 附录 A:目录结构
+
+```
+admin-server/
+├── MiaoYu.Api.Admin/                          # API 主项目
+│   ├── ApplicationServices/
+│   │   └── DevelopmentTools/
+│   │       └── LowCode/
+│   │           ├── Providers/                 # 数据源提供者
+│   │           │   ├── AdminDataSourceProvider.cs
+│   │           │   ├── MiaoYuChatDataSourceProvider.cs
+│   │           │   └── LiveForumDataSourceProvider.cs
+│   │           ├── Impl/
+│   │           │   ├── LowCodeTableService.cs
+│   │           │   └── LowCodeTableInfoService.cs
+│   │           └── README.md
+│   ├── Controllers/
+│   │   └── DevelopmentTools/
+│   │       └── CodeGenerationController.cs    # 代码生成控制器
+│   └── wwwroot/
+│       └── code_generation/
+│           ├── template/                      # Admin 模板
+│           └── templatev4/                    # MiaoYuChat 模板
+│
+├── MiaoYu.Core.CodeGenerator/                 # 代码生成器核心
+│   ├── Abstractions/                          # 抽象接口
+│   │   ├── IDataSourceProvider.cs
+│   │   ├── DataSourceConfig.cs
+│   │   ├── DataSourceConstants.cs
+│   │   └── EntityNamingStrategy.cs
+│   ├── Core/                                  # 核心工具
+│   │   ├── DataSourceManager.cs
+│   │   ├── PathResolver.cs
+│   │   └── DataSourceExtensions.cs
+│   ├── Services/                              # 核心服务
+│   │   ├── ICodeGenerationService.cs
+│   │   ├── CodeGenerationService.cs
+│   │   ├── IDatabaseTableService.cs
+│   │   ├── DatabaseTableService.cs
+│   │   ├── ITableMetaConfigService.cs
+│   │   ├── TableMetaConfigService.cs
+│   │   ├── ITableSchemaCache.cs
+│   │   └── TableSchemaCache.cs
+│   ├── Models/                                # 数据模型
+│   └── CoreCodeGeneratorStartup.cs
+│
+├── CodeGenConfig/                             # 配置文件目录
+│   ├── Admin/
+│   │   ├── SysUser.json
+│   │   └── ...
+│   ├── MiaoYuChat/
+│   │   ├── T_User.json
+│   │   └── ...
+│   └── LiveForum/
+│       └── ...
+│
+└── 代码生成器架构说明.md                       # 本文档
+```
+
+---
+
+## 附录 B:类图
+
+```
+┌─────────────────────────────────────────────────────────┐
+│                    IDataSourceProvider                   │
+│  + Config: DataSourceConfig                             │
+│  + GetTables(): List                       │
+│  + GetDbContext(): object                               │
+└─────────────────────────────────────────────────────────┘
+                         ▲
+                         │ implements
+         ┌───────────────┼───────────────┐
+         │               │               │
+┌────────────────┐ ┌────────────────┐ ┌────────────────┐
+│ AdminDataSource│ │MiaoYuChatData  │ │LiveForumData   │
+│ Provider       │ │SourceProvider  │ │SourceProvider  │
+└────────────────┘ └────────────────┘ └────────────────┘
+         │               │               │
+         └───────────────┼───────────────┘
+                         │ manages
+                         ↓
+         ┌───────────────────────────────┐
+         │      DataSourceManager        │
+         │  + GetAllProviders()          │
+         │  + GetProvider(key)           │
+         │  + GetAllTables()             │
+         └───────────────────────────────┘
+                         │ uses
+                         ↓
+         ┌───────────────────────────────┐
+         │      TableSchemaCache         │
+         │  + GetAllTables()             │
+         │  + RefreshCache()             │
+         │  + ClearCache()               │
+         └───────────────────────────────┘
+                         │ uses
+                         ↓
+         ┌───────────────────────────────┐
+         │   DatabaseTableService        │
+         │  + GetAllTables()             │
+         │  + MergeTableWithConfig()     │
+         └───────────────────────────────┘
+                         │ uses
+         ┌───────────────┴───────────────┐
+         ↓                               ↓
+┌───────────────────┐         ┌───────────────────┐
+│ TableMetaConfig   │         │ CodeGeneration    │
+│ Service           │         │ Service           │
+│ + LoadConfig()    │         │ + GenModelAsync() │
+│ + SaveConfig()    │         │ + GenServiceAsync()│
+└───────────────────┘         └───────────────────┘
+```
+
+---
+
+**文档结束**
+
+如有疑问,请联系开发团队。
+
diff --git a/admin-server/代码生成器流程文档.md b/admin-server/代码生成器流程文档.md
new file mode 100644
index 0000000..c5ad5b1
--- /dev/null
+++ b/admin-server/代码生成器流程文档.md
@@ -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,调用 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)
+    ↓
+清除缓存: 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
+    ↓
+遍历列信息:
+    @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 GetAllTables()
+```
+
+**使用场景**:
+- 初始化时收集所有数据源
+- 生成代码时根据 dataBase 获取对应的 Provider 和配置
+
+---
+
+### 3.2 DatabaseTableService (数据库表服务)
+
+**职责**:
+- 管理表结构信息
+- 合并数据库 schema 和用户配置
+- 提供统一的表数据模型
+
+**核心方法**:
+```csharp
+// 获取所有表(已合并配置)
+public List 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 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.cshtml,Razor 引擎使用了错误的类型
+
+解决: 创建 _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;
+        }
+    }
+}
+
+
+namespace @Model.EntityNamespace.Entities.Apps;
+
+/// 
+/// @classNameRemark
+/// 
+[EntityDescription(FieldIgnored = true)]
+[Table("@Model.TableName")]
+public class @className : DefaultEntityV4
+{
+    @foreach (var item in tableInfos)
+    {
+    
+    /// 
+    ///  @(item.DisplayName ?? item.ColumnName) => 备注: @(item.Describe ?? item.ColumnName)
+    /// 
+    public @GetType(item) @item.ColumnName { get; set; }
+    
+    }
+}
+
+``` + +--- + +## 六、常见问题与解决方案 + +### 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 _repository; + + public LiveForumDataSourceProvider(IRepository 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 GetTables() + { + var freeSqlTables = _repository.UnitOfWork.FreeSqlOrm + .DbFirst.GetTablesByDatabase(); + return ConvertToDbTableInfoList(freeSqlTables); + } + + public object GetDbContext() => _repository.GetContext()!; + + private List 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 +