S8TaskFlowService.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. using Admin.NET.Plugin.AiDOP.Entity.S8;
  2. using Admin.NET.Plugin.AiDOP.Infrastructure.S8;
  3. using Admin.NET.Plugin.ApprovalFlow.Service;
  4. namespace Admin.NET.Plugin.AiDOP.Service.S8;
  5. public class S8TaskFlowService : ITransient
  6. {
  7. private readonly SqlSugarRepository<AdoS8Exception> _rep;
  8. private readonly SqlSugarRepository<AdoS8ExceptionTimeline> _timelineRep;
  9. private readonly FlowEngineService _flowEngine;
  10. private readonly UserManager _userManager;
  11. public S8TaskFlowService(
  12. SqlSugarRepository<AdoS8Exception> rep,
  13. SqlSugarRepository<AdoS8ExceptionTimeline> timelineRep,
  14. FlowEngineService flowEngine,
  15. UserManager userManager)
  16. {
  17. _rep = rep;
  18. _timelineRep = timelineRep;
  19. _flowEngine = flowEngine;
  20. _userManager = userManager;
  21. }
  22. public async Task<AdoS8Exception> ClaimAsync(long id, long tenantId, long factoryId, long assigneeId, string? remark)
  23. {
  24. if (assigneeId <= 0) throw new S8BizException("认领需指定处理人 AssigneeId");
  25. var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
  26. if (!S8StatusRules.IsAllowedTransition(e.Status, "ASSIGNED"))
  27. throw new S8BizException($"状态 {e.Status} 不可认领");
  28. var fromStatus = e.Status;
  29. e.Status = "ASSIGNED";
  30. e.AssigneeId = assigneeId;
  31. e.AssignedAt = DateTime.Now;
  32. e.UpdatedAt = DateTime.Now;
  33. await _rep.AsTenant().UseTranAsync(async () =>
  34. {
  35. await _rep.UpdateAsync(e);
  36. await InsertTimelineAsync(e.Id, "CLAIM", "认领", fromStatus, "ASSIGNED", assigneeId, null, remark);
  37. }, ex => throw ex);
  38. return e;
  39. }
  40. public async Task<AdoS8Exception> TransferAsync(long id, long tenantId, long factoryId, long newAssigneeId, string? remark)
  41. {
  42. var allowedStatuses = new HashSet<string> { "ASSIGNED", "IN_PROGRESS" };
  43. if (newAssigneeId <= 0) throw new S8BizException("转派目标无效");
  44. var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
  45. if (e.ActiveFlowInstanceId.HasValue)
  46. throw new S8BizException("审批进行中不可转派");
  47. if (e.Status == "ESCALATED")
  48. throw new S8BizException("升级审批中不可转派");
  49. if (S8StatusRules.IsTerminal(e.Status))
  50. throw new S8BizException("已关闭不可转派");
  51. if (!allowedStatuses.Contains(e.Status))
  52. throw new S8BizException($"状态 {e.Status} 不可转派");
  53. e.AssigneeId = newAssigneeId;
  54. e.UpdatedAt = DateTime.Now;
  55. await _rep.AsTenant().UseTranAsync(async () =>
  56. {
  57. await _rep.UpdateAsync(e);
  58. await InsertTimelineAsync(e.Id, "TRANSFER", "转派", e.Status, e.Status, newAssigneeId, null, remark);
  59. }, ex => throw ex);
  60. return e;
  61. }
  62. public async Task<AdoS8Exception> StartProgressAsync(long id, long tenantId, long factoryId, string? remark)
  63. {
  64. var currentUserId = GetCurrentUserId();
  65. var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
  66. if (!S8StatusRules.IsAllowedTransition(e.Status, "IN_PROGRESS"))
  67. throw new S8BizException($"状态 {e.Status} 不可开始处理");
  68. var fromStatus = e.Status;
  69. e.Status = "IN_PROGRESS";
  70. e.UpdatedAt = DateTime.Now;
  71. await _rep.AsTenant().UseTranAsync(async () =>
  72. {
  73. await _rep.UpdateAsync(e);
  74. await InsertTimelineAsync(e.Id, "START_PROGRESS", "开始处理", fromStatus, "IN_PROGRESS", currentUserId, null, remark);
  75. }, ex => throw ex);
  76. return e;
  77. }
  78. public async Task<AdoS8Exception> UpgradeAsync(long id, long tenantId, long factoryId, string? remark)
  79. {
  80. var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
  81. if (e.ActiveFlowInstanceId.HasValue)
  82. throw new S8BizException("该异常已有进行中的审批流程,请等待审批完成");
  83. if (!S8StatusRules.IsAllowedTransition(e.Status, "ESCALATED"))
  84. throw new S8BizException($"状态 {e.Status} 不可升级");
  85. await _flowEngine.StartFlow(new StartFlowInput
  86. {
  87. BizType = "EXCEPTION_ESCALATION",
  88. BizId = e.Id,
  89. Title = $"异常升级审批 - {e.ExceptionCode}",
  90. Comment = remark,
  91. BizData = new Dictionary<string, object>
  92. {
  93. ["severity"] = e.Severity,
  94. ["sceneCode"] = e.SceneCode,
  95. ["priorityLevel"] = e.PriorityLevel,
  96. }
  97. });
  98. // 状态和时间线由 ExceptionEscalationBizHandler.OnFlowStarted 回调更新
  99. return await LoadAsync(id, tenantId, factoryId) ?? e;
  100. }
  101. public async Task<AdoS8Exception> RejectAsync(long id, long tenantId, long factoryId, string? remark)
  102. {
  103. var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
  104. if (!S8StatusRules.IsAllowedTransition(e.Status, "REJECTED"))
  105. throw new S8BizException($"状态 {e.Status} 不可驳回");
  106. var from = e.Status;
  107. e.Status = "REJECTED";
  108. e.UpdatedAt = DateTime.Now;
  109. await _rep.AsTenant().UseTranAsync(async () =>
  110. {
  111. await _rep.UpdateAsync(e);
  112. await InsertTimelineAsync(e.Id, "REJECT", "驳回", from, "REJECTED", null, null, remark);
  113. }, ex => throw ex);
  114. return e;
  115. }
  116. public async Task<AdoS8Exception> CloseAsync(long id, long tenantId, long factoryId, string? remark)
  117. {
  118. var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
  119. if (e.ActiveFlowInstanceId.HasValue)
  120. throw new S8BizException("该异常已有进行中的审批流程,请等待审批完成");
  121. if (!S8StatusRules.IsAllowedTransition(e.Status, "CLOSED"))
  122. throw new S8BizException($"状态 {e.Status} 不可关闭");
  123. await _flowEngine.StartFlow(new StartFlowInput
  124. {
  125. BizType = "EXCEPTION_CLOSURE",
  126. BizId = e.Id,
  127. Title = $"异常关闭确认 - {e.ExceptionCode}",
  128. Comment = remark,
  129. BizData = new Dictionary<string, object>
  130. {
  131. ["sceneCode"] = e.SceneCode,
  132. }
  133. });
  134. // 状态和时间线由 ExceptionClosureBizHandler.OnFlowStarted 回调更新
  135. return await LoadAsync(id, tenantId, factoryId) ?? e;
  136. }
  137. public async Task<AdoS8Exception> SubmitVerificationAsync(
  138. long id, long tenantId, long factoryId,
  139. long verifierId, string? remark)
  140. {
  141. var currentUserId = GetCurrentUserId();
  142. var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
  143. if (e.AssigneeId != currentUserId)
  144. throw new S8BizException("只有当前处理人才能提交复检");
  145. if (verifierId <= 0)
  146. throw new S8BizException("请选择检验人");
  147. if (!S8StatusRules.IsAllowedTransition(e.Status, "PENDING_VERIFICATION"))
  148. throw new S8BizException($"状态 {e.Status} 不可提交复检");
  149. var from = e.Status;
  150. e.Status = "PENDING_VERIFICATION";
  151. e.VerifierId = verifierId;
  152. e.VerificationAssignedAt = DateTime.Now;
  153. e.UpdatedAt = DateTime.Now;
  154. await _rep.AsTenant().UseTranAsync(async () =>
  155. {
  156. await _rep.UpdateAsync(e);
  157. await InsertTimelineAsync(e.Id, "VERIFY_SUBMITTED", "提交复检", from, "PENDING_VERIFICATION",
  158. currentUserId, null, remark);
  159. }, ex => throw ex);
  160. return e;
  161. }
  162. public async Task<AdoS8Exception> ApproveVerificationAsync(
  163. long id, long tenantId, long factoryId,
  164. string? remark)
  165. {
  166. var currentUserId = GetCurrentUserId();
  167. var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
  168. if (e.VerifierId != currentUserId)
  169. throw new S8BizException("只有指定检验人才能检验通过");
  170. if (!S8StatusRules.IsAllowedTransition(e.Status, "RESOLVED"))
  171. throw new S8BizException($"状态 {e.Status} 不可检验通过");
  172. var from = e.Status;
  173. e.Status = "RESOLVED";
  174. e.VerifiedAt = DateTime.Now;
  175. e.VerificationResult = "APPROVED";
  176. e.VerificationRemark = remark;
  177. e.UpdatedAt = DateTime.Now;
  178. await _rep.AsTenant().UseTranAsync(async () =>
  179. {
  180. await _rep.UpdateAsync(e);
  181. await InsertTimelineAsync(e.Id, "VERIFY_APPROVED", "检验通过", from, "RESOLVED",
  182. currentUserId, null, remark);
  183. }, ex => throw ex);
  184. return e;
  185. }
  186. public async Task<AdoS8Exception> RejectVerificationAsync(
  187. long id, long tenantId, long factoryId,
  188. string remark)
  189. {
  190. var currentUserId = GetCurrentUserId();
  191. var e = await LoadAsync(id, tenantId, factoryId) ?? throw new S8BizException("异常不存在");
  192. if (e.VerifierId != currentUserId)
  193. throw new S8BizException("只有指定检验人才能检验退回");
  194. if (!S8StatusRules.IsAllowedTransition(e.Status, "IN_PROGRESS"))
  195. throw new S8BizException($"状态 {e.Status} 不可检验退回");
  196. if (string.IsNullOrWhiteSpace(remark))
  197. throw new S8BizException("检验退回必须填写退回原因");
  198. var from = e.Status;
  199. e.Status = "IN_PROGRESS";
  200. e.VerifiedAt = DateTime.Now;
  201. e.VerificationResult = "REJECTED";
  202. e.VerificationRemark = remark;
  203. e.UpdatedAt = DateTime.Now;
  204. await _rep.AsTenant().UseTranAsync(async () =>
  205. {
  206. await _rep.UpdateAsync(e);
  207. await InsertTimelineAsync(e.Id, "VERIFY_REJECTED", "检验退回", from, "IN_PROGRESS",
  208. currentUserId, null, remark);
  209. }, ex => throw ex);
  210. return e;
  211. }
  212. public async Task CommentAsync(long id, string? remark)
  213. {
  214. var e = await _rep.GetFirstAsync(x => x.Id == id && !x.IsDeleted)
  215. ?? throw new S8BizException("异常不存在");
  216. await InsertTimelineAsync(e.Id, "COMMENT", "补充说明", e.Status, e.Status, null, null, remark);
  217. }
  218. private Task<AdoS8Exception?> LoadAsync(long id, long tenantId, long factoryId) =>
  219. _rep.GetFirstAsync(x => x.Id == id && x.TenantId == tenantId && x.FactoryId == factoryId && !x.IsDeleted);
  220. // 统一复用框架登录上下文,避免业务身份继续信任前端传参。
  221. private long GetCurrentUserId()
  222. {
  223. var currentUserId = _userManager.UserId;
  224. if (currentUserId <= 0)
  225. throw new S8BizException("未获取到当前登录用户");
  226. return currentUserId;
  227. }
  228. private async Task InsertTimelineAsync(long exceptionId, string code, string label, string? from, string? to,
  229. long? operatorId, string? operatorName, string? remark) =>
  230. await _timelineRep.InsertAsync(new AdoS8ExceptionTimeline
  231. {
  232. ExceptionId = exceptionId,
  233. ActionCode = code,
  234. ActionLabel = label,
  235. FromStatus = from,
  236. ToStatus = to,
  237. OperatorId = operatorId,
  238. OperatorName = operatorName,
  239. ActionRemark = remark,
  240. CreatedAt = DateTime.Now
  241. });
  242. }