WorkflowStep.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. using Hotline.File;
  2. using Hotline.Share.Dtos;
  3. using Hotline.Share.Dtos.FlowEngine;
  4. using Hotline.Share.Dtos.FlowEngine.Definition;
  5. using Hotline.Share.Dtos.FlowEngine.Workflow;
  6. using Hotline.Share.Enums.FlowEngine;
  7. using SqlSugar;
  8. using XF.Domain.Entities;
  9. using XF.Domain.Exceptions;
  10. namespace Hotline.FlowEngine.Workflows;
  11. public class WorkflowStep : StepBasicEntity
  12. {
  13. [Navigate(NavigateType.ManyToOne, nameof(WorkflowId))]
  14. public Workflow Workflow { get; set; }
  15. [Navigate(NavigateType.OneToOne, nameof(Id),
  16. nameof(Workflows.WorkflowTrace.StepId))]
  17. public WorkflowTrace WorkflowTrace { get; set; }
  18. //[Navigate(NavigateType.OneToMany, nameof(WorkflowStepHandler.WorkflowStepId))]
  19. //public List<WorkflowStepHandler> StepHandlers { get; set; }
  20. #region Method
  21. /// <summary>
  22. /// 是否处于会签流程中(不包含顶层发起会签节点)
  23. /// </summary>
  24. public bool IsInCountersign() => CountersignPosition != ECountersignPosition.None;
  25. ///// <summary>
  26. ///// 是否发起过会签
  27. ///// </summary>
  28. //public bool HasStartedCountersign() => !string.IsNullOrEmpty(StartCountersignId);
  29. ///// <summary>
  30. ///// 接办
  31. ///// </summary>
  32. //public void Accept(string userId, string? userName, string orgId, string? orgName, string? orgAreaCode,
  33. // string? orgAreaName)
  34. //{
  35. // AcceptorId = userId;
  36. // AcceptorName = userName;
  37. // AcceptorOrgId = orgId;
  38. // AcceptorOrgName = orgName;
  39. // AcceptorOrgAreaCode = orgAreaCode;
  40. // AcceptorOrgAreaName = orgAreaName;
  41. // AcceptTime = DateTime.Now;
  42. // Status = EWorkflowStepStatus.WaitForHandle;
  43. //}
  44. ///// <summary>
  45. ///// 节点办理完成
  46. ///// </summary>
  47. //public void Handle(
  48. // string userId, string? userName,
  49. // string orgId, string? orgName,
  50. // string? orgAreaCode, string? orgAreaName,
  51. // bool orgIsCenter, string opinion, string nextStepCode)
  52. //{
  53. // base.Handle(userId, userName, orgId, orgName, orgAreaCode, orgAreaName, orgIsCenter, opinion);
  54. // if (!IsInCountersign()
  55. // && InstanceMode is EInstanceMode.Config
  56. // && StepType is not EStepType.End)
  57. // {
  58. // var step = NextSteps.FirstOrDefault(d => d.Code == nextStepCode);
  59. // if (step != null)
  60. // step.Selected = true;
  61. // }
  62. //}
  63. /// <summary>
  64. /// 会签结束
  65. /// </summary>
  66. public void CountersignEnd() => IsStartedCountersignEnd = true;
  67. /// <summary>
  68. /// 开启会签
  69. /// </summary>
  70. public void StartCountersign(string startCountersignId)
  71. {
  72. IsStartCountersign = true;
  73. StartCountersignId = startCountersignId;
  74. IsStartedCountersignEnd = false;
  75. }
  76. //public void CreateCountersignSteps(List<WorkflowStep> nextSteps) =>
  77. // CountersignSteps = nextSteps
  78. // .Select(d => new CountersignStep { StepId = d.Id })
  79. // .ToList();
  80. /// <summary>
  81. /// step设置为可接办状态
  82. /// </summary>
  83. public void SetAssigned() => Status = EWorkflowStepStatus.WaitForAccept;
  84. ///// <summary>
  85. ///// 依据当前节点获取下一节点会签状态
  86. ///// </summary>
  87. ///// <returns></returns>
  88. //public ECountersignPosition GetNextStepCountersignPosition() =>
  89. // IsStartCountersign
  90. // ? ECountersignPosition.Multi
  91. // : IsInCountersign()
  92. // ? ECountersignPosition.Single
  93. // : ECountersignPosition.None;
  94. public bool IsCenter() => BusinessType is EBusinessType.Center or EBusinessType.Send;
  95. public bool IsOrg() => BusinessType is EBusinessType.Department;
  96. /// <summary>
  97. /// 重置节点,只清除办理痕迹(退回场景/依据会签开始节点复制会签汇总节点,将上一级节点重置等待重新办理)
  98. /// </summary>
  99. public void Reset()
  100. {
  101. HandleTime = null;
  102. AcceptorId = null;
  103. AcceptorName = null;
  104. AcceptorOrgId = null;
  105. AcceptorOrgName = null;
  106. AcceptorOrgAreaCode = null;
  107. AcceptorOrgAreaName = null;
  108. AcceptTime = null;
  109. IsStartCountersign = false;
  110. NextSteps.ForEach(d => d.Selected = false);
  111. }
  112. /// <summary>
  113. /// 重置办理参数
  114. /// </summary>
  115. public void ResetParameters()
  116. {
  117. NextHandlers = new();
  118. NextMainHandler = null;
  119. NextStepCode = null;
  120. BackToCountersignEnd = false;
  121. IsSms = false;
  122. Opinion = null;
  123. FileJson = new();
  124. StepExpiredTime = null;
  125. }
  126. ///// <summary>
  127. ///// 发起会签节点发起的会签是否全都办理完成
  128. ///// </summary>
  129. ///// <returns></returns>
  130. //public bool StartedCountersignHasAllHandled()
  131. //{
  132. // //if (!HasStartedCountersign())
  133. // // throw new UserFriendlyException("该节点未发起会签");
  134. // //outer属于特殊会签
  135. // return CountersignSteps.All(d => d.Completed);
  136. //}
  137. /// <summary>
  138. /// 动态实例化节点是否应该终止
  139. /// </summary>
  140. /// <returns></returns>
  141. public bool DynamicShouldTerminal() => !string.IsNullOrEmpty(NextStepCode) && TerminalDynamicMark == NextStepCode;
  142. /// <summary>
  143. /// 是否是顶级会签汇总节点
  144. /// </summary>
  145. public bool IsTopCountersignEndStep(string? topCountersignStepId)
  146. {
  147. if (string.IsNullOrEmpty(topCountersignStepId))
  148. throw new UserFriendlyException($"无效顶级会签节点编号,wfId: {WorkflowId}");
  149. return IsCountersignEndStep && CountersignStartStepId == topCountersignStepId;
  150. }
  151. //public WorkflowStepHandler?
  152. // FindActualHandler(ICollection<string> roles, string? userId = null, string? orgId = null) =>
  153. // StepHandlers.FirstOrDefault(d => d.IsHandler(roles, userId, orgId));
  154. //public WorkflowStepHandler? GetActualHandler() =>
  155. // StepHandlers.FirstOrDefault(d => d.IsActualHandler);
  156. public FlowStepHandler GetWorkflowStepHandler()
  157. {
  158. switch (FlowAssignType)
  159. {
  160. case EFlowAssignType.Org:
  161. return new FlowStepHandler
  162. {
  163. Key = HandlerOrgId,
  164. Value = HandlerOrgName,
  165. OrgId = HandlerOrgId,
  166. OrgName = HandlerOrgName
  167. };
  168. case EFlowAssignType.User:
  169. return new FlowStepHandler
  170. {
  171. Key = HandlerId,
  172. Value = HandlerName,
  173. UserId = HandlerId,
  174. Username = HandlerName,
  175. OrgId = HandlerOrgId,
  176. OrgName = HandlerOrgName
  177. };
  178. case EFlowAssignType.Role:
  179. return new FlowStepHandler
  180. {
  181. Key = RoleId,
  182. Value = RoleName,
  183. RoleId = RoleId,
  184. RoleName = RoleName,
  185. };
  186. default:
  187. throw new ArgumentOutOfRangeException();
  188. }
  189. }
  190. public bool IsCanHandle(string userId, string orgId, string[] roleIds)
  191. {
  192. if(Status is EWorkflowStepStatus.Handled) return false;
  193. return FlowAssignType switch
  194. {
  195. EFlowAssignType.Org => !string.IsNullOrEmpty(HandlerOrgId) && HandlerOrgId == orgId,
  196. EFlowAssignType.User => !string.IsNullOrEmpty(HandlerId) && HandlerId == userId,
  197. EFlowAssignType.Role => !string.IsNullOrEmpty(RoleId) && roleIds.Contains(RoleId),
  198. _ => throw new ArgumentOutOfRangeException()
  199. };
  200. }
  201. #endregion
  202. }
  203. /*
  204. 获取开始节点下一步待选节点:
  205. 是否属于多次实例化配置(dynamic动态实例化配置:结合办理人动态生成待办节点)
  206. t:无需检查是否到结束标识,所以按配置的重复实例化策略结合当前操作人生成待办节点,如:生成x级部门办理,下一接口再选具体部门
  207. f:开始节点配置的下一节点集合
  208. 获取开始节点所选下一步节点的具体参数:
  209. 是否属于多次实例化配置
  210. t:所选x级部门返回对应办理对象集合(具体部门)
  211. f:按模板配置返回办理对象集合
  212. 获取待办节点下一步待选节点:
  213. 是否属于多次实例化配置(无需检查definetion,直接找到当前待办节点获取参数(需要的参数冗余至待办节点上))
  214. t:检查是否到结束标识(检查当前办理节点的办理对象与结束标识(orglevel:int)是否一致)
  215. t:返回配置的下一节点(看作单次实例化,当前节点已办完)//definitionId,stepCodes,按配置走
  216. f:按配置的重复实例化策略结合当前操作人,生成如:{x:X级部门办理},{0:中心办理},最高级最多只能到中心,按动态策略走,动态策略枚举
  217. 是否处于会签中
  218. t:按会签策略的下一级 + 上一级(isCSEnd会签汇总节点标记),并对上一级选项打上标记,如:{x:X级部门办理},{0:中心办理},按动态策略走,动态策略枚举
  219. f:按照definition(按配置走)
  220. 返回当前节点的配置下一步
  221. 获取下一步的具体参数:
  222. 检查模式
  223. 按动态策略:依据当前操作人、动态类型枚举 查询对应待选部门
  224. 按配置策略:原有实现方案
  225. start:
  226. check配置是否允许当前用户发起
  227. next: (nextStepCode/x, userId[]/orgId[],dto.other)
  228. check该不该当前用户办(handlers,all不需要check)
  229. 办理当前节点
  230. 创建后面节点
  231. 是否动态实例化
  232. t:检查是否到结束标识(参数orgId==mark?)
  233. t:(参数时stepcode)按配置创建
  234. f:(参数是x级部门)按动态实例化策略创建
  235. 是否处于会签中
  236. t: 是否返回上一级汇总 (isCSEnd)
  237. t: 1.update上一级的会签完成情况[]
  238. 2.如果会签全完成,创建新的待办汇总节点(IsCountersignSummaryStep = true)?更新发起节点状态?thk
  239. f: 创建新的下一步节点
  240. */