.NET Core micro-services workflow permissions system + (B) workflow system

I. Introduction

  Connect one  . NET Micro Core Services permissions system + workflow (a) rights system , come out of

  Workflow, I've been curious about this development at the time of contact with its implementation, look at a variety of workflow engine code, explore its implementation, the individuals summed up a core elements:

    Indeed workflow engine handles the transfer of the core essence is how to parse XML or JSON circulation or other persistent way , the workflow information by parsing XML or JSON determine the current state of the node and the next node and make some processing. Feel equal say anything? Bluntly, is to get through the next step is to parse JSON file who handles.

  Circulation line workflow is actually fixed dead , you can know all the possible permutations and combinations of lines, and did not imagine so difficult to understand. Good understanding of this point, then the next development is very simple, just the code base (manual smile .ing). The system analyzes the workflow specific implementation, specific implementation steps are not set forth, a detailed look at the code address GitHub.

Second, the system introduced

In-depth study over work flow friends may know, it is divided into two processes form:

1, custom forms. Closer to the business, but it will be exhausted developers. Previous company are developed this way, the business logic and specific relationship, recommend the use of more complex custom form way that developers over the development of the business functions, and processes related to.

2, the code generated form. No code, the system can be generated automatically, easily, but poor scalability functionality.

Of course, each good. This system have been achieved in two ways, it focuses on the customization process. The system artificially provides: a process can be bound to a form, a form, a process can be bound . That one, which is the premise of all. As for why do it?

Typically a process is to be linked with the form logic, basically there is no possibility of multiple, and likely to cause tissue disorder, yes, it would then draw a process in a form. ^ _ @ _ @

Third, implement workflows

Or database-oriented approach to development, look at the table:

wf_workflow: Workflow table, store basic information workflow

wf_workflow_category: Process Classification

wf_workflow_form: Process table form, divided into two types, the system generates forms and systems custom forms, custom forms only storage system URL address

wf_workflow_instance : process instance table, the core

wf_workflow_instance_form: Examples of process association table form

wf_workflow_line: Process netlist. Instead of the current two forms of storage (agree, disagree), the latter will add custom business logic flow node SQL judge

wf_workflow_operation_history: Process operation history table. Obtaining approval for opinions

wf_workflow_transition_history: process flow records. Return for obtaining a step acquisition nodes, etc.

Currently workflow implements these functions: save, submit, agree, disagree, surrender, termination, flowcharts, approval of the views , the latter will continue to upgrade iteration, such as adding countersigned, suspended, notification, and now these function should be able to meet the general business needs, like countersign this function with less than 99%, but is indeed more complex functions, involving parallel, serial calculation, 80% less than the time spent with these functions up, the so-called Pareto rule it.

All function more, to name a few: the current flow classification function not only achieve, the subsequent write it, but does not affect the use of functions, but only for screening

Process design interface: The GooFlow plug-in, make some changes to the code and its interface is indeed ugly, relatively simple design, graphic design, after all, I will not, if you do not feel ugly, when I did not say.

Core code: actually parse JSON file, easy to read and to write some node, connection method

  1 /// <summary>
  2     /// workflow context
  3     /// </summary>
  4     public class MsWorkFlowContext : WorkFlowContext
  5     {
  6         /// <summary>
  7         /// 构造器传参
  8         /// </summary>
  9         /// <param name="dbworkflow"></param>
 10         public MsWorkFlowContext(WorkFlow dbworkflow)
 11         {
 12             if (dbworkflow.FlowId == default(Guid))
 13             {
 14                 throw new ArgumentNullException("FlowId", " input workflow flowid is null");
 15             }
 16             if (dbworkflow.FlowJSON.IsNullOrEmpty())
 17             {
 18                 throw new ArgumentException("FlowJSON", "input workflow json is null");
 19             }
 20             if (dbworkflow.ActivityNodeId == null)
 21             {
 22                 throw new ArgumentException("ActivityNodeId", "input workflow ActivityNodeId is null");
 23             }
 24 
 25             this.WorkFlow = dbworkflow;
 26 
 27             dynamic jsonobj = JsonConvert.DeserializeObject(this.WorkFlow.FlowJSON);
 28             //获取节点
 29             this.WorkFlow.Nodes = this.GetNodes(jsonobj.nodes);
 30             //获取连线
 31             this.WorkFlow.Lines = this.GetFromLines(jsonobj.lines);
 32 
 33             this.WorkFlow.ActivityNodeId = dbworkflow.ActivityNodeId == default(Guid) ? this.WorkFlow.StartNodeId : dbworkflow.ActivityNodeId;
 34 
 35             this.WorkFlow.ActivityNodeType = this.GetNodeType(this.WorkFlow.ActivityNodeId);
 36 
 37             //会签开始节点和流程结束节点没有下一步
 38             if (this.WorkFlow.ActivityNodeType == WorkFlowInstanceNodeType.ChatNode || this.WorkFlow.ActivityNodeType == WorkFlowInstanceNodeType.EndRound)
 39             {
 40                 this.WorkFlow.NextNodeId = default(Guid);//未找到节点
 41                 this.WorkFlow.NextNodeType = WorkFlowInstanceNodeType.NotRun;
 42             }
 43             else
 44             {
 45                 var nodeids = this.GetNextNodeId(this.WorkFlow.ActivityNodeId);
 46                 if (nodeids.Count == 1)
 47                 {
 48                     this.WorkFlow.NextNodeId = nodeids[0];
 49                     this.WorkFlow.NextNodeType = this.GetNodeType(this.WorkFlow.NextNodeId);
 50                 }
 51                 else
 52                 {
 53                     //多个下个节点情况
 54                     this.WorkFlow.NextNodeId = default(Guid);
 55                     this.WorkFlow.NextNodeType = WorkFlowInstanceNodeType.NotRun;
 56                 }
 57             }
 58         }
 59 
 60         /// <summary>
 61         /// 下个节点是否是多个
 62         /// </summary>
 63         public bool IsMultipleNextNode { get; set; }
 64 
 65         /// <summary>
 66         /// 获取节点集合
 67         /// </summary>
 68         /// <param name="nodesobj"></param>
 69         /// <returns></returns>
 70         private Dictionary<Guid, FlowNode> GetNodes(dynamic nodesobj)
 71         {
 72             Dictionary<Guid, FlowNode> nodes = new Dictionary<Guid, FlowNode>();
 73 
 74             foreach (JObject item in nodesobj)
 75             {
 76                 FlowNode node = item.ToObject<FlowNode>();
 77                 if (!nodes.ContainsKey(node.Id))
 78                 {
 79                     nodes.Add(node.Id, node);
 80                 }
 81                 if (node.Type == FlowNode.START)
 82                 {
 83                     this.WorkFlow.StartNodeId = node.Id;
 84                 }
 85             }
 86             return nodes;
 87         }
 88 
 89         /// <summary>
 90         /// 获取工作流节点及以节点为出发点的流程
 91         /// </summary>
 92         /// <param name="linesobj"></param>
 93         /// <returns></returns>
 94         private Dictionary<Guid, List<FlowLine>> GetFromLines(dynamic linesobj)
 95         {
 96             Dictionary<Guid, List<FlowLine>> lines = new Dictionary<Guid, List<FlowLine>>();
 97 
 98             foreach (JObject item in linesobj)
 99             {
100                 FlowLine line = item.ToObject<FlowLine>();
101 
102                 if (!lines.ContainsKey(line.From))
103                 {
104                     lines.Add(line.From, new List<FlowLine> { line });
105                 }
106                 else
107                 {
108                     lines[line.From].Add(line);
109                 }
110             }
111 
112             return lines;
113         }
114 
115         /// <summary>
116         /// 获取全部流程线
117         /// </summary>
118         /// <returns></returns>
119         public List<FlowLine> GetAllLines()
120         {
121             dynamic jsonobj = JsonConvert.DeserializeObject(this.WorkFlow.FlowJSON);
122             List<FlowLine> lines = new List<FlowLine>();
123             foreach (JObject item in jsonobj.lines)
124             {
125                 FlowLine line = item.ToObject<FlowLine>();
126                 lines.Add(line);
127             }
128             return lines;
129         }
130 
131         /// <summary>
132         /// 根据节点ID获取From(流入的线条)
133         /// </summary>
134         /// <param name="nodeid"></param>
135         /// <returns></returns>
136         public List<FlowLine> GetLinesForFrom(Guid nodeid)
137         {
138             var lines = GetAllLines().Where(m => m.To == nodeid).ToList();
139             return lines;
140         }
141 
142         public List<FlowLine> GetLinesForTo(Guid nodeid)
143         {
144             var lines = GetAllLines().Where(m => m.From == nodeid).ToList();
145             return lines;
146         }
147 
148         /// <summary>
149         /// 获取全部节点
150         /// </summary>
151         /// <returns></returns>
152         public List<FlowNode> GetAllNodes()
153         {
154             dynamic jsonobj = JsonConvert.DeserializeObject(this.WorkFlow.FlowJSON);
155             List<FlowNode> nodes = new List<FlowNode>();
156             foreach (JObject item in jsonobj.nodes)
157             {
158                 FlowNode node = item.ToObject<FlowNode>();
159                 nodes.Add(node);
160             }
161             return nodes;
162         }
163 
164         /// <summary>
165         /// 根据节点ID获取节点类型
166         /// </summary>
167         /// <param name="nodeId"></param>
168         /// <returns></returns>
169         public WorkFlowInstanceNodeType GetNodeType(Guid nodeId)
170         {
171             var _thisnode = this.WorkFlow.Nodes[nodeId];
172             return _thisnode.NodeType();
173         }
174 
175         /// <summary>
176         /// 根据节点id获取下个节点id
177         /// </summary>
178         /// <param name="nodeId"></param>
179         /// <returns></returns>
180         public List<Guid> GetNextNodeId(Guid nodeId)
181         {
182             List<FlowLine> lines = this.WorkFlow.Lines[nodeId];
183             if (lines.Count > 1)
184             {
185                 this.IsMultipleNextNode = true;
186             }
187             return lines.Select(m => m.To).ToList();
188         }
189 
190         /// <summary>
191         /// 节点驳回
192         /// </summary>
193         /// <param name="rejectType">驳回节点类型</param>
194         /// <param name="rejectNodeid">要驳回到的节点</param>
195         /// <returns></returns>
196         public Guid RejectNode(NodeRejectType rejectType, Guid? rejectNodeid)
197         {
198             switch (rejectType)
199             {
200                 case NodeRejectType.PreviousStep:
201                     return this.WorkFlow.PreviousId;
202                 case NodeRejectType.FirstStep:
203                     var startNextNodeId = this.GetNextNodeId(this.WorkFlow.StartNodeId).First();
204                     return startNextNodeId;
205                 case NodeRejectType.ForOneStep:
206                     if (rejectNodeid == null || rejectNodeid == default(Guid))
207                     {
208                         throw new Exception("驳回节点没有值!");
209                     }
210                     var fornode = this.WorkFlow.Nodes[rejectNodeid.Value];
211                     return fornode.Id;
212                 case NodeRejectType.UnHandled:
213                 default:
214                     return this.WorkFlow.PreviousId;
215             }
216         }
217 
218     }

流程流转代码(主要部分):这段代码是处理流转核心功能,只完成了部分核心功能

 1         /// <summary>
 2         /// 流程过程流转处理
 3         /// </summary>
 4         /// <param name="model"></param>
 5         /// <returns></returns>
 6         public async Task<WorkFlowResult> ProcessTransitionFlowAsync(WorkFlowProcessTransition model)
 7         {
 8             WorkFlowResult result = new WorkFlowResult();
 9             switch (model.MenuType)
10             {
11                 case WorkFlowMenu.Submit:
12                     break;
13                 case WorkFlowMenu.ReSubmit:
14                     result = await ProcessTransitionReSubmitAsync(model);
15                     break;
16                 case WorkFlowMenu.Agree:
17                     result = await ProcessTransitionAgreeAsync(model);
18                     break;
19                 case WorkFlowMenu.Deprecate:
20                     result = await ProcessTransitionDeprecateAsync(model);
21                     break;
22                 case WorkFlowMenu.Back:
23                     result = await ProcessTransitionBackAsync(model);
24                     break;
25                 case WorkFlowMenu.Stop://刚开始提交,下一个节点未审批情况,流程发起人可以终止
26                     result = await ProcessTransitionStopAsync(model);
27                     break;
28                 case WorkFlowMenu.Cancel:
29                     break;
30                 case WorkFlowMenu.Throgh:
31                     break;
32                 case WorkFlowMenu.Assign:
33                     break;
34                 case WorkFlowMenu.View:
35                     break;
36                 case WorkFlowMenu.FlowImage:
37                     break;
38                 case WorkFlowMenu.Approval:
39                     break;
40                 case WorkFlowMenu.CC:
41                     break;
42                 case WorkFlowMenu.Suspend:
43                     break;
44                 case WorkFlowMenu.Resume:
45                     break;
46                 case WorkFlowMenu.Save:
47                 case WorkFlowMenu.Return:
48                 default:
49                     result = WorkFlowResult.Error("未找到匹配按钮!");
50                     break;
51             }
52             return result;
53         }

 

如果以定制表单关联流程的方式开发,会遇到一个重要问题:流程状态如何与表单同步?因为工作流与业务流是区分开的,怎么办?

  我的做法是(以请假为例):让实体先继承流程状态实体,通过CAP的方式推送和订阅,我以前的公司工作流是通过页面回调的方式实现,我感觉这个很不靠谱,实际上也是经常出问题

流程状态的判断:WfWorkflowInstance实体下的两个字段, 这块可能不太好理解,尤其是没有开发过的朋友,简单解释下:IsFinish 是表示流程运行的状态,Status表示用户操作流程的状态,我们判断这个流程是否结束不能单纯的判断根据IsFinish进行判断,

举个例子(请假):

  我提交了一个请假申请==>下个节点审批不同意。你说这个流程有没有结束?当然结束了,只不过它没有审批通过而已。简而言之,IsFinish表示流程流转是否结束,即是否最终到了最后一个结束节点。

 1         #region 结合起来判断流程是否结束
 2         /*              流转状态判断 实际情况组合
 3          * IsFinish=1 & Status=WorkFlowStatus.IsFinish      表示通过
 4          * IsFinish==null & Status=WorkFlowStatus.UnSubmit  表示未提交
 5          * IsFinish=0 & Status=WorkFlowStatus.Running       表示运行中
 6          * IsFinish=0 & Status=WorkFlowStatus.Deprecate     表示不同意
 7          * IsFinish=0 & Status=WorkFlowStatus.Back          表示流程被退回
 8          * **/
 9         /// <summary>
10         /// 流程节点是否结束
11         /// 注:此字段代表工作流流转过程中运行的状态判断
12         /// </summary>
13         public int? IsFinish { get; set; }
14 
15         /// <summary>
16         /// 用户操作状态<see cref="WorkFlowStatus"/>
17         /// 注:此字段代表用户操作流程的状态
18         /// </summary>
19         public int Status { get; set; }
20 
21         #endregion

 

至于页面审批按钮的展示,因为这个功能是公用的,我把它写在了组件里面,共两个菜单组件,一个是定制一个是系统生成,代码稍微有些不同,组件视图代码比较多,就不展示了。

下面走一个不同意的请假流程:

1、wms账号先选择要发起的流程

2、流程发起界面

3、流程提交之后的界面,注:终止:当用户提交表单之后,下个节点未进行审批的时候,流程发起人有权终止(取消流程)

4、wangwu账号登录

5、结果展示

6、审批意见查看

7、流程图查看,绿色节点表示流程当前节点。

8、也可以在OA员工请假看到结果

注:因为工作流引擎不涉及具体的业务逻辑,通常与OA系统进行表单绑定,所以我建了OA服务,并简单写了个请假流程方便测试。工作流依赖于之前的权限系统,如果登录人员显示没有权限,请先进行授权

四、结束

  每个程序员刚毕业的时候都有一种我要独立写一个超级牛逼系统的冲动,我也是,都不记得多少年了,断断续续坚持到现在,虽然不算完善,更谈不上多么牛逼,写这两篇算是给自己一个交代吧。如果大家觉得有研究价值的话,我会继续升级迭代。

运行方式参考 上一篇 (末尾)

管理员登录账号wms,密码:所有账号密码都是123

代码地址:

https://github.com/wangmaosheng/MsSystem-BPM-ServiceAndWebApps

如果觉得有点作用的话,可以 start 下,后续会持续更新。

欢迎加微信讨论,共同进步(妹子更好哟@--@

Guess you like

Origin www.cnblogs.com/wms01/p/10940565.html