Workflow.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. using Hotline.FlowEngine.Definitions;
  2. using Hotline.Orders;
  3. using Hotline.Share.Dtos.FlowEngine;
  4. using Hotline.Share.Enums.FlowEngine;
  5. using Hotline.Share.Enums.Order;
  6. using SqlSugar;
  7. using XF.Domain.Entities;
  8. using XF.Domain.Exceptions;
  9. using XF.Domain.Extensions;
  10. using XF.Domain.Repository;
  11. namespace Hotline.FlowEngine.Workflows;
  12. public class Workflow : CreationEntity
  13. {
  14. public string DefinitionId { get; set; }
  15. #region 业务模块
  16. [SugarColumn(IsNullable = true)]
  17. public string? ModuleId { get; set; }
  18. [SugarColumn(IsNullable = true)]
  19. public string? ModuleName { get; set; }
  20. [SugarColumn(IsNullable = true)]
  21. public string? ModuleCode { get; set; }
  22. #endregion
  23. [SugarColumn(Length = 2000)]
  24. public string Title { get; set; }
  25. /// <summary>
  26. /// 到期时间(期满时间)
  27. /// </summary>
  28. public DateTime ExpiredTime { get; set; }
  29. /// <summary>
  30. /// 办理时间限制(如:24小时、7个工作日)
  31. /// </summary>
  32. public string TimeLimit { get; set; }
  33. /// <summary>
  34. /// 流程流转到end节点的时间(配置模板的end节点前一个节点办理完成时间)
  35. /// </summary>
  36. public DateTime? CompleteTime { get; set; }
  37. /// <summary>
  38. /// 流程状态
  39. /// </summary>
  40. public EWorkflowStatus Status { get; set; }
  41. #region 实际办理节点,部门
  42. /// <summary>
  43. /// 实际办理节点名称(会签状态此字段保存最外层会签办理节点名称)
  44. /// </summary>
  45. [SugarColumn(IsNullable = true)]
  46. public string? ActualHandleStepName { get; set; }
  47. /// <summary>
  48. /// 到达实际办理节点时间(stepBox创建时间)
  49. /// </summary>
  50. public DateTime? ActualHandleStepTime { get; set; }
  51. /// <summary>
  52. /// 实际办理部门名称
  53. /// </summary>
  54. [SugarColumn(IsNullable = true)]
  55. public string? ActualHandleOrgName { get; set; }
  56. /// <summary>
  57. /// 实际办理部门编码
  58. /// </summary>
  59. [SugarColumn(IsNullable = true)]
  60. public string? ActualHandleOrgCode { get; set; }
  61. #endregion
  62. /// <summary>
  63. /// 会签类型
  64. /// </summary>
  65. public ECounterSignType? CounterSignType { get; set; }
  66. /// <summary>
  67. /// 会签办理节点code,嵌套会签为最外层会签办理节点code(不处于会签状态则无值)
  68. /// </summary>
  69. [SugarColumn(IsNullable = true)]
  70. public string? TopCountersignStepCode { get; set; }
  71. /// <summary>
  72. /// 处理方式(直办、交办)
  73. /// </summary>
  74. [SugarColumn(DefaultValue = "10")]
  75. public EProcessType ProcessType { get; set; } = EProcessType.Zhiban;
  76. /// <summary>
  77. /// 交办时间
  78. /// </summary>
  79. public DateTime AssignTime { get; set; }
  80. /// <summary>
  81. /// 办理意见(冗余,办理中...or 最终办理意见)
  82. /// </summary>
  83. [SugarColumn(Length = 2000)]
  84. public string Opinion { get; set; } = "办理中...";
  85. /// <summary>
  86. /// 办理人id
  87. /// </summary>
  88. [SugarColumn(ColumnDataType = "json", IsJson = true)]
  89. public List<HandlerGroupItem> HandlerUsers { get; set; } = new();
  90. /// <summary>
  91. /// 办理部门code
  92. /// </summary>
  93. [SugarColumn(ColumnDataType = "json", IsJson = true)]
  94. public List<HandlerGroupItem> HandlerOrgs { get; set; } = new();
  95. /// <summary>
  96. /// 指派部门code(所有流经部门留痕)
  97. /// </summary>
  98. [SugarColumn(ColumnDataType = "json", IsJson = true)]
  99. public List<string> AssignOrgCodes { get; set; } = new();
  100. /// <summary>
  101. /// 指派办理人Id(所有流经办理人留痕)
  102. /// </summary>
  103. [SugarColumn(ColumnDataType = "json", IsJson = true)]
  104. public List<string> AssignUserIds { get; set; } = new();
  105. /// <summary>
  106. /// 外部业务唯一标识
  107. /// </summary>
  108. public string ExternalId { get; set; }
  109. [Navigate(NavigateType.OneToOne, nameof(DefinitionId))]
  110. public Definition Definition { get; set; }
  111. /// <summary>
  112. /// 补充信息
  113. /// </summary>
  114. [Navigate(NavigateType.OneToMany, nameof(WorkflowSupplement.WorkflowId))]
  115. public List<WorkflowSupplement> Supplements { get; set; }
  116. ///// <summary>
  117. ///// 接办信息
  118. ///// </summary>
  119. //[Navigate(NavigateType.OneToMany, nameof(WorkflowAssign.WorkflowId))]
  120. //public List<WorkflowAssign> Assigns { get; set; }
  121. /// <summary>
  122. /// 会签
  123. /// </summary>
  124. [Navigate(NavigateType.OneToMany, nameof(WorkflowCountersign.WorkflowId))]
  125. public List<WorkflowCountersign> Countersigns { get; set; }
  126. /// <summary>
  127. /// 主节点,依据流转进度动态生成或删除
  128. /// </summary>
  129. [SugarColumn(IsIgnore = true)]
  130. public List<WorkflowStep> StepBoxes { get; set; }
  131. [SugarColumn(IsIgnore = true)]
  132. public List<WorkflowTrace> Traces { get; set; } = new();
  133. #region Method
  134. /// <summary>
  135. /// 流程结束
  136. /// </summary>
  137. public void Complete()
  138. {
  139. Status = EWorkflowStatus.Completed;
  140. CompleteTime = DateTime.Now;
  141. }
  142. public void Terminate(string opinion)
  143. {
  144. Status = EWorkflowStatus.Terminated;
  145. CompleteTime = DateTime.Now;
  146. Opinion = opinion;
  147. }
  148. ///// <summary>
  149. ///// 流程进行流转
  150. ///// </summary>
  151. //public void FLow(string currentStepName, DateTime currentStepTime, string? currentCountersignCode = null)
  152. //{
  153. // ActualHandleStepName = currentStepName;
  154. // ActualHandleStepTime = currentStepTime;
  155. // if (!string.IsNullOrEmpty(currentCountersignCode))
  156. // TopCountersignStepCode = currentCountersignCode;
  157. //}
  158. /// <summary>
  159. /// 更新实际办理节点、部门数据
  160. /// </summary>
  161. public void ActualHandle(
  162. string actualHandleStepName, DateTime actualHandleStepTime,
  163. string actualHandleOrgName, string actualHandleOrgCode)
  164. {
  165. ActualHandleStepName = actualHandleStepName;
  166. ActualHandleStepTime = actualHandleStepTime;
  167. ActualHandleOrgName = actualHandleOrgName;
  168. ActualHandleOrgCode = actualHandleOrgCode;
  169. }
  170. /// <summary>
  171. /// 更新workflow中实际办理节点数据(节点,时间,部门)
  172. /// </summary>
  173. public void SetWorkflowActualHandleInfo(WorkflowStep currentStepBox, WorkflowStep nextStepBox, string handlerOrgName, string handlerOrgCode)
  174. {
  175. //1.不在会签中,未发起会签(普通处理) 2.不在会签中,发起会签(保存会签节点),3.会签中,不更新
  176. if (IsInCountersign()) return;
  177. if (nextStepBox.StepType is EStepType.CountersignEnd or EStepType.End) return;
  178. ActualHandle(currentStepBox.Name, currentStepBox.CreationTime, handlerOrgName, handlerOrgCode);
  179. //if (isStartCountersign)
  180. // SetTopCountersignStepCode(stepBox.Code);
  181. }
  182. /// <summary>
  183. /// 检查会签是否结束
  184. /// </summary>
  185. public bool CheckIfCountersignOver()
  186. {
  187. //var countersignStepBox = StepBoxes.First(d => d.Code == TopCountersignStepCode);
  188. //var isCountersignOver = countersignStepBox.Steps.All(d =>
  189. // d.Status is EWorkflowStepStatus.Completed &&
  190. // (!d.HasStartCountersign || d.IsCountersignComplete.GetValueOrDefault()));
  191. //return isCountersignOver;
  192. return Countersigns.All(d => d.IsCompleted());
  193. }
  194. /// <summary>
  195. /// 流程是否处于会签中
  196. /// </summary>
  197. /// <returns></returns>
  198. public bool IsInCountersign() => !string.IsNullOrEmpty(TopCountersignStepCode);
  199. /// <summary>
  200. /// 开始会签
  201. /// </summary>
  202. public void StartCountersign(string currentCountersignCode, ECounterSignType counterSignType)
  203. {
  204. TopCountersignStepCode = currentCountersignCode;
  205. CounterSignType = counterSignType;
  206. }
  207. /// <summary>
  208. /// 结束流程会签状态
  209. /// </summary>
  210. public void EndCountersign() => TopCountersignStepCode = null;
  211. /// <summary>
  212. /// 重置最终办理意见
  213. /// </summary>
  214. public void ResetOption() => Opinion = "办理中...";
  215. /// <summary>
  216. /// 重新设置办理人(删除当前待办人/部门),撤回/跳转场景,因当前办理人不是流程指定办理人
  217. /// </summary>
  218. /// <param name="assignType"></param>
  219. /// <param name="handlerObjects"></param>
  220. public void ResetHandlers(EFlowAssignType assignType, List<HandlerGroupItem> handlerObjects)
  221. {
  222. HandlerOrgs = new List<HandlerGroupItem>();
  223. HandlerUsers = new List<HandlerGroupItem>();
  224. SetHandlers(assignType, handlerObjects);
  225. }
  226. /// <summary>
  227. /// 更新当前办理人(待办人或部门),正常流转场景
  228. /// </summary>
  229. public void UpdateHandlers(string handlerId, string handlerOrg, EFlowAssignType assignType, List<HandlerGroupItem> handlerObjects)
  230. {
  231. RemoveCurrentHandleGroup(handlerId, handlerOrg);
  232. SetHandlers(assignType, handlerObjects);
  233. }
  234. private void SetHandlers(EFlowAssignType assignType, List<HandlerGroupItem> handlerObjects)
  235. {
  236. switch (assignType)
  237. {
  238. case EFlowAssignType.Org:
  239. HandlerOrgs.AddRange(handlerObjects);
  240. HandlerOrgs = HandlerOrgs.Distinct().ToList();
  241. break;
  242. case EFlowAssignType.User:
  243. HandlerUsers.AddRange(handlerObjects);
  244. HandlerUsers = HandlerUsers.Distinct().ToList();
  245. break;
  246. default:
  247. throw new ArgumentOutOfRangeException(nameof(assignType), assignType, null);
  248. }
  249. }
  250. /// <summary>
  251. /// 更新当前办理人(逐级退回场景)
  252. /// </summary>
  253. /// <param name="handlerId"></param>
  254. /// <param name="handlerOrg"></param>
  255. /// <param name="prevStep"></param>
  256. public void UpdatePreviousHandlers(string handlerId, string handlerOrg, WorkflowStep prevStep)
  257. {
  258. RemoveCurrentHandleGroup(handlerId, handlerOrg);
  259. var groupId = Guid.NewGuid().ToString();
  260. var handlerObjects = prevStep.Handlers.Select(d => new HandlerGroupItem
  261. {
  262. GroupId = groupId,
  263. Id = d.Id,
  264. Name = d.Name
  265. });
  266. switch (prevStep.HandlerType)
  267. {
  268. case EHandlerType.OrgLevel:
  269. case EHandlerType.OrgType:
  270. case EHandlerType.AssignOrg:
  271. HandlerOrgs.AddRange(handlerObjects);
  272. HandlerOrgs = HandlerOrgs.Distinct().ToList();
  273. break;
  274. case EHandlerType.Role:
  275. case EHandlerType.AssignUser:
  276. HandlerUsers.AddRange(handlerObjects);
  277. HandlerUsers = HandlerUsers.Distinct().ToList();
  278. break;
  279. default:
  280. throw new ArgumentOutOfRangeException();
  281. }
  282. }
  283. /// <summary>
  284. /// 当前用户是否可以办理该流程
  285. /// </summary>
  286. /// <returns></returns>
  287. public bool CanHandle(string userId, string orgCode) =>
  288. Status is EWorkflowStatus.Runnable or EWorkflowStatus.Marked
  289. && (HandlerUsers.Any(d => d.Id == userId) || HandlerOrgs.Any(d => d.Id == orgCode));
  290. private void RemoveCurrentHandleGroup(string handlerId, string handlerOrg)
  291. {
  292. if (string.IsNullOrEmpty(handlerId) && string.IsNullOrEmpty(handlerOrg))
  293. throw new UserFriendlyException($"{nameof(RemoveCurrentHandleGroup)}, 办理对象参数为空");
  294. var groupOrg = HandlerOrgs.FirstOrDefault(d => d.Id == handlerOrg);
  295. if (groupOrg != null)
  296. {
  297. HandlerOrgs.RemoveAll(d => d.GroupId == groupOrg.GroupId);
  298. }
  299. var groupUser = HandlerUsers.FirstOrDefault(d => d.Id == handlerId);
  300. if (groupUser != null)
  301. {
  302. HandlerUsers.RemoveAll(d => d.GroupId == groupUser.GroupId);
  303. }
  304. }
  305. public void Assign(EFlowAssignType type, string handler)
  306. {
  307. handler = handler.ToLower();
  308. switch (type)
  309. {
  310. case EFlowAssignType.Org:
  311. var orgCodes = handler.GetHigherOrgCodes(true).ToList();
  312. AssignOrgCodes.AddRange(orgCodes);
  313. AssignOrgCodes = AssignOrgCodes.Distinct().ToList();
  314. break;
  315. case EFlowAssignType.User:
  316. if (!AssignUserIds.Exists(d => d == handler))
  317. AssignUserIds.Add(handler);
  318. break;
  319. default:
  320. throw new ArgumentOutOfRangeException(nameof(type), type, null);
  321. }
  322. }
  323. public void Assign(EFlowAssignType type, IEnumerable<string> handlers)
  324. {
  325. handlers = handlers.Select(d => d.ToLower());
  326. switch (type)
  327. {
  328. case EFlowAssignType.Org:
  329. var orgCodes = handlers.SelectMany(d => d.GetHigherOrgCodes(true)).ToList();
  330. AssignOrgCodes.AddRange(orgCodes);
  331. AssignOrgCodes = AssignOrgCodes.Distinct().ToList();
  332. break;
  333. case EFlowAssignType.User:
  334. AssignUserIds.AddRange(handlers);
  335. AssignUserIds = AssignUserIds.Distinct().ToList();
  336. break;
  337. default:
  338. throw new ArgumentOutOfRangeException(nameof(type), type, null);
  339. }
  340. }
  341. #endregion
  342. }
  343. /// <summary>
  344. /// 办理对象分组(以办理step分组,多人办理一个step为一组)
  345. /// </summary>
  346. public class HandlerGroupItem : IdName
  347. {
  348. public string GroupId { get; set; }
  349. }