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.Dto.ModuleForm; using JNPF.Systems.Entitys.Model.DataBase; using JNPF.Systems.Entitys.Permission; using JNPF.Systems.Entitys.System; using JNPF.Systems.Interfaces.System; using JNPF.VisualDev.Engine.Core; using JNPF.VisualDev.Engine.Model; using JNPF.VisualDev.Interfaces; using JNPF.WorkFlow.Entitys.Dto.FlowEngine; using JNPF.WorkFlow.Entitys.Dto.FlowForm; using JNPF.WorkFlow.Entitys.Entity; using JNPF.WorkFlow.Interfaces.Repository; using Mapster; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using SqlSugar; namespace JNPF.WorkFlow.Service; /// /// 流程设计. /// [ApiDescriptionSettings(Tag = "FlowForm", Name = "Form", Order = 301)] [Route("api/flowForm/Form")] public class FlowFormService : IDynamicApiController, ITransient { private readonly ISqlSugarRepository _repository; 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 FlowFormService( ISqlSugarRepository repository, IRunService runService, IVisualDevService visualDevService, IUserManager userManager, IFileManager fileManager, IDataBaseManager dataBaseManager, ISqlSugarClient context) { _repository = repository; _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.AsQueryable() .Where(a => a.DeleteMark == null && !(a.FormType == 2 && a.FlowType == 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 => new FlowFormListOutput { id = a.Id, 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, fullName = a.FullName, formType = a.FormType, flowType = a.FlowType, lastModifyTime = a.LastModifyTime, sortCode = a.SortCode, }).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("{id}")] public async Task GetInfo_Api(string id) { var entity = await GetInfo(id); var res = entity.Adapt(); var draft = entity.DraftJson?.ToObject().Adapt(); res.propertyJson = entity.PropertyJson; res.draftJson = draft != null && draft.propertyJson.IsNotEmptyOrNull() ? draft?.propertyJson : entity.PropertyJson; return res; } /// /// 列表(下拉). /// /// (预留字段). /// [HttpGet("Select")] public async Task Select([FromQuery] FlowEngineListInput input) { var list = await _repository.AsSugarClient().Queryable() .Where(a => a.DeleteMark == null && a.EnabledMark.Equals(1) && a.FlowType == input.flowType) .WhereIF(input.keyword.IsNotEmptyOrNull(), a => a.FullName.Contains(input.keyword) || a.EnCode.Contains(input.keyword)) .Select(a => new FlowFormListOutput { category = SqlFunc.Subqueryable().Where(it => it.EnCode.Equals(a.Category)).Select(it => it.FullName), id = a.Id, description = a.Description, creatorTime = a.CreatorTime, enCode = a.EnCode, enabledMark = a.EnabledMark, fullName = a.FullName, formType = a.FormType, flowType = a.FlowType, sortCode = a.SortCode, }).MergeTable().OrderBy(a => a.sortCode).OrderBy(a => a.creatorTime, OrderByType.Desc).ToPagedListAsync(input.currentPage, input.pageSize); return PageResult.SqlSugarPageResult(list); } /// /// 导出. /// /// /// [HttpGet("{id}/Actions/ExportData")] public async Task ActionsExport(string id) { var info = await GetInfo(id); info.DraftJson = info.ToJsonString(); var jsonStr = info.ToJsonString(); return await _fileManager.Export(jsonStr, info.FullName, ExportFileType.fff); } /// /// 复制. /// /// 主键值. /// [HttpGet("{id}/Actions/Copy")] public async Task ActionsCopy(string id) { var entity = await GetInfo(id); var random = RandomExtensions.NextLetterAndNumberString(new Random(), 5).ToLower(); entity.Id = SnowflakeIdHelper.NextId(); entity.FullName = string.Format("{0}副本{1}", entity.FullName, random); entity.EnCode = string.Format("{0}{1}", entity.EnCode, random); if (entity.FullName.Length >= 50 || entity.EnCode.Length >= 50) throw Oops.Oh(ErrorCode.COM1009); entity.EnabledMark = 0; //entity.TableJson = "[]"; entity.DraftJson = entity.ToJsonString(); entity.LastModifyTime = null; var result = await _repository.AsSugarClient().Insertable(entity).CallEntityMethod(m => m.Create()).ExecuteReturnEntityAsync(); _ = result ?? throw Oops.Oh(ErrorCode.WF0002); } /// /// 根据表单Id 获取流程信息. /// /// 主键值. /// [HttpGet("GetFormById/{id}")] public async Task GetFormById(string id) { var model = new { id = string.Empty, enCode = string.Empty, enabledMark = 0 }; var form = await GetInfo(id); if (form == null) throw Oops.Oh(ErrorCode.COM1019); if (form != null && form.FlowId.IsNotEmptyOrNull()) model = _repository.AsSugarClient().Queryable().Where(x => x.Id.Equals(form.FlowId)).Select(x => new { id = x.Id, enCode = x.EnCode, enabledMark = (int)x.EnabledMark }).First(); if (model == null || model.id.IsNullOrEmpty()) throw Oops.Oh(ErrorCode.COM1016); if (form.FlowType == 1 && form.FormType == 1 && model.enabledMark != 1) { // 代码生成的功能流程需要判断流程是否启用。 throw Oops.Oh(ErrorCode.COM1017); } return model; } #endregion #region POST /// /// 删除. /// /// 主键值. /// [HttpDelete("{id}")] public async Task Delete(string id) { var flowFormEntity = await GetInfo(id); if (flowFormEntity == null) throw Oops.Oh(ErrorCode.COM1005); if (await _repository.AsSugarClient().Queryable().Where(x => x.FormId.Equals(id)).AnyAsync()) throw Oops.Oh(ErrorCode.COM1012); var isOk = await _repository.AsSugarClient().Updateable(flowFormEntity).CallEntityMethod(m => m.Delete()).UpdateColumns(it => new { it.DeleteMark, it.DeleteTime, it.DeleteUserId }).ExecuteCommandHasChangeAsync(); await _repository.AsSugarClient().Deleteable().Where(x => x.FormId.Equals(id)).ExecuteCommandAsync(); 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); var flowFormEntity = input.Adapt(); flowFormEntity.Id = SnowflakeIdHelper.NextId(); flowFormEntity.EnabledMark = 0; flowFormEntity.PropertyJson = flowFormEntity.DraftJson; flowFormEntity.DraftJson = flowFormEntity.ToJsonString(); if (input.formType.Equals(2) && input.draftJson.IsNotEmptyOrNull()) { TemplateParsingBase? tInfo = new TemplateParsingBase(flowFormEntity.PropertyJson, flowFormEntity.TableJson); // 解析模板 if (!tInfo.VerifyTemplate()) throw Oops.Oh(ErrorCode.D1401); // 验证模板 await VerifyPrimaryKeyPolicy(tInfo, flowFormEntity.DbLinkId); // 验证雪花Id 和自增长Id 主键是否支持 } var result = await _repository.AsSugarClient().Insertable(flowFormEntity).CallEntityMethod(m => m.Create()).ExecuteReturnEntityAsync(); _ = result ?? throw Oops.Oh(ErrorCode.COM1000); } /// /// 更新. /// /// 主键值. /// 请求参数. /// [HttpPut("")] public async Task Update([FromBody] FlowEngineUpInput input) { if (await _repository.IsAnyAsync(x => x.Id != input.id && (x.EnCode == input.enCode || x.FullName == input.fullName) && x.DeleteMark == null)) throw Oops.Oh(ErrorCode.COM1004); if (await _repository.AsQueryable().AnyAsync(x => x.Id.Equals(input.id) && x.EnabledMark.Equals(1) && x.FormType == 2)) { if (input.tableJson.IsNullOrWhiteSpace() || input.tableJson.Equals("[]")) throw Oops.Oh(ErrorCode.D1416); // 已发布的模板 表不能为空. input.enabledMark = 1; } var isOk = false; var fEntity = input.Adapt(); if (fEntity != null) { if (input.formType.Equals(2) && input.draftJson.IsNotEmptyOrNull()) { TemplateParsingBase? tInfo = new TemplateParsingBase(fEntity.DraftJson, fEntity.TableJson); // 解析模板 if (!tInfo.VerifyTemplate()) throw Oops.Oh(ErrorCode.D1401); // 验证模板 await VerifyPrimaryKeyPolicy(tInfo, fEntity.DbLinkId); // 验证雪花Id 和自增长Id 主键是否支持 } // EnabledMark=0 未发布,EnabledMark=1 已发布 if (fEntity.EnabledMark.Equals(0)) { fEntity.PropertyJson = fEntity.DraftJson; fEntity.DraftJson = fEntity.ToJsonString(); // 修改流程表单 isOk = await _repository.AsSugarClient().Updateable(fEntity).IgnoreColumns(ignoreAllNullColumns: true).CallEntityMethod(m => m.LastModify()).ExecuteCommandHasChangeAsync(); } else { var nEntity = await _repository.AsSugarClient().Queryable().Where(x => x.Id.Equals(input.id)).FirstAsync(); fEntity.PropertyJson = fEntity.DraftJson; nEntity.DraftJson = fEntity.ToJsonString(); nEntity.FullName = fEntity.FullName; nEntity.EnCode = fEntity.EnCode; nEntity.SortCode = fEntity.SortCode; nEntity.Description = fEntity.Description; nEntity.TableJson = fEntity.TableJson; nEntity.DbLinkId = fEntity.DbLinkId; nEntity.AppUrlAddress = fEntity.AppUrlAddress; nEntity.UrlAddress = fEntity.UrlAddress; nEntity.InterfaceUrl = fEntity.InterfaceUrl; // 修改流程表单 isOk = await _repository.AsSugarClient().Updateable(nEntity).IgnoreColumns(ignoreAllNullColumns: true).CallEntityMethod(m => m.LastModify()).ExecuteCommandHasChangeAsync(); } } if (!isOk) throw Oops.Oh(ErrorCode.COM1001); } /// /// 导入. /// /// /// [HttpPost("Actions/ImportData")] public async Task ActionsImport(IFormFile file) { var fileType = Path.GetExtension(file.FileName).Replace(".", string.Empty); if (!fileType.ToLower().Equals(ExportFileType.fff.ToString())) throw Oops.Oh(ErrorCode.D3006); var josn = _fileManager.Import(file); var model = new FlowFormEntity(); 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, [FromQuery] int isRelease) { var entity = await GetInfo(id); // 0 回滚表单 , 1 发布表单. if (isRelease.Equals(0)) { if (entity == null) throw Oops.Oh(ErrorCode.COM1005); var isOk = await _repository.AsSugarClient().Updateable().SetColumns(it => it.DraftJson == entity.ToJsonString()).Where(it => it.Id == id).ExecuteCommandHasChangeAsync(); if (!isOk) throw Oops.Oh(ErrorCode.COM1003); } else { if (entity == null || entity.PropertyJson.IsNullOrEmpty()) throw Oops.Oh(ErrorCode.COM1013); var newEntity = entity.DraftJson.ToObject(); if (newEntity == null) { newEntity = entity; } entity.TableJson = newEntity.TableJson; entity.PropertyJson = newEntity.PropertyJson; if (entity.FormType == 2) { // 无表转有表 if (entity.TableJson.IsNullOrEmpty() || entity.TableJson == "[]") { // 主表名称 var mTableName = "wf_" + entity.EnCode; if (mTableName.Length > 24) mTableName = "wf_" + SnowflakeIdHelper.NextId(); var devEntity = new VisualDev.Entitys.VisualDevEntity() { Id = entity.Id, State = 1, WebType = 3, EnableFlow = 1, FullName = entity.FullName, EnCode = entity.EnCode, FormData = entity.PropertyJson }; var res = await _visualDevService.NoTblToTable(devEntity, mTableName); if (res == null) throw Oops.Oh(ErrorCode.COM1000); entity.TableJson = res.Tables; entity.PropertyJson = res.FormData; //await _repository.AsSugarClient().Insertable(res).CallEntityMethod(m => m.Create()).ExecuteCommandAsync(); } //else if (await _repository.AsSugarClient().Queryable().Where(x => x.Id.Equals(id)).AnyAsync()) //await _repository.AsSugarClient().Updateable().SetColumns(it => it.IsRelease == 1).Where(it => it.Id == id).ExecuteCommandHasChangeAsync(); } var dbLink = await _runService.GetDbLink(newEntity.DbLinkId); var MainTable = newEntity.TableJson.ToList().Find(m => m.typeId.Equals("1")); // 主表 if (MainTable != null) { if (!_dataBaseManager.IsAnyColumn(dbLink, MainTable?.table, "f_flowtaskid")) { var pFieldList = new List() { new DbTableFieldModel() { field = "F_FlowTaskId", fieldName = "流程任务Id", dataType = "varchar", dataLength = "50", allowNull = 1 } }; _dataBaseManager.AddTableColumn(dbLink, MainTable?.table, pFieldList); } if (!_dataBaseManager.IsAnyColumn(dbLink, MainTable?.table, "f_flowid")) { var pFieldList = new List() { new DbTableFieldModel() { field = "F_FlowId", fieldName = "流程引擎Id", dataType = "varchar", dataLength = "50", allowNull = 1 } }; _dataBaseManager.AddTableColumn(dbLink, MainTable?.table, pFieldList); } } entity.DraftJson = entity.ToJsonString(); entity.EnabledMark = 1; var isOk = await _repository.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).CallEntityMethod(x => x.LastModify()).ExecuteCommandHasChangeAsync(); if (!isOk) throw Oops.Oh(ErrorCode.COM1003); } } /// /// 回滚. /// /// 主键值. /// [HttpPost("Stop/{id}")] public async Task Stop(string id) { var entity = await GetInfo(id); } #endregion #region PrivateMethod /// /// 详情. /// /// 主键. /// [NonAction] private async Task GetInfo(string id) { return await _repository.GetFirstAsync(a => a.Id == id && a.DeleteMark == null); } /// /// 导入数据. /// /// 导入实例. /// private async Task ImportData(FlowFormEntity entity) { if (await _repository.IsAnyAsync(x => (x.EnCode.Equals(entity.EnCode) || x.FullName.Equals(entity.FullName)) && x.DeleteMark == null)) throw Oops.Oh(ErrorCode.COM1004); // 在线开发生成导入版本自带ID //entity.Id = SnowflakeIdHelper.NextId(); try { _db.BeginTran(); // 存在更新不存在插入 根据主键 var stor = _repository.AsSugarClient().Storageable(entity).Saveable().ToStorage(); // 执行插入 await stor.AsInsertable.ExecuteCommandAsync(); // await stor.AsUpdateable.ExecuteCommandAsync(); //执行更新,停用原因:Oracle 数据库环境会抛异常:ora-01704: 字符串文字太长 // 执行更新 await _repository.AsSugarClient().Updateable(entity).ExecuteCommandAsync(); _db.CommitTran(); } catch (Exception ex) { _db.RollbackTran(); throw Oops.Oh(ErrorCode.D3006); } } /// /// 验证主键策略 数据库表是否支持. /// /// 模板信息. /// 数据库连接id. private async Task VerifyPrimaryKeyPolicy(TemplateParsingBase tInfo, string dbLinkId) { if (tInfo.IsHasTable) { DbLinkEntity link = await _runService.GetDbLink(dbLinkId); tInfo.AllTable.ForEach(item => { var tableList = _dataBaseManager.GetFieldList(link, item.table); // 获取主表所有列 var mainPrimary = tableList.Find(t => t.primaryKey); // 主表主键 if (mainPrimary == null) throw Oops.Oh(ErrorCode.D1409, "主键为空", item.table); if (tInfo.FormModel.primaryKeyPolicy.Equals(2) && !mainPrimary.identity) { throw Oops.Oh(ErrorCode.D1409, "自增长ID,没有自增标识", item.table); } if (tInfo.FormModel.primaryKeyPolicy.Equals(1) && !(mainPrimary.dataType.ToLower().Equals("string") || mainPrimary.dataType.ToLower().Equals("varchar") || mainPrimary.dataType.ToLower().Equals("nvarchar"))) throw Oops.Oh(ErrorCode.D1409, "雪花ID", item.table); }); _dataBaseManager.ChangeDataBase(_dataBaseManager.GetTenantDbLink(_userManager.TenantId, _userManager.TenantDbName)); } } #endregion }