My micro workflow engine-functional design analysis and usage examples, workflow examples
I. Introduction
In the last article, I introduced the model and basic design of my workflow. In this article, I want to explain the functions and usage examples of my workflow in detail. This workflow is mainly designed for developers. In order to let everyone have a global understanding, the design and implementation of local functions will not be described in detail. I will continue to write articles to introduce to you when I have time.
Second, detailed function explanation and use example code
1. Configure the process engine, generally called during the startup of the program (Global.asax.cs)
// Initialize the process engine
BpmConfiguration
.Instance ()
.Config (@ "C: \ Configration \ BpmConfig.xml")
.Start ();
If no configuration file is specified, the process configuration is read from app.config or web.config by default
// Initialize the process engine
BpmConfiguration
.Instance ()
.Start ();
Of course, it also supports starting multiple process engines at the same time to provide SAAS program support.
// A tenant engine configuration
BpmConfiguration
.Instance ("TenantA")
.Config (@ "C: \ BpmConfigA.xml")
.Start ();
// B tenant engine configuration
BpmConfiguration
.Instance ("TenantB")
.Config (@ "C: \ BpmConfigB.xml")
.Start ();
The configuration in XML includes: database connection, log configuration file, task plan start delay, task plan execution cycle, user relationship structure mapping, and variable type expansion in the process.
2. Obtain the workflow context, that is, the entry point of the workflow. All functions are concentrated on this entry point.
var bpm = new BpmContext ()
.UseTransaction (true)
.SetActor ("Xiao Qin");
The context is currently different for different engine instances
var bpm = new BpmContext ("TenantA");
var bpm = new BpmContext ("TenantB");
When no constructor is passed, the default instance is returned.
3. Transaction support, whether to start transaction, commit, and rollback.
bpm.UseTransaction (true);
bpm.Commit ();
bpm.Rollback ();
4. Process definition
// New process definition
bpm.NewProcessDefinition ("Leave Process")
.SetXmlFile (@ "C: \ Definition \ demo1.xml")
.SetCategory ("Category 1")
.SetEffectDate (DateTime.Now)
.SetExpireDate (DateTime.Now.AddDays (180))
.SetMemo ("memo1")
.Create () // Create process definition
.Parse () // Parse the xml
When the process is created, the version number is automatically generated, starting from 1.0 by default. When the process name is the same, different versions will be generated.
Different task nodes can be defined in the xml: start node, automatic node, artificial node, decision node, divergent node, aggregation node, sub-process node, signing node, waiting node, ending node.
And the information about the routing, personnel distribution, variable definitions, event actions, etc. that connect to the task nodes, you can refer to the xml definition in my previous article
// Load process definition
var processDefinition = bpm.LoadProcessDefiniton ("1");
processDefinition.Deploy (); // Publish the process definition
processDefinition.Revoke (); // Recall process definition
processDefinition.Delete (); // Delete the process definition
5, process examples
// Initiate process instance
var process = bpm.NewProcessIntance ("Leave Process", "Xiao Qin");
process.SetVariable ("project_id", 1399); // Save process variables
process.Start (); // Start
process.Suspend (); // Suspend
process.Resume (); // Resume
process.Cancel (); // Cancel
process.End (); // End
Here, there are three parameters on the NewProcessInstance method instance. The first is the process definition ID, the second is the business ID started, and the third is the return stack point ID of the sub-process. Non-sub-processes can be ignored.
// Start process
var startTask = process.Start ();
startTask.SetRecord ("SO20150903001"); // Save form data (association)
startTask.SetAttach ("01", "02"); // Save attachment information (association), multiple
startTask.SetVariable ("var1", "value1"); // Save task variables
startTask.Signal (); // Transfer to the next step
task.SetRecord is used to save the current form data id, and the data itself is saved in the business table
task.SetAttach is used to save the attachment id of the current node, and the attachment information is stored in the attachment management table
The key method of task.Signal process flow, triggering the token token leaving action according to the process definition
// Approval task
var task = bpm.LoadTaskInstance ("00");
task.SetOpinion (SignResult.Passed, "I agree"); // Set approval opinion
task.SetReceiver ("颜经理"); // Set the approver for the next step
task.Signal (); // Transfer to the next step
The method of SetReceiver is provided here to set the approver for the next step. Normally, it has already been defined in the process definition, and there is no need to set it again. However, in actual applications, there may be cases where the task is approved by the designated leader In Tongda OA, you can also set the approver for the next step, so this method is added and can be called when needed. Note that applying this method will override the setting of the work item owner property in the definition.
// Task delegation
var task1 = bpm.LoadTaskInstance ("01");
task1.AssignCandidate ("Xiao Zheng, Xiao Hu"); // Add a task candidate
task1.AssignCandidate ("saler", ActorType.Role); // Add task candidate
task1.AssignTo ("李四"); // Assign a task to Li Si
In my workflow, there can only be one owner and one actor for a work task, but there can be multiple candidates.
A candidate is a range limit or a list of people that can be assigned to the current job. The owner is the owner of the task and the actor is the operator calculated after the owner considers the complex commission relationship.
task.AssignCandidate This method is used to add task candidates. The second parameter is the object type. You can directly add a role, organization, user group, etc.
task.AssignTo assigns tasks, and the assignment status of tasks includes the following states
public enum AssignStatus
{
// pending
Pending,
// Claim, the user claims the task and receives task input data
Claim,
// Commission, delegate to another person (for example, manager) to perform tasks on his or her behalf
Depute,
// due, did not process the approval task within the specified time period
Expire,
// Renew, if this task is not processed within the given time range, the task will be renewed for execution in another time period
Renew
}
If the process gets stuck in a certain node for a long time, we can send a reminder message
If a process node does take a long time to complete, I designed a current work progress reporting interface
// Work reminder
task.Urge ("It's urgent, ask the manager to process it as soon as possible, wait online!");
// Report the current work progress
task.Report (0.6, "It is expected to be completed this week");
task.Urge sends a reminder notification to the actual owner of the task and generates a reminder history.
task.Report sends a progress report to the task's subscribers (all those following the current process task).
// Query variables
var var0 = process.GetVariable ("project_id");
var var1 = task1.GetVariable ("var1");
var var2 = task1.GetVariable <DateTime> ("var2");
var var3 = task1.GetVariableObject ("var3");
There are three types of variables:
Process variables are persistent and exist throughout the process cycle
Task variables are persistent and exist in the current task
Temporary variables are not persisted to the database, they only exist in the current execution context (executionContext).
Set variable SetVariable GetVariable
Supports any data type
6. Approval methods with Chinese characteristics, mainly including countersignature, additional signing (front signing, post signing, and signing), reduction of visas, and free flow
Countersign: a task involving multiple people to make a decision
Signing: I feel unsure about this task. I want to join one or more people to make a decision with me (the order of signing before the current decision maker, the order of signing after the current decision maker, and signing regardless of order Parallel processing)
Reduction: as opposed to increase, disqualify someone from participating in decision-making
Free flow: Not included in the process definition. The temporarily added dynamic route directly sends the current work to the designated node for approval.
Transfer signing: from a single approving node for single decision-making to a signing node for joint decision-making by multiple people, supporting recursive signing, that is, signing child nodes can continue to transfer signing nodes
Transfer of approval: from a signing node that is jointly decided by multiple people to a common approval node that is decided by a single person
In my workflow, the countersign designed the following parameters:
a Running mode. If the nodes are divergent in parallel, the work tasks of all participants will be activated at the same time when entering the signing node, and there will be a sequence when they are serial.
public enum RunMode
{
// serial
Serial,
//parallel
Parallel
}
b Decision mode, how to make signing nodes based on the results of child nodes
public enum DecisionMode
{
// Organizer mode
Sponsor,
// voting mode
Vote,
// One vote passed
OnePass,
//One-vote veto
OneVeto
}
Sponsor mode: A sponsor needs to be set up. The results are subject to the sponsor's decision. Others' decisions are for reference only.
Voting mode: Set a passing ratio, which is determined by everyone's vote. Support setting everyone's voting weight.
One vote pass: In fact, it can be regarded as a kind of voting mode with a pass rate setting greater than 0%.
One-vote veto: It can be seen as a kind of 100% voting mode.
Of course, here are just a few of the commonly used patterns. I can also expand the decision mode myself. I just need to inherit the abstract class Decision that I have defined.
c Whether to wait, that is, there are still people who have not voted, but in the case where the result of the vote has already been confirmed, you need not wait for the other people to complete the vote before proceeding to the next step.
There are two types of countersignature. One is the countersignature defined in the process definition, and the other is the temporary conversion of a common approval node to a countersignature. In fact, Chinese-style approval is actually to be flexible. If it is defined in the process definition, in fact, you can not use the signing node, and you can also implement it with multiple ordinary nodes. The signing node is mainly designed for the transfer signing scenario: that is, the approver of the current general approval node feels unsure or does not want to take responsibility, and can join the senior leadership or other people to make decisions or provide reference opinions.
Example: General approval transfer signing Set to parallel, the decision mode is weighted voting, you need to wait for everyone to vote, the pass line is 65%
// Transfer sign
var task2 = bpm.LoadTaskInstance ("02");
task2.ToCountersign (RunMode.Parallel, DecisionMode.Vote, true, 0.65M);
task2.CountersignAdd (new Countersigner () {actor_id = "Zhang San", vote_weight = 1});
task2.CountersignAdd (new Countersigner () {actor_id = "Li Si", vote_weight = 0.5});
task2.CountersignAdd (new Countersigner () {actor_id = "Little Five", vote_weight = 2});
The parallel mode is parallel signing. The prerequisite for front signing and post signing is serial mode. It is assumed that task2 is serial, sponsor mode, and the original approver is Xiao Qin
// Before signing
task2.CountersignAddBefore ("Xiao Qin", new Countersigner () {actor_id = "张三"});
// Post sign
task2.CountersignAddAfter ("肖 秦", new Countersigner () {actor_id = "李四", is_sponsor = true});
Reduction is relatively simple
// Subtract
task2.CountersignRemove ("王 五");
The signing node is transferred to ordinary approval, allowing one person to make a decision directly
// Transfer approval
task2.ToSinglesign ("The Pharaoh next door");
Free flow mode, create temporary roads and jump directly to designated nodes for approval
// free flow
var task3 = bpm.LoadTaskInstance ("03");
task3.SetFreeRedirect ("General Manager approval");
task3.Signal ();
7, fallback mechanism
Process rollback to specified node
// The process instance specifies any node to fallback
var process2 = bpm.LoadProcessInstance ("02");
process2.Rollback ("Fill in the leave form");
The task instance goes back to the previous step
// The current work task is rolled back to the previous approval node
var task4 = bpm.LoadTaskInstance ("04");
task4.Rollback ();
8. Work commission
Zhang San directly delegated a task to Li Si to support the recursive commissioning relationship, that is, Zhang San entrusted to Li Si, Li Si entrusted to Wang Wu, Wang Wu was entrusted to Zhao Liu ...
bpm.NewDeputeService ("张三", "李四")
.ForTaskInstance ("Task Instance ID")
.Depute ();
Entrust the entire process instance to Li Si, that is, all Zhang San's tasks under this process instance will be delegated to Li Si
bpm.NewDeputeService ("张三", "李四")
.ForProcessInstance ("Process Instance ID")
.Depute ();
Delegate a task node in a process definition to Li Si, that is, all task instances created by this node will be delegated to Li Si if the task is Zhang San
bpm.NewDeputeService ("张三", "李四")
.ForTaskDefinition ("Task Definition ID")
.Depute ();
Delegate a process definition to Li Si, that is, all task instances created in this process. If it is a Zhang San task, it will be delegated to Li Si during the effective period
bpm.NewDeputeService ("张三", "李四")
.ForProcessDefinition ("Process Definition ID")
.SetDateRange (DateTime.Now, DateTime.Now.AddDays (30)) // Valid period
.SetMemo ("This month's business trip, this process is entrusted to Li Sidai Office") // Description of commission
.Depute ();
Withdraw the delegation relationship, just replace Revoke with Depute
// Retract commissioned work
bpm.NewDeputeService ("张三", "李四")
.ForProcessInstance ("Process Instance ID")
.Revoke ();
9.Follow subscriptions
This function is similar to delegation. After subscribing, you will receive process or task dynamic message alerts, such as: process has been created, started, suspended ..., task has been created, to whom it is assigned, progress report, task completion, etc
// Follow the subscription
bpm.NewSubscribeService ("Zhang San")
.ForTaskInstance ("Task Instance ID")
.Subscribe ();
bpm.NewSubscribeService ("Zhang San")
.ForProcessInstance ("Process Instance ID")
.Subscribe ();
bpm.NewSubscribeService ("Zhang San")
.ForProcessDefinition ("Process Definition ID")
.Subscribe ();
bpm.NewSubscribeService ("Zhang San", "Li Si", "Wang Wu")
.ForTaskDefinition ("Task Definition ID")
.Subscribe ();
To cancel a subscription, just change Subscribe to Unsubscribe.
//unsubscribe
bpm.NewSubscribeService ("Li Si")
.ForProcessDefinition ("Purchasing Process")
.Unsubscribe ();
10.Data query
I did not provide an interface for querying. Opening a database query directly would be more flexible than the interface I provided, such as:
a Query the published process definition
select * from bpm_definition_process where state = 'Deploy'
Process definition status includes
public enum ProcessDefinitionState
{
//create
Create,
// parse
Parse,
//release
Deploy,
// recycling
Revoke,
//delete
Delete
}
b my process
select * from bpm_instance_process where state = 'Run' and starter = '肖 秦'
Process status includes
public enum ProcessState
{
//create
Create,
//run
Run,
// hang
Pending,
//termination
Termination,
//carry out
Complete,
//cancel
Cancel
}
c My To Do
select * from bpm_instance_task where state = 'Run' and actor_id = '肖 秦'
To-do tasks include tasks entrusted to you, if you only want to see your own tasks
select * from bpm_instance_task where state = 'Run' and owner_id = '肖 秦'
Task status includes
public enum TaskState
{
// create, task has been created
Create,
// Blocking, there are blocking tasks in the arrival line that have not been completed
Blocking,
//start up
Run,
// Complete, the user has completed the task and provided the output data of the task
Complete,
// Failed, the user has completed the task and provided an error message
Failure,
//go back
Rollback
}
d query candidate information
select * from bpm_instance_assignment where task_instance_id = 'ID'
e query my message
select * from bpm_application_notify where state = 'Unread' and reciever_id = '萧 秦'
Other queries are no longer examples
Summary
I have said before that the purpose of developing this engine is to have a lightweight and easy-to-use single dll file (1.1M post-release size) workflow engine when doing a project, and the interface is simple and easy for secondary development. , Supports multiple databases and is quite powerful. The design and development of the simple version from the previous year to the present has basically taken shape, and the test has also taken a lot of time. There may still be problems that have not been detected, but now it is basically stable. Next, if I have time, I will slowly introduce you to the design and implementation of the functional details, what other functions I have not thought through or opinions, or any part I want to know in detail can leave me a message.