Workflow (2): workflow flow chart design_Sneko's Blog-CSDN Blog
Workflow flow framework code
Workflow start, sign for or verify, send out, return, withdraw, close and other process processing
1. Create the entity class WorkFlow_Trace corresponding to the workflow link tracking table
The source program of WorkFlow_Trace.cs is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WorkFlowDemo
{
/// <summary>
/// 工作流跟踪数据表
/// </summary>
public class WorkFlow_Trace
{
#region 实体属性
/// <summary>
/// 主键自增编号
/// </summary>
public int? CoreId { get; set; }
/// <summary>
/// 上一跟踪编号:-1代表根节点
/// </summary>
public int? ParentCoreId { get; set; }
/// <summary>
/// 模组码
/// </summary>
public string Barcode { get; set; }
/// <summary>
/// 工单号
/// </summary>
public string WorkOrder { get; set; }
/// <summary>
/// 操作工序编码
/// </summary>
public string Operation { get; set; }
/// <summary>
/// 操作工序名称
/// </summary>
public string OperationName { get; set; }
/// <summary>
/// 处理开始时间
/// </summary>
public DateTime? ProcessStartTime { get; set; }
/// <summary>
/// 处理结束时间
/// </summary>
public DateTime? ProcessEndTime { get; set; }
/// <summary>
/// 状态【Queue排队中,Working工作中,Completed完成,DirectDelivery直送-拉状态,Abolished作废的】
/// </summary>
public string WorkStatus { get; set; }
/// <summary>
/// 创建人
/// </summary>
public string CreateName { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreateTime { get; set; }
/// <summary>
/// 是否完工下料
/// </summary>
public int? IsAllCompleted { get; set; }
#endregion
}
}
2. New database operation class SugarClient
The source program of SugarClient.cs is as follows:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using SqlSugar;
namespace WorkFlowDemo
{
/// <summary>
/// SqlSugar客户端操作数据库
/// </summary>
public class SugarClient
{
/// <summary>
/// 添加一行数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static int Insert<T>(T obj) where T : class, new()
{
using (SqlSugarClient sugarClient = SugarDao.GetInstance())
{
return sugarClient.Insertable(obj).ExecuteCommand();
}
}
/// <summary>
/// 更新一行数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static int Update<T>(T obj) where T : class, new()
{
using (SqlSugarClient sugarClient = SugarDao.GetInstance())
{
return sugarClient.Updateable(obj).ExecuteCommand();
}
}
}
}
3. WorkFlowEngineUtil for key workflow start, send, and finish classes
The source program of WorkFlowEngineUtil.cs is as follows:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WorkFlowDemo
{
/// <summary>
/// 工作流启动、流转(送出)、完成、办结【比如由组件装配工序 自动 流转到 拍照工序】
/// 斯内科 2023-03-28
/// </summary>
public class WorkFlowEngineUtil
{
/// <summary>
/// 启动工作流:找到工艺路线的起始工序,然后将其设置为排队中即可
/// 返回元组:第一项代表错误号【为0代表OK】,第二项代表当前操作工序环节,第三项代表错误信息
/// </summary>
/// <param name="barcode"></param>
/// <returns></returns>
public static Tuple<int, string, string> StartWorkFlow(string barcode)
{
string sqlStart = "select * from process_router where PreviousOperationName='' or PreviousOperationName is null";
DataTable dtStart = RawSql.GetDataTable(sqlStart, null);
if (dtStart == null || dtStart.Rows.Count < 1)
{
return Tuple.Create(10001, "", $"没有配置工艺路线【process_router】的起始工序,模组码【{barcode}】");
}
string operation = dtStart.Rows[0]["Operation"].ToString();
string operationName = dtStart.Rows[0]["OperationName"].ToString();
string workOrder = "WorkOrder001";
WorkFlow_Trace workFlow_Trace = new WorkFlow_Trace()
{
ParentCoreId = -1,
Barcode = barcode,
WorkOrder = workOrder,
Operation = operation,
OperationName = operationName,
ProcessStartTime = DateTime.Now,
WorkStatus = "Queue",
CreateName = "test",
CreateTime = DateTime.Now,
IsAllCompleted = 0
};
int result = SugarClient.Insert(workFlow_Trace);
if (result <= 0)
{
return Tuple.Create(10002, operationName, $"启动工作流失败,模组码【{barcode}】");
}
return Tuple.Create(0, operationName, $"启动工作流成功,模组码【{barcode}】,起始环节【{workFlow_Trace.OperationName}】,状态【{workFlow_Trace.WorkStatus}】");
}
/// <summary>
/// 进站校验,查看是否在该工序的排队中,如果不在排队中,就报错。如果在排队Queue中,就将其修改为工作Working中
/// </summary>
/// <param name="barcode"></param>
/// <param name="operationName"></param>
/// <returns></returns>
public static Tuple<int, string> Inbound(string barcode, string operationName)
{
//进站校验,查看是否在该工序的排队中,如果不在排队中,就报错。如果在排队Queue中,就将其修改为工作Working中
string sql = "select * from workflow_trace where barcode=@Barcode";
DataTable dtStart = RawSql.GetDataTable(sql, new Dictionary<string, object>() { { "Barcode", barcode } });
if (dtStart == null || dtStart.Rows.Count < 1)
{
return Tuple.Create(10003, $"模组条码不存在,查看是否已删除或者工作流未启动,模组码【{barcode}】");
}
DataRow[] dataRows = dtStart.Select("WorkStatus='Queue'", "CoreId desc");
if (dataRows == null || dataRows.Length < 1)
{
return Tuple.Create(10004, $"模组条码不在排队中,请检查是否已完成。模组码【{barcode}】");
}
string coreId = dataRows[0]["CoreId"].ToString();
string queueOperationName = dataRows[0]["OperationName"].ToString();
if (queueOperationName != operationName)
{
return Tuple.Create(10005, $"模组不在【{operationName}】的排队中,它在【{queueOperationName}】的排队中,模组码【{barcode}】");
}
string sqlUpdateStatus = "update workflow_trace set WorkStatus='Working' where CoreId=@CoreId";
RawSql.ExecuteCommand(sqlUpdateStatus, new Dictionary<string, object>() { { "CoreId", coreId } });
return Tuple.Create(0, $"进站校验成功,已将模组状态设置为工作中,模组码【{barcode}】");
}
/// <summary>
/// 先判断当前环节是否在工作中,如果在工作中,就将状态设置为完成,并自动送出到下一个环节。
/// 如果当前环节无下一个环节,则自动完成工序【下料】,并自动办结Archive
/// </summary>
/// <param name="barcode"></param>
/// <param name="operationName"></param>
/// <returns></returns>
public static Tuple<int, string, string> SendNext(string barcode, string operationName)
{
//送出:update当前环节为已完成,然后新建下一环节
string sql = "select * from workflow_trace where barcode=@Barcode";
DataTable dt = RawSql.GetDataTable(sql, new Dictionary<string, object>() { { "Barcode", barcode } });
if (dt == null || dt.Rows.Count < 1)
{
return Tuple.Create(10006, "", $"模组条码不存在,查看是否已删除或者工作流未启动,模组码【{barcode}】");
}
DataRow[] dataRows = dt.Select("WorkStatus='Working'", "CoreId desc");
if (dataRows == null || dataRows.Length < 1)
{
return Tuple.Create(10007, "", $"模组条码不在工作中,请检查是否在【{operationName}】工作中。模组码【{barcode}】");
}
string coreId = dataRows[0]["CoreId"].ToString();
string workingOperationName = dataRows[0]["OperationName"].ToString();
if (workingOperationName != operationName)
{
return Tuple.Create(10008, workingOperationName, $"模组不在【{operationName}】的工作中,它在【{workingOperationName}】的工作中,模组码【{barcode}】");
}
string sqlFinish = "update workflow_trace set WorkStatus='Completed',ProcessEndTime=@ProcessEndTime where CoreId=@CoreId";
RawSql.ExecuteCommand(sqlFinish,
new Dictionary<string, object>() { { "CoreId", coreId }, { "ProcessEndTime", DateTime.Now } });
//找出当前环节的下一个环节,如果当前环节无下一个环节,则自动下料完成
string sqlNext = "select * from process_router where PreviousOperationName=@PreviousOperationName";
DataTable dtNext = RawSql.GetDataTable(sqlNext, new Dictionary<string, object>() { { "PreviousOperationName", operationName } });
if (dtNext == null || dtNext.Rows.Count < 1)
{
//办结
string sqlArchive = "update workflow_trace set IsAllCompleted='1' where CoreId=@CoreId";
RawSql.ExecuteCommand(sqlArchive, new Dictionary<string, object>() { { "CoreId", coreId } });
//办结表迁移
Tuple<int, string> tupleArchive = Archive(barcode);
if (tupleArchive.Item1 != 0)
{
return Tuple.Create(tupleArchive.Item1, "", tupleArchive.Item2); ;
}
return Tuple.Create(0, "", $"当前工序【{operationName}】已经是终结工序,已生产完成,模组码【{barcode}】");
}
string nextOperation = dtNext.Rows[0]["Operation"].ToString();
string nextOperationName = dtNext.Rows[0]["OperationName"].ToString();
string workOrder = dataRows[0]["WorkOrder"].ToString();
WorkFlow_Trace workFlow_Trace = new WorkFlow_Trace()
{
ParentCoreId = int.Parse(coreId),
Barcode = barcode,
WorkOrder = workOrder,
Operation = nextOperation,
OperationName = nextOperationName,
ProcessStartTime = DateTime.Now,
WorkStatus = "Queue",
CreateName = "test",
CreateTime = DateTime.Now,
IsAllCompleted = 0
};
int result = SugarClient.Insert(workFlow_Trace);
if (result <= 0)
{
return Tuple.Create(10009, nextOperationName, $"从工序【{operationName}】自动流转到下一工序【{nextOperationName}】失败,模组码【{barcode}】");
}
return Tuple.Create(0, nextOperationName, $"流转成功,已将模组从工序【{operationName}】自动流转到下一工序【{nextOperationName}】的排队中,模组码【{barcode}】");
}
/// <summary>
/// 办结归档表,将已完成的工作流工序数据迁移到表【workflow_trace_archive】中,可以在历史记录中查看
/// 先删除workflow_trace_archive的当前条码数据
/// 然后将workflow_trace的数据迁移到workflow_trace_archive,最后删除表workflow_trace_archive的数据
/// </summary>
/// <param name="barcode"></param>
/// <returns></returns>
public static Tuple<int, string> Archive(string barcode)
{
string sql = "select * from workflow_trace where barcode=@Barcode";
DataTable dt = RawSql.GetDataTable(sql, new Dictionary<string, object>() { { "Barcode", barcode } });
if (dt == null || dt.Rows.Count < 1)
{
return Tuple.Create(10010, $"模组条码不存在,查看是否已删除或已办结或者工作流未启动,模组码【{barcode}】");
}
string sqlArchive = $@"delete from workflow_trace_archive where Barcode=@Barcode;
insert into workflow_trace_archive select * from workflow_trace where Barcode=@Barcode;
delete from workflow_trace where Barcode=@Barcode;";
RawSql.ExecuteCommand(sqlArchive, new Dictionary<string, object>() { { "Barcode", barcode } });
return Tuple.Create(0, $"已办结归档成功,模组码【{barcode}】");
}
}
}
4. Create a new test winform form FormFlowTest, as shown in the figure:
FormFlowTest.Designer.cs source program is as follows:
namespace WorkFlowDemo
{
partial class FormFlowTest
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.rtxbDisplay = new System.Windows.Forms.RichTextBox();
this.btnFlowTest = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.txtBarcode = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// rtxbDisplay
//
this.rtxbDisplay.Location = new System.Drawing.Point(319, 12);
this.rtxbDisplay.Name = "rtxbDisplay";
this.rtxbDisplay.Size = new System.Drawing.Size(703, 492);
this.rtxbDisplay.TabIndex = 0;
this.rtxbDisplay.Text = "";
//
// btnFlowTest
//
this.btnFlowTest.Font = new System.Drawing.Font("宋体", 13F);
this.btnFlowTest.Location = new System.Drawing.Point(78, 88);
this.btnFlowTest.Name = "btnFlowTest";
this.btnFlowTest.Size = new System.Drawing.Size(111, 55);
this.btnFlowTest.TabIndex = 1;
this.btnFlowTest.Text = "流转测试";
this.btnFlowTest.UseVisualStyleBackColor = true;
this.btnFlowTest.Click += new System.EventHandler(this.btnFlowTest_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("宋体", 13F);
this.label1.Location = new System.Drawing.Point(17, 13);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(134, 18);
this.label1.TabIndex = 2;
this.label1.Text = "条码或流程编号";
//
// txtBarcode
//
this.txtBarcode.Font = new System.Drawing.Font("宋体", 13F);
this.txtBarcode.Location = new System.Drawing.Point(12, 45);
this.txtBarcode.Name = "txtBarcode";
this.txtBarcode.Size = new System.Drawing.Size(274, 27);
this.txtBarcode.TabIndex = 3;
this.txtBarcode.Text = "WorkFlow123456";
//
// FormFlowTest
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1034, 516);
this.Controls.Add(this.txtBarcode);
this.Controls.Add(this.label1);
this.Controls.Add(this.btnFlowTest);
this.Controls.Add(this.rtxbDisplay);
this.Name = "FormFlowTest";
this.Text = "流转测试";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.RichTextBox rtxbDisplay;
private System.Windows.Forms.Button btnFlowTest;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox txtBarcode;
}
}
5. Test the FormFlowTest.cs program as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WorkFlowDemo
{
public partial class FormFlowTest : Form
{
public FormFlowTest()
{
InitializeComponent();
rtxbDisplay.ReadOnly = true;
}
/// <summary>
/// 显示文本框内容
/// </summary>
/// <param name="message"></param>
private void DisplayContent(string message)
{
this.BeginInvoke(new Action(() =>
{
if (rtxbDisplay.TextLength > 10240)
{
rtxbDisplay.Clear();
}
rtxbDisplay.AppendText($"{DateTime.Now.ToString("HH:mm:ss.fff")}->{message}\n");
rtxbDisplay.ScrollToCaret();
}));
}
/// <summary>
/// 按钮激活或屏蔽
/// </summary>
/// <param name="enabled"></param>
private void EnableButton(bool enabled)
{
this.BeginInvoke(new Action(() =>
{
btnFlowTest.Enabled = enabled;
}));
}
private async void btnFlowTest_Click(object sender, EventArgs e)
{
string barcode = txtBarcode.Text.Trim();
await Task.Run(()=>
{
EnableButton(false);
Tuple<int, string, string> tupleStart = WorkFlowEngineUtil.StartWorkFlow(barcode);
DisplayContent($"【{barcode}】已启动工作流.错误号【{tupleStart.Item1}】,操作工序【{tupleStart.Item2}】,消息【{tupleStart.Item3}】");
Thread.Sleep(2000);
string operationName = tupleStart.Item2;
do
{
Tuple<int, string> tupleInbound = WorkFlowEngineUtil.Inbound(barcode, operationName);
DisplayContent($"【{barcode}】在工序【{operationName}】进站.错误号【{tupleInbound.Item1}】,操作工序【{operationName}】,消息【{tupleInbound.Item2}】");
if (tupleInbound.Item1 != 0)
{
break;
}
Tuple<int, string, string> tupleNext = WorkFlowEngineUtil.SendNext(barcode, operationName);
DisplayContent($"【{barcode}】从工序【{operationName}】自动流转到工序【{tupleNext.Item2}】.错误号【{tupleNext.Item1}】,消息【{tupleNext.Item3}】");
if (tupleNext.Item1 == 0 && string.IsNullOrEmpty(tupleNext.Item2))
{
DisplayContent($"【{barcode}】已办结【已生产完成】.错误号【{tupleNext.Item1}】,消息【{tupleNext.Item3}】");
break;
}
else if (tupleNext.Item1 != 0)
{
break;
}
//自动流转到下一工序
operationName = tupleNext.Item2;
Thread.Sleep(2000);
} while (true);
EnableButton(true);
});
}
}
}
Sixth, the test operation is as shown in the figure: