using MiaoYu.Core.CodeGenerator.Abstractions;
using MiaoYu.Core.CodeGenerator.Core;
namespace MiaoYu.Core.CodeGenerator.Services;
///
/// 代码生成服务
///
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;
private readonly ITableMetaConfigService _tableMetaConfigService;
///
/// 构造函数
///
/// 数据库表服务
/// Razor视图渲染器
/// Web宿主环境
/// 数据源管理器
/// 路径解析器
/// 表元信息配置服务
public CodeGenerationService(IDatabaseTableService databaseTableService,
IRazorViewRender razorViewRender,
IWebHostEnvironment webHostEnvironment,
DataSourceManager dataSourceManager,
PathResolver pathResolver,
ITableMetaConfigService tableMetaConfigService)
{
_databaseTableService = databaseTableService;
_razorViewRender = razorViewRender;
_webRootPath = webHostEnvironment.WebRootPath;
_dataSourceManager = dataSourceManager;
_pathResolver = pathResolver;
_tableMetaConfigService = tableMetaConfigService;
}
///
/// 生成上下文集合
///
///
public PagingView GetGenContextDtos(int page, int size, GenFormDto search)
{
var PagingView = new PagingView(page, size);
var result = new List>();
var query = _databaseTableService.GetAllTablesByCache().AsEnumerable();
if (!string.IsNullOrWhiteSpace(search.TableName))
{
query = query.Where(w => w.TableName != null && w.TableName.Contains(search.TableName));
}
if (!string.IsNullOrWhiteSpace(search.DataBase))
{
query = query.Where(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 SaveTableMetaConfigAsync(item);
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 MiaoYu.Core.Zips.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)
{
LogUtil.Log.Warning($"找不到数据表: {genFormDto.TableName} (数据库: {genFormDto.DataBase})");
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;
}
///
/// 保存表元信息到配置文件
///
/// 表信息
private async Task SaveTableMetaConfigAsync(GenDbTableDto tableDto)
{
if (string.IsNullOrEmpty(tableDto.DataBase) || string.IsNullOrEmpty(tableDto.TableName))
{
return;
}
var config = new TableMetaConfig
{
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 ColumnMetaConfig
{
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);
}
#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
}