ProcessOverviewRepository.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. using AutoMapper;
  2. using Newtonsoft.Json;
  3. using OASystem.Domain;
  4. using OASystem.Domain.Dtos.Groups;
  5. using OASystem.Domain.Entities.Groups;
  6. using OASystem.Domain.ViewModels.JuHeExchangeRate;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. namespace OASystem.Infrastructure.Repositories.Groups
  13. {
  14. /// <summary>
  15. /// 团组流程总览表仓储
  16. /// </summary>
  17. public class ProcessOverviewRepository : BaseRepository<Grp_ProcessOverview, Grp_ProcessOverview>
  18. {
  19. private readonly IMapper _mapper;
  20. private readonly DelegationInfoRepository _groupRep;
  21. public ProcessOverviewRepository(SqlSugarClient sqlSugar, IMapper mapper, DelegationInfoRepository groupRep) : base(sqlSugar)
  22. {
  23. _mapper = mapper;
  24. _groupRep = groupRep;
  25. }
  26. /// <summary>
  27. /// 团组流程初始化
  28. /// </summary>
  29. /// <param name="request">创建流程请求参数</param>
  30. /// <returns>创建的流程信息</returns>
  31. public async Task<Result> ProcessInitAsync(int groupId, int currUserId)
  32. {
  33. //团组验证
  34. var groupInfo = await _sqlSugar.Queryable<Grp_DelegationInfo>().FirstAsync(g => g.Id == groupId);
  35. if (groupInfo == null)
  36. {
  37. return new Result { Code = 400, Msg = "团组不存在" };
  38. }
  39. // 检查是否已存在流程
  40. var existingProcesses = await _sqlSugar.Queryable<Grp_ProcessOverview>().Where(p => p.GroupId == groupId).ToListAsync();
  41. if (existingProcesses.Any())
  42. {
  43. return new Result { Code = 400, Msg = "该团组的流程已存在" };
  44. }
  45. //处理签证国家
  46. var visaCountries = _groupRep.GroupSplitCountry(groupInfo.VisitCountry);
  47. // 定义默认的流程节点
  48. var processs = Grp_ProcessOverview.ProcessInit(groupId, currUserId, visaCountries);
  49. _sqlSugar.BeginTran();
  50. foreach (var item in processs)
  51. {
  52. var processId = await _sqlSugar.Insertable(item).ExecuteReturnIdentityAsync();
  53. if (processId < 1)
  54. {
  55. _sqlSugar.RollbackTran();
  56. return new Result { Code = 400, Msg = "团组流程进度总览表添加失败!" };
  57. }
  58. var nodes = item.Nodes.Select((nodeDto, index) => new Grp_ProcessNode
  59. {
  60. ProcessId = processId,
  61. NodeName = nodeDto.NodeName,
  62. NodeOrder = nodeDto.NodeOrder,
  63. OverallStatus = nodeDto.OverallStatus,
  64. //Country = nodeDto.Country,
  65. IsCurrent = nodeDto.IsCurrent,
  66. Remark = nodeDto.Remark
  67. }).ToList();
  68. var nodeIds = await _sqlSugar.Insertable(nodes).ExecuteCommandAsync();
  69. if (nodeIds < 1)
  70. {
  71. _sqlSugar.RollbackTran();
  72. return new Result { Code = 400, Msg = "团组流程进度流程表添加失败!" };
  73. }
  74. }
  75. _sqlSugar.CommitTran();
  76. return new Result { Code = 200, Msg = "添加成功!" }; ;
  77. }
  78. /// <summary>
  79. /// 获取团组的所有流程及流程详情
  80. /// </summary>
  81. /// <param name="request">创建流程请求参数</param>
  82. /// <returns>创建的流程信息</returns>
  83. public async Task<Result> ProcessesDetailsAsync(int groupId)
  84. {
  85. //团组验证
  86. var groupInfo = await _sqlSugar.Queryable<Grp_DelegationInfo>().FirstAsync(g => g.Id == groupId);
  87. if (groupInfo == null)
  88. {
  89. return new Result { Code = 400, Msg = "团组不存在" };
  90. }
  91. // 检查是否已存在流程
  92. var existingProcesses = await _sqlSugar.Queryable<Grp_ProcessOverview>().Where(p => p.GroupId == groupId).ToListAsync();
  93. if (!existingProcesses.Any())
  94. {
  95. return new Result { Code = 400, Msg = "该团组的流程不存在" };
  96. }
  97. var users = await _sqlSugar.Queryable<Sys_Users>().ToListAsync();
  98. var processData = await _sqlSugar.Queryable<Grp_ProcessOverview>()
  99. .Where(p => p.GroupId == groupId && p.IsDel == 0)
  100. .Mapper(p => p.Nodes, p => p.Nodes.First().ProcessId)
  101. .ToListAsync();
  102. var processes = processData.Select(p => new
  103. {
  104. p.Id,
  105. p.GroupId,
  106. p.ProcessType,
  107. ProcessName = p.ProcessType.GetEnumDescription(),
  108. //p.OverallStatus,
  109. //StatusText = p.OverallStatus.GetDescription(),
  110. Nodes = p.Nodes.Select(n =>
  111. {
  112. //单独处理签证板块
  113. var visaSubNodes = new List<VisaProcessNode>();
  114. string remark = string.Empty;
  115. if (p.ProcessType == GroupProcessType.Visa)
  116. {
  117. visaSubNodes = JsonConvert.DeserializeObject<List<VisaProcessNode>>(n.Remark);
  118. }
  119. return new
  120. {
  121. n.Id,
  122. n.ProcessId,
  123. n.NodeOrder,
  124. n.NodeName,
  125. n.OverallStatus,
  126. StatusText = n.OverallStatus.GetEnumDescription(),
  127. Operator = users.FirstOrDefault(u => u.Id == n.Operator)?.CnName ?? "-",
  128. OpeateTime = n.OperationTime.HasValue ? n.OperationTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : "-",
  129. //节点类型为签证时使用
  130. visaSubNodes
  131. };
  132. }).OrderBy(n => n.NodeOrder).ToList()
  133. }).ToList();
  134. return new Result { Code = 200, Data = processes, Msg = "查询成功!" };
  135. }
  136. /// <summary>
  137. /// 更新节点状态
  138. /// </summary>
  139. /// <param name="nodeId">节点ID</param>
  140. /// <param name="currUserId">当前用户ID</param>
  141. /// <param name="processStatus">流程状态,默认为已完成</param>
  142. /// <returns>操作结果</returns>
  143. public async Task<Result> UpdateNodeStatusAsync(int nodeId, int currUserId, ProcessStatus processStatus = ProcessStatus.Completed)
  144. {
  145. try
  146. {
  147. // 使用事务确保数据一致性
  148. var result = await _sqlSugar.Ado.UseTranAsync(async () =>
  149. {
  150. // 1. 获取并验证节点
  151. var node = await _sqlSugar.Queryable<Grp_ProcessNode>()
  152. .FirstAsync(n => n.Id == nodeId && n.IsDel == 0);
  153. if (node == null)
  154. {
  155. throw new BusinessException("当前节点不存在或已被删除。");
  156. }
  157. // 新增验证:当前节点已完成,不可操作
  158. ValidateNodeOperation(node, processStatus);
  159. // 2. 更新节点状态
  160. node.OverallStatus = processStatus;
  161. node.Operator = currUserId;
  162. node.OperationTime = DateTime.Now;
  163. var updateCount = await _sqlSugar.Updateable(node)
  164. .UpdateColumns(n => new
  165. {
  166. n.OverallStatus,
  167. n.Operator,
  168. n.OperationTime
  169. })
  170. .ExecuteCommandAsync();
  171. if (updateCount == 0)
  172. {
  173. throw new BusinessException("节点状态更新失败。");
  174. }
  175. // 3. 如果是完成当前节点,处理流程流转
  176. if (processStatus == ProcessStatus.Completed && node.IsCurrent)
  177. {
  178. await ProcessCurrentNodeCompletionAsync(node, currUserId);
  179. }
  180. return new Result { Code = StatusCodes.Status200OK, Msg = "操作成功。" };
  181. });
  182. return result.IsSuccess ? result.Data : new Result
  183. {
  184. Code = StatusCodes.Status500InternalServerError,
  185. Msg = result.ErrorMessage
  186. };
  187. }
  188. catch (BusinessException ex)
  189. {
  190. // 业务异常
  191. return new Result { Code = StatusCodes.Status400BadRequest, Msg = ex.Message };
  192. }
  193. catch (Exception ex)
  194. {
  195. // 系统异常
  196. return new Result { Code = StatusCodes.Status500InternalServerError, Msg = "系统错误,请稍后重试" };
  197. }
  198. }
  199. /// <summary>
  200. /// 验证节点操作权限
  201. /// </summary>
  202. /// <param name="node">流程节点</param>
  203. /// <param name="targetStatus">目标状态</param>
  204. private void ValidateNodeOperation(Grp_ProcessNode node, ProcessStatus targetStatus)
  205. {
  206. // 验证节点是否已完成
  207. if (node.OverallStatus == ProcessStatus.Completed)
  208. {
  209. throw new BusinessException("当前节点已完成,不可重复操作。");
  210. }
  211. // 验证状态流转是否合法(可选)
  212. if (targetStatus != ProcessStatus.Completed)
  213. {
  214. throw new BusinessException("未开始或者进行中的节点只能重新完成,不可进行其他操作。");
  215. }
  216. // 验证是否尝试将已完成节点改为其他状态
  217. if (node.OverallStatus == ProcessStatus.Completed && targetStatus != ProcessStatus.Completed)
  218. {
  219. throw new BusinessException("已完成节点不可修改状态。");
  220. }
  221. }
  222. /// <summary>
  223. /// 处理当前节点完成后的流程流转
  224. /// </summary>
  225. private async Task ProcessCurrentNodeCompletionAsync(Grp_ProcessNode currentNode, int currUserId)
  226. {
  227. // 1. 获取流程信息
  228. var process = await _sqlSugar.Queryable<Grp_ProcessOverview>()
  229. .FirstAsync(p => p.Id == currentNode.ProcessId && p.IsDel == 0);
  230. if (process == null)
  231. {
  232. throw new BusinessException("关联的流程不存在。");
  233. }
  234. // 2. 取消当前节点的当前状态
  235. currentNode.IsCurrent = false;
  236. await _sqlSugar.Updateable(currentNode)
  237. .UpdateColumns(n => new { n.IsCurrent })
  238. .ExecuteCommandAsync();
  239. // 3. 查找并激活下一个节点
  240. var nextNode = await _sqlSugar.Queryable<Grp_ProcessNode>()
  241. .Where(n => n.ProcessId == currentNode.ProcessId
  242. && n.NodeOrder == currentNode.NodeOrder + 1
  243. && n.IsDel == 0)
  244. .FirstAsync();
  245. if (nextNode != null)
  246. {
  247. // 激活下一个节点
  248. nextNode.IsCurrent = true;
  249. nextNode.OverallStatus = ProcessStatus.InProgress;
  250. //nextNode.Operator = currUserId;
  251. //nextNode.OperationTime = DateTime.Now;
  252. var updateCount = await _sqlSugar.Updateable(nextNode)
  253. .UpdateColumns(n => new
  254. {
  255. n.IsCurrent,
  256. n.OverallStatus,
  257. n.Operator,
  258. n.OperationTime
  259. })
  260. .ExecuteCommandAsync();
  261. if (updateCount == 0)
  262. {
  263. throw new BusinessException("激活下一节点失败");
  264. }
  265. // 更新流程状态为进行中
  266. process.OverallStatus = ProcessStatus.InProgress;
  267. }
  268. else
  269. {
  270. // 下一节点不存在,整个流程完成
  271. process.OverallStatus = ProcessStatus.Completed;
  272. process.EndTime = DateTime.Now;
  273. }
  274. // 4. 更新流程信息
  275. process.UpdatedUserId = currUserId;
  276. process.UpdatedTime = DateTime.Now;
  277. var processUpdateCount = await _sqlSugar.Updateable(process)
  278. .UpdateColumns(p => new
  279. {
  280. p.OverallStatus,
  281. p.EndTime,
  282. p.UpdatedUserId,
  283. p.UpdatedTime
  284. })
  285. .ExecuteCommandAsync();
  286. if (processUpdateCount == 0)
  287. {
  288. throw new BusinessException("流程状态更新失败。");
  289. }
  290. }
  291. /// <summary>
  292. /// 更新签证节点信息及状态
  293. /// </summary>
  294. /// <param name="dto">签证节点更新数据传输对象</param>
  295. /// <returns>操作结果</returns>
  296. public async Task<Result> UpdateVisaNodeDetailsAsync(GroupProcessUpdateVisaNodeDetailsDto dto)
  297. {
  298. // 1. 获取并验证节点和流程
  299. var node = await _sqlSugar.Queryable<Grp_ProcessNode>()
  300. .FirstAsync(n => n.Id == dto.NodeId && n.IsDel == 0)
  301. ?? throw new BusinessException("当前节点不存在或已被删除。");
  302. var process = await _sqlSugar.Queryable<Grp_ProcessOverview>()
  303. .FirstAsync(p => p.Id == node.ProcessId && p.IsDel == 0)
  304. ?? throw new BusinessException("当前流程不存在或已被删除。");
  305. if (process.ProcessType != GroupProcessType.Visa)
  306. {
  307. throw new BusinessException("当前流程节点不为签证流程,不可编辑。");
  308. }
  309. // 2. 检查签证子节点 字段信息是否全部填写
  310. var allSubNodesCompleted = dto.VisaSubNodes?.All(subNode => EntityExtensions.IsCompleted(subNode)) ?? false;
  311. // 3. 更新节点信息
  312. node.Remark = JsonConvert.SerializeObject(dto.VisaSubNodes);
  313. node.Operator = dto.CurrUserId;
  314. node.OperationTime = DateTime.Now;
  315. if (allSubNodesCompleted)
  316. {
  317. node.OverallStatus = ProcessStatus.Completed;
  318. // 更新流程状态
  319. await _sqlSugar.Updateable<Grp_ProcessOverview>()
  320. .SetColumns(p => new Grp_ProcessOverview
  321. {
  322. OverallStatus = ProcessStatus.Completed,
  323. EndTime = DateTime.Now,
  324. UpdatedUserId = dto.CurrUserId,
  325. UpdatedTime = DateTime.Now
  326. })
  327. .Where(p => p.Id == process.Id)
  328. .ExecuteCommandAsync();
  329. }
  330. // 4. 保存节点更新
  331. await _sqlSugar.Updateable(node)
  332. .UpdateColumns(n => new
  333. {
  334. n.Remark,
  335. n.Operator,
  336. n.OperationTime,
  337. n.OverallStatus
  338. })
  339. .ExecuteCommandAsync();
  340. return new Result { Code = 200, Msg = "节点信息更新成功。" };
  341. }
  342. }
  343. }