using JNPF.Common.Const; using JNPF.Common.Core.Manager; using JNPF.Common.Core.Manager.Files; using JNPF.Common.Enums; using JNPF.Common.Extension; using JNPF.Common.Filter; using JNPF.Common.Security; using JNPF.DependencyInjection; using JNPF.DynamicApiController; using JNPF.FriendlyException; using JNPF.Systems.Entitys.Permission; using JNPF.Systems.Entitys.System; using JNPF.Systems.Interfaces.System; using JNPF.VisualDev.Engine.Core; using JNPF.VisualDev.Entitys; using JNPF.VisualDev.Interfaces; using JNPF.WorkFlow.Entitys.Dto.FlowEngine; using JNPF.WorkFlow.Entitys.Entity; using JNPF.WorkFlow.Entitys.Model; using JNPF.WorkFlow.Entitys.Model.Properties; using JNPF.WorkFlow.Interfaces.Repository; using JNPF.WorkFlow.Interfaces.Service; using Mapster; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using SqlSugar; namespace JNPF.WorkFlow.Service; /// /// 流程设计. /// [ApiDescriptionSettings(Tag = "WorkflowEngine", Name = "FlowEngine", Order = 301)] [Route("api/workflow/Engine/[controller]")] public class FlowEngineService : IFlowEngineService, IDynamicApiController, ITransient { private readonly ISqlSugarRepository _repository; private readonly IFlowTaskRepository _flowTaskRepository; private readonly IDictionaryDataService _dictionaryDataService; private readonly IRunService _runService; private readonly IVisualDevService _visualDevService; private readonly IUserManager _userManager; private readonly IFileManager _fileManager; private readonly IDataBaseManager _dataBaseManager; private readonly ITenant _db; public FlowEngineService( ISqlSugarRepository repository, IFlowTaskRepository flowTaskRepository, IDictionaryDataService dictionaryDataService, IRunService runService, IVisualDevService visualDevService, IUserManager userManager, IFileManager fileManager, IDataBaseManager dataBaseManager, ISqlSugarClient context) { _repository = repository; _flowTaskRepository = flowTaskRepository; _dictionaryDataService = dictionaryDataService; _runService = runService; _visualDevService = visualDevService; _userManager = userManager; _fileManager = fileManager; _dataBaseManager = dataBaseManager; _db = context.AsTenant(); } #region GET /// /// 列表. /// /// 请求参数. /// [HttpGet("")] public async Task GetList([FromQuery] FlowEngineListInput input) { var list = await _repository.AsSugarClient().Queryable((a, b) => new JoinQueryInfos(JoinType.Left, a.Category == b.EnCode)) .Where((a, b) => a.DeleteMark == null && b.DictionaryTypeId == "507f4f5df86b47588138f321e0b0dac7") .Where(a => !(a.FormType == 2 && a.Type == 1)) .WhereIF(input.category.IsNotEmptyOrNull(), a => a.Category == input.category) .WhereIF(input.keyword.IsNotEmptyOrNull(), a => a.FullName.Contains(input.keyword) || a.EnCode.Contains(input.keyword)) .Select((a, b) => new FlowEngineListAllOutput { category = b.FullName, id = a.Id, description = a.Description, creatorTime = a.CreatorTime, creatorUser = SqlFunc.Subqueryable().Where(u => u.Id == a.CreatorUserId).Select(u => SqlFunc.MergeString(u.RealName, "/", u.Account)), enCode = a.EnCode, enabledMark = a.EnabledMark, flowTemplateJson = a.FlowTemplateJson, formData = a.FormTemplateJson, fullName = a.FullName, formType = a.FormType, icon = a.Icon, iconBackground = a.IconBackground, lastModifyTime = a.LastModifyTime, lastModifyUser = SqlFunc.Subqueryable().Where(u => u.Id == a.LastModifyUserId).Select(u => SqlFunc.MergeString(u.RealName, "/", u.Account)), sortCode = a.SortCode, type = a.Type, visibleType = a.VisibleType, }).MergeTable().OrderBy(a => a.sortCode).OrderBy(a => a.creatorTime, OrderByType.Desc) .OrderByIF(!string.IsNullOrEmpty(input.keyword), t => t.lastModifyTime, OrderByType.Desc).ToPagedListAsync(input.currentPage, input.pageSize); return PageResult.SqlSugarPageResult(list); } /// /// 列表(树形). /// /// [HttpGet("ListAll")] public async Task GetListAll() { var list1 = await GetFlowFormList(); var dicDataInfo = await _dictionaryDataService.GetInfo(list1.First().parentId); var dicDataList = await _dictionaryDataService.GetList(dicDataInfo.DictionaryTypeId); var list2 = new List(); foreach (var item in dicDataList) { list2.Add(new FlowEngineListOutput() { fullName = item.FullName, parentId = "0", id = item.Id, num = list1.FindAll(x => x.category == item.EnCode).Count }); } var output = list1.Union(list2).ToList().ToTree(); return new { list = output }; } /// /// 列表(分页). /// /// 请求参数. /// [HttpGet("PageListAll")] public async Task GetListPageAll([FromQuery] FlowEngineListInput input) { var data = await GetFlowFormList(); if (input.category.IsNotEmptyOrNull()) data = data.FindAll(x => x.category == input.category); if (input.keyword.IsNotEmptyOrNull()) data = data.FindAll(o => o.fullName.Contains(input.keyword) || o.enCode.Contains(input.keyword)); var pageList = new SqlSugarPagedList() { list = data.Skip((input.currentPage - 1) * input.pageSize).Take(input.pageSize).ToList(), pagination = new Pagination() { CurrentPage = input.currentPage, PageSize = input.pageSize, Total = data.Count } }; return PageResult.SqlSugarPageResult(pageList); } /// /// 信息. /// /// 主键值. /// [HttpGet("{id}")] public async Task GetInfo_Api(string id) { return (await GetInfo(id)).Adapt(); } /// /// 列表(子流程选择流程). /// /// (预留字段). /// [HttpGet("Selector")] public async Task ListSelect([FromQuery] int type) { var list1 = await GetOutList(); if (type.IsEmpty()) list1 = list1.FindAll(x => x.formType == type); var dicDataInfo = await _dictionaryDataService.GetInfo(list1.First().parentId); var dicDataList = (await _dictionaryDataService.GetList(dicDataInfo.DictionaryTypeId)).FindAll(x => x.EnabledMark == 1); var list2 = new List(); foreach (var item in dicDataList) { var index = list1.FindAll(x => x.category == item.EnCode).Count; if (index > 0) { list2.Add(new FlowEngineListOutput() { fullName = item.FullName, parentId = "0", id = item.Id, num = index }); } } var output = list1.Union(list2).ToList().ToTree(); return new { list = output }; } /// /// 表单主表属性. /// /// 主键. /// [HttpGet("{id}/FormDataFields")] public async Task getFormDataField(string id) { var entity = await GetInfo(id); List formDataFieldList = new List(); if (entity.FormType == 1) { var dicList = entity.FormTemplateJson.ToList>(); formDataFieldList = dicList.Select(x => new FlowEngineFieldOutput() { vmodel = x.ContainsKey("filedId") ? x["filedId"].ToString() : string.Empty, label = x.ContainsKey("filedName") ? x["filedName"].ToString() : string.Empty }).ToList(); } else { var formTemplateBase = new TemplateParsingBase(entity.FormTemplateJson, entity.Tables, true); formDataFieldList = formTemplateBase.SingleFormData .Where(x => x.__config__.jnpfKey != JnpfKeyConst.RELATIONFORM && x.__config__.jnpfKey != "relationFlow") .Select(x => new FlowEngineFieldOutput() { vmodel = x.__vModel__, label = x.__config__.label }).ToList(); } return new { list = formDataFieldList }; } /// /// 表单列表. /// /// 流程id. /// [HttpGet("{id}/FieldDataSelect")] public async Task getFormData(string id) { var flowTaskList = await _flowTaskRepository.GetTaskList(id); return flowTaskList.Select(x => new FlowEngineListSelectOutput() { id = x.Id, fullName = SqlFunc.MergeString(x.FullName, "/", x.EnCode) }).ToList(); } /// /// 导出. /// /// /// [HttpGet("{id}/Actions/ExportData")] public async Task ActionsExport(string id) { var importModel = new FlowEngineImportOutput(); importModel.flowEngine = await _repository.GetFirstAsync(x => x.Id == id && x.DeleteMark == null); importModel.visibleList = await _repository.AsSugarClient().Queryable().Where(x => x.FlowId == id).ToListAsync(); var jsonStr = importModel.ToJsonString(); return await _fileManager.Export(jsonStr, importModel.flowEngine.FullName, ExportFileType.ffe); } #endregion #region POST /// /// 删除. /// /// 主键值. /// [HttpDelete("{id}")] public async Task Delete(string id) { var flowEngineEntity = await GetInfo(id); if (flowEngineEntity == null) throw Oops.Oh(ErrorCode.COM1005); if (await _flowTaskRepository.AnyFlowTask(x => x.DeleteMark == null && x.FlowId == id)) throw Oops.Oh(ErrorCode.WF0024); _db.BeginTran(); await _repository.AsSugarClient().Deleteable(a => a.FlowId == flowEngineEntity.Id).ExecuteCommandHasChangeAsync(); var isOk = await _repository.AsUpdateable(flowEngineEntity).CallEntityMethod(m => m.Delete()).UpdateColumns(it => new { it.DeleteMark, it.DeleteTime, it.DeleteUserId }).ExecuteCommandHasChangeAsync(); _db.CommitTran(); if (!isOk) throw Oops.Oh(ErrorCode.COM1002); } /// /// 新建. /// /// 请求参数. /// [HttpPost("")] public async Task Create([FromBody] FlowEngineCrInput input) { if (await _repository.IsAnyAsync(x => (x.EnCode == input.enCode || x.FullName == input.fullName) && x.DeleteMark == null)) throw Oops.Oh(ErrorCode.COM1004); if (input.formType == 2) { var formTemplateBase = new TemplateParsingBase(input.formData, input.tables, true); if (!formTemplateBase.VerifyTemplate()) throw Oops.Oh(ErrorCode.D1401); } var flowEngineEntity = input.Adapt(); flowEngineEntity.Version = "1"; var flowVisibleList = GetFlowEngineVisibleList(input.flowTemplateJson); var result = await Create(flowEngineEntity, flowVisibleList); _ = result ?? throw Oops.Oh(ErrorCode.COM1000); } /// /// 更新. /// /// 主键值. /// 请求参数. /// [HttpPut("{id}")] public async Task Update(string id, [FromBody] FlowEngineUpInput input) { if (await _repository.IsAnyAsync(x => x.Id != id && (x.EnCode == input.enCode || x.FullName == input.fullName) && x.DeleteMark == null)) throw Oops.Oh(ErrorCode.COM1004); if (input.formType == 2) { var formTemplateBase = new TemplateParsingBase(input.formData, input.tables, true); if (!formTemplateBase.VerifyTemplate()) throw Oops.Oh(ErrorCode.D1401); } var flowEngineEntity = input.Adapt(); flowEngineEntity.Version = ((await _repository.GetFirstAsync(x => x.Id == id && x.DeleteMark == null)).Version.ParseToInt() + 1).ToString(); var flowVisibleList = GetFlowEngineVisibleList(input.flowTemplateJson); var isOk = await Update(flowEngineEntity, flowVisibleList); if (!isOk) throw Oops.Oh(ErrorCode.COM1001); } /// /// 复制. /// /// 主键值. /// [HttpPost("{id}/Actions/Copy")] public async Task ActionsCopy(string id) { var entity = await GetInfo(id); var random = RandomExtensions.NextLetterAndNumberString(new Random(), 5).ToLower(); entity.FullName = string.Format("{0}副本{1}", entity.FullName, random); entity.EnCode = string.Format("{0}{1}", entity.EnCode, random); entity.Version = "1"; if (entity.FullName.Length >= 50 || entity.EnCode.Length >= 50) throw Oops.Oh(ErrorCode.COM1009); var flowVisibleList = GetFlowEngineVisibleList(entity.FlowTemplateJson); var result = await Create(entity, flowVisibleList); _ = result ?? throw Oops.Oh(ErrorCode.WF0002); } /// /// 导入. /// /// /// [HttpPost("Actions/ImportData")] public async Task ActionsImport(IFormFile file) { var fileType = Path.GetExtension(file.FileName).Replace(".", string.Empty); if (!fileType.ToLower().Equals(ExportFileType.ffe.ToString())) throw Oops.Oh(ErrorCode.D3006); var josn = _fileManager.Import(file); FlowEngineImportOutput model; try { model = josn.ToObject(); } catch { throw Oops.Oh(ErrorCode.D3006); } if (model == null) throw Oops.Oh(ErrorCode.D3006); await ImportData(model); } /// /// 发布. /// /// 主键值. /// [HttpPost("Release/{id}")] public async Task Release(string id) { var entity = await GetInfo(id); if (entity == null) throw Oops.Oh(ErrorCode.COM1005); var isOk = await _repository.AsSugarClient().Updateable().SetColumns(it => it.EnabledMark == 1).Where(it => it.Id == id).ExecuteCommandHasChangeAsync(); if (!isOk) throw Oops.Oh(ErrorCode.COM1003); } /// /// 停止. /// /// 主键值. /// [HttpPost("Stop/{id}")] public async Task Stop(string id) { var entity = await GetInfo(id); if (entity == null) throw Oops.Oh(ErrorCode.COM1005); var isOk = await _repository.AsSugarClient().Updateable().SetColumns(it => it.EnabledMark == 0).Where(it => it.Id == id).ExecuteCommandHasChangeAsync(); if (!isOk) throw Oops.Oh(ErrorCode.COM1003); } #endregion #region PublicMethod /// /// 发起列表. /// /// [NonAction] public async Task> GetFlowFormList() { var list = await GetOutList(); if (_userManager.User.IsAdministrator == 0) { var data = await GetVisibleFlowList(); data = data.Union(list.FindAll(x => x.visibleType == 0)).ToList(); return data; } else { return list; } } #endregion #region PrivateMethod /// /// 详情. /// /// 主键. /// [NonAction] private async Task GetInfo(string id) { return await _repository.GetFirstAsync(a => a.Id == id && a.DeleteMark == null); } /// /// 新增流程. /// /// 流程实例. /// 可见范围. /// [NonAction] private async Task Create(FlowEngineEntity entity, List visibleList) { try { _db.BeginTran(); entity.VisibleType = visibleList.Count == 0 ? 0 : 1; entity.Id = SnowflakeIdHelper.NextId(); foreach (var item in visibleList) { item.FlowId = entity.Id; item.SortCode = visibleList.IndexOf(item); } if (visibleList.Count > 0) await _repository.AsSugarClient().Insertable(visibleList).CallEntityMethod(m => m.Creator()).ExecuteCommandAsync(); if (entity.FormType == 2) { // 无表转有表 if (entity.Tables.IsNullOrEmpty() || entity.Tables == "[]") { var random = RandomExtensions.NextLetterAndNumberString(new Random(), 5).ToLower(); // 主表名称 var mTableName = "wform_" + entity.EnCode + "_" + random; var devEntity = new VisualDevEntity() { //FlowId = entity.Id, FormData = entity.FormTemplateJson, }; var res = await _visualDevService.NoTblToTable(devEntity, mTableName); entity.Tables = res.Tables; entity.FormTemplateJson = res.FormData; } } var result = await _repository.AsSugarClient().Insertable(entity).CallEntityMethod(m => m.Create()).ExecuteReturnEntityAsync(); if (result == null) throw Oops.Oh(ErrorCode.COM1005); _db.CommitTran(); return result; } catch (Exception ex) { _db.RollbackTran(); return null; } } /// /// 修改流程. /// /// 流程实体. /// 可见范围. /// [NonAction] private async Task Update(FlowEngineEntity entity, List visibleList) { try { _db.BeginTran(); entity.VisibleType = visibleList.Count == 0 ? 0 : 1; await _repository.AsSugarClient().Deleteable(a => a.FlowId == entity.Id).ExecuteCommandHasChangeAsync(); foreach (var item in visibleList) { item.FlowId = entity.Id; item.SortCode = visibleList.IndexOf(item); } if (visibleList.Count > 0) await _repository.AsSugarClient().Insertable(visibleList).CallEntityMethod(m => m.Creator()).ExecuteCommandAsync(); if (entity.FormType == 2) { #region 处理旧无表数据 //无表转有表 var mTableName = "wform_" + entity.EnCode;//主表名称 if (entity.Tables.IsNullOrEmpty() || entity.Tables == "[]") { var devEntity = new VisualDevEntity() { //FlowId = entity.Id, FormData = entity.FormTemplateJson }; var res = await _visualDevService.NoTblToTable(devEntity, mTableName); } #endregion } var isOk = await _repository.AsSugarClient().Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).CallEntityMethod(m => m.LastModify()).ExecuteCommandHasChangeAsync(); _db.CommitTran(); return isOk; } catch (Exception ex) { _db.RollbackTran(); return false; } } /// /// 解析流程可见参数. /// /// /// private List GetFlowEngineVisibleList(string josnStr) { var output = new List(); // 发起节点属性. var pro = josnStr.ToObject().properties.ToObject(); if (pro.initiator != null && pro.initiator.Count > 0) { var list = pro.initiator.Select(x => new FlowEngineVisibleEntity() { OperatorId = x, OperatorType = "user" }).ToList(); output.AddRange(list); } if (pro.initiatePos != null && pro.initiatePos.Count > 0) { var list = pro.initiatePos.Select(x => new FlowEngineVisibleEntity() { OperatorId = x, OperatorType = "Position" }).ToList(); output.AddRange(list); } if (pro.initiateRole != null && pro.initiateRole.Count > 0) { var list = pro.initiateRole.Select(x => new FlowEngineVisibleEntity() { OperatorId = x, OperatorType = "Role" }).ToList(); output.AddRange(list); } return output; } /// /// 获取当前用户可见流程. /// /// [NonAction] private async Task> GetVisibleFlowList() { return await _repository.AsSugarClient().Queryable((a, b, c) => new JoinQueryInfos(JoinType.Left, a.OperatorId == b.ObjectId, JoinType.Left, a.FlowId == c.Id)) .Where((a, b, c) => (a.OperatorId == _userManager.UserId || b.UserId == _userManager.UserId) && c.DeleteMark == null && c.EnabledMark == 1 && c.Type == 0) .Select((a, b, c) => new FlowEngineListOutput { category = c.Category, id = c.Id, description = c.Description, creatorTime = c.CreatorTime, creatorUser = SqlFunc.Subqueryable().Where(u => u.Id == c.CreatorUserId).Select(u => SqlFunc.MergeString(u.RealName, "/", u.Account)), enCode = c.EnCode, enabledMark = c.EnabledMark, flowTemplateJson = c.FlowTemplateJson, formData = c.FormTemplateJson, fullName = c.FullName, formType = c.FormType, icon = c.Icon, iconBackground = c.IconBackground, lastModifyTime = c.LastModifyTime, lastModifyUser = SqlFunc.Subqueryable().Where(u => u.Id == c.LastModifyUserId).Select(u => SqlFunc.MergeString(u.RealName, "/", u.Account)), sortCode = a.SortCode, type = c.Type, visibleType = c.VisibleType, parentId = SqlFunc.Subqueryable().Where(d => d.EnCode == c.Category && d.DictionaryTypeId == "507f4f5df86b47588138f321e0b0dac7").Select(d => d.Id), }).MergeTable().OrderBy(a => a.sortCode).OrderBy(a => a.creatorTime, OrderByType.Desc) .OrderBy(a => a.lastModifyTime, OrderByType.Desc).ToListAsync(); } /// /// 流程列表(功能流程不显示). /// /// private async Task> GetOutList() { return await _repository.AsSugarClient().Queryable((a, b) => new JoinQueryInfos(JoinType.Left, a.Category == b.EnCode)) .Where((a, b) => a.DeleteMark == null && a.EnabledMark == 1 && a.Type == 0 && b.DictionaryTypeId == "507f4f5df86b47588138f321e0b0dac7") .Select((a, b) => new FlowEngineListOutput { category = a.Category, id = a.Id, description = a.Description, creatorTime = a.CreatorTime, creatorUser = SqlFunc.Subqueryable().Where(u => u.Id == a.CreatorUserId).Select(u => SqlFunc.MergeString(u.RealName, "/", u.Account)), enCode = a.EnCode, enabledMark = a.EnabledMark, flowTemplateJson = a.FlowTemplateJson, formData = a.FormTemplateJson, fullName = a.FullName, formType = a.FormType, icon = a.Icon, iconBackground = a.IconBackground, lastModifyTime = a.LastModifyTime, lastModifyUser = SqlFunc.Subqueryable().Where(u => u.Id == a.LastModifyUserId).Select(u => SqlFunc.MergeString(u.RealName, "/", u.Account)), sortCode = a.SortCode, type = a.Type, visibleType = a.VisibleType, parentId = b.Id }).MergeTable().OrderBy(a => a.sortCode).OrderBy(a => a.creatorTime, OrderByType.Desc) .OrderBy(a => a.lastModifyTime, OrderByType.Desc).ToListAsync(); } /// /// 导入数据. /// /// 导入实例. /// private async Task ImportData(FlowEngineImportOutput model) { try { _db.BeginTran(); // 存在更新不存在插入 根据主键 var stor = _repository.AsSugarClient().Storageable(model.flowEngine).Saveable().ToStorage(); // 执行插入 await stor.AsInsertable.ExecuteCommandAsync(); // await stor.AsUpdateable.ExecuteCommandAsync(); //执行更新,停用原因:Oracle 数据库环境会抛异常:ora-01704: 字符串文字太长 // 执行更新 await _repository.AsSugarClient().Updateable(model.flowEngine).ExecuteCommandAsync(); // 存在更新不存在插入 根据主键 var stor1 = _repository.AsSugarClient().Storageable(model.visibleList).Saveable().ToStorage(); // 执行插入 await stor1.AsInsertable.ExecuteCommandAsync(); // 执行更新 await stor1.AsUpdateable.ExecuteCommandAsync(); _db.CommitTran(); } catch (Exception ex) { _db.RollbackTran(); throw Oops.Oh(ErrorCode.D3006); } } #endregion }