You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

683 lines
27 KiB

  1. using Learun.Util;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Data;
  5. using System.Linq;
  6. namespace Learun.Workflow.Engine
  7. {
  8. /// <summary>
  9. /// 版 本 Learun-ADMS V7.0.3 数字化智慧校园
  10. /// Copyright (c) 2013-2018 北京泉江科技有限公司
  11. /// 创建人:框架开发组
  12. /// 日 期:2018.12.10
  13. /// 描 述:工作流引擎
  14. /// </summary>
  15. public class NWFEngine : NWFIEngine
  16. {
  17. #region 构造函数
  18. public NWFEngine(NWFEngineConfig nWFEngineConfig)
  19. {
  20. // 初始化模板数据
  21. config = nWFEngineConfig;
  22. wfScheme = config.ParamConfig.Scheme.ToObject<NWFScheme>();
  23. nodesMap = new Dictionary<string, NWFNodeInfo>();
  24. foreach (var node in wfScheme.nodes)
  25. {
  26. if (!nodesMap.ContainsKey(node.id))
  27. {
  28. nodesMap.Add(node.id, node);
  29. }
  30. if (node.type == "startround")
  31. {
  32. startNode = node;
  33. }
  34. if (node.type == "endround")
  35. {
  36. endNode = node;
  37. }
  38. }
  39. }
  40. #endregion
  41. #region 模板数据信息
  42. private NWFEngineConfig config;
  43. private NWFScheme wfScheme = null;
  44. private Dictionary<string, NWFNodeInfo> nodesMap = null;
  45. private NWFNodeInfo startNode = null;
  46. private NWFNodeInfo endNode = null;
  47. #endregion
  48. #region 私有方法
  49. /// <summary>
  50. /// 计算条件
  51. /// </summary>
  52. /// <param name="node">节点信息</param>
  53. /// <returns></returns>
  54. private bool CalcCondition(NWFNodeInfo node)
  55. {
  56. bool res = true;
  57. if (node.conditions.Count > 0)
  58. {
  59. #region 字段条件判断
  60. foreach (var condition in node.conditions)
  61. {
  62. if (!string.IsNullOrEmpty(condition.dbId) && !string.IsNullOrEmpty(condition.table) && !string.IsNullOrEmpty(condition.field1) && !string.IsNullOrEmpty(condition.field2))
  63. {
  64. string sql = "select " + condition.field2 + " from " + condition.table + " where " + condition.field1 + " =@processId ";
  65. DataTable dataTable = config.DbFindTable(condition.dbId, sql, new { processId = config.ParamConfig.ProcessId });
  66. if (dataTable.Rows.Count > 0)
  67. {
  68. string value = dataTable.Rows[0][0].ToString();
  69. if (string.IsNullOrEmpty(value))
  70. {
  71. return false;
  72. }
  73. switch (condition.compareType)//比较类型1.等于2.不等于3.大于4.大于等于5.小于6.小于等于7.包含8.不包含9.包含于10.不包含于
  74. {
  75. case 1:// 等于
  76. if (value != condition.value)
  77. {
  78. res = false;
  79. }
  80. break;
  81. case 2:// 不等于
  82. if (value == condition.value)
  83. {
  84. res = false;
  85. }
  86. break;
  87. case 3:// 大于
  88. if (Convert.ToDecimal(value) <= Convert.ToDecimal(condition.value))
  89. {
  90. res = false;
  91. }
  92. break;
  93. case 4:// 大于等于
  94. if (Convert.ToDecimal(value) < Convert.ToDecimal(condition.value))
  95. {
  96. res = false;
  97. }
  98. break;
  99. case 5:// 小于
  100. if (Convert.ToDecimal(value) >= Convert.ToDecimal(condition.value))
  101. {
  102. res = false;
  103. }
  104. break;
  105. case 6:// 小于等于
  106. if (Convert.ToDecimal(value) > Convert.ToDecimal(condition.value))
  107. {
  108. res = false;
  109. }
  110. break;
  111. case 7:// 包含
  112. if (!value.Contains(condition.value))
  113. {
  114. res = false;
  115. }
  116. break;
  117. case 8:// 不包含
  118. if (value.Contains(condition.value))
  119. {
  120. res = false;
  121. }
  122. break;
  123. case 9:// 包含于
  124. if (!condition.value.Contains(value))
  125. {
  126. res = false;
  127. }
  128. break;
  129. case 10:// 不包含于
  130. if (condition.value.Contains(value))
  131. {
  132. res = false;
  133. }
  134. break;
  135. }
  136. }
  137. else
  138. {
  139. res = false;
  140. }
  141. }
  142. if (!res)
  143. {
  144. break;
  145. }
  146. }
  147. #endregion
  148. }
  149. else if (!string.IsNullOrEmpty(node.conditionSql))
  150. {
  151. // 流程进程ID
  152. string conditionSql = node.conditionSql.Replace("{processId}", "@processId");
  153. // 流程创建人用户
  154. conditionSql = conditionSql.Replace("{userId}", "@userId");
  155. conditionSql = conditionSql.Replace("{userAccount}", "@userAccount");
  156. conditionSql = conditionSql.Replace("{companyId}", "@companyId");
  157. conditionSql = conditionSql.Replace("{departmentId}", "@departmentId");
  158. var param = new
  159. {
  160. processId = config.ParamConfig.ProcessId,
  161. userId = config.ParamConfig.CreateUser.Id,
  162. userAccount = config.ParamConfig.CreateUser.Account,
  163. companyId = config.ParamConfig.CreateUser.CompanyId,
  164. departmentId = config.ParamConfig.CreateUser.DepartmentId,
  165. };
  166. DataTable dataTable = config.DbFindTable(node.dbConditionId, conditionSql, param);
  167. if (dataTable.Rows.Count > 0)
  168. {
  169. res = true;
  170. }
  171. }
  172. else
  173. {
  174. res = true;
  175. }
  176. return res;
  177. }
  178. /// <summary>
  179. /// 计算会签
  180. /// </summary>
  181. /// <param name="wfNodeInfo">节点信息</param>
  182. /// <param name="preNodeId">上一节点Id</param>
  183. /// <param name="isAgree">同意</param>
  184. /// <returns>0 不做处理 1 通过 -1 不通过</returns>
  185. private int CalcConfluence(NWFNodeInfo wfNodeInfo, string preNodeId, bool isAgree)
  186. {
  187. int res = 0;
  188. int agreeNum = config.GetAgreeNum(config.ParamConfig.ProcessId, wfNodeInfo.id);
  189. int disAgreeNum = config.GetDisAgreeNum(config.ParamConfig.ProcessId, wfNodeInfo.id);
  190. List<string> preNodeList = GetPreNodes(wfNodeInfo.id);
  191. switch (wfNodeInfo.confluenceType)//会签策略1-所有步骤通过,2-一个步骤通过即可,3-按百分比计算
  192. {
  193. case 1://所有步骤通过
  194. if (isAgree)
  195. {
  196. if (preNodeList.Count == agreeNum + 1)
  197. {
  198. res = 1;
  199. }
  200. }
  201. else
  202. {
  203. res = -1;
  204. }
  205. break;
  206. case 2:
  207. if (isAgree)
  208. {
  209. res = 1;
  210. }
  211. else if (preNodeList.Count == disAgreeNum + 1)
  212. {
  213. res = -1;
  214. }
  215. break;
  216. case 3:
  217. if (isAgree)
  218. {
  219. if ((agreeNum + 1) * 100 / preNodeList.Count >= Convert.ToDecimal(wfNodeInfo.confluenceRate))
  220. {
  221. res = 1;
  222. }
  223. }
  224. else
  225. {
  226. if ((preNodeList.Count - disAgreeNum - 1) * 100 / preNodeList.Count < Convert.ToDecimal(wfNodeInfo.confluenceRate))
  227. {
  228. res = -1;
  229. }
  230. }
  231. break;
  232. }
  233. return res;
  234. }
  235. #endregion
  236. #region 流程模板操作方法
  237. /// <summary>
  238. /// 获取流程模板
  239. /// </summary>
  240. /// <returns></returns>
  241. public string GetScheme()
  242. {
  243. return config.ParamConfig.Scheme;
  244. }
  245. /// <summary>
  246. /// 获取流程模板
  247. /// </summary>
  248. /// <returns></returns>
  249. public NWFScheme GetSchemeObj()
  250. {
  251. return wfScheme;
  252. }
  253. /// <summary>
  254. /// 获取开始节点
  255. /// </summary>
  256. /// <returns>节点信息</returns>
  257. public NWFNodeInfo GetStartNode()
  258. {
  259. return startNode;
  260. }
  261. /// <summary>
  262. /// 获取节点
  263. /// </summary>
  264. /// <param name="nodeId">流程处理节点ID</param>
  265. /// <returns>节点信息</returns>
  266. public NWFNodeInfo GetNode(string nodeId)
  267. {
  268. if (nodesMap.ContainsKey(nodeId))
  269. {
  270. return nodesMap[nodeId];
  271. }
  272. else
  273. {
  274. return null;
  275. }
  276. }
  277. /// <summary>
  278. /// 获取下一节点
  279. /// </summary>
  280. /// <param name="nodeId">当前节点Id</param>
  281. /// <param name="code">节点操作码 agree 同意 disagree 不同意 lrtimeout 超时</param>
  282. /// <returns>节点信息列表</returns>
  283. public List<NWFNodeInfo> GetNextNodes(string nodeId, string code, List<NWFLineInfo> lineList, bool overFW = false)
  284. {
  285. List<NWFNodeInfo> nextNodes = new List<NWFNodeInfo>();
  286. //如果lastNode 不为空,直接执行最后一条线的绑定方法 并终止任务
  287. List<NWFLineInfo> lineList1 = new List<NWFLineInfo>();
  288. if (overFW)
  289. {
  290. //20221202 liangkun
  291. //修改判断最后线段方法,之前直接获取last线段有问题
  292. //设计器并非所有情况都会把最后一条线段放在last位置
  293. //判断last节点是否是end节点之前的线
  294. //先获取所节点
  295. var allnodes = wfScheme.nodes;
  296. //查找结束节点
  297. var endnode = allnodes.Find(m => m.type == "endround");
  298. //查到结束节点
  299. if (endnode != null)
  300. {
  301. //使用结束节点的id到线条集合内查找,查询到to结束节点的线条本身并赋值
  302. //to结束节点的线,即是最后的线条,一般绑定事件都会在此线段上
  303. var endline = wfScheme.lines.Find(m => m.to == endnode.id);
  304. if (endline != null)
  305. {
  306. lineList1.Add(endline);
  307. }
  308. else
  309. {
  310. lineList1.Add(wfScheme.lines.Last());
  311. }
  312. }
  313. else//未查到结束节点,直接赋值最后的线条
  314. {
  315. lineList1.Add(wfScheme.lines.Last());
  316. }
  317. }
  318. else
  319. {
  320. lineList1 = wfScheme.lines;
  321. }
  322. // 找到与当前节点相连的线条
  323. foreach (var line in lineList1)
  324. {
  325. if (line.from == nodeId)
  326. {
  327. bool isOk = false;
  328. if (string.IsNullOrEmpty(line.strategy) || line.strategy == "1")
  329. {
  330. isOk = true;
  331. }
  332. else
  333. {
  334. var codeList = line.agreeList.Split(',');
  335. foreach (string _code in codeList)
  336. {
  337. if (_code == code)
  338. {
  339. isOk = true;
  340. break;
  341. }
  342. }
  343. }
  344. if (isOk)
  345. {
  346. if (nodesMap.ContainsKey(line.to))
  347. {
  348. nextNodes.Add(nodesMap[line.to]);
  349. switch (line.operationType)
  350. {// 绑定的操作类型
  351. case "sql": // sql 语句
  352. if (!string.IsNullOrEmpty(line.dbId) && !string.IsNullOrEmpty(line.strSql))
  353. {
  354. lineList.Add(line);
  355. }
  356. break;
  357. case "interface": // interface 接口
  358. if (!string.IsNullOrEmpty(line.strInterface))
  359. {
  360. lineList.Add(line);
  361. }
  362. break;
  363. case "ioc": // 依赖注入
  364. if (!string.IsNullOrEmpty(line.iocName))
  365. {
  366. lineList.Add(line);
  367. }
  368. break;
  369. }
  370. }
  371. }
  372. }
  373. //20221202 liangkun 配合以上代码添加结束节点以及线段并返回
  374. else
  375. {
  376. if (overFW)
  377. {
  378. lineList.Add(line);
  379. nextNodes.Add(nodesMap[line.to]);
  380. }
  381. }
  382. }
  383. return nextNodes;
  384. }
  385. /// <summary>
  386. /// 获取上一节点列表
  387. /// </summary>
  388. /// <param name="nodeId">当前节点Id</param>
  389. /// <returns></returns>
  390. public List<string> GetPreNodes(string nodeId)
  391. {
  392. List<string> list = new List<string>();
  393. // 找到与当前节点相连的线条
  394. foreach (var line in wfScheme.lines)
  395. {
  396. if (line.to == nodeId)
  397. {
  398. list.Add(line.from);
  399. }
  400. }
  401. return list;
  402. }
  403. /// <summary>
  404. /// 判断两节点是否连接
  405. /// </summary>
  406. /// <param name="formNodeId">开始节点</param>
  407. /// <param name="toNodeId">结束节点</param>
  408. /// <returns></returns>
  409. public bool IsToNode(string formNodeId, string toNodeId)
  410. {
  411. bool res = false;
  412. foreach (var line in wfScheme.lines)
  413. {
  414. if (line.from == formNodeId)
  415. {
  416. if (line.to == toNodeId)
  417. {
  418. res = true;
  419. break;
  420. }
  421. else
  422. {
  423. if (line.to == formNodeId || nodesMap[line.to] == null || nodesMap[line.to].type == "endround")
  424. {
  425. break;
  426. }
  427. else
  428. {
  429. if (IsToNode(line.to, toNodeId))
  430. {
  431. res = true;
  432. break;
  433. }
  434. }
  435. }
  436. }
  437. }
  438. return res;
  439. }
  440. #endregion
  441. #region 流程运行操作方法
  442. /// <summary>
  443. /// 获取配置参数信息
  444. /// </summary>
  445. /// <returns></returns>
  446. public NWFEngineParamConfig GetConfig()
  447. {
  448. return config.ParamConfig;
  449. }
  450. /// <summary>
  451. /// 获取接下来的任务节点信息
  452. /// </summary>
  453. /// <param name="beginNode">起始节点</param>
  454. /// <param name="code">节点操作码 agree 同意 disagree 不同意 lrtimeout 超时</param>
  455. /// <param name="isGetAuditors">是否获取下一节点审核人</param>
  456. /// <param name="lineList">经过的线段需要执行操作的</param>
  457. /// <returns></returns>
  458. public List<NWFNodeInfo> GetNextTaskNode(NWFNodeInfo beginNode, string code, bool isGetAuditors, List<NWFLineInfo> lineList, bool overWF = false)
  459. {
  460. List<NWFNodeInfo> list = new List<NWFNodeInfo>();
  461. List<NWFNodeInfo> nextNodeList = new List<NWFNodeInfo>();
  462. nextNodeList = GetNextNodes(beginNode.id, code, lineList, overWF);
  463. Dictionary<string, string> auditers = null;
  464. if (!string.IsNullOrEmpty(config.ParamConfig.Auditers))
  465. {
  466. auditers = config.ParamConfig.Auditers.ToObject<Dictionary<string, string>>();
  467. }
  468. foreach (var node in nextNodeList)
  469. {
  470. if (auditers != null && auditers.ContainsKey(node.id))
  471. {
  472. node.auditors = new List<NWFAuditor>();
  473. node.auditors.Add(new NWFAuditor()
  474. {
  475. type = 3,
  476. auditorId = auditers[node.id]
  477. });
  478. }
  479. switch (node.type)
  480. {
  481. case "conditionnode": // 条件节点
  482. if (!isGetAuditors)
  483. {
  484. if (CalcCondition(node))
  485. {
  486. list.AddRange(GetNextTaskNode(node, "agree", isGetAuditors, lineList));
  487. }
  488. else
  489. {
  490. list.AddRange(GetNextTaskNode(node, "disagree", isGetAuditors, lineList));
  491. }
  492. }
  493. else
  494. {
  495. list.AddRange(GetNextTaskNode(node, "agree", isGetAuditors, lineList));
  496. list.AddRange(GetNextTaskNode(node, "disagree", isGetAuditors, lineList));
  497. }
  498. break;
  499. case "confluencenode":// 会签节点
  500. if (!isGetAuditors)
  501. {
  502. int confluenceRes;
  503. if (code == "agree")
  504. {
  505. confluenceRes = CalcConfluence(node, beginNode.id, true);
  506. }
  507. else
  508. {
  509. confluenceRes = CalcConfluence(node, beginNode.id, false);
  510. }
  511. if (confluenceRes == 1)// 会签审核通过
  512. {
  513. list.AddRange(GetNextTaskNode(node, "agree", false, lineList));
  514. }
  515. else if (confluenceRes == -1)// 会签审核不通过
  516. {
  517. list.AddRange(GetNextTaskNode(node, "disagree", false, lineList));
  518. }
  519. node.confluenceRes = confluenceRes;
  520. list.Add(node);
  521. }
  522. break;
  523. case "auditornode":// 传阅节点
  524. list.Add(node);
  525. break;
  526. case "childwfnode":// 子流程节点
  527. list.Add(node);
  528. if (node.childType == "2")
  529. { // 异步的情况下直接往下走
  530. list.AddRange(GetNextTaskNode(node, "agree", isGetAuditors, lineList));
  531. }
  532. break;
  533. case "startround":// 开始节点 需要重新审核
  534. list.Add(node);
  535. config.ParamConfig.State = 1;
  536. break;
  537. case "endround":// 结束节点
  538. config.ParamConfig.State = 2;
  539. break;
  540. default: // 默认一般审核界定啊
  541. list.Add(node);
  542. break;
  543. }
  544. }
  545. return list;
  546. }
  547. /// <summary>
  548. /// 获取上一步的任务节点信息(只有审批操作码为back(退回至上一个审核节点)情况下执行)【暂定】
  549. /// </summary>
  550. /// <param name="beginNode">起始节点</param>
  551. /// <param name="code">节点操作码 agree 同意 disagree 不同意 lrtimeout 超时 back 退回至上一个审核节点</param>
  552. /// <param name="isGetAuditors">是否获取下一节点审核人</param>
  553. /// <param name="lineList">经过的线段需要执行操作的</param>
  554. /// <returns></returns>
  555. public List<NWFNodeInfo> GetPrevTaskNode(NWFNodeInfo beginNode, string code, bool isGetAuditors, List<NWFLineInfo> lineList, bool overWF = false)
  556. {
  557. List<NWFNodeInfo> list = new List<NWFNodeInfo>();
  558. List<NWFNodeInfo> nextNodeList = new List<NWFNodeInfo>();
  559. //nextNodeList = GetNextNodes(beginNode.id, code, lineList, overWF);
  560. var prevNodes = GetPreNodes(beginNode.id);
  561. foreach (var item in prevNodes)
  562. {
  563. var prevNode = GetNode(item);
  564. nextNodeList.Add(prevNode);
  565. }
  566. Dictionary<string, string> auditers = null;
  567. if (!string.IsNullOrEmpty(config.ParamConfig.Auditers))
  568. {
  569. auditers = config.ParamConfig.Auditers.ToObject<Dictionary<string, string>>();
  570. }
  571. foreach (var node in nextNodeList)
  572. {
  573. if (auditers != null && auditers.ContainsKey(node.id))
  574. {
  575. node.auditors = new List<NWFAuditor>();
  576. node.auditors.Add(new NWFAuditor()
  577. {
  578. type = 3,
  579. auditorId = auditers[node.id]
  580. });
  581. }
  582. switch (node.type)
  583. {
  584. case "conditionnode": // 条件节点
  585. if (!isGetAuditors)
  586. {
  587. if (CalcCondition(node))
  588. {
  589. list.AddRange(GetPrevTaskNode(node, "agree", isGetAuditors, lineList));
  590. }
  591. else
  592. {
  593. list.AddRange(GetPrevTaskNode(node, "disagree", isGetAuditors, lineList));
  594. }
  595. }
  596. else
  597. {
  598. list.AddRange(GetPrevTaskNode(node, "agree", isGetAuditors, lineList));
  599. list.AddRange(GetPrevTaskNode(node, "disagree", isGetAuditors, lineList));
  600. }
  601. break;
  602. case "confluencenode":// 会签节点
  603. if (!isGetAuditors)
  604. {
  605. int confluenceRes;
  606. if (code == "agree")
  607. {
  608. confluenceRes = CalcConfluence(node, beginNode.id, true);
  609. }
  610. else
  611. {
  612. confluenceRes = CalcConfluence(node, beginNode.id, false);
  613. }
  614. if (confluenceRes == 1)// 会签审核通过
  615. {
  616. list.AddRange(GetPrevTaskNode(node, "agree", false, lineList));
  617. }
  618. else if (confluenceRes == -1)// 会签审核不通过
  619. {
  620. list.AddRange(GetPrevTaskNode(node, "disagree", false, lineList));
  621. }
  622. node.confluenceRes = confluenceRes;
  623. list.Add(node);
  624. }
  625. break;
  626. case "auditornode":// 传阅节点
  627. list.Add(node);
  628. break;
  629. case "childwfnode":// 子流程节点
  630. list.Add(node);
  631. if (node.childType == "2")
  632. { // 异步的情况下直接往下走
  633. list.AddRange(GetPrevTaskNode(node, "agree", isGetAuditors, lineList));
  634. }
  635. break;
  636. case "startround":// 开始节点 需要重新审核
  637. list.Add(node);
  638. config.ParamConfig.State = 1;
  639. break;
  640. case "endround":// 结束节点
  641. config.ParamConfig.State = 2;
  642. break;
  643. default: // 默认一般审核界定啊
  644. list.Add(node);
  645. break;
  646. }
  647. }
  648. return list;
  649. }
  650. #endregion
  651. }
  652. }