WorkflowController.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. using Hotline.Application.FlowEngine;
  2. using Hotline.FlowEngine.Definitions;
  3. using Hotline.FlowEngine.Workflows;
  4. using Hotline.Identity.Roles;
  5. using Hotline.Permissions;
  6. using Hotline.Repository.SqlSugar.Extensions;
  7. using Hotline.Settings;
  8. using Hotline.Share.Dtos;
  9. using Hotline.Share.Dtos.FlowEngine;
  10. using Hotline.Share.Enums.FlowEngine;
  11. using Hotline.Users;
  12. using MapsterMapper;
  13. using Microsoft.AspNetCore.Mvc;
  14. using SqlSugar;
  15. using Hotline.FlowEngine.WorkflowModules;
  16. using XF.Domain.Authentications;
  17. using XF.Domain.Exceptions;
  18. using XF.Domain.Extensions;
  19. using XF.Utility.EnumExtensions;
  20. using XF.Domain.Repository;
  21. using Hotline.Share.Dtos.FlowEngine.Workflow;
  22. using Hotline.Caching.Interfaces;
  23. using Hotline.File;
  24. using Hotline.Orders;
  25. using Hotline.Share.Dtos.Order;
  26. using Hotline.Share.Dtos.FlowEngine.Definition;
  27. using Microsoft.AspNetCore.Authorization;
  28. using Hotline.Caching.Services;
  29. using Hotline.Settings.TimeLimits;
  30. namespace Hotline.Api.Controllers;
  31. /// <summary>
  32. /// 工作流管理
  33. /// </summary>
  34. public class WorkflowController : BaseController
  35. {
  36. private readonly IDefinitionDomainService _definitionDomainService;
  37. private readonly IRepository<WorkflowDefinition> _definitionRepository;
  38. private readonly IWorkflowApplication _workflowApplication;
  39. private readonly IWorkflowDomainService _workflowDomainService;
  40. private readonly IWorkflowRepository _workflowRepository;
  41. private readonly IRepository<User> _userRepository;
  42. private readonly ISystemOrganizeRepository _organizeRepository;
  43. private readonly IRepository<Role> _roleRepository;
  44. private readonly ISystemDomainService _systemDomainService;
  45. private readonly IWfModuleDomainService _wfModuleDomainService;
  46. private readonly IRepository<WorkflowStep> _workflowStepRepository;
  47. private readonly IRepository<WorkflowModule> _wfModuleRepository;
  48. private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
  49. private readonly IRepository<WorkflowCountersign> _workflowCountersignRepository;
  50. private readonly IRepository<WorkflowCountersignMember> _workflowCountersignMemberRepository;
  51. private readonly ISessionContext _sessionContext;
  52. private readonly IMapper _mapper;
  53. private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
  54. private readonly IFileRepository _fileRepository;
  55. private readonly ISystemDicDataCacheManager _sysDicDataCacheManager;
  56. public WorkflowController(
  57. IDefinitionDomainService definitionDomainService,
  58. IRepository<WorkflowDefinition> definitionRepository,
  59. IWorkflowApplication workflowApplication,
  60. IWorkflowDomainService workflowDomainService,
  61. IWorkflowRepository workflowRepository,
  62. IRepository<User> userRepository,
  63. ISystemOrganizeRepository organizeRepository,
  64. IRepository<Role> roleRepository,
  65. ISystemDomainService systemDomainService,
  66. IWfModuleDomainService wfModuleDomainService,
  67. IRepository<WorkflowStep> workflowStepRepository,
  68. IRepository<WorkflowModule> wfModuleRepository,
  69. IRepository<WorkflowTrace> workflowTraceRepository,
  70. IRepository<WorkflowCountersign> workflowCountersignRepository,
  71. ISessionContext sessionContext,
  72. IMapper mapper,
  73. ISystemDicDataCacheManager systemDicDataCacheManager,
  74. IFileRepository fileRepository,
  75. IRepository<WorkflowCountersignMember> workflowCountersignMemberRepository,
  76. ISystemDicDataCacheManager sysDicDataCacheManager
  77. )
  78. {
  79. _definitionDomainService = definitionDomainService;
  80. _definitionRepository = definitionRepository;
  81. _workflowApplication = workflowApplication;
  82. _workflowDomainService = workflowDomainService;
  83. _workflowRepository = workflowRepository;
  84. _userRepository = userRepository;
  85. _organizeRepository = organizeRepository;
  86. _roleRepository = roleRepository;
  87. _systemDomainService = systemDomainService;
  88. _wfModuleDomainService = wfModuleDomainService;
  89. _workflowStepRepository = workflowStepRepository;
  90. _wfModuleRepository = wfModuleRepository;
  91. _sessionContext = sessionContext;
  92. _mapper = mapper;
  93. _workflowTraceRepository = workflowTraceRepository;
  94. _workflowCountersignRepository = workflowCountersignRepository;
  95. _systemDicDataCacheManager = systemDicDataCacheManager;
  96. _fileRepository = fileRepository;
  97. _workflowCountersignMemberRepository = workflowCountersignMemberRepository;
  98. _sysDicDataCacheManager = sysDicDataCacheManager;
  99. }
  100. #region definition
  101. /// <summary>
  102. /// 分页查询最新版本号的模板
  103. /// </summary>
  104. /// <param name="dto"></param>
  105. /// <returns></returns>
  106. [HttpGet("definition/latest")]
  107. public async Task<PagedDto<DefinitionDto>> QueryDefinitionLatest([FromQuery] QueryDefinitionDto dto)
  108. {
  109. var query2 = await _definitionRepository.Queryable()
  110. .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status)
  111. .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
  112. d => d.Code.Contains(dto.Keyword) || d.Name.Contains(dto.Keyword))
  113. .Select(d => new { i = SqlFunc.RowNumber($"{d.Version} desc", d.Code), d })
  114. .MergeTable()
  115. .Where(d => d.i == 1)
  116. .ToListAsync();
  117. var items = query2.Select(d => d.d).ToList();
  118. return new PagedDto<DefinitionDto>(query2.Count, _mapper.Map<IReadOnlyList<DefinitionDto>>(items));
  119. }
  120. /// <summary>
  121. /// 分页查询流程模板
  122. /// </summary>
  123. /// <param name="dto"></param>
  124. /// <returns></returns>
  125. [Permission(EPermission.FlowDefinitionQuery)]
  126. [HttpGet("definition")]
  127. public async Task<PagedDto<DefinitionDto>> QueryDefinitions([FromQuery] QueryDefinitionDto dto)
  128. {
  129. #region old version:只查询草稿、禁用以及已启用模板的最新版本
  130. ////todo 数据量大需重构
  131. //var query1 = await _definitionRepository.Queryable()
  132. // .Where(d => d.Status == EDefinitionStatus.Temporary)
  133. // .ToListAsync();
  134. //var query2 = await _definitionRepository.Queryable()
  135. // .Where(d => d.Status != EDefinitionStatus.Temporary)
  136. // .Select(d => new { i = SqlFunc.RowNumber($"{d.Version} desc", d.Code), d })
  137. // .MergeTable()
  138. // .Where(d => d.i == 1)
  139. // .ToListAsync();
  140. //var query = query1.Union(query2.Select(d => d.d));
  141. //var total = query.Count();
  142. //var items = query
  143. // .OrderBy(d => d.Status)
  144. // .ThenByDescending(d => d.CreationTime)
  145. // .Skip(dto.Skip())
  146. // .Take(dto.PageSize)
  147. // .ToList();
  148. #endregion
  149. var (total, items) = await _definitionRepository.Queryable()
  150. .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status)
  151. .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
  152. d => d.Code.Contains(dto.Keyword!) || d.Name.Contains(dto.Keyword!))
  153. .OrderBy(d => d.Status)
  154. .OrderBy(d => d.Code)
  155. .OrderByDescending(d => d.Version)
  156. .ToPagedListAsync(dto, HttpContext.RequestAborted);
  157. return new PagedDto<DefinitionDto>(total, _mapper.Map<IReadOnlyList<DefinitionDto>>(items));
  158. }
  159. /// <summary>
  160. /// 查询流程模板
  161. /// </summary>
  162. /// <param name="id"></param>
  163. /// <returns></returns>
  164. [HttpGet("definition/{id}")]
  165. public async Task<DefinitionDto> GetDefinition(string id)
  166. {
  167. var definition = await _definitionRepository.GetAsync(id, HttpContext.RequestAborted);
  168. if (definition == null) return new();
  169. return _mapper.Map<DefinitionDto>(definition);
  170. }
  171. /// <summary>
  172. /// 新增流程模板草稿
  173. /// </summary>
  174. /// <param name="dto"></param>
  175. /// <returns></returns>
  176. [Permission(EPermission.FlowDefinitionAdd)]
  177. [HttpPost("definition")]
  178. public async Task<string> AddDefinition([FromBody] AddDefinitionDto dto)
  179. {
  180. return await _definitionDomainService.AddAsync(dto, HttpContext.RequestAborted);
  181. }
  182. /// <summary>
  183. /// 更新流程模板草稿
  184. /// </summary>
  185. /// <param name="dto"></param>
  186. /// <returns></returns>
  187. [Permission(EPermission.FlowDefinitionUpdate)]
  188. [HttpPut("definition")]
  189. public async Task UpdateDefinition([FromBody] UpdateDefinitionDto dto)
  190. {
  191. var definition = await _definitionRepository.GetAsync(dto.Id, HttpContext.RequestAborted);
  192. if (definition == null)
  193. throw UserFriendlyException.SameMessage("无效模板编号");
  194. if (definition.Status == EDefinitionStatus.Temporary)
  195. {
  196. _mapper.Map(dto, definition);
  197. await _definitionRepository.UpdateAsync(definition, HttpContext.RequestAborted);
  198. }
  199. else
  200. {
  201. var newDefinition = _mapper.Map<WorkflowDefinition>(dto);
  202. await _definitionRepository.AddAsync(newDefinition, HttpContext.RequestAborted);
  203. }
  204. }
  205. /// <summary>
  206. /// 删除草稿
  207. /// </summary>
  208. /// <param name="id"></param>
  209. /// <returns></returns>
  210. /// <exception cref="UserFriendlyException"></exception>
  211. [Permission(EPermission.FlowDefinitionRemove)]
  212. [HttpDelete("definition/{id}")]
  213. public async Task RemoveDefinition(string id)
  214. {
  215. var definition = await _definitionRepository.GetAsync(id, HttpContext.RequestAborted);
  216. if (definition == null) return;
  217. if (definition.Status != EDefinitionStatus.Temporary)
  218. throw new UserFriendlyException("已发布模板不能删除");
  219. await _definitionRepository.RemoveAsync(id, false, HttpContext.RequestAborted);
  220. }
  221. /// <summary>
  222. /// 发布(列表操作)
  223. /// </summary>
  224. /// <returns></returns>
  225. [Permission(EPermission.FlowDefinitionPublish)]
  226. [HttpPost("definition/{id}/publish")]
  227. public async Task Publish(string id)
  228. {
  229. await _definitionDomainService.PublishAsync(id, HttpContext.RequestAborted);
  230. }
  231. /// <summary>
  232. /// 发布(保存并发布)
  233. /// </summary>
  234. /// <returns></returns>
  235. [Obsolete]
  236. [Permission(EPermission.FlowDefinitionPublish)]
  237. [HttpPost("definition/publish")]
  238. public async Task Publish([FromBody] AddDefinitionDto dto)
  239. {
  240. await _definitionDomainService.PublishAsync(dto, HttpContext.RequestAborted);
  241. }
  242. #endregion
  243. #region WorkflowModule
  244. /// <summary>
  245. /// 持久化新增工作流业务
  246. /// </summary>
  247. /// <returns></returns>
  248. [HttpGet("wfmodule/persistence")]
  249. public async Task PersistenceWfModule()
  250. {
  251. await _wfModuleDomainService.PersistenceModulesAsync(HttpContext.RequestAborted);
  252. }
  253. /// <summary>
  254. /// 查询所有工作流模块
  255. /// </summary>
  256. /// <returns></returns>
  257. [HttpGet("wfmodules")]
  258. public async Task<IReadOnlyList<WorkflowModule>> QueryWfModules()
  259. {
  260. return await _wfModuleRepository.Queryable()
  261. .Includes(d => d.Definition)
  262. .ToListAsync();
  263. }
  264. /// <summary>
  265. /// 为工作流业务匹配或取消流程模板
  266. /// </summary>
  267. /// <param name="dto"></param>
  268. /// <returns></returns>
  269. [HttpPut("wfmodule/match")]
  270. public async Task MatchDefinition([FromBody] MatchDefinitionDto dto)
  271. {
  272. if (string.IsNullOrEmpty(dto.DefinitionId))
  273. {
  274. //取消当前已配置模板
  275. await _wfModuleDomainService.MatchDefinitionAsync(dto, HttpContext.RequestAborted);
  276. }
  277. else
  278. {
  279. var definition = await _definitionRepository.GetAsync(dto.DefinitionId);
  280. if (definition == null)
  281. throw UserFriendlyException.SameMessage("无效模板编号");
  282. if (definition.Status != EDefinitionStatus.Enable)
  283. throw UserFriendlyException.SameMessage("该模板未发布");
  284. await _wfModuleDomainService.MatchDefinitionAsync(dto, HttpContext.RequestAborted);
  285. }
  286. }
  287. #endregion
  288. #region workflow
  289. /// <summary>
  290. /// 分页查询流程
  291. /// </summary>
  292. /// <param name="dto"></param>
  293. /// <returns></returns>
  294. [HttpGet]
  295. public async Task<PagedDto<WorkflowDto>> QueryPaged([FromQuery] QueryWorkflowPagedDto dto)
  296. {
  297. var (total, items) = await _workflowRepository.Queryable()
  298. .WhereIF(!string.IsNullOrEmpty(dto.ModuleCode), d => d.ModuleCode == dto.ModuleCode)
  299. .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
  300. d => d.Id.Contains(dto.Keyword!) || d.Title.Contains(dto.Keyword!))
  301. .OrderByDescending(d => d.CreationTime)
  302. .ToPagedListAsync(dto, HttpContext.RequestAborted);
  303. return new PagedDto<WorkflowDto>(total, _mapper.Map<IReadOnlyList<WorkflowDto>>(items));
  304. }
  305. /// <summary>
  306. /// 查询流程办理下一步可选节点
  307. /// </summary>
  308. [HttpGet("{workflowId}/nextsteps")]
  309. public async Task<NextStepsDto> GetNextStepDefine(string workflowId)
  310. {
  311. return await _workflowApplication.GetNextStepsAsync(workflowId, HttpContext.RequestAborted);
  312. }
  313. /// <summary>
  314. /// 办理节点
  315. /// </summary>
  316. [HttpPost("next")]
  317. public async Task Next([FromBody] NextWorkflowDto dto)
  318. {
  319. await _workflowApplication.NextAsync(dto, _sessionContext, cancellationToken: HttpContext.RequestAborted);
  320. }
  321. /// <summary>
  322. /// 退回(返回前一节点)
  323. /// </summary>
  324. [HttpPost("previous")]
  325. public async Task Previous([FromBody] PreviousWorkflowDto dto)
  326. {
  327. await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
  328. }
  329. /// <summary>
  330. /// 获取撤回可选节点
  331. /// </summary>
  332. /// <param name="workflowId"></param>
  333. /// <returns></returns>
  334. [HttpGet("{workflowId}/recall")]
  335. public async Task<NextStepsDto<RecallStepOption>> GetRecallSteps(string workflowId)
  336. {
  337. return await _workflowApplication.GetRecallStepsAsync(workflowId, HttpContext.RequestAborted);
  338. }
  339. /// <summary>
  340. /// 撤回至任意节点
  341. /// </summary>
  342. /// <param name="dto"></param>
  343. /// <returns></returns>
  344. [HttpPost("recall")]
  345. public async Task Recall([FromBody] RecallDto dto)
  346. {
  347. await _workflowApplication.RecallAsync(dto, null, HttpContext.RequestAborted);
  348. }
  349. /// <summary>
  350. /// 终止流程
  351. /// </summary>
  352. [HttpPost("terminate")]
  353. public async Task Terminate([FromBody] TerminateDto dto)
  354. {
  355. await _workflowDomainService.TerminateAsync(dto, HttpContext.RequestAborted);
  356. }
  357. ///// <summary>
  358. ///// 撤销流程
  359. ///// </summary>
  360. //[HttpPost("cancel")]
  361. //public async Task Cancel([FromBody] CancelDto dto)
  362. //{
  363. // await _workflowDomainService.CancelAsync(dto, DateTime.Now, _sessionContext, HttpContext.RequestAborted);
  364. //}
  365. /// <summary>
  366. /// 否决
  367. /// </summary>
  368. [HttpPost("reject")]
  369. public async Task Reject([FromBody] RejectDto dto)
  370. {
  371. await _workflowApplication.RejectAsync(dto, HttpContext.RequestAborted);
  372. }
  373. /// <summary>
  374. /// 补充
  375. /// </summary>
  376. /// <param name="dto"></param>
  377. /// <returns></returns>
  378. //[Permission(EPermission.FlowSupplement)]
  379. [HttpPost("supplement")]
  380. public async Task Supplement([FromBody] SupplementDto dto)
  381. {
  382. var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId);
  383. await _workflowDomainService.SupplementAsync(workflow, dto, HttpContext.RequestAborted);
  384. }
  385. /// <summary>
  386. /// 查询办理类型参数
  387. /// </summary>
  388. [HttpGet("handlerclassify/{handlerType}")]
  389. public async Task<List<KeyValuePair<string, string>>> GetHandlerClassifies(EHandlerType handlerType)
  390. {
  391. switch (handlerType)
  392. {
  393. case EHandlerType.Role:
  394. var roles = await _roleRepository.QueryAsync();
  395. return roles.Select(d => new KeyValuePair<string, string>(d.Name, d.DisplayName)).ToList();
  396. case EHandlerType.OrgLevel:
  397. var orgs1 = await _systemDomainService.QueryOrgLevelStringOptionsAsync(HttpContext.RequestAborted);
  398. return orgs1.ToList();
  399. case EHandlerType.OrgType:
  400. return EnumExts.GetDescriptions<EOrgType>()
  401. .Select(d => new KeyValuePair<string, string>(d.Key.ToString(), d.Value)).ToList();
  402. case EHandlerType.AssignedOrg:
  403. var orgs = await _organizeRepository.GetOrgJson();
  404. return orgs.Select(d => new KeyValuePair<string, string>(d.Id, d.Name)).ToList();
  405. case EHandlerType.AssignedUser:
  406. default:
  407. throw new ArgumentOutOfRangeException(nameof(handlerType), handlerType, null);
  408. }
  409. }
  410. /// <summary>
  411. /// 查询流程流转记录
  412. /// </summary>
  413. /// <param name="workflowId"></param>
  414. /// <returns></returns>
  415. [HttpGet("{workflowId}/traces")]
  416. public async Task<WorkflowDto> GetWorkflowTraces(string workflowId)
  417. {
  418. var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withTracesTree: true,
  419. cancellationToken: HttpContext.RequestAborted);
  420. var workflowDto = _mapper.Map<WorkflowDto>(workflow);
  421. if (workflowDto.Traces.Any())
  422. {
  423. workflowDto.Traces = await _fileRepository.WorkflowTraceRecursion(workflowDto.Traces, HttpContext.RequestAborted);
  424. }
  425. return workflowDto;
  426. }
  427. /// <summary>
  428. /// 查询被督办/催办部门
  429. /// </summary>
  430. /// <param name="workflowId"></param>
  431. /// <returns></returns>
  432. [HttpGet("{workflowId}/urge")]
  433. public async Task<IReadOnlyList<Kv>> GetUrgeOrgs(string workflowId)
  434. {
  435. /*
  436. * 非会签:当前部门上至一级部门(过期)
  437. * 中心会签:一级部门下至所有当前办理部门(遍历所有会签分支)(过期)
  438. */
  439. var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true,
  440. cancellationToken: HttpContext.RequestAborted);
  441. //所有节点的待办对象
  442. return workflow.Steps
  443. .Where(d => d.StepType != EStepType.Start && d.StepType != EStepType.End)
  444. .SelectMany(d => d.Handlers)
  445. .DistinctBy(d => d.Key)
  446. .ToList();
  447. }
  448. [HttpGet("base-data")]
  449. public async Task<dynamic> BaseData()
  450. {
  451. var levels = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  452. var orgs = levels.Select(d => new KeyValuePair<string, string>(d.ToString(), $"{d.ToChinese()}级部门办理")).ToList();
  453. var center = new KeyValuePair<string, string>("0", "中心办理");
  454. var centerIsTop = new List<KeyValuePair<string, string>> { center };
  455. centerIsTop.AddRange(orgs);
  456. return new
  457. {
  458. ModuleOptions = WorkflowModuleConsts.AllModules.Select(d => new KeyValuePair<string, string>(d.Code, d.Name)),
  459. HandlerTypeOptions = EnumExts.GetDescriptions<EHandlerType>(),
  460. BusinessTypeOptions = EnumExts.GetDescriptions<EBusinessType>(),
  461. StepPropertiesOptions = _systemDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.WorkflowStepComponent),
  462. //PathPolicyOptions = EnumExts.GetDescriptions<EPathPolicy>(),
  463. ExecuteModeOptions = EnumExts.GetDescriptions<EExecuteMode>(),
  464. InstanceModeOptions = EnumExts.GetDescriptions<EInstanceMode>(),
  465. StepTypeOptions = EnumExts.GetDescriptions<EStepType>().Where(d => d.Key != 1 && d.Key != 2),
  466. DynamicPolicyOptions = EnumExts.GetDescriptions<EDynamicPolicy>()
  467. .Select(d => new
  468. {
  469. Key = d.Key,
  470. Value = d.Value,
  471. Items = d.Key is 0 or 2 ? centerIsTop : orgs
  472. }),
  473. FlowTypeOptions = EnumExts.GetDescriptions<EFlowType>()
  474. };
  475. }
  476. /// <summary>
  477. /// 查询会签信息
  478. /// </summary>
  479. /// <param name="dto"></param>
  480. /// <returns></returns>
  481. [HttpGet("countersign")]
  482. public async Task<PagedDto<WorkflowCountersignDto>> QueryWorkflowCountersigns([FromQuery] QueryWorkflowCountersignDto dto)
  483. {
  484. RefAsync<int> total = 0;
  485. var query = _workflowCountersignRepository.Queryable()
  486. .Includes(x => x.Members)
  487. .LeftJoin<Workflow>((c, w) => c.WorkflowId == w.Id)
  488. .InnerJoin<Order>((c, w, o) => w.ExternalId == o.Id)
  489. .WhereIF(!_sessionContext.OrgIsCenter, (c, w, o) => c.Members.Any(m => m.Key == _sessionContext.OrgId))
  490. .WhereIF(dto.IsProvince.HasValue, (c, w, o) => o.IsProvince == dto.IsProvince.Value)
  491. .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
  492. (c, w, o) => o.No.Contains(dto.Keyword) || o.Title.Contains(dto.Keyword));
  493. //if (dto.IsOnlyStarter)
  494. // query = query.Where((c, w, o) => c.StarterId == _sessionContext.RequiredUserId);
  495. var items = await query
  496. .OrderByDescending((c, w, o) => o.ExpiredTime)
  497. .Select((c, w, o) => new { c, o })
  498. .ToPageListAsync(dto.PageIndex, dto.PageSize, total, HttpContext.RequestAborted);
  499. var dtos = items.Select(d =>
  500. {
  501. var dto = _mapper.Map<WorkflowCountersignDto>(d.c);
  502. dto.Order = _mapper.Map<OrderDto>(d.o);
  503. return dto;
  504. }).ToList();
  505. return new PagedDto<WorkflowCountersignDto>(total, dtos);
  506. }
  507. /// <summary>
  508. /// 查询会签信息
  509. /// </summary>
  510. /// <param name="dto"></param>
  511. /// <returns></returns>
  512. [HttpGet("order-countersign")]
  513. public async Task<PagedDto<WorkflowCountersignDto>> QueryOrderCountersigns([FromQuery] QueryOrderCountersignDto dto)
  514. {
  515. RefAsync<int> total = 0;
  516. var Role = _sessionContext.Roles;
  517. var query = _workflowCountersignRepository.Queryable()
  518. .Includes(x => x.Members)
  519. .LeftJoin<Workflow>((c, w) => c.WorkflowId == w.Id)
  520. .InnerJoin<Order>((c, w, o) => w.ExternalId == o.Id)
  521. .WhereIF(!string.IsNullOrEmpty(dto.Title), (c, w, o) => o.Title.Contains(dto.Title))
  522. .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (c, w, o) => o.No.Contains(dto.OrderNo))
  523. .WhereIF(dto.AcceptTypes.Any(), (c, w, o) => dto.AcceptTypes.Contains(o.AcceptTypeCode)) //受理类型
  524. .WhereIF(dto.Channels.Any(), (c, w, o) => dto.Channels.Contains(o.SourceChannelCode)) //来源渠道
  525. .WhereIF(dto.HotspotIds.Any(), (c, w, o) => dto.HotspotIds.Contains(o.HotspotId)) //热点类型
  526. .WhereIF(!string.IsNullOrEmpty(dto.OrgName), (c, w, o) => c.FinisherOrgName.Contains(dto.OrgName)) //接办部门
  527. .WhereIF(dto.CounterSignType != null, (c, w, o) => c.CounterSignType == dto.CounterSignType) //会签类型
  528. ;
  529. //发起会签:班长角色能看所有会签信件;
  530. //派单员角色只能看到自己发起的会签信件;
  531. //承办部门用户能看到自己发起的和同级部门用户发起的会签件
  532. if (dto.InitiatedCountersignature.HasValue && dto.InitiatedCountersignature == true)
  533. {
  534. if (_sessionContext.Roles.Any(p => p == "banzhang"))
  535. {
  536. }
  537. else
  538. if (_sessionContext.Roles.Any(p => p == "paidanyuan"))
  539. {
  540. query = query.Where((c, w, o) => c.StarterId == _sessionContext.UserId);
  541. }
  542. else
  543. {
  544. query = query.Where((c, w, o) => c.StarterOrgId == _sessionContext.RequiredOrgId);
  545. }
  546. }
  547. //会签已办(会签状态为已结束):
  548. //班长角色能看所有已结束的会签信件;
  549. //派单员不会办理会签件只会发起会签件所以这一点派单员角色可以忽略;
  550. //承办部门用户能看到和同级部门用户已办理过的会签件
  551. if (dto.HandleCountersignature.HasValue && dto.HandleCountersignature == true)
  552. {
  553. if (_sessionContext.Roles.Any(p => p == "banzhang"))
  554. {
  555. query = query.Where((c, w, o) => c.EndTime.HasValue);
  556. }
  557. else
  558. if (_sessionContext.Roles.Any(p => p == "paidanyuan"))
  559. {
  560. query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
  561. }
  562. else
  563. {
  564. query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.OrgId || m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
  565. }
  566. }
  567. var items = await query
  568. .OrderByDescending((c, w, o) => o.ExpiredTime)
  569. .Select((c, w, o) => new { c, o })
  570. .ToPageListAsync(dto.PageIndex, dto.PageSize, total, HttpContext.RequestAborted);
  571. var dtos = items.Select(d =>
  572. {
  573. var dto = _mapper.Map<WorkflowCountersignDto>(d.c);
  574. dto.Order = _mapper.Map<OrderDto>(d.o);
  575. dto.CounterSignCount = _workflowCountersignRepository.Queryable().Where(p => p.WorkflowId == d.c.WorkflowId).CountAsync().GetAwaiter().GetResult();
  576. return dto;
  577. }).ToList();
  578. return new PagedDto<WorkflowCountersignDto>(total, dtos);
  579. }
  580. /// <summary>
  581. ///
  582. /// </summary>
  583. /// <returns></returns>
  584. [HttpGet("order-countersign-base-data")]
  585. public async Task<object> QueryOrderCountersignsBaseData()
  586. {
  587. return new
  588. {
  589. CounterSignType = EnumExts.GetDescriptions<ECounterSignType>(),
  590. ChannelOptions = _sysDicDataCacheManager.GetSysDicDataCache(TimeLimitBaseDataConsts.SourceChannel),
  591. AcceptTypeOptions = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.AcceptType),
  592. };
  593. }
  594. /// <summary>
  595. /// 改变某节点办理人
  596. /// </summary>
  597. [HttpPost("change-handler")]
  598. public async Task ChangeHandler([FromBody] ChangeHandlerDto dto)
  599. {
  600. var step = await _workflowStepRepository.GetAsync(dto.StepId, HttpContext.RequestAborted);
  601. if (step is null)
  602. throw new UserFriendlyException("无效节点编号");
  603. await _workflowDomainService.ChangeHandlerBatchAsync(new List<(string userId, string username, string orgId, string orgName, ICollection<WorkflowStep> steps)>
  604. {
  605. new(dto.Handler.UserId,dto.Handler.Username,dto.Handler.OrgId,dto.Handler.OrgName, new List<WorkflowStep>{step})
  606. }, HttpContext.RequestAborted);
  607. }
  608. #endregion
  609. }