using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Sys.Api;
using NetAdmin.Domain.Dto.Sys.Dev;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
///
public sealed class DevService(IApiService apiService) : ServiceBase, IDevService
{
private const string _REPLACE_TO_EMPTY = "//~";
private static readonly string _clientProjectPath = Path.Combine( //
Environment.CurrentDirectory, "../../frontend/admin");
private static readonly string[] _projectDirs
= Directory.GetDirectories(Path.Combine(Environment.CurrentDirectory, "../"));
private static readonly Regex _regex = new(@"\.(\w)");
private static readonly Regex _regex2 = new("([a-zA-Z]+):");
///
public async Task GenerateCsCodeAsync(GenerateCsCodeReq req)
{
// 模块类型(Sys、Biz、等)
var moduleType = Enum.GetName(req.Type)!;
// 模板层目录
var tplHostDir = GetDir("SysComponent.Host");
var tplCacheDir = GetDir("SysComponent.Cache");
var tplAppDir = GetDir("SysComponent.Application");
// 主机层目录
var hostControllerDir = Path.Combine(GetDir($"{moduleType}.Host"), "Controllers", moduleType[..3]);
// 缓存层目录
var cacheDir = Path.Combine(GetDir($"{moduleType}.Cache"), moduleType[..3]);
var cacheDependencyDir = Path.Combine(cacheDir, "Dependency");
// 业务逻辑层目录
var appDir = GetDir($"{moduleType}.Application");
var appModulesDir = Path.Combine(appDir, "Modules", moduleType[..3]);
var appServicesDir = Path.Combine(appDir, "Services", moduleType[..3]);
var appServicesDependencyDir = Path.Combine(appServicesDir, "Dependency");
// 数据契约层目录
var dataDir = GetDir("NetAdmin.Domain");
var dtoDir = Path.Combine(dataDir, "Dto", moduleType[..3], req.ModuleName);
var entityDir = Path.Combine(dataDir, "DbMaps", moduleType[..3]);
// 创建缺少的目录
CreateDir(hostControllerDir, cacheDir, cacheDependencyDir, appDir, appModulesDir, appServicesDir
, appServicesDependencyDir, dataDir, dtoDir, entityDir);
// Controller
await WriteCodeFileAsync(req, Path.Combine(tplHostDir, "Controllers", "Tpl", "ExampleController.cs")
, Path.Combine(hostControllerDir, $"{req.ModuleName}Controller.cs"))
.ConfigureAwait(false);
// CreateReq
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "CreateExampleReq.cs")
, Path.Combine(dtoDir, $"Create{req.ModuleName}Req.cs"))
.ConfigureAwait(false);
// UpdateReq
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "UpdateExampleReq.cs")
, Path.Combine(dtoDir, $"Update{req.ModuleName}Req.cs"))
.ConfigureAwait(false);
// QueryReq
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "QueryExampleReq.cs")
, Path.Combine(dtoDir, $"Query{req.ModuleName}Req.cs"))
.ConfigureAwait(false);
// QueryRsp
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "QueryExampleRsp.cs")
, Path.Combine(dtoDir, $"Query{req.ModuleName}Rsp.cs"))
.ConfigureAwait(false);
// ICache
await WriteCodeFileAsync(req, Path.Combine(tplCacheDir, "Tpl", "Dependency", "IExampleCache.cs")
, Path.Combine(cacheDependencyDir, $"I{req.ModuleName}Cache.cs"))
.ConfigureAwait(false);
// Cache
await WriteCodeFileAsync(req, Path.Combine(tplCacheDir, "Tpl", "ExampleCache.cs")
, Path.Combine(cacheDir, $"{req.ModuleName}Cache.cs"))
.ConfigureAwait(false);
// IModule
await WriteCodeFileAsync(req, Path.Combine(tplAppDir, "Modules", "Tpl", "IExampleModule.cs")
, Path.Combine(appModulesDir, $"I{req.ModuleName}Module.cs"))
.ConfigureAwait(false);
// IService
await WriteCodeFileAsync(req, Path.Combine(tplAppDir, "Services", "Tpl", "Dependency", "IExampleService.cs")
, Path.Combine(appServicesDependencyDir, $"I{req.ModuleName}Service.cs"))
.ConfigureAwait(false);
// Service
await WriteCodeFileAsync(req, Path.Combine(tplAppDir, "Services", "Tpl", "ExampleService.cs")
, Path.Combine(appServicesDir, $"{req.ModuleName}Service.cs"))
.ConfigureAwait(false);
// Entity
await WriteCodeFileAsync(req, Path.Combine(dataDir, "DbMaps", "Tpl", "Tpl_Example.cs")
, Path.Combine(entityDir, $"{moduleType[..3]}_{req.ModuleName}.cs"))
.ConfigureAwait(false);
}
///
public async Task GenerateIconCodeAsync(GenerateIconCodeReq req)
{
var tplSvg = await File.ReadAllTextAsync(
Path.Combine(_clientProjectPath, "src", "assets", "icons", "tpl", "Svg.vue"))
.ConfigureAwait(false);
var tplExport = await File
.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "assets", "icons", "tpl"
, "export.js"))
.ConfigureAwait(false);
var vueContent = tplSvg.Replace("$svgCode$", req.SvgCode).Replace(_REPLACE_TO_EMPTY, string.Empty);
var dir = Path.Combine(_clientProjectPath, "src", "assets", "icons");
if (!Directory.Exists(dir)) {
_ = Directory.CreateDirectory(dir);
}
var vueFile = Path.Combine(dir, $"{req.IconName}.vue");
await File.WriteAllTextAsync(vueFile, vueContent).ConfigureAwait(false);
var indexJsFile = Path.Combine(dir, "index.js");
await File.AppendAllTextAsync(
indexJsFile
, Environment.NewLine +
tplExport.Replace("$iconName$", req.IconName).Replace(_REPLACE_TO_EMPTY, string.Empty))
.ConfigureAwait(false);
// 修改iconSelect.js
var iconSelectFile = Path.Combine(_clientProjectPath, "src", "config", "iconSelect.js");
var iconSelectContent = await File.ReadAllTextAsync(iconSelectFile).ConfigureAwait(false);
iconSelectContent = iconSelectContent.Replace("export default", "exportDefault:").Replace("'", "\"");
iconSelectContent = _regex2.Replace(iconSelectContent, "\"$1\":");
iconSelectContent = "{" + iconSelectContent + "}";
var iconExportJsInfo = iconSelectContent.ToObject();
iconExportJsInfo.ExportDefault.Icons.Last().Icons.Add($"sc-icon-{req.IconName}");
var newContent = iconExportJsInfo.ToJson().TrimStart('{')[..^1].Replace("\"exportDefault\":", "export default");
await File.WriteAllTextAsync(iconSelectFile, newContent).ConfigureAwait(false);
}
///
public async Task GenerateJsCodeAsync()
{
// 模板文件
var tplOuter = await File.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "api", "tpl", "outer.js"))
.ConfigureAwait(false);
var tplInner = await File.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "api", "tpl", "inner.js"))
.ConfigureAwait(false);
foreach (var item in apiService.ReflectionList(false)) {
var dir = Path.Combine(_clientProjectPath, "src", "api", item.Namespace);
if (!Directory.Exists(dir)) {
_ = Directory.CreateDirectory(dir);
}
var file = Path.Combine(dir, $"{item.Name.Replace(".", string.Empty)}.js");
var content = tplOuter.Replace("$controllerDesc$", item.Summary)
.Replace("$controllerPath$", item.Id)
.Replace( //
"$inner$"
, string.Join(Environment.NewLine + Environment.NewLine, Select(item)))
.Replace(_REPLACE_TO_EMPTY, string.Empty);
await File.WriteAllTextAsync(file, content).ConfigureAwait(false);
}
IEnumerable Select(QueryApiRsp item)
{
return item.Children.Select(x => tplInner.Replace("$actionDesc$", x.Summary)
.Replace( //
"$actionName$"
, _regex.Replace(
x.Name, y => y.Groups[1].Value.ToUpperInvariant()))
.Replace("$actionPath$", x.Id)
.Replace( //
"$actionMethod$", x.Method?.ToLowerInvariant())
.Replace(_REPLACE_TO_EMPTY, string.Empty)); //
}
}
private static void CreateDir(params string[] dirs)
{
foreach (var dir in dirs) {
if (!Directory.Exists(dir)) {
_ = Directory.CreateDirectory(dir!);
}
}
}
private static string GetDir(string key)
{
return _projectDirs.First(x => x.EndsWith(key, true, CultureInfo.InvariantCulture));
}
private static async Task WriteCodeFileAsync(GenerateCsCodeReq req, string tplFile, string writeFile)
{
var tplContent = await File.ReadAllTextAsync(tplFile).ConfigureAwait(false);
tplContent = tplContent.Replace("Tpl", Enum.GetName(req.Type)![..3])
.Replace("示例", req.ModuleRemark)
.Replace("Example", req.ModuleName)
.Replace("NetAdmin.SysComponent", "SysComponent");
await File.WriteAllTextAsync(writeFile, tplContent).ConfigureAwait(false);
}
}