using iWare.Wms.Application; using iWare.Wms.Application.Service.System.LowCode.Dto; using iWare.Wms.Core; using iWare.Wms.Core.Util.LowCode.Front.Code; using iWare.Wms.Core.Util.LowCode.Front.Model; using Furion.DatabaseAccessor; using Furion.DatabaseAccessor.Extensions; using Furion.DependencyInjection; using Furion.DynamicApiController; using Furion.Extras.iWare.Wms.Entity; using Furion.Extras.iWare.Wms.Service.LowCode.Dto; using Furion.Extras.iWare.Wms.Util; using Furion.Extras.iWare.Wms.Util.LowCode.Front.Code; using Furion.Extras.iWare.Wms.Util.LowCode.Front.Interface; using Furion.FriendlyException; using Furion.ViewEngine; using Mapster; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using NString; using System.Linq.Dynamic.Core; using System.Text; namespace Furion.Extras.iWare.Wms.Service.LowCode { /// /// 低代码模块服务 /// [ApiDescriptionSettings(Name = "LowCode", Order = 100)] [Route("api/lowcode")] public class LowCodeService : ILowCodeService, IDynamicApiController, ITransient { private readonly IRepository _sysLowCodeRep; private readonly IRepository _sysLowCodeDataBaseRep; private readonly IRepository _sysMenuRep; // 菜单表仓储 private readonly IConfiguration _configuration; private readonly IViewEngine _viewEngine; public LowCodeService(IRepository sysLowCodeRep, IRepository sysLowCodeDataBaseRep, IRepository sysMenuRep, IConfiguration configuration, IViewEngine viewEngine) { _sysLowCodeRep = sysLowCodeRep; _sysLowCodeDataBaseRep = sysLowCodeDataBaseRep; _sysMenuRep = sysMenuRep; _configuration = configuration; _viewEngine = viewEngine; } /// /// 分页查询 /// /// /// /// [HttpGet("page")] public async Task> QueryPageList([FromQuery] LowCodePageInput input) { var busName = !string.IsNullOrEmpty(input.BusName?.Trim()); var lowCodes = await _sysLowCodeRep.DetachedEntities .Where((busName, u => EF.Functions.Like(u.BusName, $"%{input.BusName.Trim()}%"))) .ToADPagedListAsync(input.PageNo, input.PageSize); return lowCodes; } /// /// 获取模块详情 /// /// /// [HttpGet("info/{id}")] public SysLowCode Info(long id) { return _sysLowCodeRep.Where(x => x.Id == id).Include(x => x.Databases).FirstOrDefault(); } /// /// 添加 /// /// /// /// [HttpPost("add")] public async Task Add(AddLowCodeInput input) { var isExist = await _sysLowCodeRep.DetachedEntities.AnyAsync(u => u.BusName == input.BusName); if (isExist) throw Oops.Oh(ErrorCode.D1600); var lowCode = input.Adapt(); var newLowCode = await lowCode.InsertNowAsync(); } /// /// 删除 /// /// /// /// [HttpPost("del")] public async Task Delete(List inputs) { if (inputs == null || inputs.Count < 1) return; foreach (var u in inputs) { _sysLowCodeDataBaseRep.Where(x => x.SysLowCodeId == u.Id).DeleteRange(_sysLowCodeDataBaseRep.Context); await _sysLowCodeRep.DeleteAsync(u.Id); } } /// /// 更新 /// /// /// /// [HttpPost("edit")] public async Task Update(UpdateLowCodeInput input) { var lowCode = input.Adapt(); await lowCode.UpdateAsync(); await _sysLowCodeDataBaseRep.Context.DeleteRangeAsync(x => x.SysLowCodeId == input.Id); await _sysLowCodeDataBaseRep.InsertAsync(lowCode.Databases); } /// /// 对比组件 /// /// /// /// [HttpPost("contrast")] public ContrasOutput Contrast(ContrastLowCode contrast) { List fronts = contrast.Controls.ConvertToFront().AllFront(); DataCompareUtil dataCompare = new DataCompareUtil(x => x.Key, x => x.Id); dataCompare.PushCompare(x => x.Key, x => x.Control_Key); dataCompare.PushCompare(x => x.Label, x => x.Control_Label); dataCompare.PushCompare(x => x.Model, x => x.Control_Model); dataCompare.PushCompare(x => x.Type, x => x.Control_Type); var compare = dataCompare.Compare(fronts, contrast.Databases); List list = new List(); var tables = contrast.Databases.Where(x => !string.IsNullOrEmpty(x.TableName) && !string.IsNullOrEmpty(x.TableName)) .Select(x => new { x.TableName, x.ClassName, x.TableDesc }) .Distinct() .ToList(); if (tables.Count != 1) { tables.Clear(); } compare.NoContain_2.ForEach(item => { list.AddRange(item.ReadFront_BindDatabase(_sysLowCodeRep.ProviderName).Select(x => new ContrastLowCode_Database() { Control_Key = item.Key, Control_Label = item.Label, Control_Model = item.Model, Control_Type = item.Type, DbParam = x.DbParam, DbType = x.DbType, DbTypeName = x.DbType.Name, DtoTypeName = x.DtoType == null ? x.DbType.Name : x.DtoType.Name, FieldName = $"{item.Model}{x.Suffix}", IsRequired = true, Id = Guid.NewGuid(), TableName = tables.Select(x => x.TableName).FirstOrDefault(), ClassName = tables.Select(x => x.ClassName).FirstOrDefault(), TableDesc = tables.Select(x => x.TableDesc).FirstOrDefault(), QueryType = "equery", QueryWhether = true, whetherAddUpdate = true, WhetherOrderBy = true, WhetherTable = true })); }); return new ContrasOutput() { Add = list, Del = compare.NoContain_1 }; } /// /// 生成代码 /// /// /// [HttpGet("runLocal/{id}")] public async Task RunLocal(long id) { var info = Info(id); var list = info.Databases.Select(x => new GenEntity() { NameSpace = info.NameSpace, ClassName = x.ClassName, TableDesc = x.TableDesc, TableName = x.TableName, DatabaseName = info.DatabaseName, AuthorName = info.AuthorName, BusName = info.BusName, ProName = info.ProName, FormDesign = info.FormDesign }).Distinct(new GenEntityComparer()).ToList(); list.ForEach(item => { item.DataBase = info.Databases.ToList(); item.Fields = info.Databases.Where(x => x.ClassName == item.ClassName).Select(x => new GenEntity_Field() { ColumnComment = x.Control_Label, DbParam = x.DbParam, FieldName = x.FieldName, IsRequired = x.IsRequired == null ? false : x.IsRequired.Value, NetType = x.DbTypeName, DtoNetType = x.DtoTypeName == null ? x.DbTypeName : x.DtoTypeName, }).ToList(); }); string TableName = string.Empty; list.ForEach(item => { TableName = item.TableName; var AllDynamic = item.FormDesign.ConvertToFront().AllFront().AllDynamic(); Dictionary> dynamicData = new Dictionary>(); List dynamicLoad_dict = new List(); AllDynamic.Where(x => x.Dynamic).Select(x => x.DynamicKey).ToList().ForEach(item => { dynamicData.Add(item, new List()); var d = item.GetDynamic(); if (d != null) { if (d.Head == "dict") { dynamicLoad_dict.Add(d); } } }); List tableFieldList = item.DataBase.Select(x => new CodeGenConfig() { ColumnComment = x.Control_Label, ColumnKey = String.Empty, ColumnName = x.FieldName, DataType = x.DbTypeName, DtoNetType = x.DtoTypeName, NetType = x.DbTypeName, OriginalColumnName = x.FieldName, QueryType = x.QueryType, QueryWhether = x.QueryWhether.HasValue && x.QueryWhether.Value ? YesOrNot.Y.ToString() : YesOrNot.N.ToString(), WhetherAddUpdate = x.WhetherAddUpdate.HasValue && x.WhetherAddUpdate.Value ? YesOrNot.Y.ToString() : YesOrNot.N.ToString(), WhetherOrderBy = x.WhetherOrderBy.HasValue && x.WhetherOrderBy.Value ? YesOrNot.Y.ToString() : YesOrNot.N.ToString(), WhetherRequired = x.IsRequired.HasValue && x.IsRequired.Value ? YesOrNot.Y.ToString() : YesOrNot.N.ToString(), WhetherTable = x.WhetherTable.HasValue && x.WhetherTable.Value ? YesOrNot.Y.ToString() : YesOrNot.N.ToString(), WhetherRetract = YesOrNot.N.ToString(), WhetherCommon = YesOrNot.Y.ToString(), NetTypeIsNullLable = String.Empty, Id = 0, FkEntityName = null, FkColumnNetType = null, FkColumnName = null, EffectType = null, DictTypeCode = null, CodeGen = null, CodeGenId = 0, }).ToList(); tableFieldList.Add(new CodeGenConfig() { ColumnComment = "Id", ColumnKey = "True", ColumnName = "Id", DataType = null, DtoNetType = "long", NetType = "long", OriginalColumnName = "Id", QueryType = null, QueryWhether = YesOrNot.N.ToString(), WhetherAddUpdate = YesOrNot.N.ToString(), WhetherOrderBy = YesOrNot.N.ToString(), WhetherRequired = YesOrNot.N.ToString(), WhetherTable = YesOrNot.N.ToString(), WhetherRetract = YesOrNot.N.ToString(), WhetherCommon = YesOrNot.Y.ToString(), NetTypeIsNullLable = String.Empty, Id = 0, FkEntityName = null, FkColumnNetType = null, FkColumnName = null, EffectType = null, DictTypeCode = null, CodeGen = null, CodeGenId = 0, }); tableFieldList.ForEach(u => { switch (u.NetType.ToLower()) { case "int": case "int32": case "long": case "guid": case "decimal": case "datetime": case "datetimeoffset": u.NetTypeIsNullLable = "?"; break; } u.OriginalColumnName = u.ColumnName; }); var queryWhetherList = tableFieldList.Where(u => u.QueryWhether == YesOrNot.Y.ToString()).ToList(); // 前端查询集合 var ss = App.Configuration.GetSection("CodeGenConfig"); int config_index = 0; string ResultHead = "@model iWare.Wms.Application.Service.System.LowCode.Dto.Front_CodeGenerate\r\n"; while (!string.IsNullOrEmpty(_configuration.GetValue($"LowCodeConfig:{config_index}:Name"))) { var config_data = new { HostPath = App.WebHostEnvironment.WebRootPath, CodePath = new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName, FrontendPath = new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.Parent.FullName + @"\iwara-wms-web\src\views\main\", ApiJsPath = new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.Parent.FullName + @"\iwara-wms-web\src\api\modular\main\", NameSpace = item.NameSpace, ClassName = item.ClassName }; var SourceFile = StringTemplate.Format(_configuration.GetValue($"LowCodeConfig:{config_index}:Source:File"), config_data); var TargetFile = StringTemplate.Format(_configuration.GetValue($"LowCodeConfig:{config_index}:Target:File"), config_data); var TargetDir = StringTemplate.Format(_configuration.GetValue($"LowCodeConfig:{config_index}:Target:Dir"), config_data); var IsFrontend = _configuration.GetValue($"LowCodeConfig:{config_index}:IsFrontend"); if (IsFrontend.HasValue && IsFrontend.Value == true) // 适应前端首字母小写 { tableFieldList.ForEach(u => { u.ColumnName = u.ColumnName.Substring(0, 1).ToLower() + u.ColumnName[1..]; }); } #region 执行代码生成 var tContent = File.ReadAllText(SourceFile); if (tContent.IndexOf(ResultHead) == 0) tContent = tContent.Substring(ResultHead.Length); var data = new { TableName = item.TableName, NameSpace = item.NameSpace, Fields = item.Fields, ClassName = item.ClassName, TableDesc = item.TableDesc, DatabaseName = item.DatabaseName, AuthorName = item.AuthorName, BusName = item.BusName, ProName = item.ProName, CamelizeClassName = item.TableName.Substring(0, 1).ToLower() + item.TableName[1..], //首字母小写 QueryWhetherList = queryWhetherList, TableField = tableFieldList, LowCodeId = id, FormDesign = item.FormDesign, DynamicData = JsonConvert.SerializeObject(dynamicData), DynamicLoad_Dict = dynamicLoad_dict, IsFile = tableFieldList.Where(x => x.DtoNetType.Contains("Front_FileDto")).Any(), FileTableField = tableFieldList.Where(x => x.DtoNetType.Contains("Front_FileDto")).ToList() }; var tResult = _viewEngine.RunCompileFromCached(tContent, data); if (!Directory.Exists(TargetDir)) Directory.CreateDirectory(TargetDir); File.WriteAllText($"{TargetDir}{TargetFile}", tResult, Encoding.UTF8); #endregion 执行代码生成 config_index++; } }); if (!string.IsNullOrEmpty(TableName)) { await AddMenu(info.DatabaseName.Substring(0, 5), TableName, info.BusName, info.MenuApplication, info.MenuPid); } return true; } private async Task AddMenu(string menucodePre, string className, string busName, string application, long pid) { // 定义菜单编码前缀 var codePrefix = menucodePre + "_" + className.ToLower();//改为取数据库定位器的前五个字母方便区分业务 //"dilon_" + className.ToLower(); // 先删除该表已生成的菜单列表 var menus = await _sysMenuRep.DetachedEntities.Where(u => u.Code == codePrefix || u.Code.StartsWith(codePrefix + "_")).ToListAsync(); await _sysMenuRep.DeleteAsync(menus); // 如果 pid 为 0 说明为顶级菜单, 需要创建顶级目录 if (pid == 0) { // 目录 var menuType0 = new SysMenu { Pid = 0, Pids = "[0],", Name = busName + "管理", Code = codePrefix, Type = MenuType.DIR, Icon = "robot", Router = "/" + className.ToLower(), Component = "PageView", Application = application }; pid = _sysMenuRep.InsertNowAsync(menuType0).GetAwaiter().GetResult().Entity.Id; } // 由于后续菜单会有修改, 需要判断下 pid 是否存在, 不存在报错 else if (!await _sysMenuRep.DetachedEntities.AnyAsync(e => e.Id == pid)) throw Oops.Oh(ErrorCode.D1505); // 菜单 var menuType1 = new SysMenu { Pid = pid, Pids = "[0],[" + pid + "],", Name = busName + "管理", Code = codePrefix + "_mgr", Type = MenuType.MENU, Router = "/" + className.ToLower(), Component = "main/" + className + "/index", Application = application, OpenType = MenuOpenType.COMPONENT }; var pid1 = _sysMenuRep.InsertNowAsync(menuType1).GetAwaiter().GetResult().Entity.Id; // 按钮-page var menuType2 = new SysMenu { Pid = pid1, Pids = "[0],[" + pid + "],[" + pid1 + "],", Name = busName + "查询", Code = codePrefix + "_mgr_page", Type = MenuType.BTN, Permission = className + ":page", Application = application, }.InsertAsync(); // 按钮-detail var menuType2_1 = new SysMenu { Pid = pid1, Pids = "[0],[" + pid + "],[" + pid1 + "],", Name = busName + "详情", Code = codePrefix + "_mgr_detail", Type = MenuType.BTN, Permission = className + ":detail", Application = application, }.InsertAsync(); // 按钮-add var menuType2_2 = new SysMenu { Pid = pid1, Pids = "[0],[" + pid + "],[" + pid1 + "],", Name = busName + "增加", Code = codePrefix + "_mgr_add", Type = MenuType.BTN, Permission = className + ":add", Application = application, }.InsertAsync(); // 按钮-delete var menuType2_3 = new SysMenu { Pid = pid1, Pids = "[0],[" + pid + "],[" + pid1 + "],", Name = busName + "删除", Code = codePrefix + "_mgr_delete", Type = MenuType.BTN, Permission = className + ":delete", Application = application, }.InsertAsync(); // 按钮-edit var menuType2_4 = new SysMenu { Pid = pid1, Pids = "[0],[" + pid + "],[" + pid1 + "],", Name = busName + "编辑", Code = codePrefix + "_mgr_edit", Type = MenuType.BTN, Permission = className + ":edit", Application = application, }.InsertAsync(); } private List GetTemplatePathList() { var templatePath = App.WebHostEnvironment.WebRootPath + @"\Template\"; return new List() { templatePath + "Entity.cs.cshtml" }; } /// /// 设置生成文件路径 /// /// /// private List GetTargetPathList(GenEntity input) { var backendPath = new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName + @"\" + input.NameSpace + @"\Entity\"; var outputPath = backendPath + @"\" + input.ClassName + ".cs"; return new List() { outputPath }; } } }