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
|
{
|
/// <summary>
|
/// 低代码模块服务
|
/// </summary>
|
[ApiDescriptionSettings(Name = "LowCode", Order = 100)]
|
[Route("api/lowcode")]
|
public class LowCodeService : ILowCodeService, IDynamicApiController, ITransient
|
{
|
private readonly IRepository<SysLowCode> _sysLowCodeRep;
|
private readonly IRepository<SysLowCodeDataBase> _sysLowCodeDataBaseRep;
|
private readonly IRepository<SysMenu> _sysMenuRep; // 菜单表仓储
|
private readonly IConfiguration _configuration;
|
private readonly IViewEngine _viewEngine;
|
|
public LowCodeService(IRepository<SysLowCode> sysLowCodeRep,
|
IRepository<SysLowCodeDataBase> sysLowCodeDataBaseRep,
|
IRepository<SysMenu> sysMenuRep,
|
IConfiguration configuration,
|
IViewEngine viewEngine)
|
{
|
_sysLowCodeRep = sysLowCodeRep;
|
_sysLowCodeDataBaseRep = sysLowCodeDataBaseRep;
|
_sysMenuRep = sysMenuRep;
|
_configuration = configuration;
|
_viewEngine = viewEngine;
|
}
|
|
/// <summary>
|
/// 分页查询
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
/// <exception cref="NotImplementedException"></exception>
|
[HttpGet("page")]
|
public async Task<PageResult<SysLowCode>> 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;
|
}
|
|
/// <summary>
|
/// 获取模块详情
|
/// </summary>
|
/// <param name="id"></param>
|
/// <returns></returns>
|
[HttpGet("info/{id}")]
|
public SysLowCode Info(long id)
|
{
|
return _sysLowCodeRep.Where(x => x.Id == id).Include(x => x.Databases).FirstOrDefault();
|
}
|
|
/// <summary>
|
/// 添加
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
/// <exception cref="NotImplementedException"></exception>
|
[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<SysLowCode>();
|
var newLowCode = await lowCode.InsertNowAsync();
|
}
|
|
/// <summary>
|
/// 删除
|
/// </summary>
|
/// <param name="inputs"></param>
|
/// <returns></returns>
|
/// <exception cref="NotImplementedException"></exception>
|
[HttpPost("del")]
|
public async Task Delete(List<DeleteLowCodeInput> 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);
|
}
|
}
|
|
/// <summary>
|
/// 更新
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
/// <exception cref="NotImplementedException"></exception>
|
[HttpPost("edit")]
|
public async Task Update(UpdateLowCodeInput input)
|
{
|
var lowCode = input.Adapt<SysLowCode>();
|
await lowCode.UpdateAsync();
|
|
await _sysLowCodeDataBaseRep.Context.DeleteRangeAsync<SysLowCodeDataBase>(x => x.SysLowCodeId == input.Id);
|
await _sysLowCodeDataBaseRep.InsertAsync(lowCode.Databases);
|
}
|
|
/// <summary>
|
/// 对比组件
|
/// </summary>
|
/// <param name="contrast"></param>
|
/// <returns></returns>
|
/// <exception cref="NotImplementedException"></exception>
|
[HttpPost("contrast")]
|
public ContrasOutput Contrast(ContrastLowCode contrast)
|
{
|
List<IFront> fronts = contrast.Controls.ConvertToFront().AllFront();
|
|
DataCompareUtil<IFront, ContrastLowCode_Database> dataCompare
|
= new DataCompareUtil<IFront, ContrastLowCode_Database>(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<ContrastLowCode_Database> list = new List<ContrastLowCode_Database>();
|
|
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
|
};
|
}
|
|
/// <summary>
|
/// 生成代码
|
/// </summary>
|
/// <param name="id"></param>
|
/// <returns></returns>
|
[HttpGet("runLocal/{id}")]
|
public async Task<bool> 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<string, List<string>> dynamicData = new Dictionary<string, List<string>>();
|
List<Front_Dynamic> dynamicLoad_dict = new List<Front_Dynamic>();
|
|
AllDynamic.Where(x => x.Dynamic).Select(x => x.DynamicKey).ToList().ForEach(item =>
|
{
|
dynamicData.Add(item, new List<string>());
|
var d = item.GetDynamic();
|
if (d != null)
|
{
|
if (d.Head == "dict")
|
{
|
dynamicLoad_dict.Add(d);
|
}
|
}
|
});
|
|
List<CodeGenConfig> 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<string>($"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<string>($"LowCodeConfig:{config_index}:Source:File"), config_data);
|
var TargetFile = StringTemplate.Format(_configuration.GetValue<string>($"LowCodeConfig:{config_index}:Target:File"), config_data);
|
var TargetDir = StringTemplate.Format(_configuration.GetValue<string>($"LowCodeConfig:{config_index}:Target:Dir"), config_data);
|
var IsFrontend = _configuration.GetValue<bool?>($"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<string> GetTemplatePathList()
|
{
|
var templatePath = App.WebHostEnvironment.WebRootPath + @"\Template\";
|
return new List<string>()
|
{
|
templatePath + "Entity.cs.cshtml"
|
};
|
}
|
|
/// <summary>
|
/// 设置生成文件路径
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
private List<string> GetTargetPathList(GenEntity input)
|
{
|
var backendPath = new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName + @"\" + input.NameSpace + @"\Entity\";
|
var outputPath = backendPath + @"\" + input.ClassName + ".cs";
|
|
return new List<string>()
|
{
|
outputPath
|
};
|
}
|
}
|
}
|