{"id":72579,"date":"2026-02-06T01:16:46","date_gmt":"2026-02-05T17:16:46","guid":{"rendered":"https:\/\/www.wsisp.com\/helps\/72579.html"},"modified":"2026-02-06T01:16:46","modified_gmt":"2026-02-05T17:16:46","slug":"mes-erp-%e5%9c%ba%e6%99%af%e5%a4%9a%e5%b1%82%e7%ba%a7%e5%a7%94%e6%89%98%e5%b0%81%e8%a3%85%e7%bb%84%e4%bb%b6","status":"publish","type":"post","link":"https:\/\/www.wsisp.com\/helps\/72579.html","title":{"rendered":"MES\/ERP \u573a\u666f\u591a\u5c42\u7ea7\u59d4\u6258\u5c01\u88c5\u7ec4\u4ef6"},"content":{"rendered":"<p>MES\/ERP \u573a\u666f\u6253\u9020\u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u7684\u591a\u5c42\u7ea7\u59d4\u6258\u62fc\u63a5\u5c01\u88c5WinForm \u4fe1\u606f\u4e2d\u95f4\u4ef6<\/p>\n<h2>MES\/ERP \u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u5c01\u88c5 WinForm \u4e2d\u95f4\u4ef6\u89e3\u51b3\u65b9\u6848<\/h2>\n<p>\u672c\u6b21\u4e3a MES\/ERP \u573a\u666f\u6253\u9020\u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u7684\u591a\u5c42\u7ea7\u59d4\u6258\u62fc\u63a5\u5c01\u88c5WinForm \u4fe1\u606f\u4e2d\u95f4\u4ef6&#xff0c;\u6838\u5fc3\u56f4\u7ed5\u5de5\u5e8f\u6d41\u7a0b\u7684\u751f\u547d\u5468\u671f\u4e8b\u4ef6&#xff08;\u521d\u59cb\u5316\u3001\u542f\u52a8\u3001\u6267\u884c\u3001\u5b8c\u6210\u3001\u5f02\u5e38\u3001\u8df3\u8f6c\u7b49&#xff09;&#xff0c;\u8bbe\u8ba1\u591a\u7ed3\u6784\u62fc\u63a5\u7684\u59d4\u6258\u51fd\u6570\u4f53\u7cfb&#xff08;\u57fa\u7840\u59d4\u6258\u3001\u7ec4\u5408\u59d4\u6258\u3001\u6d41\u7a0b\u59d4\u6258&#xff09;&#xff0c;\u5b9e\u73b0\u4e1a\u52a1\u903b\u8f91\u4e0e UI \u5c42\u3001\u6570\u636e\u5c42\u5b8c\u5168\u89e3\u8026&#xff0c;\u540c\u65f6\u63d0\u4f9b WinForm \u53ef\u89c6\u5316\u754c\u9762 &#043; \u540e\u53f0\u670d\u52a1\u5316\u80fd\u529b&#xff0c;\u9002\u914d MES\/ERP \u7684\u5de5\u5e8f\u6d41\u7a0b\u6807\u51c6\u5316\u3001\u53ef\u914d\u7f6e\u3001\u9ad8\u6269\u5c55\u9700\u6c42\u3002<\/p>\n<h3>\u4e00\u3001\u6838\u5fc3\u8bbe\u8ba1\u7406\u5ff5\u4e0e\u67b6\u6784<\/h3>\n<h4>1. \u6838\u5fc3\u75db\u70b9\u4e0e\u89e3\u51b3\u65b9\u6848<\/h4>\n<table>\n<tr>MES\/ERP \u5de5\u5e8f\u6d41\u7a0b\u75db\u70b9\u6280\u672f\u89e3\u51b3\u65b9\u6848<\/tr>\n<tbody>\n<tr>\n<td>\u5de5\u5e8f\u6d41\u7a0b\u4e8b\u4ef6\u96f6\u6563&#xff0c;\u8026\u5408\u6027\u9ad8<\/td>\n<td>\u6309\u5de5\u5e8f\u751f\u547d\u5468\u671f\u5c01\u88c5\u6807\u51c6\u5316\u57fa\u7840\u4e8b\u4ef6\u59d4\u6258&#xff0c;\u8986\u76d6\u5168\u6d41\u7a0b\u8282\u70b9<\/td>\n<\/tr>\n<tr>\n<td>\u4e0d\u540c\u4ea7\u54c1 \/ \u4ea7\u7ebf\u5de5\u5e8f\u7ec4\u5408\u4e0d\u540c<\/td>\n<td>\u8bbe\u8ba1\u591a\u7ed3\u6784\u62fc\u63a5\u59d4\u6258&#xff08;\u57fa\u7840\u59d4\u6258\u7ec4\u5408\u6210\u5de5\u5e8f\u59d4\u6258&#xff0c;\u5de5\u5e8f\u59d4\u6258\u62fc\u63a5\u6210\u6d41\u7a0b\u59d4\u6258&#xff09;<\/td>\n<\/tr>\n<tr>\n<td>\u4e1a\u52a1\u903b\u8f91\u4fee\u6539\u9700\u6539\u52a8\u5927\u91cf\u4ee3\u7801<\/td>\n<td>\u59d4\u6258\u51fd\u6570\u52a8\u6001\u6ce8\u518c \/ \u6ce8\u9500 \/ \u7ec4\u5408&#xff0c;\u652f\u6301\u914d\u7f6e\u5316\u62fc\u63a5\u5de5\u5e8f\u6d41\u7a0b<\/td>\n<\/tr>\n<tr>\n<td>\u9700\u540c\u65f6\u652f\u6301\u53ef\u89c6\u5316\u64cd\u4f5c\u548c\u540e\u53f0\u8fd0\u884c<\/td>\n<td>WinForm \u754c\u9762 &#043;\u540e\u53f0\u670d\u52a1\u7ebf\u7a0b\u6c60&#xff0c;\u754c\u9762\u4e0e\u540e\u53f0\u89e3\u8026&#xff0c;\u652f\u6301\u6700\u5c0f\u5316\u5230\u6258\u76d8\u540e\u53f0\u8fd0\u884c<\/td>\n<\/tr>\n<tr>\n<td>\u4e0e MES\/ERP \u6570\u636e\u4ea4\u4e92\u590d\u6742<\/td>\n<td>\u5185\u7f6e\u6807\u51c6\u5316\u6570\u636e\u4ea4\u4e92\u63a5\u53e3&#xff0c;\u9002\u914d ERP \u5de5\u5355\u4e0b\u53d1\u3001MES \u5de5\u5e8f\u6267\u884c\u53cd\u9988<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4>2. \u6574\u4f53\u67b6\u6784&#xff08;\u4e09\u5c42\u59d4\u6258 &#043; \u4e09\u7aef\u89e3\u8026&#xff09;<\/h4>\n<p><img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"1288\" src=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/02\/20260205171643-6984d07ba2b2f.png\" width=\"2290\" \/><\/p>\n<h4>3. \u59d4\u6258\u51fd\u6570\u4f53\u7cfb&#xff08;\u591a\u7ed3\u6784\u62fc\u63a5\u8bbe\u8ba1&#xff09;<\/h4>\n<p>\u91c7\u7528\u4e09\u7ea7\u59d4\u6258\u62fc\u63a5&#xff0c;\u4ece\u539f\u5b50\u64cd\u4f5c\u5230\u5b8c\u6574\u6d41\u7a0b&#xff0c;\u652f\u6301\u65e0\u9650\u7ec4\u5408&#xff0c;\u9002\u914d\u4efb\u610f\u5de5\u5e8f\u7ed3\u6784&#xff1a;<\/p>\n<li>\u57fa\u7840\u4e8b\u4ef6\u59d4\u6258&#xff1a;\u5de5\u5e8f\u5355\u8282\u70b9\u7684\u539f\u5b50\u64cd\u4f5c&#xff08;\u5982 PLC \u6307\u4ee4\u53d1\u9001\u3001\u4ea7\u91cf\u4e0a\u62a5\u3001\u65e5\u5fd7\u8bb0\u5f55&#xff09;&#xff0c;\u662f\u6700\u7ec6\u7c92\u5ea6\u59d4\u6258&#xff1b;<\/li>\n<li>\u5de5\u5e8f\u7ec4\u5408\u59d4\u6258&#xff1a;\u5c06\u591a\u4e2a\u57fa\u7840\u59d4\u6258\u6309\u6267\u884c\u987a\u5e8f \/ \u6761\u4ef6\u62fc\u63a5&#xff0c;\u5c01\u88c5\u6210\u5355\u4e2a\u5de5\u5e8f\u7684\u5b8c\u6574\u6267\u884c\u903b\u8f91&#xff1b;<\/li>\n<li>\u6d41\u7a0b\u62fc\u63a5\u59d4\u6258&#xff1a;\u5c06\u591a\u4e2a\u5de5\u5e8f\u7ec4\u5408\u59d4\u6258\u6309\u5de5\u827a\u8def\u7ebf&#xff08;\u4e32\u884c \/ \u5e76\u884c \/ \u8df3\u8f6c&#xff09;\u62fc\u63a5&#xff0c;\u5c01\u88c5\u6210\u6574\u6761\u4ea7\u7ebf\u7684\u5de5\u5e8f\u6d41\u7a0b\u903b\u8f91\u3002<\/li>\n<h3>\u4e8c\u3001\u524d\u7f6e\u51c6\u5907<\/h3>\n<h4>1. \u73af\u5883\u4e0e NuGet \u4f9d\u8d56<\/h4>\n<ul>\n<li>\u5f00\u53d1\u73af\u5883&#xff1a;.NET Framework 4.8&#xff08;WinForm \u517c\u5bb9\u6027\u6700\u4f18&#xff09;\u3001Visual Studio 2022&#043;<\/li>\n<li>\u6838\u5fc3 NuGet \u5305&#xff1a;\n<p>bash<\/p>\n<p>\u8fd0\u884c<\/p>\n<p> Install-Package Dapper        # \u8f7b\u91cfORM&#xff0c;\u672c\u5730\/ERP\u6570\u636e\u4ea4\u4e92<br \/>\nInstall-Package System.Data.SQLite.Core  # \u672c\u5730\u5de5\u5e8f\/\u59d4\u6258\u914d\u7f6e\u5b58\u50a8<br \/>\nInstall-Package Newtonsoft.Json  # JSON\u5e8f\u5217\u5316&#xff0c;\u5de5\u827a\u8def\u7ebf\/\u59d4\u6258\u53c2\u6570\u89e3\u6790<br \/>\nInstall-Package System.Configuration  # \u914d\u7f6e\u6587\u4ef6\u7ba1\u7406&#xff0c;ERP\/MES\u63a5\u53e3\u914d\u7f6e\n <\/li>\n<\/ul>\n<h4>2. \u6838\u5fc3\u679a\u4e3e&#xff08;MES\/ERP \u5de5\u5e8f\u6d41\u7a0b\u6807\u51c6\u5316&#xff09;<\/h4>\n<p>\u5c01\u88c5 MES\/ERP \u901a\u7528\u7684\u5de5\u5e8f\u3001\u6d41\u7a0b\u3001\u4e8b\u4ef6\u679a\u4e3e&#xff0c;\u4f5c\u4e3a\u59d4\u6258\u4e8b\u4ef6\u548c\u4e1a\u52a1\u6267\u884c\u7684\u57fa\u7840\u6807\u8bc6&#xff1a;<\/p>\n<p>using System;<br \/>\nusing System.Collections.Generic;<br \/>\nusing System.Threading;<br \/>\nusing System.Threading.Tasks;<br \/>\nusing System.Windows.Forms;<br \/>\nusing Dapper;<br \/>\nusing System.Data.SQLite;<br \/>\nusing System.IO;<br \/>\nusing Newtonsoft.Json;<\/p>\n<p>#region MES\/ERP\u6838\u5fc3\u679a\u4e3e<br \/>\n\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u5de5\u5e8f\u6267\u884c\u72b6\u6001&#xff08;MES\/ERP\u901a\u7528\u6807\u51c6&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic enum ProcessStatus<br \/>\n{<br \/>\n    UnInitialized &#061; 0,  \/\/ \u672a\u521d\u59cb\u5316<br \/>\n    Ready &#061; 1,          \/\/ \u5c31\u7eea&#xff08;\u53ef\u6267\u884c&#xff09;<br \/>\n    Running &#061; 2,        \/\/ \u6267\u884c\u4e2d<br \/>\n    Completed &#061; 3,      \/\/ \u6267\u884c\u5b8c\u6210<br \/>\n    Exception &#061; 4,      \/\/ \u6267\u884c\u5f02\u5e38<br \/>\n    Paused &#061; 5,         \/\/ \u6682\u505c<br \/>\n    Skipped &#061; 6         \/\/ \u5de5\u5e8f\u8df3\u8fc7<br \/>\n}<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u5de5\u5e8f\u6d41\u7a0b\u4e8b\u4ef6\u7c7b\u578b&#xff08;\u751f\u547d\u5468\u671f\u5168\u8282\u70b9&#xff0c;\u59d4\u6258\u7ed1\u5b9a\u6838\u5fc3&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic enum ProcessFlowEventType<br \/>\n{<br \/>\n    ProcessInit,        \/\/ \u5de5\u5e8f\u521d\u59cb\u5316<br \/>\n    ProcessStart,       \/\/ \u5de5\u5e8f\u542f\u52a8<br \/>\n    ProcessExec,        \/\/ \u5de5\u5e8f\u6267\u884c\u4e2d&#xff08;\u5b9e\u65f6&#xff09;<br \/>\n    ProcessComplete,    \/\/ \u5de5\u5e8f\u5b8c\u6210<br \/>\n    ProcessException,   \/\/ \u5de5\u5e8f\u5f02\u5e38<br \/>\n    ProcessRecover,     \/\/ \u5f02\u5e38\u6062\u590d<br \/>\n    ProcessPause,       \/\/ \u5de5\u5e8f\u6682\u505c<br \/>\n    ProcessResume,      \/\/ \u5de5\u5e8f\u6062\u590d<br \/>\n    FlowStart,          \/\/ \u6d41\u7a0b\u6574\u4f53\u542f\u52a8<br \/>\n    FlowComplete,       \/\/ \u6d41\u7a0b\u6574\u4f53\u5b8c\u6210<br \/>\n    FlowException       \/\/ \u6d41\u7a0b\u6574\u4f53\u5f02\u5e38<br \/>\n}<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u59d4\u6258\u62fc\u63a5\u7c7b\u578b&#xff08;\u63a7\u5236\u591a\u59d4\u6258\u6267\u884c\u903b\u8f91&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic enum DelegateCombineType<br \/>\n{<br \/>\n    Serial,             \/\/ \u4e32\u884c\u6267\u884c&#xff08;\u6309\u6ce8\u518c\u987a\u5e8f&#xff09;<br \/>\n    Parallel,           \/\/ \u5e76\u884c\u6267\u884c&#xff08;\u540c\u65f6\u89e6\u53d1&#xff09;<br \/>\n    Conditional         \/\/ \u6761\u4ef6\u6267\u884c&#xff08;\u6ee1\u8db3\u524d\u7f6e\u6761\u4ef6\u624d\u6267\u884c&#xff09;<br \/>\n}<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u5de5\u5e8f\u6d41\u7a0b\u6267\u884c\u6a21\u5f0f<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic enum FlowExecuteMode<br \/>\n{<br \/>\n    WinFormUI,          \/\/ UI\u7ebf\u7a0b\u6267\u884c&#xff08;\u5e26\u8fdb\u5ea6\u53cd\u9988&#xff09;<br \/>\n    BackendService      \/\/ \u540e\u53f0\u670d\u52a1\u6267\u884c&#xff08;\u65e0UI\u963b\u585e&#xff0c;\u63a8\u8350&#xff09;<br \/>\n}<br \/>\n#endregion<\/p>\n<h3>\u4e09\u3001\u6838\u5fc3\u6a21\u578b&#xff08;MES\/ERP \u5de5\u5e8f\u6d41\u7a0b &#043; \u59d4\u6258\u5c01\u88c5&#xff09;<\/h3>\n<p>\u8bbe\u8ba1\u4e0e ERP\/MES \u65e0\u7f1d\u5bf9\u63a5\u7684\u4e1a\u52a1\u6a21\u578b&#xff0c;\u540c\u65f6\u5c01\u88c5\u59d4\u6258\u62fc\u63a5\u7684\u6838\u5fc3\u6a21\u578b&#xff0c;\u652f\u6301\u59d4\u6258\u7684\u6ce8\u518c\u3001\u7ec4\u5408\u3001\u5b58\u50a8\u3001\u6267\u884c&#xff1a;<\/p>\n<p>#region MES\/ERP\u4e1a\u52a1\u6838\u5fc3\u6a21\u578b<br \/>\n\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ ERP\u5de5\u5355\u4fe1\u606f&#xff08;\u4e0a\u6e38ERP\u4e0b\u53d1&#xff0c;MES\u6267\u884c\u4f9d\u636e&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic class ErpWorkOrder<br \/>\n{<br \/>\n    public string OrderNo { get; set; }          \/\/ \u5de5\u5355\u53f7&#xff08;ERP\u552f\u4e00\u6807\u8bc6&#xff09;<br \/>\n    public string ProductCode { get; set; }      \/\/ \u4ea7\u54c1\u7f16\u7801<br \/>\n    public string ProductName { get; set; }      \/\/ \u4ea7\u54c1\u540d\u79f0<br \/>\n    public int PlanQty { get; set; }             \/\/ \u8ba1\u5212\u4ea7\u91cf<br \/>\n    public int CompleteQty { get; set; }         \/\/ \u5b8c\u6210\u4ea7\u91cf<br \/>\n    public DateTime CreateTime { get; set; } &#061; DateTime.Now; \/\/ \u5de5\u5355\u521b\u5efa\u65f6\u95f4<br \/>\n    public string LineCode { get; set; }         \/\/ \u7ed1\u5b9a\u4ea7\u7ebf\u7f16\u7801<br \/>\n}<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u5de5\u827a\u8def\u7ebf&#xff08;ERP\u4e0b\u53d1&#xff0c;\u5b9a\u4e49\u5de5\u5e8f\u6267\u884c\u987a\u5e8f\u548c\u53c2\u6570&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic class ProcessRoute<br \/>\n{<br \/>\n    public string RouteId { get; set; } &#061; Guid.NewGuid().ToString(&#034;N&#034;); \/\/ \u5de5\u827a\u8def\u7ebfID<br \/>\n    public string OrderNo { get; set; }          \/\/ \u5173\u8054\u5de5\u5355\u53f7<br \/>\n    public List&lt;ProcessNode&gt; ProcessNodes { get; set; } &#061; new List&lt;ProcessNode&gt;(); \/\/ \u5de5\u5e8f\u8282\u70b9\u5217\u8868<br \/>\n    public string LineCode { get; set; }         \/\/ \u4ea7\u7ebf\u7f16\u7801<br \/>\n    public int CurrentProcessIndex { get; set; } &#061; 0; \/\/ \u5f53\u524d\u6267\u884c\u5de5\u5e8f\u7d22\u5f15<br \/>\n}<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u5de5\u5e8f\u8282\u70b9&#xff08;\u5de5\u827a\u8def\u7ebf\u6700\u5c0f\u5355\u4f4d&#xff0c;\u7ed1\u5b9a\u59d4\u6258\u7ec4\u5408&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic class ProcessNode<br \/>\n{<br \/>\n    public string ProcessId { get; set; } &#061; Guid.NewGuid().ToString(&#034;N&#034;); \/\/ \u5de5\u5e8fID<br \/>\n    public string ProcessCode { get; set; }      \/\/ \u5de5\u5e8f\u7f16\u7801&#xff08;MES\/ERP\u6807\u51c6\u5316&#xff09;<br \/>\n    public string ProcessName { get; set; }      \/\/ \u5de5\u5e8f\u540d\u79f0<br \/>\n    public int SortNo { get; set; }              \/\/ \u5de5\u5e8f\u6267\u884c\u987a\u5e8f<br \/>\n    public ProcessStatus Status { get; set; } &#061; ProcessStatus.UnInitialized; \/\/ \u5de5\u5e8f\u72b6\u6001<br \/>\n    public string DelegateCombineId { get; set; } \/\/ \u7ed1\u5b9a\u7684\u59d4\u6258\u7ec4\u5408ID<br \/>\n    public Dictionary&lt;string, string&gt; Params { get; set; } &#061; new Dictionary&lt;string, string&gt;(); \/\/ \u5de5\u5e8f\u53c2\u6570&#xff08;\u5982PLC\u5730\u5740\u3001\u76ee\u6807\u4ea7\u91cf&#xff09;<br \/>\n    public int CompleteQty { get; set; } &#061; 0;    \/\/ \u672c\u5de5\u5e8f\u5b8c\u6210\u4ea7\u91cf<br \/>\n}<br \/>\n#endregion<\/p>\n<p>#region \u59d4\u6258\u62fc\u63a5\u6838\u5fc3\u6a21\u578b<br \/>\n\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u57fa\u7840\u59d4\u6258\u4fe1\u606f&#xff08;\u539f\u5b50\u64cd\u4f5c&#xff0c;\u53ef\u72ec\u7acb\u6267\u884c\/\u7ec4\u5408\u6267\u884c&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic class BaseDelegateInfo<br \/>\n{<br \/>\n    public string DelegateId { get; set; } &#061; Guid.NewGuid().ToString(&#034;N&#034;); \/\/ \u59d4\u6258ID<br \/>\n    public string DelegateName { get; set; }      \/\/ \u59d4\u6258\u540d\u79f0&#xff08;\u5982&#034;PLC\u53d1\u9001\u542f\u52a8\u6307\u4ee4&#034;&#xff09;<br \/>\n    public ProcessFlowEventType BindEvent { get; set; } \/\/ \u7ed1\u5b9a\u7684\u5de5\u5e8f\u6d41\u7a0b\u4e8b\u4ef6<br \/>\n    public string MethodName { get; set; }        \/\/ \u59d4\u6258\u7ed1\u5b9a\u7684\u65b9\u6cd5\u540d&#xff08;\u53cd\u5c04\/\u76f4\u63a5\u7ed1\u5b9a&#xff09;<br \/>\n    public Delegate DelegateFunc { get; set; }    \/\/ \u5b9e\u9645\u59d4\u6258\u51fd\u6570<br \/>\n    public Dictionary&lt;string, string&gt; Params { get; set; } &#061; new Dictionary&lt;string, string&gt;(); \/\/ \u59d4\u6258\u6267\u884c\u53c2\u6570<br \/>\n}<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u59d4\u6258\u7ec4\u5408\u4fe1\u606f&#xff08;\u591a\u4e2a\u57fa\u7840\u59d4\u6258\u62fc\u63a5\u6210\u5de5\u5e8f\u7ea7\u903b\u8f91&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic class DelegateCombineInfo<br \/>\n{<br \/>\n    public string CombineId { get; set; } &#061; Guid.NewGuid().ToString(&#034;N&#034;); \/\/ \u7ec4\u5408ID&#xff08;\u5de5\u5e8f\u8282\u70b9\u7ed1\u5b9a&#xff09;<br \/>\n    public string CombineName { get; set; }       \/\/ \u7ec4\u5408\u540d\u79f0&#xff08;\u5982&#034;\u9884\u88c5\u5de5\u5e8f\u5b8c\u6574\u6267\u884c\u903b\u8f91&#034;&#xff09;<br \/>\n    public DelegateCombineType CombineType { get; set; } \/\/ \u62fc\u63a5\u7c7b\u578b&#xff08;\u4e32\u884c\/\u5e76\u884c\/\u6761\u4ef6&#xff09;<br \/>\n    public List&lt;BaseDelegateInfo&gt; BaseDelegates { get; set; } &#061; new List&lt;BaseDelegateInfo&gt;(); \/\/ \u5305\u542b\u7684\u57fa\u7840\u59d4\u6258<br \/>\n    public string ConditionalExpr { get; set; } &#061; &#034;true&#034;; \/\/ \u6761\u4ef6\u6267\u884c\u8868\u8fbe\u5f0f&#xff08;\u5982&#034;CompleteQty&gt;0&#034;&#xff09;<br \/>\n}<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u6d41\u7a0b\u59d4\u6258\u62fc\u63a5&#xff08;\u591a\u4e2a\u5de5\u5e8f\u7ec4\u5408\u59d4\u6258\u62fc\u63a5\u6210\u4ea7\u7ebf\u6d41\u7a0b\u903b\u8f91&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic class FlowDelegateCombineInfo<br \/>\n{<br \/>\n    public string FlowCombineId { get; set; } &#061; Guid.NewGuid().ToString(&#034;N&#034;); \/\/ \u6d41\u7a0b\u7ec4\u5408ID<br \/>\n    public string FlowName { get; set; }          \/\/ \u6d41\u7a0b\u540d\u79f0&#xff08;\u5982&#034;B\u4ea7\u54c1\u603b\u88c5\u6d41\u7a0b&#034;&#xff09;<br \/>\n    public string RouteId { get; set; }          \/\/ \u5173\u8054\u5de5\u827a\u8def\u7ebfID<br \/>\n    public List&lt;DelegateCombineInfo&gt; ProcessCombines { get; set; } &#061; new List&lt;DelegateCombineInfo&gt;(); \/\/ \u5de5\u5e8f\u7ec4\u5408\u59d4\u6258\u5217\u8868<br \/>\n    public FlowExecuteMode ExecuteMode { get; set; } &#061; FlowExecuteMode.BackendService; \/\/ \u6267\u884c\u6a21\u5f0f<br \/>\n}<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u59d4\u6258\u6267\u884c\u4e0a\u4e0b\u6587&#xff08;\u4f20\u9012\u7ed9\u6240\u6709\u59d4\u6258&#xff0c;\u5305\u542b\u5168\u91cf\u4e1a\u52a1\u4fe1\u606f&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic class DelegateExecuteContext<br \/>\n{<br \/>\n    public ErpWorkOrder WorkOrder { get; set; }   \/\/ \u5173\u8054\u5de5\u5355<br \/>\n    public ProcessRoute ProcessRoute { get; set; } \/\/ \u5173\u8054\u5de5\u827a\u8def\u7ebf<br \/>\n    public ProcessNode CurrentProcess { get; set; } \/\/ \u5f53\u524d\u6267\u884c\u5de5\u5e8f<br \/>\n    public ProcessFlowEventType CurrentEvent { get; set; } \/\/ \u5f53\u524d\u89e6\u53d1\u4e8b\u4ef6<br \/>\n    public CancellationToken Cts { get; set; }   \/\/ \u53d6\u6d88\u4ee4\u724c&#xff08;\u7528\u4e8e\u7ec8\u6b62\u6267\u884c&#xff09;<br \/>\n    public string ErrorMsg { get; set; }          \/\/ \u6267\u884c\u9519\u8bef\u4fe1\u606f<br \/>\n    public bool IsSuccess { get; set; } &#061; true;   \/\/ \u6267\u884c\u662f\u5426\u6210\u529f<br \/>\n}<br \/>\n#endregion<\/p>\n<h3>\u56db\u3001\u6838\u5fc3\u59d4\u6258\u5b9a\u4e49&#xff08;MES\/ERP \u5de5\u5e8f\u6d41\u7a0b\u4e13\u7528&#xff09;<\/h3>\n<p>\u5c01\u88c5\u4e0e\u5de5\u5e8f\u6d41\u7a0b\u4e8b\u4ef6\u5f3a\u7ed1\u5b9a\u7684\u901a\u7528\u59d4\u6258\u7c7b\u578b&#xff0c;\u8986\u76d6\u65e0\u8fd4\u56de\u503c \/ \u6709\u8fd4\u56de\u503c \/ \u5f02\u6b65\u6267\u884c\u4e09\u79cd\u573a\u666f&#xff0c;\u9002\u914d MES\/ERP \u7684\u5b9e\u65f6\u6027\u548c\u590d\u6742\u6027\u8981\u6c42&#xff1a;<\/p>\n<p>csharp<\/p>\n<p>\u8fd0\u884c<\/p>\n<p>#region MES\/ERP\u5de5\u5e8f\u6d41\u7a0b\u4e13\u7528\u59d4\u6258\u5b9a\u4e49<br \/>\n\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u57fa\u7840\u65e0\u8fd4\u56de\u503c\u59d4\u6258&#xff08;\u6700\u5e38\u7528&#xff0c;\u5982\u65e5\u5fd7\u3001PLC\u6307\u4ee4\u3001\u72b6\u6001\u66f4\u65b0&#xff09;<br \/>\n\/\/\/ \u5165\u53c2&#xff1a;\u59d4\u6258\u6267\u884c\u4e0a\u4e0b\u6587<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\n\/\/\/ &lt;param name&#061;&#034;context&#034;&gt;\u59d4\u6258\u6267\u884c\u4e0a\u4e0b\u6587&lt;\/param&gt;<br \/>\npublic delegate void ProcessFlowBaseDelegate(DelegateExecuteContext context);<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u6709\u8fd4\u56de\u503c\u59d4\u6258&#xff08;\u5982\u6570\u636e\u67e5\u8be2\u3001\u53c2\u6570\u6821\u9a8c\u3001\u6761\u4ef6\u5224\u65ad&#xff09;<br \/>\n\/\/\/ \u5165\u53c2&#xff1a;\u59d4\u6258\u6267\u884c\u4e0a\u4e0b\u6587&#xff1b;\u8fd4\u56de\u503c&#xff1a;\u6267\u884c\u7ed3\u679c&#xff08;bool&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\n\/\/\/ &lt;param name&#061;&#034;context&#034;&gt;\u59d4\u6258\u6267\u884c\u4e0a\u4e0b\u6587&lt;\/param&gt;<br \/>\n\/\/\/ &lt;returns&gt;\u6267\u884c\u7ed3\u679c&lt;\/returns&gt;<br \/>\npublic delegate bool ProcessFlowFuncDelegate(DelegateExecuteContext context);<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u5f02\u6b65\u59d4\u6258&#xff08;\u5982\u8017\u65f6\u64cd\u4f5c&#xff1a;PLC\u901a\u4fe1\u3001ERP\u6570\u636e\u4ea4\u4e92\u3001\u8bbe\u5907\u6821\u51c6&#xff09;<br \/>\n\/\/\/ \u5165\u53c2&#xff1a;\u59d4\u6258\u6267\u884c\u4e0a\u4e0b\u6587&#043;\u53d6\u6d88\u4ee4\u724c&#xff1b;\u8fd4\u56de\u503c&#xff1a;\u5f02\u6b65\u4efb\u52a1<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\n\/\/\/ &lt;param name&#061;&#034;context&#034;&gt;\u59d4\u6258\u6267\u884c\u4e0a\u4e0b\u6587&lt;\/param&gt;<br \/>\n\/\/\/ &lt;param name&#061;&#034;cancellationToken&#034;&gt;\u53d6\u6d88\u4ee4\u724c&lt;\/param&gt;<br \/>\n\/\/\/ &lt;returns&gt;\u5f02\u6b65\u4efb\u52a1&lt;\/returns&gt;<br \/>\npublic delegate Task ProcessFlowAsyncDelegate(DelegateExecuteContext context, CancellationToken cancellationToken);<br \/>\n#endregion<\/p>\n<h3>\u4e94\u3001\u6838\u5fc3\u670d\u52a1\u5c01\u88c5&#xff08;\u59d4\u6258\u7ba1\u7406 &#043; \u6d41\u7a0b\u5f15\u64ce &#043; \u540e\u53f0\u670d\u52a1&#xff09;<\/h3>\n<h4>1. \u672c\u5730\u6570\u636e\u5e93\u52a9\u624b&#xff08;SQLite&#xff09;<\/h4>\n<p>\u5c01\u88c5\u5de5\u5e8f\u6d41\u7a0b \/ \u59d4\u6258\u914d\u7f6e \/ \u5de5\u5355\u4fe1\u606f\u7684\u672c\u5730\u5b58\u50a8&#xff0c;\u5b9e\u73b0\u914d\u7f6e\u6301\u4e45\u5316&#xff0c;\u7a0b\u5e8f\u91cd\u542f\u540e\u59d4\u6258\u62fc\u63a5\u5173\u7cfb\u3001\u5de5\u5e8f\u72b6\u6001\u4e0d\u4e22\u5931&#xff1a;<\/p>\n<p>csharp<\/p>\n<p>\u8fd0\u884c<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ SQLite\u672c\u5730\u6570\u636e\u5e93\u52a9\u624b&#xff08;\u5355\u4f8b&#xff09;- MES\/ERP\u5de5\u5e8f\u59d4\u6258\u4e13\u7528<br \/>\n\/\/\/ \u5b58\u50a8&#xff1a;\u5de5\u5355\u3001\u5de5\u827a\u8def\u7ebf\u3001\u5de5\u5e8f\u8282\u70b9\u3001\u59d4\u6258\u914d\u7f6e\u3001\u59d4\u6258\u7ec4\u5408<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic sealed class MesErpDbHelper<br \/>\n{<br \/>\n    \/\/ \u5355\u4f8b\u5b9e\u4f8b<br \/>\n    private static readonly Lazy&lt;MesErpDbHelper&gt; _instance &#061; new Lazy&lt;MesErpDbHelper&gt;(() &#061;&gt; new MesErpDbHelper());<br \/>\n    public static MesErpDbHelper Instance &#061;&gt; _instance.Value;<\/p>\n<p>    \/\/ \u6570\u636e\u5e93\u8def\u5f84&#xff08;\u7a0b\u5e8f\u76ee\u5f55\u4e0bMesErpProcessDelegate.db&#xff09;<br \/>\n    private readonly string _dbPath;<br \/>\n    private readonly string _connStr;<br \/>\n    private readonly object _dbLock &#061; new object();<\/p>\n<p>    private MesErpDbHelper()<br \/>\n    {<br \/>\n        _dbPath &#061; Path.Combine(AppDomain.CurrentDomain.BaseDirectory, &#034;MesErpProcessDelegate.db&#034;);<br \/>\n        \/\/ \u5f00\u542fWAL\u6a21\u5f0f&#xff0c;\u63d0\u5347\u540e\u53f0\u670d\u52a1\u591a\u7ebf\u7a0b\u8bfb\u5199\u6027\u80fd<br \/>\n        _connStr &#061; $&#034;Data Source&#061;{_dbPath};Version&#061;3;Journal Mode&#061;WAL;Pooling&#061;true;Max Pool Size&#061;20;&#034;;<br \/>\n        \/\/ \u521d\u59cb\u5316\u6570\u636e\u5e93\u8868\u7ed3\u6784<br \/>\n        InitDbTables();<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u521d\u59cb\u5316\u6570\u636e\u5e93\u8868&#xff08;\u4e0d\u5b58\u5728\u5219\u521b\u5efa&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void InitDbTables()<br \/>\n    {<br \/>\n        lock (_dbLock)<br \/>\n        {<br \/>\n            if (!File.Exists(_dbPath)) SQLiteConnection.CreateFile(_dbPath);<\/p>\n<p>            using (var conn &#061; new SQLiteConnection(_connStr))<br \/>\n            {<br \/>\n                conn.Open();<br \/>\n                \/\/ 1. ERP\u5de5\u5355\u8868<br \/>\n                conn.Execute($&#064;&#034;CREATE TABLE IF NOT EXISTS ErpWorkOrder (<br \/>\n                    OrderNo TEXT PRIMARY KEY, ProductCode TEXT, ProductName TEXT, PlanQty INT,<br \/>\n                    CompleteQty INT, CreateTime DATETIME, LineCode TEXT)&#034;);<\/p>\n<p>                \/\/ 2. \u5de5\u827a\u8def\u7ebf\u8868<br \/>\n                conn.Execute($&#064;&#034;CREATE TABLE IF NOT EXISTS ProcessRoute (<br \/>\n                    RouteId TEXT PRIMARY KEY, OrderNo TEXT, LineCode TEXT, CurrentProcessIndex INT,<br \/>\n                    ProcessNodes TEXT, FOREIGN KEY(OrderNo) REFERENCES ErpWorkOrder(OrderNo))&#034;);<\/p>\n<p>                \/\/ 3. \u59d4\u6258\u7ec4\u5408\u8868&#xff08;\u5de5\u5e8f\u7ea7&#xff09;<br \/>\n                conn.Execute($&#064;&#034;CREATE TABLE IF NOT EXISTS DelegateCombine (<br \/>\n                    CombineId TEXT PRIMARY KEY, CombineName TEXT, CombineType INT,<br \/>\n                    ConditionalExpr TEXT, BaseDelegates TEXT)&#034;);<\/p>\n<p>                \/\/ 4. \u6d41\u7a0b\u59d4\u6258\u7ec4\u5408\u8868&#xff08;\u4ea7\u7ebf\u7ea7&#xff09;<br \/>\n                conn.Execute($&#064;&#034;CREATE TABLE IF NOT EXISTS FlowDelegateCombine (<br \/>\n                    FlowCombineId TEXT PRIMARY KEY, FlowName TEXT, RouteId TEXT,<br \/>\n                    ExecuteMode INT, ProcessCombines TEXT,<br \/>\n                    FOREIGN KEY(RouteId) REFERENCES ProcessRoute(RouteId))&#034;);<\/p>\n<p>                LogHelper.Info(&#034;MES\/ERP\u672c\u5730\u6570\u636e\u5e93\u521d\u59cb\u5316\u5b8c\u6210&#xff0c;\u8def\u5f84&#xff1a;&#034; &#043; _dbPath);<br \/>\n            }<br \/>\n        }<br \/>\n    }<\/p>\n<p>    #region \u901a\u7528CRUD\u65b9\u6cd5<br \/>\n    public T QueryFirst&lt;T&gt;(string sql, object param &#061; null)<br \/>\n    {<br \/>\n        lock (_dbLock)<br \/>\n        {<br \/>\n            using (var conn &#061; new SQLiteConnection(_connStr))<br \/>\n            {<br \/>\n                conn.Open();<br \/>\n                return conn.QueryFirstOrDefault&lt;T&gt;(sql, param);<br \/>\n            }<br \/>\n        }<br \/>\n    }<\/p>\n<p>    public List&lt;T&gt; QueryList&lt;T&gt;(string sql, object param &#061; null)<br \/>\n    {<br \/>\n        lock (_dbLock)<br \/>\n        {<br \/>\n            using (var conn &#061; new SQLiteConnection(_connStr))<br \/>\n            {<br \/>\n                conn.Open();<br \/>\n                return conn.Query&lt;T&gt;(sql, param).ToList();<br \/>\n            }<br \/>\n        }<br \/>\n    }<\/p>\n<p>    public int Execute(string sql, object param &#061; null)<br \/>\n    {<br \/>\n        lock (_dbLock)<br \/>\n        {<br \/>\n            using (var conn &#061; new SQLiteConnection(_connStr))<br \/>\n            {<br \/>\n                conn.Open();<br \/>\n                using (var tran &#061; conn.BeginTransaction())<br \/>\n                {<br \/>\n                    try<br \/>\n                    {<br \/>\n                        var rows &#061; conn.Execute(sql, param, tran);<br \/>\n                        tran.Commit();<br \/>\n                        return rows;<br \/>\n                    }<br \/>\n                    catch (Exception ex)<br \/>\n                    {<br \/>\n                        tran.Rollback();<br \/>\n                        LogHelper.Error(&#034;SQL\u6267\u884c\u5931\u8d25&#xff1a;&#034; &#043; sql, ex);<br \/>\n                        throw;<br \/>\n                    }<br \/>\n                }<br \/>\n            }<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u4fdd\u5b58\u5bf9\u8c61\u5230\u6570\u636e\u5e93&#xff08;JSON\u5e8f\u5217\u5316\u590d\u6742\u5b57\u6bb5&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public void SaveObject&lt;T&gt;(string tableName, string keyCol, T model)<br \/>\n    {<br \/>\n        var jsonProps &#061; model.GetType().GetProperties()<br \/>\n            .Where(p &#061;&gt; p.PropertyType.IsGenericType || p.PropertyType &#061;&#061; typeof(Dictionary&lt;string, string&gt;))<br \/>\n            .Select(p &#061;&gt; p.Name).ToList();<\/p>\n<p>        var sqlCols &#061; string.Join(&#034;,&#034;, model.GetType().GetProperties()<br \/>\n            .Where(p &#061;&gt; !jsonProps.Contains(p.Name))<br \/>\n            .Select(p &#061;&gt; p.Name));<br \/>\n        var sqlVals &#061; string.Join(&#034;,&#034;, model.GetType().GetProperties()<br \/>\n            .Where(p &#061;&gt; !jsonProps.Contains(p.Name))<br \/>\n            .Select(p &#061;&gt; $&#034;&#064;{p.Name}&#034;));<\/p>\n<p>        \/\/ \u5148\u5220\u9664\u539f\u6709\u6570\u636e<br \/>\n        Execute($&#034;DELETE FROM {tableName} WHERE {keyCol} &#061; &#064;{keyCol}&#034;, model);<br \/>\n        \/\/ \u63d2\u5165\u65b0\u6570\u636e&#xff08;\u590d\u6742\u5b57\u6bb5JSON\u5e8f\u5217\u5316&#xff09;<br \/>\n        var insertSql &#061; $&#034;INSERT INTO {tableName} ({sqlCols},{string.Join(&#034;,&#034;, jsonProps)}) VALUES ({sqlVals},{string.Join(&#034;,&#034;, jsonProps.Select(p &#061;&gt; $&#034;&#064;{p}&#034;))})&#034;;<br \/>\n        var param &#061; new DynamicParameters(model);<br \/>\n        foreach (var prop in jsonProps)<br \/>\n        {<br \/>\n            var val &#061; model.GetType().GetProperty(prop).GetValue(model);<br \/>\n            param.Add(prop, JsonConvert.SerializeObject(val));<br \/>\n        }<br \/>\n        Execute(insertSql, param);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u8bfb\u53d6\u5bf9\u8c61\u4ece\u6570\u636e\u5e93&#xff08;JSON\u53cd\u5e8f\u5217\u5316\u590d\u6742\u5b57\u6bb5&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public T GetObject&lt;T&gt;(string tableName, string keyCol, string keyVal)<br \/>\n    {<br \/>\n        var sql &#061; $&#034;SELECT * FROM {tableName} WHERE {keyCol} &#061; &#064;KeyVal&#034;;<br \/>\n        var dynamicModel &#061; QueryFirst&lt;dynamic&gt;(sql, new { KeyVal &#061; keyVal });<br \/>\n        if (dynamicModel &#061;&#061; null) return default;<\/p>\n<p>        var model &#061; JsonConvert.DeserializeObject&lt;T&gt;(JsonConvert.SerializeObject(dynamicModel));<br \/>\n        var jsonProps &#061; typeof(T).GetProperties()<br \/>\n            .Where(p &#061;&gt; p.PropertyType.IsGenericType || p.PropertyType &#061;&#061; typeof(Dictionary&lt;string, string&gt;))<br \/>\n            .Select(p &#061;&gt; p.Name).ToList();<\/p>\n<p>        foreach (var prop in jsonProps)<br \/>\n        {<br \/>\n            var jsonVal &#061; dynamicModel.GetType().GetProperty(prop)?.GetValue(dynamicModel)?.ToString();<br \/>\n            if (!string.IsNullOrEmpty(jsonVal))<br \/>\n            {<br \/>\n                var val &#061; JsonConvert.DeserializeObject(jsonVal, typeof(T).GetProperty(prop).PropertyType);<br \/>\n                typeof(T).GetProperty(prop).SetValue(model, val);<br \/>\n            }<br \/>\n        }<br \/>\n        return model;<br \/>\n    }<br \/>\n    #endregion<br \/>\n}<\/p>\n<h4>2. \u59d4\u6258\u7ba1\u7406\u4e2d\u5fc3&#xff08;\u6838\u5fc3&#xff0c;\u591a\u7ed3\u6784\u62fc\u63a5 &#043; \u52a8\u6001\u6ce8\u518c&#xff09;<\/h4>\n<p>\u5b9e\u73b0\u57fa\u7840\u59d4\u6258\u7684\u6ce8\u518c \/ \u6ce8\u9500\u3001\u59d4\u6258\u7684\u7ec4\u5408\u62fc\u63a5\u3001\u59d4\u6258\u7684\u6301\u4e45\u5316 \/ \u52a0\u8f7d&#xff0c;\u662f\u6574\u4e2a\u4e2d\u95f4\u4ef6\u7684\u59d4\u6258\u5927\u8111&#xff0c;\u652f\u6301\u53ef\u89c6\u5316\u914d\u7f6e\u548c\u52a8\u6001\u4fee\u6539&#xff1a;<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ \u5de5\u5e8f\u6d41\u7a0b\u59d4\u6258\u7ba1\u7406\u4e2d\u5fc3&#xff08;\u5355\u4f8b&#xff09;- MES\/ERP\u4e13\u7528<br \/>\n\/\/\/ \u6838\u5fc3&#xff1a;\u57fa\u7840\u59d4\u6258\u6ce8\u518c\u3001\u591a\u7ed3\u6784\u62fc\u63a5\u3001\u59d4\u6258\u52a0\u8f7d\/\u4fdd\u5b58\u3001\u59d4\u6258\u6267\u884c<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic sealed class ProcessDelegateManager<br \/>\n{<br \/>\n    \/\/ \u5355\u4f8b\u5b9e\u4f8b<br \/>\n    private static readonly Lazy&lt;ProcessDelegateManager&gt; _instance &#061; new Lazy&lt;ProcessDelegateManager&gt;(() &#061;&gt; new ProcessDelegateManager());<br \/>\n    public static ProcessDelegateManager Instance &#061;&gt; _instance.Value;<\/p>\n<p>    \/\/ \u672c\u5730\u6570\u636e\u5e93\u52a9\u624b<br \/>\n    private readonly MesErpDbHelper _dbHelper &#061; MesErpDbHelper.Instance;<br \/>\n    \/\/ \u57fa\u7840\u59d4\u6258\u6ce8\u518c\u8868&#xff08;\u5185\u5b58\u7f13\u5b58&#xff0c;\u63d0\u9ad8\u6267\u884c\u6548\u7387&#xff09;<br \/>\n    private readonly Dictionary&lt;string, BaseDelegateInfo&gt; _baseDelegateDict &#061; new Dictionary&lt;string, BaseDelegateInfo&gt;();<br \/>\n    \/\/ \u59d4\u6258\u7ec4\u5408\u6ce8\u518c\u8868<br \/>\n    private readonly Dictionary&lt;string, DelegateCombineInfo&gt; _combineDelegateDict &#061; new Dictionary&lt;string, DelegateCombineInfo&gt;();<br \/>\n    \/\/ \u6d41\u7a0b\u59d4\u6258\u7ec4\u5408\u6ce8\u518c\u8868<br \/>\n    private readonly Dictionary&lt;string, FlowDelegateCombineInfo&gt; _flowCombineDelegateDict &#061; new Dictionary&lt;string, FlowDelegateCombineInfo&gt;();<br \/>\n    \/\/ \u7ebf\u7a0b\u5b89\u5168\u9501<br \/>\n    private readonly object _delegateLock &#061; new object();<\/p>\n<p>    private ProcessDelegateManager()<br \/>\n    {<br \/>\n        \/\/ \u7a0b\u5e8f\u542f\u52a8\u65f6\u52a0\u8f7d\u672c\u5730\u5b58\u50a8\u7684\u59d4\u6258\u914d\u7f6e<br \/>\n        LoadAllDelegatesFromDb();<br \/>\n        \/\/ \u6ce8\u518cMES\/ERP\u901a\u7528\u57fa\u7840\u59d4\u6258&#xff08;\u539f\u5b50\u64cd\u4f5c&#xff0c;\u53ef\u76f4\u63a5\u4f7f\u7528&#xff09;<br \/>\n        RegisterDefaultBaseDelegates();<br \/>\n    }<\/p>\n<p>    #region \u57fa\u7840\u59d4\u6258\u64cd\u4f5c&#xff08;\u6ce8\u518c\/\u6ce8\u9500\/\u52a0\u8f7d\/\u4fdd\u5b58&#xff09;<br \/>\n    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6ce8\u518c\u57fa\u7840\u59d4\u6258&#xff08;\u652f\u6301\u4efb\u610f\u7c7b\u578b\u59d4\u6258&#xff1a;Base\/Func\/Async&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public void RegisterBaseDelegate(BaseDelegateInfo delegateInfo)<br \/>\n    {<br \/>\n        lock (_delegateLock)<br \/>\n        {<br \/>\n            if (_baseDelegateDict.ContainsKey(delegateInfo.DelegateId))<br \/>\n                _baseDelegateDict[delegateInfo.DelegateId] &#061; delegateInfo;<br \/>\n            else<br \/>\n                _baseDelegateDict.Add(delegateInfo.DelegateId, delegateInfo);<br \/>\n            LogHelper.Info($&#034;\u57fa\u7840\u59d4\u6258\u6ce8\u518c\u6210\u529f&#xff1a;{delegateInfo.DelegateName}&#xff08;ID&#xff1a;{delegateInfo.DelegateId}&#xff09;&#034;);<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6ce8\u9500\u57fa\u7840\u59d4\u6258<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public void UnRegisterBaseDelegate(string delegateId)<br \/>\n    {<br \/>\n        lock (_delegateLock)<br \/>\n        {<br \/>\n            if (_baseDelegateDict.ContainsKey(delegateId))<br \/>\n            {<br \/>\n                _baseDelegateDict.Remove(delegateId);<br \/>\n                LogHelper.Info($&#034;\u57fa\u7840\u59d4\u6258\u6ce8\u9500\u6210\u529f&#xff1a;ID&#061;{delegateId}&#034;);<br \/>\n            }<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6ce8\u518cMES\/ERP\u901a\u7528\u9ed8\u8ba4\u57fa\u7840\u59d4\u6258&#xff08;\u5f00\u7bb1\u5373\u7528&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void RegisterDefaultBaseDelegates()<br \/>\n    {<br \/>\n        \/\/ 1. \u5de5\u5e8f\u72b6\u6001\u66f4\u65b0\u59d4\u6258&#xff08;\u7ed1\u5b9aProcessStart\u4e8b\u4ef6&#xff09;<br \/>\n        RegisterBaseDelegate(new BaseDelegateInfo<br \/>\n        {<br \/>\n            DelegateName &#061; &#034;\u5de5\u5e8f\u72b6\u6001\u66f4\u65b0\u4e3a\u6267\u884c\u4e2d&#034;,<br \/>\n            BindEvent &#061; ProcessFlowEventType.ProcessStart,<br \/>\n            DelegateFunc &#061; new ProcessFlowBaseDelegate(DefaultDelegates.UpdateProcessStatusToRunning)<br \/>\n        });<\/p>\n<p>        \/\/ 2. \u4ea7\u91cf\u4e0a\u62a5\u59d4\u6258&#xff08;\u7ed1\u5b9aProcessComplete\u4e8b\u4ef6&#xff09;<br \/>\n        RegisterBaseDelegate(new BaseDelegateInfo<br \/>\n        {<br \/>\n            DelegateName &#061; &#034;\u5de5\u5e8f\u4ea7\u91cf\u4e0a\u62a5\u5230\u672c\u5730\u5e93&#034;,<br \/>\n            BindEvent &#061; ProcessFlowEventType.ProcessComplete,<br \/>\n            DelegateFunc &#061; new ProcessFlowBaseDelegate(DefaultDelegates.ReportProcessYieldToDb)<br \/>\n        });<\/p>\n<p>        \/\/ 3. ERP\u5de5\u5355\u8fdb\u5ea6\u66f4\u65b0\u59d4\u6258&#xff08;\u5f02\u6b65&#xff0c;\u7ed1\u5b9aFlowComplete\u4e8b\u4ef6&#xff09;<br \/>\n        RegisterBaseDelegate(new BaseDelegateInfo<br \/>\n        {<br \/>\n            DelegateName &#061; &#034;\u5f02\u6b65\u66f4\u65b0ERP\u5de5\u5355\u8fdb\u5ea6&#034;,<br \/>\n            BindEvent &#061; ProcessFlowEventType.FlowComplete,<br \/>\n            DelegateFunc &#061; new ProcessFlowAsyncDelegate(DefaultDelegates.AsyncUpdateErpOrderProgress)<br \/>\n        });<\/p>\n<p>        \/\/ 4. \u5de5\u5e8f\u5f02\u5e38\u65e5\u5fd7\u59d4\u6258&#xff08;\u7ed1\u5b9aProcessException\u4e8b\u4ef6&#xff09;<br \/>\n        RegisterBaseDelegate(new BaseDelegateInfo<br \/>\n        {<br \/>\n            DelegateName &#061; &#034;\u5de5\u5e8f\u5f02\u5e38\u65e5\u5fd7\u8bb0\u5f55&#034;,<br \/>\n            BindEvent &#061; ProcessFlowEventType.ProcessException,<br \/>\n            DelegateFunc &#061; new ProcessFlowBaseDelegate(DefaultDelegates.RecordProcessExceptionLog)<br \/>\n        });<br \/>\n    }<br \/>\n    #endregion<\/p>\n<p>    #region \u59d4\u6258\u7ec4\u5408\u64cd\u4f5c&#xff08;\u5de5\u5e8f\u7ea7\/\u6d41\u7a0b\u7ea7\u62fc\u63a5&#xff09;<br \/>\n    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u62fc\u63a5\u5de5\u5e8f\u7ea7\u59d4\u6258\u7ec4\u5408&#xff08;\u591a\u4e2a\u57fa\u7840\u59d4\u6258\u6309\u89c4\u5219\u7ec4\u5408&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public DelegateCombineInfo CombineProcessDelegates(string combineName, DelegateCombineType combineType, List&lt;BaseDelegateInfo&gt; baseDelegates, string conditionalExpr &#061; &#034;true&#034;)<br \/>\n    {<br \/>\n        lock (_delegateLock)<br \/>\n        {<br \/>\n            var combine &#061; new DelegateCombineInfo<br \/>\n            {<br \/>\n                CombineName &#061; combineName,<br \/>\n                CombineType &#061; combineType,<br \/>\n                BaseDelegates &#061; baseDelegates,<br \/>\n                ConditionalExpr &#061; conditionalExpr<br \/>\n            };<br \/>\n            _combineDelegateDict.Add(combine.CombineId, combine);<br \/>\n            \/\/ \u6301\u4e45\u5316\u5230\u672c\u5730\u5e93<br \/>\n            _dbHelper.SaveObject(&#034;DelegateCombine&#034;, &#034;CombineId&#034;, combine);<br \/>\n            LogHelper.Info($&#034;\u5de5\u5e8f\u59d4\u6258\u7ec4\u5408\u62fc\u63a5\u6210\u529f&#xff1a;{combineName}&#xff08;\u5305\u542b{baseDelegates.Count}\u4e2a\u57fa\u7840\u59d4\u6258&#xff09;&#034;);<br \/>\n            return combine;<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u62fc\u63a5\u4ea7\u7ebf\u7ea7\u6d41\u7a0b\u59d4\u6258&#xff08;\u591a\u4e2a\u5de5\u5e8f\u7ec4\u5408\u59d4\u6258\u62fc\u63a5\u6210\u5b8c\u6574\u6d41\u7a0b&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public FlowDelegateCombineInfo CombineFlowDelegates(string flowName, string routeId, List&lt;DelegateCombineInfo&gt; processCombines, FlowExecuteMode executeMode)<br \/>\n    {<br \/>\n        lock (_delegateLock)<br \/>\n        {<br \/>\n            var flowCombine &#061; new FlowDelegateCombineInfo<br \/>\n            {<br \/>\n                FlowName &#061; flowName,<br \/>\n                RouteId &#061; routeId,<br \/>\n                ProcessCombines &#061; processCombines,<br \/>\n                ExecuteMode &#061; executeMode<br \/>\n            };<br \/>\n            _flowCombineDelegateDict.Add(flowCombine.FlowCombineId, flowCombine);<br \/>\n            \/\/ \u6301\u4e45\u5316\u5230\u672c\u5730\u5e93<br \/>\n            _dbHelper.SaveObject(&#034;FlowDelegateCombine&#034;, &#034;FlowCombineId&#034;, flowCombine);<br \/>\n            LogHelper.Info($&#034;\u6d41\u7a0b\u59d4\u6258\u62fc\u63a5\u6210\u529f&#xff1a;{flowName}&#xff08;\u5305\u542b{processCombines.Count}\u4e2a\u5de5\u5e8f\u7ec4\u5408&#xff09;&#034;);<br \/>\n            return flowCombine;<br \/>\n        }<br \/>\n    }<br \/>\n    #endregion<\/p>\n<p>    #region \u59d4\u6258\u52a0\u8f7d\/\u4fdd\u5b58<br \/>\n    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u4ece\u672c\u5730\u5e93\u52a0\u8f7d\u6240\u6709\u59d4\u6258\u914d\u7f6e<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void LoadAllDelegatesFromDb()<br \/>\n    {<br \/>\n        \/\/ \u52a0\u8f7d\u5de5\u5e8f\u59d4\u6258\u7ec4\u5408<br \/>\n        var processCombines &#061; _dbHelper.QueryList&lt;dynamic&gt;(&#034;SELECT CombineId FROM DelegateCombine&#034;);<br \/>\n        foreach (var item in processCombines)<br \/>\n        {<br \/>\n            var combine &#061; _dbHelper.GetObject&lt;DelegateCombineInfo&gt;(&#034;DelegateCombine&#034;, &#034;CombineId&#034;, item.CombineId);<br \/>\n            _combineDelegateDict.Add(combine.CombineId, combine);<br \/>\n        }<\/p>\n<p>        \/\/ \u52a0\u8f7d\u6d41\u7a0b\u59d4\u6258\u7ec4\u5408<br \/>\n        var flowCombines &#061; _dbHelper.QueryList&lt;dynamic&gt;(&#034;SELECT FlowCombineId FROM FlowDelegateCombine&#034;);<br \/>\n        foreach (var item in flowCombines)<br \/>\n        {<br \/>\n            var flowCombine &#061; _dbHelper.GetObject&lt;FlowDelegateCombineInfo&gt;(&#034;FlowDelegateCombine&#034;, &#034;FlowCombineId&#034;, item.FlowCombineId);<br \/>\n            _flowCombineDelegateDict.Add(flowCombine.FlowCombineId, flowCombine);<br \/>\n        }<\/p>\n<p>        LogHelper.Info($&#034;\u4ece\u672c\u5730\u5e93\u52a0\u8f7d\u59d4\u6258\u914d\u7f6e\u5b8c\u6210&#xff1a;\u5de5\u5e8f\u7ec4\u5408{_combineDelegateDict.Count}\u4e2a&#xff0c;\u6d41\u7a0b\u7ec4\u5408{_flowCombineDelegateDict.Count}\u4e2a&#034;);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6839\u636e\u5de5\u5e8fID\u83b7\u53d6\u7ed1\u5b9a\u7684\u59d4\u6258\u7ec4\u5408<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public DelegateCombineInfo GetProcessCombineByProcessId(string processId, ProcessRoute route)<br \/>\n    {<br \/>\n        var processNode &#061; route.ProcessNodes.FirstOrDefault(p &#061;&gt; p.ProcessId &#061;&#061; processId);<br \/>\n        if (processNode &#061;&#061; null || string.IsNullOrEmpty(processNode.DelegateCombineId))<br \/>\n            throw new KeyNotFoundException($&#034;\u5de5\u5e8f{processId}\u672a\u7ed1\u5b9a\u59d4\u6258\u7ec4\u5408&#034;);<\/p>\n<p>        return _combineDelegateDict.TryGetValue(processNode.DelegateCombineId, out var combine) ? combine : null;<br \/>\n    }<br \/>\n    #endregion<\/p>\n<p>    #region \u59d4\u6258\u6267\u884c&#xff08;\u6838\u5fc3&#xff0c;\u652f\u6301\u591a\u7c7b\u578b\/\u591a\u62fc\u63a5\u89c4\u5219&#xff09;<br \/>\n    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6267\u884c\u59d4\u6258\u7ec4\u5408&#xff08;\u81ea\u52a8\u8bc6\u522b\u4e32\u884c\/\u5e76\u884c\/\u6761\u4ef6&#xff0c;\u652f\u6301\u5f02\u6b65&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public async Task ExecuteCombineDelegate(DelegateCombineInfo combine, DelegateExecuteContext context)<br \/>\n    {<br \/>\n        if (combine &#061;&#061; null || !combine.BaseDelegates.Any()) return;<br \/>\n        \/\/ \u6761\u4ef6\u6267\u884c&#xff1a;\u5148\u5224\u65ad\u6761\u4ef6\u662f\u5426\u6ee1\u8db3<br \/>\n        if (!EvaluateConditionalExpr(combine.ConditionalExpr, context))<br \/>\n        {<br \/>\n            LogHelper.Info($&#034;\u59d4\u6258\u7ec4\u5408{combine.CombineName}\u6761\u4ef6\u4e0d\u6ee1\u8db3&#xff0c;\u8df3\u8fc7\u6267\u884c&#034;);<br \/>\n            return;<br \/>\n        }<\/p>\n<p>        LogHelper.Info($&#034;\u5f00\u59cb\u6267\u884c\u59d4\u6258\u7ec4\u5408&#xff1a;{combine.CombineName}&#xff08;\u7c7b\u578b&#xff1a;{combine.CombineType}&#xff09;&#034;);<br \/>\n        switch (combine.CombineType)<br \/>\n        {<br \/>\n            case DelegateCombineType.Serial:<br \/>\n                \/\/ \u4e32\u884c\u6267\u884c&#xff1a;\u6309\u987a\u5e8f\u6267\u884c\u6bcf\u4e2a\u57fa\u7840\u59d4\u6258<br \/>\n                foreach (var baseDel in combine.BaseDelegates)<br \/>\n                {<br \/>\n                    if (context.Cts.IsCancellationRequested) break;<br \/>\n                    await ExecuteBaseDelegate(baseDel, context);<br \/>\n                }<br \/>\n                break;<br \/>\n            case DelegateCombineType.Parallel:<br \/>\n                \/\/ \u5e76\u884c\u6267\u884c&#xff1a;\u6240\u6709\u59d4\u6258\u540c\u65f6\u6267\u884c&#xff0c;\u7b49\u5f85\u5168\u90e8\u5b8c\u6210<br \/>\n                var parallelTasks &#061; new List&lt;Task&gt;();<br \/>\n                foreach (var baseDel in combine.BaseDelegates)<br \/>\n                {<br \/>\n                    if (context.Cts.IsCancellationRequested) break;<br \/>\n                    parallelTasks.Add(ExecuteBaseDelegate(baseDel, context));<br \/>\n                }<br \/>\n                await Task.WhenAll(parallelTasks);<br \/>\n                break;<br \/>\n            case DelegateCombineType.Conditional:<br \/>\n                \/\/ \u6761\u4ef6\u6267\u884c&#xff1a;\u6bcf\u4e2a\u59d4\u6258\u72ec\u7acb\u5224\u65ad\u6761\u4ef6&#xff08;\u7b80\u5316\u7248&#xff0c;\u53ef\u6269\u5c55&#xff09;<br \/>\n                foreach (var baseDel in combine.BaseDelegates)<br \/>\n                {<br \/>\n                    if (context.Cts.IsCancellationRequested) break;<br \/>\n                    var expr &#061; baseDel.Params.ContainsKey(&#034;ConditionalExpr&#034;) ? baseDel.Params[&#034;ConditionalExpr&#034;] : &#034;true&#034;;<br \/>\n                    if (EvaluateConditionalExpr(expr, context))<br \/>\n                        await ExecuteBaseDelegate(baseDel, context);<br \/>\n                }<br \/>\n                break;<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6267\u884c\u57fa\u7840\u59d4\u6258&#xff08;\u81ea\u52a8\u8bc6\u522b\u59d4\u6258\u7c7b\u578b&#xff1a;Base\/Func\/Async&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private async Task ExecuteBaseDelegate(BaseDelegateInfo baseDel, DelegateExecuteContext context)<br \/>\n    {<br \/>\n        if (baseDel.DelegateFunc &#061;&#061; null)<br \/>\n        {<br \/>\n            LogHelper.Warn($&#034;\u57fa\u7840\u59d4\u6258{baseDel.DelegateName}\u672a\u7ed1\u5b9a\u51fd\u6570&#xff0c;\u8df3\u8fc7&#034;);<br \/>\n            return;<br \/>\n        }<\/p>\n<p>        try<br \/>\n        {<br \/>\n            LogHelper.Info($&#034;\u6267\u884c\u57fa\u7840\u59d4\u6258&#xff1a;{baseDel.DelegateName}&#034;);<br \/>\n            \/\/ \u6309\u59d4\u6258\u7c7b\u578b\u5206\u53d1\u6267\u884c<br \/>\n            if (baseDel.DelegateFunc is ProcessFlowBaseDelegate baseFunc)<br \/>\n            {<br \/>\n                baseFunc(context); \/\/ \u540c\u6b65\u65e0\u8fd4\u56de\u503c<br \/>\n            }<br \/>\n            else if (baseDel.DelegateFunc is ProcessFlowFuncDelegate func)<br \/>\n            {<br \/>\n                context.IsSuccess &#061; func(context); \/\/ \u540c\u6b65\u6709\u8fd4\u56de\u503c<br \/>\n            }<br \/>\n            else if (baseDel.DelegateFunc is ProcessFlowAsyncDelegate asyncFunc)<br \/>\n            {<br \/>\n                await asyncFunc(context, context.Cts); \/\/ \u5f02\u6b65\u6267\u884c<br \/>\n            }<br \/>\n            else<br \/>\n            {<br \/>\n                throw new NotSupportedException($&#034;\u4e0d\u652f\u6301\u7684\u59d4\u6258\u7c7b\u578b&#xff1a;{baseDel.DelegateFunc.GetType().Name}&#034;);<br \/>\n            }<br \/>\n        }<br \/>\n        catch (Exception ex)<br \/>\n        {<br \/>\n            context.IsSuccess &#061; false;<br \/>\n            context.ErrorMsg &#061; ex.Message;<br \/>\n            LogHelper.Error($&#034;\u57fa\u7840\u59d4\u6258{baseDel.DelegateName}\u6267\u884c\u5931\u8d25&#xff1a;{ex.Message}&#034;, ex);<br \/>\n            throw;<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u89e3\u6790\u6761\u4ef6\u6267\u884c\u8868\u8fbe\u5f0f&#xff08;\u7b80\u5316\u7248&#xff0c;\u53ef\u6269\u5c55\u4e3a\u8868\u8fbe\u5f0f\u5f15\u64ce&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private bool EvaluateConditionalExpr(string expr, DelegateExecuteContext context)<br \/>\n    {<br \/>\n        \/\/ \u57fa\u7840\u5b9e\u73b0&#xff1a;\u652f\u6301\u7b80\u5355\u8868\u8fbe\u5f0f\u5982&#034;true&#034;\u3001&#034;CompleteQty&gt;0&#034;\u3001&#034;Status&#061;&#061;2&#034;<br \/>\n        if (expr.Trim().ToLower() &#061;&#061; &#034;true&#034;) return true;<br \/>\n        if (expr.Trim().ToLower() &#061;&#061; &#034;false&#034;) return false;<\/p>\n<p>        \/\/ \u6269\u5c55\u70b9&#xff1a;\u96c6\u6210\u8868\u8fbe\u5f0f\u5f15\u64ce&#xff08;\u5982NCalc&#xff09;\u652f\u6301\u590d\u6742\u8868\u8fbe\u5f0f<br \/>\n        try<br \/>\n        {<br \/>\n            \/\/ \u793a\u4f8b&#xff1a;\u89e3\u6790CompleteQty&gt;0<br \/>\n            if (expr.Contains(&#034;CompleteQty&gt;&#034;))<br \/>\n            {<br \/>\n                var qty &#061; int.Parse(expr.Split(&#039;&gt;&#039;)[1].Trim());<br \/>\n                return context.CurrentProcess.CompleteQty &gt; qty;<br \/>\n            }<br \/>\n            return true;<br \/>\n        }<br \/>\n        catch<br \/>\n        {<br \/>\n            return false;<br \/>\n        }<br \/>\n    }<br \/>\n    #endregion<br \/>\n}<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ MES\/ERP\u901a\u7528\u9ed8\u8ba4\u59d4\u6258\u5b9e\u73b0&#xff08;\u539f\u5b50\u64cd\u4f5c&#xff0c;\u5f00\u7bb1\u5373\u7528&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic static class DefaultDelegates<br \/>\n{<br \/>\n    private static readonly MesErpDbHelper _dbHelper &#061; MesErpDbHelper.Instance;<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u57fa\u7840\u59d4\u6258&#xff1a;\u66f4\u65b0\u5de5\u5e8f\u72b6\u6001\u4e3a\u6267\u884c\u4e2d<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public static void UpdateProcessStatusToRunning(DelegateExecuteContext context)<br \/>\n    {<br \/>\n        context.CurrentProcess.Status &#061; ProcessStatus.Running;<br \/>\n        \/\/ \u6301\u4e45\u5316\u5de5\u5e8f\u72b6\u6001<br \/>\n        _dbHelper.SaveObject(&#034;ProcessRoute&#034;, &#034;RouteId&#034;, context.ProcessRoute);<br \/>\n        LogHelper.Info($&#034;\u5de5\u5e8f{context.CurrentProcess.ProcessCode}\u72b6\u6001\u66f4\u65b0\u4e3a&#xff1a;\u6267\u884c\u4e2d&#034;);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u57fa\u7840\u59d4\u6258&#xff1a;\u5de5\u5e8f\u4ea7\u91cf\u4e0a\u62a5\u5230\u672c\u5730\u5e93<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public static void ReportProcessYieldToDb(DelegateExecuteContext context)<br \/>\n    {<br \/>\n        context.WorkOrder.CompleteQty &#061; context.ProcessRoute.ProcessNodes.Sum(p &#061;&gt; p.CompleteQty);<br \/>\n        \/\/ \u66f4\u65b0\u5de5\u5355\u548c\u5de5\u827a\u8def\u7ebf<br \/>\n        _dbHelper.SaveObject(&#034;ErpWorkOrder&#034;, &#034;OrderNo&#034;, context.WorkOrder);<br \/>\n        _dbHelper.SaveObject(&#034;ProcessRoute&#034;, &#034;RouteId&#034;, context.ProcessRoute);<br \/>\n        LogHelper.Info($&#034;\u5de5\u5e8f{context.CurrentProcess.ProcessCode}\u4ea7\u91cf\u4e0a\u62a5&#xff1a;{context.CurrentProcess.CompleteQty}\u4ef6&#xff0c;\u5de5\u5355\u7d2f\u8ba1&#xff1a;{context.WorkOrder.CompleteQty}\u4ef6&#034;);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u5f02\u6b65\u59d4\u6258&#xff1a;\u66f4\u65b0ERP\u5de5\u5355\u8fdb\u5ea6&#xff08;\u6a21\u62dfERP\u63a5\u53e3\u8c03\u7528&#xff0c;\u5b9e\u9645\u66ff\u6362\u4e3a\u771f\u5b9eERP\u4ea4\u4e92\u4ee3\u7801&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public static async Task AsyncUpdateErpOrderProgress(DelegateExecuteContext context, CancellationToken ct)<br \/>\n    {<br \/>\n        \/\/ \u6a21\u62dfERP\u63a5\u53e3\u7f51\u7edc\u8bf7\u6c42&#xff08;\u8017\u65f62\u79d2&#xff09;<br \/>\n        await Task.Delay(2000, ct);<br \/>\n        \/\/ \u5b9e\u9645\u573a\u666f&#xff1a;\u8c03\u7528ERP\u7684WebAPI\/WS\u66f4\u65b0\u5de5\u5355\u8fdb\u5ea6<br \/>\n        var progress &#061; (double)context.WorkOrder.CompleteQty \/ context.WorkOrder.PlanQty * 100;<br \/>\n        LogHelper.Info($&#034;\u5f02\u6b65\u66f4\u65b0ERP\u5de5\u5355{context.WorkOrder.OrderNo}\u8fdb\u5ea6&#xff1a;{progress:F2}%&#034;);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u57fa\u7840\u59d4\u6258&#xff1a;\u8bb0\u5f55\u5de5\u5e8f\u5f02\u5e38\u65e5\u5fd7<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public static void RecordProcessExceptionLog(DelegateExecuteContext context)<br \/>\n    {<br \/>\n        LogHelper.Error($&#034;\u3010\u5de5\u5e8f\u5f02\u5e38\u3011{context.CurrentProcess.ProcessCode}&#xff1a;{context.ErrorMsg}&#xff0c;\u5de5\u5355&#xff1a;{context.WorkOrder.OrderNo}&#034;);<br \/>\n        context.CurrentProcess.Status &#061; ProcessStatus.Exception;<br \/>\n        _dbHelper.SaveObject(&#034;ProcessRoute&#034;, &#034;RouteId&#034;, context.ProcessRoute);<br \/>\n    }<br \/>\n}<\/p>\n<h4>3. \u5de5\u5e8f\u6d41\u7a0b\u5f15\u64ce&#xff08;\u59d4\u6258\u6267\u884c &#043; \u6d41\u7a0b\u63a7\u5236&#xff09;<\/h4>\n<p>\u5b9e\u73b0\u5de5\u5e8f\u6d41\u7a0b\u7684\u751f\u547d\u5468\u671f\u7ba1\u7406&#xff0c;\u89e6\u53d1\u5bf9\u5e94\u4e8b\u4ef6\u5e76\u5206\u53d1\u5230\u59d4\u6258\u7ba1\u7406\u4e2d\u5fc3\u6267\u884c\u62fc\u63a5\u540e\u7684\u59d4\u6258&#xff0c;\u652f\u6301\u4e32\u884c \/ \u5e76\u884c\u5de5\u5e8f\u3001\u5f02\u5e38\u5904\u7406\u3001\u6d41\u7a0b\u6682\u505c \/ \u6062\u590d \/ \u7ec8\u6b62&#xff1a;<\/p>\n<p>csharp<\/p>\n<p>\u8fd0\u884c<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ MES\/ERP\u5de5\u5e8f\u6d41\u7a0b\u5f15\u64ce&#xff08;\u5355\u4f8b&#xff09;<br \/>\n\/\/\/ \u6838\u5fc3&#xff1a;\u6d41\u7a0b\u751f\u547d\u5468\u671f\u7ba1\u7406\u3001\u4e8b\u4ef6\u89e6\u53d1\u3001\u59d4\u6258\u5206\u53d1\u3001\u6d41\u7a0b\u63a7\u5236&#xff08;\u6682\u505c\/\u6062\u590d\/\u7ec8\u6b62&#xff09;<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic sealed class ProcessFlowEngine<br \/>\n{<br \/>\n    \/\/ \u5355\u4f8b\u5b9e\u4f8b<br \/>\n    private static readonly Lazy&lt;ProcessFlowEngine&gt; _instance &#061; new Lazy&lt;ProcessFlowEngine&gt;(() &#061;&gt; new ProcessFlowEngine());<br \/>\n    public static ProcessFlowEngine Instance &#061;&gt; _instance.Value;<\/p>\n<p>    \/\/ \u4f9d\u8d56\u670d\u52a1<br \/>\n    private readonly ProcessDelegateManager _delegateManager &#061; ProcessDelegateManager.Instance;<br \/>\n    private readonly MesErpDbHelper _dbHelper &#061; MesErpDbHelper.Instance;<br \/>\n    \/\/ \u6b63\u5728\u6267\u884c\u7684\u6d41\u7a0b&#xff08;\u6d41\u7a0bID-&gt;\u53d6\u6d88\u4ee4\u724c\u6e90&#xff09;<br \/>\n    private readonly Dictionary&lt;string, CancellationTokenSource&gt; _runningFlows &#061; new Dictionary&lt;string, CancellationTokenSource&gt;();<br \/>\n    private readonly object _flowLock &#061; new object();<\/p>\n<p>    #region \u6d41\u7a0b\u751f\u547d\u5468\u671f\u64cd\u4f5c&#xff08;\u542f\u52a8\/\u6682\u505c\/\u6062\u590d\/\u7ec8\u6b62&#xff09;<br \/>\n    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u542f\u52a8\u5de5\u5e8f\u6d41\u7a0b&#xff08;\u6838\u5fc3\u5165\u53e3&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    \/\/\/ &lt;param name&#061;&#034;flowCombine&#034;&gt;\u6d41\u7a0b\u59d4\u6258\u7ec4\u5408&lt;\/param&gt;<br \/>\n    \/\/\/ &lt;param name&#061;&#034;workOrder&#034;&gt;ERP\u5de5\u5355&lt;\/param&gt;<br \/>\n    \/\/\/ &lt;param name&#061;&#034;route&#034;&gt;\u5de5\u827a\u8def\u7ebf&lt;\/param&gt;<br \/>\n    \/\/\/ &lt;returns&gt;\u6267\u884c\u4efb\u52a1&lt;\/returns&gt;<br \/>\n    public async Task StartFlowAsync(FlowDelegateCombineInfo flowCombine, ErpWorkOrder workOrder, ProcessRoute route)<br \/>\n    {<br \/>\n        lock (_flowLock)<br \/>\n        {<br \/>\n            if (_runningFlows.ContainsKey(flowCombine.FlowCombineId))<br \/>\n                throw new InvalidOperationException($&#034;\u6d41\u7a0b{flowCombine.FlowName}\u5df2\u5728\u6267\u884c\u4e2d&#034;);<\/p>\n<p>            \/\/ \u521b\u5efa\u53d6\u6d88\u4ee4\u724c\u6e90&#xff08;\u7528\u4e8e\u6682\u505c\/\u7ec8\u6b62\u6d41\u7a0b&#xff09;<br \/>\n            var cts &#061; new CancellationTokenSource();<br \/>\n            _runningFlows.Add(flowCombine.FlowCombineId, cts);<br \/>\n            \/\/ \u540e\u53f0\u6267\u884c\u6d41\u7a0b&#xff0c;\u4e0d\u963b\u585eUI<br \/>\n            Task.Run(async () &#061;&gt; await ExecuteFlowAsync(flowCombine, workOrder, route, cts.Token), cts.Token);<br \/>\n        }<\/p>\n<p>        LogHelper.Info($&#034;\u6d41\u7a0b{flowCombine.FlowName}\u542f\u52a8\u6210\u529f&#xff08;\u5de5\u5355&#xff1a;{workOrder.OrderNo}&#xff09;&#xff0c;\u6267\u884c\u6a21\u5f0f&#xff1a;{flowCombine.ExecuteMode}&#034;);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6682\u505c\u5de5\u5e8f\u6d41\u7a0b<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public void PauseFlow(string flowCombineId)<br \/>\n    {<br \/>\n        lock (_flowLock)<br \/>\n        {<br \/>\n            if (_runningFlows.TryGetValue(flowCombineId, out var cts))<br \/>\n            {<br \/>\n                cts.Cancel();<br \/>\n                LogHelper.Info($&#034;\u6d41\u7a0b{flowCombineId}\u5df2\u6682\u505c&#034;);<br \/>\n            }<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u7ec8\u6b62\u5de5\u5e8f\u6d41\u7a0b<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public void StopFlow(string flowCombineId)<br \/>\n    {<br \/>\n        lock (_flowLock)<br \/>\n        {<br \/>\n            if (_runningFlows.TryGetValue(flowCombineId, out var cts))<br \/>\n            {<br \/>\n                cts.Cancel();<br \/>\n                _runningFlows.Remove(flowCombineId);<br \/>\n                cts.Dispose();<br \/>\n                LogHelper.Info($&#034;\u6d41\u7a0b{flowCombineId}\u5df2\u7ec8\u6b62&#034;);<br \/>\n            }<br \/>\n        }<br \/>\n    }<br \/>\n    #endregion<\/p>\n<p>    #region \u6d41\u7a0b\u6267\u884c\u6838\u5fc3\u903b\u8f91<br \/>\n    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6267\u884c\u5b8c\u6574\u5de5\u5e8f\u6d41\u7a0b&#xff08;\u89e6\u53d1\u4e8b\u4ef6&#043;\u6267\u884c\u59d4\u6258&#043;\u6d41\u7a0b\u63a7\u5236&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private async Task ExecuteFlowAsync(FlowDelegateCombineInfo flowCombine, ErpWorkOrder workOrder, ProcessRoute route, CancellationToken ct)<br \/>\n    {<br \/>\n        var flowContext &#061; new DelegateExecuteContext<br \/>\n        {<br \/>\n            WorkOrder &#061; workOrder,<br \/>\n            ProcessRoute &#061; route,<br \/>\n            CurrentEvent &#061; ProcessFlowEventType.FlowStart,<br \/>\n            Cts &#061; ct<br \/>\n        };<\/p>\n<p>        try<br \/>\n        {<br \/>\n            \/\/ \u89e6\u53d1\u6d41\u7a0b\u542f\u52a8\u4e8b\u4ef6<br \/>\n            await TriggerFlowEvent(ProcessFlowEventType.FlowStart, flowContext);<\/p>\n<p>            \/\/ \u6309\u5de5\u827a\u8def\u7ebf\u987a\u5e8f\u6267\u884c\u6bcf\u4e2a\u5de5\u5e8f<br \/>\n            while (route.CurrentProcessIndex &lt; route.ProcessNodes.Count &amp;&amp; !ct.IsCancellationRequested)<br \/>\n            {<br \/>\n                var currentProcess &#061; route.ProcessNodes[route.CurrentProcessIndex];<br \/>\n                flowContext.CurrentProcess &#061; currentProcess;<br \/>\n                flowContext.CurrentEvent &#061; ProcessFlowEventType.ProcessInit;<\/p>\n<p>                \/\/ \u6267\u884c\u5355\u4e2a\u5de5\u5e8f\u7684\u5b8c\u6574\u751f\u547d\u5468\u671f<br \/>\n                await ExecuteSingleProcessAsync(currentProcess, flowCombine, flowContext, ct);<\/p>\n<p>                \/\/ \u5de5\u5e8f\u6267\u884c\u6210\u529f&#xff0c;\u5207\u6362\u5230\u4e0b\u4e00\u4e2a\u5de5\u5e8f<br \/>\n                if (currentProcess.Status &#061;&#061; ProcessStatus.Completed || currentProcess.Status &#061;&#061; ProcessStatus.Skipped)<br \/>\n                {<br \/>\n                    route.CurrentProcessIndex&#043;&#043;;<br \/>\n                    _dbHelper.SaveObject(&#034;ProcessRoute&#034;, &#034;RouteId&#034;, route);<br \/>\n                }<br \/>\n                \/\/ \u5de5\u5e8f\u5f02\u5e38&#xff0c;\u7ec8\u6b62\u6d41\u7a0b<br \/>\n                else if (currentProcess.Status &#061;&#061; ProcessStatus.Exception)<br \/>\n                {<br \/>\n                    throw new Exception($&#034;\u5de5\u5e8f{currentProcess.ProcessCode}\u6267\u884c\u5f02\u5e38&#xff0c;\u6d41\u7a0b\u7ec8\u6b62&#034;);<br \/>\n                }<br \/>\n            }<\/p>\n<p>            \/\/ \u6240\u6709\u5de5\u5e8f\u6267\u884c\u5b8c\u6210&#xff0c;\u89e6\u53d1\u6d41\u7a0b\u5b8c\u6210\u4e8b\u4ef6<br \/>\n            if (!ct.IsCancellationRequested)<br \/>\n            {<br \/>\n                flowContext.CurrentEvent &#061; ProcessFlowEventType.FlowComplete;<br \/>\n                await TriggerFlowEvent(ProcessFlowEventType.FlowComplete, flowContext);<br \/>\n                LogHelper.Info($&#034;\u6d41\u7a0b{flowCombine.FlowName}\u6267\u884c\u5b8c\u6210&#xff08;\u5de5\u5355&#xff1a;{workOrder.OrderNo}&#xff09;&#xff0c;\u5b8c\u6210\u4ea7\u91cf&#xff1a;{workOrder.CompleteQty}\u4ef6&#034;);<br \/>\n            }<br \/>\n        }<br \/>\n        catch (OperationCanceledException)<br \/>\n        {<br \/>\n            LogHelper.Info($&#034;\u6d41\u7a0b{flowCombine.FlowName}\u5df2\u6682\u505c\/\u7ec8\u6b62&#xff08;\u5de5\u5355&#xff1a;{workOrder.OrderNo}&#xff09;&#034;);<br \/>\n        }<br \/>\n        catch (Exception ex)<br \/>\n        {<br \/>\n            flowContext.CurrentEvent &#061; ProcessFlowEventType.FlowException;<br \/>\n            flowContext.IsSuccess &#061; false;<br \/>\n            flowContext.ErrorMsg &#061; ex.Message;<br \/>\n            await TriggerFlowEvent(ProcessFlowEventType.FlowException, flowContext);<br \/>\n            LogHelper.Error($&#034;\u6d41\u7a0b{flowCombine.FlowName}\u6267\u884c\u5931\u8d25&#xff08;\u5de5\u5355&#xff1a;{workOrder.OrderNo}&#xff09;&#xff1a;{ex.Message}&#034;, ex);<br \/>\n        }<br \/>\n        finally<br \/>\n        {<br \/>\n            lock (_flowLock)<br \/>\n            {<br \/>\n                if (_runningFlows.ContainsKey(flowCombine.FlowCombineId))<br \/>\n                {<br \/>\n                    _runningFlows[flowCombine.FlowCombineId].Dispose();<br \/>\n                    _runningFlows.Remove(flowCombine.FlowCombineId);<br \/>\n                }<br \/>\n            }<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6267\u884c\u5355\u4e2a\u5de5\u5e8f\u7684\u5b8c\u6574\u751f\u547d\u5468\u671f&#xff08;\u521d\u59cb\u5316-&gt;\u542f\u52a8-&gt;\u6267\u884c-&gt;\u5b8c\u6210&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private async Task ExecuteSingleProcessAsync(ProcessNode process, FlowDelegateCombineInfo flowCombine, DelegateExecuteContext context, CancellationToken ct)<br \/>\n    {<br \/>\n        \/\/ 1. \u5de5\u5e8f\u521d\u59cb\u5316\u4e8b\u4ef6<br \/>\n        context.CurrentEvent &#061; ProcessFlowEventType.ProcessInit;<br \/>\n        process.Status &#061; ProcessStatus.Ready;<br \/>\n        await TriggerFlowEvent(ProcessFlowEventType.ProcessInit, context);<br \/>\n        await Task.Delay(500, ct); \/\/ \u6a21\u62df\u5de5\u5e8f\u521d\u59cb\u5316\u8017\u65f6<\/p>\n<p>        \/\/ 2. \u5de5\u5e8f\u542f\u52a8\u4e8b\u4ef6<br \/>\n        context.CurrentEvent &#061; ProcessFlowEventType.ProcessStart;<br \/>\n        await TriggerFlowEvent(ProcessFlowEventType.ProcessStart, context);<\/p>\n<p>        \/\/ 3. \u6267\u884c\u5de5\u5e8f\u7ed1\u5b9a\u7684\u59d4\u6258\u7ec4\u5408&#xff08;\u6838\u5fc3&#xff09;<br \/>\n        var processCombine &#061; _delegateManager.GetProcessCombineByProcessId(process.ProcessId, context.ProcessRoute);<br \/>\n        await _delegateManager.ExecuteCombineDelegate(processCombine, context);<\/p>\n<p>        \/\/ 4. \u5de5\u5e8f\u5b8c\u6210\u4e8b\u4ef6<br \/>\n        if (context.IsSuccess &amp;&amp; !ct.IsCancellationRequested)<br \/>\n        {<br \/>\n            context.CurrentEvent &#061; ProcessFlowEventType.ProcessComplete;<br \/>\n            process.Status &#061; ProcessStatus.Completed;<br \/>\n            await TriggerFlowEvent(ProcessFlowEventType.ProcessComplete, context);<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u89e6\u53d1\u5de5\u5e8f\u6d41\u7a0b\u4e8b\u4ef6&#xff08;\u5206\u53d1\u5230\u59d4\u6258\u7ba1\u7406\u4e2d\u5fc3\u6267\u884c\u7ed1\u5b9a\u7684\u59d4\u6258&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private async Task TriggerFlowEvent(ProcessFlowEventType eventType, DelegateExecuteContext context)<br \/>\n    {<br \/>\n        context.CurrentEvent &#061; eventType;<br \/>\n        LogHelper.Info($&#034;\u89e6\u53d1\u5de5\u5e8f\u6d41\u7a0b\u4e8b\u4ef6&#xff1a;{eventType}&#xff08;\u5de5\u5e8f&#xff1a;{context.CurrentProcess?.ProcessCode ?? &#034;\u6d41\u7a0b\u6574\u4f53&#034;}&#xff09;&#034;);<\/p>\n<p>        \/\/ \u6267\u884c\u6240\u6709\u7ed1\u5b9a\u8be5\u4e8b\u4ef6\u7684\u57fa\u7840\u59d4\u6258&#xff08;\u7b80\u5316\u7248&#xff0c;\u53ef\u6269\u5c55\u4e3a\u6309\u6d41\u7a0b\/\u5de5\u5e8f\u8fc7\u6ee4&#xff09;<br \/>\n        var allBaseDelegates &#061; _delegateManager.GetType().GetField(&#034;_baseDelegateDict&#034;, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)<br \/>\n            .GetValue(_delegateManager) as Dictionary&lt;string, BaseDelegateInfo&gt;;<\/p>\n<p>        var bindDelegates &#061; allBaseDelegates?.Values.Where(d &#061;&gt; d.BindEvent &#061;&#061; eventType).ToList() ?? new List&lt;BaseDelegateInfo&gt;();<br \/>\n        foreach (var del in bindDelegates)<br \/>\n        {<br \/>\n            if (context.Cts.IsCancellationRequested) break;<br \/>\n            await _delegateManager.ExecuteBaseDelegate(del, context);<br \/>\n        }<br \/>\n    }<br \/>\n    #endregion<br \/>\n}<\/p>\n<h4>4. \u540e\u53f0\u670d\u52a1\u7ba1\u7406\u5668&#xff08;WinForm \u65e0 UI \u963b\u585e\u6267\u884c&#xff09;<\/h4>\n<p>\u5b9e\u73b0WinForm \u754c\u9762\u4e0e\u540e\u53f0\u670d\u52a1\u89e3\u8026&#xff0c;\u652f\u6301\u6d41\u7a0b\u540e\u53f0\u8fd0\u884c\u3001\u6700\u5c0f\u5316\u5230\u7cfb\u7edf\u6258\u76d8\u3001\u540e\u53f0\u65e5\u5fd7\u8bb0\u5f55\u3001\u72b6\u6001\u5b9e\u65f6\u540c\u6b65\u5230 UI&#xff1a;<\/p>\n<p>csharp<\/p>\n<p>\u8fd0\u884c<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ MES\/ERP\u5de5\u5e8f\u6d41\u7a0b\u540e\u53f0\u670d\u52a1\u7ba1\u7406\u5668<br \/>\n\/\/\/ \u6838\u5fc3&#xff1a;\u65e0UI\u963b\u585e\u6267\u884c\u3001\u7cfb\u7edf\u6258\u76d8\u3001\u540e\u53f0\u7ebf\u7a0b\u6c60\u3001UI\u72b6\u6001\u540c\u6b65<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic class ProcessFlowBackendService<br \/>\n{<br \/>\n    \/\/ \u540e\u53f0\u670d\u52a1\u7ebf\u7a0b\u6c60<br \/>\n    private readonly TaskFactory _backendTaskFactory;<br \/>\n    \/\/ UI\u72b6\u6001\u66f4\u65b0\u59d4\u6258&#xff08;\u8de8\u7ebf\u7a0b\u5b89\u5168&#xff09;<br \/>\n    public Action&lt;string, string&gt; UiStatusUpdateAction;<br \/>\n    \/\/ \u540e\u53f0\u65e5\u5fd7\u59d4\u6258<br \/>\n    public Action&lt;string&gt; BackendLogAction;<\/p>\n<p>    public ProcessFlowBackendService()<br \/>\n    {<br \/>\n        \/\/ \u914d\u7f6e\u540e\u53f0\u7ebf\u7a0b\u6c60&#xff08;\u65e0\u540c\u6b65\u4e0a\u4e0b\u6587&#xff0c;\u907f\u514dUI\u963b\u585e&#xff09;<br \/>\n        var scheduler &#061; TaskScheduler.Default;<br \/>\n        _backendTaskFactory &#061; new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None, scheduler);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u540e\u53f0\u6267\u884c\u5de5\u5e8f\u6d41\u7a0b&#xff08;\u65e0UI\u963b\u585e&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public void RunFlowInBackend(FlowDelegateCombineInfo flowCombine, ErpWorkOrder workOrder, ProcessRoute route)<br \/>\n    {<br \/>\n        _backendTaskFactory.StartNew(async () &#061;&gt;<br \/>\n        {<br \/>\n            try<br \/>\n            {<br \/>\n                UiStatusUpdateAction?.Invoke(flowCombine.FlowName, &#034;\u540e\u53f0\u6267\u884c\u4e2d&#034;);<br \/>\n                BackendLogAction?.Invoke($&#034;\u540e\u53f0\u670d\u52a1\u542f\u52a8\u6d41\u7a0b&#xff1a;{flowCombine.FlowName}&#xff08;\u5de5\u5355&#xff1a;{workOrder.OrderNo}&#xff09;&#034;);<br \/>\n                \/\/ \u8c03\u7528\u6d41\u7a0b\u5f15\u64ce\u542f\u52a8\u6d41\u7a0b<br \/>\n                await ProcessFlowEngine.Instance.StartFlowAsync(flowCombine, workOrder, route);<br \/>\n                UiStatusUpdateAction?.Invoke(flowCombine.FlowName, &#034;\u6267\u884c\u5b8c\u6210&#034;);<br \/>\n            }<br \/>\n            catch (Exception ex)<br \/>\n            {<br \/>\n                UiStatusUpdateAction?.Invoke(flowCombine.FlowName, &#034;\u6267\u884c\u5931\u8d25&#034;);<br \/>\n                BackendLogAction?.Invoke($&#034;\u540e\u53f0\u6d41\u7a0b\u6267\u884c\u5931\u8d25&#xff1a;{ex.Message}&#034;);<br \/>\n                LogHelper.Error(&#034;\u540e\u53f0\u670d\u52a1\u6267\u884c\u5f02\u5e38&#034;, ex);<br \/>\n            }<br \/>\n        }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6ce8\u518c\u7cfb\u7edf\u6258\u76d8&#xff08;WinForm\u6700\u5c0f\u5316\u5230\u540e\u53f0\u8fd0\u884c&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public void RegisterSystemTray(Form mainForm, NotifyIcon notifyIcon)<br \/>\n    {<br \/>\n        \/\/ \u521d\u59cb\u5316\u6258\u76d8\u56fe\u6807<br \/>\n        notifyIcon.Icon &#061; SystemIcons.Application;<br \/>\n        notifyIcon.Text &#061; &#034;MES\/ERP\u5de5\u5e8f\u6d41\u7a0b\u4e2d\u95f4\u4ef6&#034;;<br \/>\n        notifyIcon.Visible &#061; true;<\/p>\n<p>        \/\/ \u6258\u76d8\u53f3\u952e\u83dc\u5355<br \/>\n        var contextMenu &#061; new ContextMenuStrip();<br \/>\n        contextMenu.Items.Add(&#034;\u663e\u793a\u754c\u9762&#034;, null, (s, e) &#061;&gt; { mainForm.Show(); mainForm.WindowState &#061; FormWindowState.Normal; });<br \/>\n        contextMenu.Items.Add(&#034;\u9000\u51fa&#034;, null, (s, e) &#061;&gt; { Application.Exit(); });<br \/>\n        notifyIcon.ContextMenuStrip &#061; contextMenu;<\/p>\n<p>        \/\/ \u7a97\u4f53\u6700\u5c0f\u5316\u5230\u6258\u76d8<br \/>\n        mainForm.Resize &#043;&#061; (s, e) &#061;&gt;<br \/>\n        {<br \/>\n            if (mainForm.WindowState &#061;&#061; FormWindowState.Minimized)<br \/>\n            {<br \/>\n                mainForm.Hide();<br \/>\n                notifyIcon.ShowBalloonTip(2000, &#034;\u63d0\u793a&#034;, &#034;MES\/ERP\u5de5\u5e8f\u6d41\u7a0b\u4e2d\u95f4\u4ef6\u5df2\u6700\u5c0f\u5316\u5230\u540e\u53f0\u8fd0\u884c&#034;, ToolTipIcon.Info);<br \/>\n            }<br \/>\n        };<\/p>\n<p>        \/\/ \u6258\u76d8\u5de6\u952e\u53cc\u51fb\u663e\u793a\u754c\u9762<br \/>\n        notifyIcon.MouseDoubleClick &#043;&#061; (s, e) &#061;&gt;<br \/>\n        {<br \/>\n            if (e.Button &#061;&#061; MouseButtons.Left)<br \/>\n            {<br \/>\n                mainForm.Show();<br \/>\n                mainForm.WindowState &#061; FormWindowState.Normal;<br \/>\n            }<br \/>\n        };<\/p>\n<p>        BackendLogAction?.Invoke(&#034;\u7cfb\u7edf\u6258\u76d8\u6ce8\u518c\u6210\u529f&#xff0c;\u652f\u6301\u540e\u53f0\u8fd0\u884c&#034;);<br \/>\n    }<br \/>\n}<\/p>\n<h4>5. \u901a\u7528\u65e5\u5fd7\u52a9\u624b<\/h4>\n<p>\u5c01\u88c5\u6587\u4ef6\u65e5\u5fd7 &#043; \u63a7\u5236\u53f0\u65e5\u5fd7&#xff0c;\u652f\u6301\u5206\u7ea7\u65e5\u5fd7&#xff08;Info\/Warn\/Error&#xff09;&#xff0c;\u65b9\u4fbf MES\/ERP \u5de5\u5e8f\u6d41\u7a0b\u7684\u95ee\u9898\u6392\u67e5&#xff1a;<\/p>\n<p>csharp<\/p>\n<p>\u8fd0\u884c<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ MES\/ERP\u901a\u7528\u65e5\u5fd7\u52a9\u624b<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic static class LogHelper<br \/>\n{<br \/>\n    private static readonly string _logDir &#061; Path.Combine(AppDomain.CurrentDomain.BaseDirectory, &#034;MesErpLogs&#034;);<\/p>\n<p>    static LogHelper()<br \/>\n    {<br \/>\n        if (!Directory.Exists(_logDir)) Directory.CreateDirectory(_logDir);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u4fe1\u606f\u65e5\u5fd7<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public static void Info(string msg)<br \/>\n    {<br \/>\n        WriteLog(&#034;INFO&#034;, msg);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u8b66\u544a\u65e5\u5fd7<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public static void Warn(string msg)<br \/>\n    {<br \/>\n        WriteLog(&#034;WARN&#034;, msg);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u9519\u8bef\u65e5\u5fd7<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    public static void Error(string msg, Exception ex &#061; null)<br \/>\n    {<br \/>\n        var fullMsg &#061; ex &#061;&#061; null ? msg : $&#034;{msg}\\\\r\\\\n\u5f02\u5e38\u4fe1\u606f&#xff1a;{ex.Message}\\\\r\\\\n\u5806\u6808\u8ddf\u8e2a&#xff1a;{ex.StackTrace}&#034;;<br \/>\n        WriteLog(&#034;ERROR&#034;, fullMsg);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u5199\u5165\u65e5\u5fd7\u6587\u4ef6<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private static void WriteLog(string level, string msg)<br \/>\n    {<br \/>\n        var logContent &#061; $&#034;[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{level}] {msg}\\\\r\\\\n&#034;;<br \/>\n        \/\/ \u63a7\u5236\u53f0\u8f93\u51fa<br \/>\n        Console.WriteLine(logContent);<br \/>\n        \/\/ \u6587\u4ef6\u8f93\u51fa&#xff08;\u6309\u65e5\u671f\u5206\u6587\u4ef6&#xff09;<br \/>\n        var logFile &#061; Path.Combine(_logDir, $&#034;MesErp_ProcessDelegate_{DateTime.Now:yyyyMMdd}.log&#034;);<br \/>\n        try<br \/>\n        {<br \/>\n            File.AppendAllText(logFile, logContent, System.Text.Encoding.UTF8);<br \/>\n        }<br \/>\n        catch<br \/>\n        {<br \/>\n            \/\/ \u5ffd\u7565\u65e5\u5fd7\u5199\u5165\u5f02\u5e38&#xff0c;\u907f\u514d\u5f71\u54cd\u4e3b\u6d41\u7a0b<br \/>\n        }<br \/>\n    }<br \/>\n}<\/p>\n<h3>\u516d\u3001WinForm \u754c\u9762\u5b9e\u73b0&#xff08;\u53ef\u89c6\u5316\u4e2d\u95f4\u4ef6&#xff09;<\/h3>\n<p>\u8bbe\u8ba1\u7b80\u6d01\u6613\u7528\u7684 WinForm \u754c\u9762&#xff0c;\u5b9e\u73b0ERP \u5de5\u5355\u5bfc\u5165 \/ \u5de5\u827a\u8def\u7ebf\u914d\u7f6e \/ \u59d4\u6258\u62fc\u63a5 \/ \u6d41\u7a0b\u542f\u52a8 \/ \u540e\u53f0\u8fd0\u884c \/ \u72b6\u6001\u76d1\u63a7 \/ \u65e5\u5fd7\u67e5\u770b\u5168\u529f\u80fd&#xff0c;\u652f\u6301\u8de8\u7ebf\u7a0b UI \u72b6\u6001\u540c\u6b65&#xff1a;<\/p>\n<p>\/\/\/ &lt;summary&gt;<br \/>\n\/\/\/ MES\/ERP\u5de5\u5e8f\u6d41\u7a0b\u59d4\u6258\u4e2d\u95f4\u4ef6\u4e3b\u754c\u9762<br \/>\n\/\/\/ \u6838\u5fc3\u529f\u80fd&#xff1a;\u5de5\u5355\u7ba1\u7406\u3001\u5de5\u827a\u8def\u7ebf\u914d\u7f6e\u3001\u59d4\u6258\u62fc\u63a5\u3001\u6d41\u7a0b\u6267\u884c\u3001\u540e\u53f0\u8fd0\u884c\u3001\u72b6\u6001\u76d1\u63a7\u3001\u65e5\u5fd7\u67e5\u770b<br \/>\n\/\/\/ &lt;\/summary&gt;<br \/>\npublic partial class MainForm : Form<br \/>\n{<br \/>\n    #region \u670d\u52a1\u5b9e\u4f8b<br \/>\n    private readonly MesErpDbHelper _dbHelper &#061; MesErpDbHelper.Instance;<br \/>\n    private readonly ProcessDelegateManager _delegateManager &#061; ProcessDelegateManager.Instance;<br \/>\n    private readonly ProcessFlowEngine _flowEngine &#061; ProcessFlowEngine.Instance;<br \/>\n    private readonly ProcessFlowBackendService _backendService &#061; new ProcessFlowBackendService();<br \/>\n    private readonly NotifyIcon _notifyIcon &#061; new NotifyIcon();<br \/>\n    #endregion<\/p>\n<p>    #region \u754c\u9762\u63a7\u4ef6<br \/>\n    private ComboBox cboWorkOrder;<br \/>\n    private Button btnAddOrder;<br \/>\n    private Button btnAddProcess;<br \/>\n    private Button btnCombineDelegate;<br \/>\n    private Button btnStartFlow;<br \/>\n    private Button btnPauseFlow;<br \/>\n    private Button btnStopFlow;<br \/>\n    private DataGridView dgvProcessRoute;<br \/>\n    private DataGridView dgvRunningFlow;<br \/>\n    private TextBox txtLog;<br \/>\n    private Label lblBackendStatus;<br \/>\n    private ComboBox cboExecuteMode;<br \/>\n    #endregion<\/p>\n<p>    public MainForm()<br \/>\n    {<br \/>\n        InitializeComponent();<br \/>\n        \/\/ \u521d\u59cb\u5316UI<br \/>\n        InitMainUI();<br \/>\n        \/\/ \u6ce8\u518c\u540e\u53f0\u670d\u52a1\u59d4\u6258&#xff08;UI\u72b6\u6001\u540c\u6b65&#043;\u65e5\u5fd7&#xff09;<br \/>\n        RegisterBackendServiceDelegates();<br \/>\n        \/\/ \u6ce8\u518c\u7cfb\u7edf\u6258\u76d8&#xff08;\u540e\u53f0\u8fd0\u884c&#xff09;<br \/>\n        _backendService.RegisterSystemTray(this, _notifyIcon);<br \/>\n        \/\/ \u52a0\u8f7d\u521d\u59cb\u6570\u636e<br \/>\n        LoadInitData();<br \/>\n    }<\/p>\n<p>    #region UI\u521d\u59cb\u5316<br \/>\n    private void InitMainUI()<br \/>\n    {<br \/>\n        \/\/ \u7a97\u4f53\u57fa\u7840\u8bbe\u7f6e<br \/>\n        this.Text &#061; &#034;MES\/ERP\u5de5\u5e8f\u6d41\u7a0b\u59d4\u6258\u4e2d\u95f4\u4ef6\u670d\u52a1&#034;;<br \/>\n        this.Size &#061; new Size(1000, 600);<br \/>\n        this.StartPosition &#061; FormStartPosition.CenterScreen;<br \/>\n        this.FormClosing &#043;&#061; (s, e) &#061;&gt; { _notifyIcon.Visible &#061; false; };<\/p>\n<p>        \/\/ \u63a7\u4ef6\u521d\u59cb\u5316<br \/>\n        cboWorkOrder &#061; new ComboBox { Location &#061; new Point(10, 10), Size &#061; new Size(200, 23), DropDownStyle &#061; ComboBoxStyle.DropDownList };<br \/>\n        btnAddOrder &#061; new Button { Location &#061; new Point(220, 10), Size &#061; new Size(80, 23), Text &#061; &#034;\u65b0\u589e\u5de5\u5355&#034; };<br \/>\n        btnAddProcess &#061; new Button { Location &#061; new Point(310, 10), Size &#061; new Size(80, 23), Text &#061; &#034;\u65b0\u589e\u5de5\u5e8f&#034; };<br \/>\n        btnCombineDelegate &#061; new Button { Location &#061; new Point(400, 10), Size &#061; new Size(100, 23), Text &#061; &#034;\u59d4\u6258\u62fc\u63a5&#034; };<br \/>\n        cboExecuteMode &#061; new ComboBox { Location &#061; new Point(510, 10), Size &#061; new Size(120, 23), DataSource &#061; Enum.GetValues(typeof(FlowExecuteMode)) };<br \/>\n        btnStartFlow &#061; new Button { Location &#061; new Point(640, 10), Size &#061; new Size(80, 23), Text &#061; &#034;\u542f\u52a8\u6d41\u7a0b&#034;, BackColor &#061; Color.LightGreen };<br \/>\n        btnPauseFlow &#061; new Button { Location &#061; new Point(730, 10), Size &#061; new Size(80, 23), Text &#061; &#034;\u6682\u505c\u6d41\u7a0b&#034;, BackColor &#061; Color.Orange };<br \/>\n        btnStopFlow &#061; new Button { Location &#061; new Point(820, 10), Size &#061; new Size(80, 23), Text &#061; &#034;\u7ec8\u6b62\u6d41\u7a0b&#034;, BackColor &#061; Color.LightPink };<br \/>\n        lblBackendStatus &#061; new Label { Location &#061; new Point(910, 10), Size &#061; new Size(80, 23), Text &#061; &#034;\u540e\u53f0\u72b6\u6001&#xff1a;\u7a7a\u95f2&#034; };<\/p>\n<p>        \/\/ \u5de5\u827a\u8def\u7ebf\u7f51\u683c<br \/>\n        dgvProcessRoute &#061; new DataGridView { Location &#061; new Point(10, 40), Size &#061; new Size(980, 200), ReadOnly &#061; true, AllowUserToAddRows &#061; false };<br \/>\n        dgvProcessRoute.Columns.AddRange(new[]<br \/>\n        {<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;ProcessCode&#034;, HeaderText &#061; &#034;\u5de5\u5e8f\u7f16\u7801&#034;, Width &#061; 100 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;ProcessName&#034;, HeaderText &#061; &#034;\u5de5\u5e8f\u540d\u79f0&#034;, Width &#061; 100 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;SortNo&#034;, HeaderText &#061; &#034;\u6267\u884c\u987a\u5e8f&#034;, Width &#061; 80 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;Status&#034;, HeaderText &#061; &#034;\u5de5\u5e8f\u72b6\u6001&#034;, Width &#061; 100 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;CompleteQty&#034;, HeaderText &#061; &#034;\u5b8c\u6210\u4ea7\u91cf&#034;, Width &#061; 100 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;DelegateCombine&#034;, HeaderText &#061; &#034;\u59d4\u6258\u7ec4\u5408&#034;, Width &#061; 200 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;Params&#034;, HeaderText &#061; &#034;\u5de5\u5e8f\u53c2\u6570&#034;, Width &#061; 300 }<br \/>\n        });<\/p>\n<p>        \/\/ \u8fd0\u884c\u6d41\u7a0b\u7f51\u683c<br \/>\n        dgvRunningFlow &#061; new DataGridView { Location &#061; new Point(10, 250), Size &#061; new Size(980, 100), ReadOnly &#061; true, AllowUserToAddRows &#061; false };<br \/>\n        dgvRunningFlow.Columns.AddRange(new[]<br \/>\n        {<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;FlowName&#034;, HeaderText &#061; &#034;\u6d41\u7a0b\u540d\u79f0&#034;, Width &#061; 200 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;OrderNo&#034;, HeaderText &#061; &#034;\u5173\u8054\u5de5\u5355&#034;, Width &#061; 150 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;ExecuteMode&#034;, HeaderText &#061; &#034;\u6267\u884c\u6a21\u5f0f&#034;, Width &#061; 150 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;Status&#034;, HeaderText &#061; &#034;\u6d41\u7a0b\u72b6\u6001&#034;, Width &#061; 150 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;CurrentProcess&#034;, HeaderText &#061; &#034;\u5f53\u524d\u5de5\u5e8f&#034;, Width &#061; 200 },<br \/>\n            new DataGridViewTextBoxColumn { Name &#061; &#034;CompleteQty&#034;, HeaderText &#061; &#034;\u5b8c\u6210\u4ea7\u91cf&#034;, Width &#061; 130 }<br \/>\n        });<\/p>\n<p>        \/\/ \u65e5\u5fd7\u6587\u672c\u6846<br \/>\n        txtLog &#061; new TextBox { Location &#061; new Point(10, 360), Size &#061; new Size(980, 220), Multiline &#061; true, ReadOnly &#061; true, ScrollBars &#061; ScrollBars.Vertical };<\/p>\n<p>        \/\/ \u7ed1\u5b9a\u63a7\u4ef6\u4e8b\u4ef6<br \/>\n        btnAddOrder.Click &#043;&#061; BtnAddOrder_Click;<br \/>\n        btnAddProcess.Click &#043;&#061; BtnAddProcess_Click;<br \/>\n        btnCombineDelegate.Click &#043;&#061; BtnCombineDelegate_Click;<br \/>\n        btnStartFlow.Click &#043;&#061; BtnStartFlow_Click;<br \/>\n        btnPauseFlow.Click &#043;&#061; BtnPauseFlow_Click;<br \/>\n        btnStopFlow.Click &#043;&#061; BtnStopFlow_Click;<br \/>\n        cboWorkOrder.SelectedIndexChanged &#043;&#061; CboWorkOrder_SelectedIndexChanged;<\/p>\n<p>        \/\/ \u6dfb\u52a0\u63a7\u4ef6\u5230\u7a97\u4f53<br \/>\n        this.Controls.AddRange(new Control[] { cboWorkOrder, btnAddOrder, btnAddProcess, btnCombineDelegate, cboExecuteMode,<br \/>\n            btnStartFlow, btnPauseFlow, btnStopFlow, lblBackendStatus, dgvProcessRoute, dgvRunningFlow, txtLog });<br \/>\n    }<br \/>\n    #endregion<\/p>\n<p>    #region \u59d4\u6258\u6ce8\u518c&#xff08;\u8de8\u7ebf\u7a0bUI\u540c\u6b65&#xff09;<br \/>\n    private void RegisterBackendServiceDelegates()<br \/>\n    {<br \/>\n        \/\/ \u540e\u53f0\u670d\u52a1\u72b6\u6001\u66f4\u65b0<br \/>\n        _backendService.UiStatusUpdateAction &#061; (flowName, status) &#061;&gt;<br \/>\n        {<br \/>\n            if (this.InvokeRequired)<br \/>\n                this.Invoke(new Action&lt;string, string&gt;((f, s) &#061;&gt; { lblBackendStatus.Text &#061; $&#034;\u540e\u53f0\u72b6\u6001&#xff1a;{s}&#034;; }), flowName, status);<br \/>\n            else<br \/>\n                lblBackendStatus.Text &#061; $&#034;\u540e\u53f0\u72b6\u6001&#xff1a;{status}&#034;;<br \/>\n        };<\/p>\n<p>        \/\/ \u540e\u53f0\u670d\u52a1\u65e5\u5fd7\u540c\u6b65\u5230UI<br \/>\n        _backendService.BackendLogAction &#061; (log) &#061;&gt;<br \/>\n        {<br \/>\n            if (txtLog.InvokeRequired)<br \/>\n                txtLog.Invoke(new Action&lt;string&gt;(AddLogToUi), log);<br \/>\n            else<br \/>\n                AddLogToUi(log);<br \/>\n        };<\/p>\n<p>        \/\/ \u5168\u5c40\u65e5\u5fd7\u540c\u6b65\u5230UI<br \/>\n        typeof(LogHelper).GetField(&#034;_logDir&#034;, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);<br \/>\n        LogHelper.Info(&#034;UI\u59d4\u6258\u6ce8\u518c\u6210\u529f&#xff0c;\u652f\u6301\u8de8\u7ebf\u7a0b\u72b6\u6001\u540c\u6b65&#034;);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6dfb\u52a0\u65e5\u5fd7\u5230UI\u6587\u672c\u6846<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void AddLogToUi(string log)<br \/>\n    {<br \/>\n        txtLog.AppendText($&#034;[{DateTime.Now:HH:mm:ss.fff}] {log}\\\\r\\\\n&#034;);<br \/>\n        txtLog.ScrollToCaret();<br \/>\n    }<br \/>\n    #endregion<\/p>\n<p>    #region \u6570\u636e\u52a0\u8f7d<br \/>\n    private void LoadInitData()<br \/>\n    {<br \/>\n        \/\/ \u52a0\u8f7dERP\u5de5\u5355\u5230\u4e0b\u62c9\u6846<br \/>\n        var orders &#061; _dbHelper.QueryList&lt;ErpWorkOrder&gt;(&#034;SELECT * FROM ErpWorkOrder&#034;);<br \/>\n        cboWorkOrder.DataSource &#061; orders;<br \/>\n        cboWorkOrder.DisplayMember &#061; &#034;OrderNo&#034;;<br \/>\n        cboWorkOrder.ValueMember &#061; &#034;OrderNo&#034;;<\/p>\n<p>        AddLogToUi(&#034;\u521d\u59cb\u6570\u636e\u52a0\u8f7d\u5b8c\u6210&#xff0c;\u5de5\u5355\u6570\u91cf&#xff1a;&#034; &#043; orders.Count);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u52a0\u8f7d\u9009\u4e2d\u5de5\u5355\u7684\u5de5\u827a\u8def\u7ebf<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void LoadProcessRoute(string orderNo)<br \/>\n    {<br \/>\n        var route &#061; _dbHelper.GetObject&lt;ProcessRoute&gt;(&#034;ProcessRoute&#034;, &#034;OrderNo&#034;, orderNo);<br \/>\n        if (route &#061;&#061; null) return;<\/p>\n<p>        dgvProcessRoute.Rows.Clear();<br \/>\n        foreach (var process in route.ProcessNodes.OrderBy(p &#061;&gt; p.SortNo))<br \/>\n        {<br \/>\n            var row &#061; dgvProcessRoute.Rows.Add();<br \/>\n            dgvProcessRoute.Rows[row].Cells[&#034;ProcessCode&#034;].Value &#061; process.ProcessCode;<br \/>\n            dgvProcessRoute.Rows[row].Cells[&#034;ProcessName&#034;].Value &#061; process.ProcessName;<br \/>\n            dgvProcessRoute.Rows[row].Cells[&#034;SortNo&#034;].Value &#061; process.SortNo;<br \/>\n            dgvProcessRoute.Rows[row].Cells[&#034;Status&#034;].Value &#061; process.Status;<br \/>\n            dgvProcessRoute.Rows[row].Cells[&#034;CompleteQty&#034;].Value &#061; process.CompleteQty;<br \/>\n            dgvProcessRoute.Rows[row].Cells[&#034;DelegateCombine&#034;].Value &#061; process.DelegateCombineId;<br \/>\n            dgvProcessRoute.Rows[row].Cells[&#034;Params&#034;].Value &#061; JsonConvert.SerializeObject(process.Params);<br \/>\n        }<br \/>\n    }<br \/>\n    #endregion<\/p>\n<p>    #region \u754c\u9762\u4e8b\u4ef6\u5904\u7406&#xff08;\u6838\u5fc3\u529f\u80fd&#xff09;<br \/>\n    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u65b0\u589eERP\u5de5\u5355<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void BtnAddOrder_Click(object sender, EventArgs e)<br \/>\n    {<br \/>\n        var order &#061; new ErpWorkOrder<br \/>\n        {<br \/>\n            OrderNo &#061; &#034;WO-&#034; &#043; DateTime.Now.ToString(&#034;yyyyMMddHHmmss&#034;),<br \/>\n            ProductCode &#061; &#034;PROD-&#034; &#043; new Random().Next(1000, 9999),<br \/>\n            ProductName &#061; &#034;\u6d4b\u8bd5\u4ea7\u54c1-&#034; &#043; new Random().Next(100, 999),<br \/>\n            PlanQty &#061; 1000,<br \/>\n            LineCode &#061; &#034;LINE-001&#034;<br \/>\n        };<br \/>\n        _dbHelper.SaveObject(&#034;ErpWorkOrder&#034;, &#034;OrderNo&#034;, order);<br \/>\n        \/\/ \u521d\u59cb\u5316\u5de5\u827a\u8def\u7ebf<br \/>\n        var route &#061; new ProcessRoute { OrderNo &#061; order.OrderNo, LineCode &#061; order.LineCode };<br \/>\n        _dbHelper.SaveObject(&#034;ProcessRoute&#034;, &#034;RouteId&#034;, route);<\/p>\n<p>        LoadInitData();<br \/>\n        AddLogToUi($&#034;\u65b0\u589eERP\u5de5\u5355\u6210\u529f&#xff1a;{order.OrderNo}&#xff0c;\u4ea7\u54c1&#xff1a;{order.ProductName}&#xff0c;\u8ba1\u5212\u4ea7\u91cf&#xff1a;{order.PlanQty}\u4ef6&#034;);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u65b0\u589e\u5de5\u5e8f\u8282\u70b9<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void BtnAddProcess_Click(object sender, EventArgs e)<br \/>\n    {<br \/>\n        if (cboWorkOrder.SelectedValue &#061;&#061; null)<br \/>\n        {<br \/>\n            MessageBox.Show(&#034;\u8bf7\u5148\u9009\u62e9\u5de5\u5355&#xff01;&#034;, &#034;\u63d0\u793a&#034;, MessageBoxButtons.OK, MessageBoxIcon.Warning);<br \/>\n            return;<br \/>\n        }<\/p>\n<p>        var orderNo &#061; cboWorkOrder.SelectedValue.ToString();<br \/>\n        var route &#061; _dbHelper.GetObject&lt;ProcessRoute&gt;(&#034;ProcessRoute&#034;, &#034;OrderNo&#034;, orderNo);<br \/>\n        if (route &#061;&#061; null) route &#061; new ProcessRoute { OrderNo &#061; orderNo };<\/p>\n<p>        var process &#061; new ProcessNode<br \/>\n        {<br \/>\n            ProcessCode &#061; &#034;PROC-&#034; &#043; new Random().Next(1000, 9999),<br \/>\n            ProcessName &#061; &#034;\u6d4b\u8bd5\u5de5\u5e8f-&#034; &#043; new Random().Next(100, 999),<br \/>\n            SortNo &#061; route.ProcessNodes.Count &#043; 1,<br \/>\n            Status &#061; ProcessStatus.UnInitialized<br \/>\n        };<br \/>\n        route.ProcessNodes.Add(process);<br \/>\n        _dbHelper.SaveObject(&#034;ProcessRoute&#034;, &#034;RouteId&#034;, route);<\/p>\n<p>        LoadProcessRoute(orderNo);<br \/>\n        AddLogToUi($&#034;\u65b0\u589e\u5de5\u5e8f\u6210\u529f&#xff1a;{process.ProcessCode}&#xff0c;\u5de5\u5e8f\u540d\u79f0&#xff1a;{process.ProcessName}&#xff0c;\u6267\u884c\u987a\u5e8f&#xff1a;{process.SortNo}&#034;);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u59d4\u6258\u62fc\u63a5&#xff08;\u57fa\u7840\u59d4\u6258-&gt;\u5de5\u5e8f\u7ec4\u5408\u59d4\u6258&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void BtnCombineDelegate_Click(object sender, EventArgs e)<br \/>\n    {<br \/>\n        if (cboWorkOrder.SelectedValue &#061;&#061; null || dgvProcessRoute.SelectedRows.Count &#061;&#061; 0)<br \/>\n        {<br \/>\n            MessageBox.Show(&#034;\u8bf7\u9009\u62e9\u5de5\u5355\u548c\u5de5\u5e8f&#xff01;&#034;, &#034;\u63d0\u793a&#034;, MessageBoxButtons.OK, MessageBoxIcon.Warning);<br \/>\n            return;<br \/>\n        }<\/p>\n<p>        var orderNo &#061; cboWorkOrder.SelectedValue.ToString();<br \/>\n        var processRow &#061; dgvProcessRoute.SelectedRows[0];<br \/>\n        var processCode &#061; processRow.Cells[&#034;ProcessCode&#034;].Value.ToString();<br \/>\n        var route &#061; _dbHelper.GetObject&lt;ProcessRoute&gt;(&#034;ProcessRoute&#034;, &#034;OrderNo&#034;, orderNo);<br \/>\n        var process &#061; route.ProcessNodes.First(p &#061;&gt; p.ProcessCode &#061;&#061; processCode);<\/p>\n<p>        \/\/ \u83b7\u53d6\u6240\u6709\u57fa\u7840\u59d4\u6258&#xff08;\u7b80\u5316\u7248&#xff0c;\u5b9e\u9645\u53ef\u505a\u53ef\u89c6\u5316\u9009\u62e9&#xff09;<br \/>\n        var allBaseDelegates &#061; _delegateManager.GetType().GetField(&#034;_baseDelegateDict&#034;, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)<br \/>\n            .GetValue(_delegateManager) as Dictionary&lt;string, BaseDelegateInfo&gt;;<br \/>\n        var selectBaseDelegates &#061; allBaseDelegates.Values.Take(2).ToList(); \/\/ \u9009\u62e9\u524d2\u4e2a\u57fa\u7840\u59d4\u6258<\/p>\n<p>        \/\/ \u62fc\u63a5\u5de5\u5e8f\u7ea7\u59d4\u6258\u7ec4\u5408&#xff08;\u4e32\u884c\u6267\u884c&#xff09;<br \/>\n        var combine &#061; _delegateManager.CombineProcessDelegates($&#034;{processCode}\u59d4\u6258\u7ec4\u5408&#034;, DelegateCombineType.Serial, selectBaseDelegates);<br \/>\n        \/\/ \u7ed1\u5b9a\u5230\u5de5\u5e8f\u8282\u70b9<br \/>\n        process.DelegateCombineId &#061; combine.CombineId;<br \/>\n        _dbHelper.SaveObject(&#034;ProcessRoute&#034;, &#034;RouteId&#034;, route);<\/p>\n<p>        LoadProcessRoute(orderNo);<br \/>\n        AddLogToUi($&#034;\u5de5\u5e8f{processCode}\u59d4\u6258\u62fc\u63a5\u6210\u529f&#xff0c;\u7ed1\u5b9a\u7ec4\u5408ID&#xff1a;{combine.CombineId}&#xff0c;\u5305\u542b{selectBaseDelegates.Count}\u4e2a\u57fa\u7840\u59d4\u6258&#034;);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u542f\u52a8\u5de5\u5e8f\u6d41\u7a0b&#xff08;UI\/\u540e\u53f0\u6a21\u5f0f\u53ef\u9009&#xff09;<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void BtnStartFlow_Click(object sender, EventArgs e)<br \/>\n    {<br \/>\n        if (cboWorkOrder.SelectedValue &#061;&#061; null || dgvProcessRoute.Rows.Count &#061;&#061; 0)<br \/>\n        {<br \/>\n            MessageBox.Show(&#034;\u8bf7\u5148\u914d\u7f6e\u5de5\u5355\u548c\u5de5\u5e8f&#xff01;&#034;, &#034;\u63d0\u793a&#034;, MessageBoxButtons.OK, MessageBoxIcon.Warning);<br \/>\n            return;<br \/>\n        }<\/p>\n<p>        var orderNo &#061; cboWorkOrder.SelectedValue.ToString();<br \/>\n        var order &#061; _dbHelper.GetObject&lt;ErpWorkOrder&gt;(&#034;ErpWorkOrder&#034;, &#034;OrderNo&#034;, orderNo);<br \/>\n        var route &#061; _dbHelper.GetObject&lt;ProcessRoute&gt;(&#034;ProcessRoute&#034;, &#034;OrderNo&#034;, orderNo);<br \/>\n        var executeMode &#061; (FlowExecuteMode)cboExecuteMode.SelectedValue;<\/p>\n<p>        \/\/ \u62fc\u63a5\u4ea7\u7ebf\u7ea7\u6d41\u7a0b\u59d4\u6258<br \/>\n        var processCombines &#061; route.ProcessNodes.Where(p &#061;&gt; !string.IsNullOrEmpty(p.DelegateCombineId))<br \/>\n            .Select(p &#061;&gt; _delegateManager.GetProcessCombineByProcessId(p.ProcessId, route)).ToList();<br \/>\n        var flowCombine &#061; _delegateManager.CombineFlowDelegates($&#034;{orderNo}\u6d41\u7a0b&#034;, route.RouteId, processCombines, executeMode);<\/p>\n<p>        \/\/ \u6267\u884c\u6d41\u7a0b&#xff08;UI\u6a21\u5f0f\/\u540e\u53f0\u6a21\u5f0f&#xff09;<br \/>\n        if (executeMode &#061;&#061; FlowExecuteMode.WinFormUI)<br \/>\n        {<br \/>\n            _flowEngine.StartFlowAsync(flowCombine, order, route);<br \/>\n            AddLogToUi($&#034;UI\u6a21\u5f0f\u542f\u52a8\u6d41\u7a0b&#xff1a;{flowCombine.FlowName}&#xff0c;\u5de5\u5355&#xff1a;{orderNo}&#034;);<br \/>\n        }<br \/>\n        else<br \/>\n        {<br \/>\n            _backendService.RunFlowInBackend(flowCombine, order, route);<br \/>\n            AddLogToUi($&#034;\u540e\u53f0\u6a21\u5f0f\u542f\u52a8\u6d41\u7a0b&#xff1a;{flowCombine.FlowName}&#xff0c;\u5de5\u5355&#xff1a;{orderNo}&#xff0c;\u540e\u53f0\u670d\u52a1\u5df2\u63a5\u7ba1&#034;);<br \/>\n        }<\/p>\n<p>        \/\/ \u66f4\u65b0\u8fd0\u884c\u6d41\u7a0b\u7f51\u683c<br \/>\n        UpdateRunningFlowGrid(flowCombine, order, route);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u6682\u505c\u6d41\u7a0b<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void BtnPauseFlow_Click(object sender, EventArgs e)<br \/>\n    {<br \/>\n        if (dgvRunningFlow.SelectedRows.Count &#061;&#061; 0)<br \/>\n        {<br \/>\n            MessageBox.Show(&#034;\u8bf7\u9009\u62e9\u8fd0\u884c\u4e2d\u7684\u6d41\u7a0b&#xff01;&#034;, &#034;\u63d0\u793a&#034;, MessageBoxButtons.OK, MessageBoxIcon.Warning);<br \/>\n            return;<br \/>\n        }<\/p>\n<p>        var flowName &#061; dgvRunningFlow.SelectedRows[0].Cells[&#034;FlowName&#034;].Value.ToString();<br \/>\n        var flowCombine &#061; _delegateManager.GetType().GetField(&#034;_flowCombineDelegateDict&#034;, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)<br \/>\n            .GetValue(_delegateManager) as Dictionary&lt;string, FlowDelegateCombineInfo&gt;;<br \/>\n        var flow &#061; flowCombine.Values.First(f &#061;&gt; f.FlowName &#061;&#061; flowName);<\/p>\n<p>        _flowEngine.PauseFlow(flow.FlowCombineId);<br \/>\n        AddLogToUi($&#034;\u6682\u505c\u6d41\u7a0b&#xff1a;{flowName}&#xff0c;\u6d41\u7a0bID&#xff1a;{flow.FlowCombineId}&#034;);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u7ec8\u6b62\u6d41\u7a0b<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void BtnStopFlow_Click(object sender, EventArgs e)<br \/>\n    {<br \/>\n        if (dgvRunningFlow.SelectedRows.Count &#061;&#061; 0)<br \/>\n        {<br \/>\n            MessageBox.Show(&#034;\u8bf7\u9009\u62e9\u8fd0\u884c\u4e2d\u7684\u6d41\u7a0b&#xff01;&#034;, &#034;\u63d0\u793a&#034;, MessageBoxButtons.OK, MessageBoxIcon.Warning);<br \/>\n            return;<br \/>\n        }<\/p>\n<p>        var flowName &#061; dgvRunningFlow.SelectedRows[0].Cells[&#034;FlowName&#034;].Value.ToString();<br \/>\n        var flowCombine &#061; _delegateManager.GetType().GetField(&#034;_flowCombineDelegateDict&#034;, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)<br \/>\n            .GetValue(_delegateManager) as Dictionary&lt;string, FlowDelegateCombineInfo&gt;;<br \/>\n        var flow &#061; flowCombine.Values.First(f &#061;&gt; f.FlowName &#061;&#061; flowName);<\/p>\n<p>        _flowEngine.StopFlow(flow.FlowCombineId);<br \/>\n        AddLogToUi($&#034;\u7ec8\u6b62\u6d41\u7a0b&#xff1a;{flowName}&#xff0c;\u6d41\u7a0bID&#xff1a;{flow.FlowCombineId}&#034;);<br \/>\n        dgvRunningFlow.Rows.Remove(dgvRunningFlow.SelectedRows[0]);<br \/>\n    }<\/p>\n<p>    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u5de5\u5355\u9009\u62e9\u53d8\u66f4&#xff0c;\u52a0\u8f7d\u5bf9\u5e94\u5de5\u827a\u8def\u7ebf<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void CboWorkOrder_SelectedIndexChanged(object sender, EventArgs e)<br \/>\n    {<br \/>\n        if (cboWorkOrder.SelectedValue !&#061; null)<br \/>\n            LoadProcessRoute(cboWorkOrder.SelectedValue.ToString());<br \/>\n    }<br \/>\n    #endregion<\/p>\n<p>    #region UI\u8f85\u52a9\u65b9\u6cd5<br \/>\n    \/\/\/ &lt;summary&gt;<br \/>\n    \/\/\/ \u66f4\u65b0\u8fd0\u884c\u6d41\u7a0b\u7f51\u683c<br \/>\n    \/\/\/ &lt;\/summary&gt;<br \/>\n    private void UpdateRunningFlowGrid(FlowDelegateCombineInfo flowCombine, ErpWorkOrder order, ProcessRoute route)<br \/>\n    {<br \/>\n        var row &#061; dgvRunningFlow.Rows.Add();<br \/>\n        dgvRunningFlow.Rows[row].Cells[&#034;FlowName&#034;].Value &#061; flowCombine.FlowName;<br \/>\n        dgvRunningFlow.Rows[row].Cells[&#034;OrderNo&#034;].Value &#061; order.OrderNo;<br \/>\n        dgvRunningFlow.Rows[row].Cells[&#034;ExecuteMode&#034;].Value &#061; flowCombine.ExecuteMode;<br \/>\n        dgvRunningFlow.Rows[row].Cells[&#034;Status&#034;].Value &#061; &#034;\u6267\u884c\u4e2d&#034;;<br \/>\n        dgvRunningFlow.Rows[row].Cells[&#034;CurrentProcess&#034;].Value &#061; route.ProcessNodes[route.CurrentProcessIndex].ProcessCode;<br \/>\n        dgvRunningFlow.Rows[row].Cells[&#034;CompleteQty&#034;].Value &#061; order.CompleteQty;<br \/>\n    }<br \/>\n    #endregion<\/p>\n<p>    \/\/ \u8bbe\u8ba1\u5668\u4ee3\u7801&#xff08;\u81ea\u52a8\u751f\u6210&#xff0c;\u65e0\u9700\u624b\u52a8\u7f16\u5199&#xff09;<br \/>\n    private void InitializeComponent()<br \/>\n    {<br \/>\n        this.SuspendLayout();<br \/>\n        this.ClientSize &#061; new System.Drawing.Size(1000, 600);<br \/>\n        this.Name &#061; &#034;MainForm&#034;;<br \/>\n        this.Text &#061; &#034;MES\/ERP\u5de5\u5e8f\u6d41\u7a0b\u59d4\u6258\u4e2d\u95f4\u4ef6\u670d\u52a1&#034;;<br \/>\n        this.ResumeLayout(false);<br \/>\n    }<br \/>\n}<\/p>\n<p>\/\/ \u7a0b\u5e8f\u5165\u53e3<br \/>\nstatic class Program<br \/>\n{<br \/>\n    [STAThread]<br \/>\n    static void Main()<br \/>\n    {<br \/>\n        Application.EnableVisualStyles();<br \/>\n        Application.SetCompatibleTextRenderingDefault(false);<br \/>\n        Application.Run(new MainForm());<br \/>\n    }<br \/>\n}<\/p>\n<h3>\u4e03\u3001\u90e8\u7f72\u4e0e\u4f7f\u7528\u8bf4\u660e<\/h3>\n<h4>1. \u90e8\u7f72\u6b65\u9aa4<\/h4>\n<li>\u521b\u5efa\u9879\u76ee&#xff1a;\u5728 Visual Studio 2022 \u4e2d\u521b\u5efa\u300cWindows \u7a97\u4f53\u5e94\u7528&#xff08;.NET Framework 4.8&#xff09;\u300d\u9879\u76ee&#xff1b;<\/li>\n<li>\u5b89\u88c5 NuGet \u5305&#xff1a;\u6267\u884c\u524d\u7f6e\u51c6\u5907\u4e2d\u7684 NuGet \u5b89\u88c5\u547d\u4ee4&#xff0c;\u5b89\u88c5\u6240\u6709\u4f9d\u8d56&#xff1b;<\/li>\n<li>\u6dfb\u52a0\u4ee3\u7801&#xff1a;\u5c06\u4e0a\u8ff0\u6240\u6709\u4ee3\u7801\u6309\u7c7b\u62c6\u5206\u5230\u5bf9\u5e94\u6587\u4ef6\u4e2d&#xff08;\u6216\u76f4\u63a5\u590d\u5236\u5230 Program.cs&#xff0c;\u5220\u9664\u539f\u6709\u4ee3\u7801&#xff09;&#xff1b;<\/li>\n<li>\u7f16\u8bd1\u9879\u76ee&#xff1a;\u751f\u6210 EXE \u53ef\u6267\u884c\u6587\u4ef6&#xff08;\u9ed8\u8ba4\u5728 bin\/Debug\/ \u4e0b&#xff09;&#xff1b;<\/li>\n<li>\u76f4\u63a5\u8fd0\u884c&#xff1a;\u65e0\u9700\u5b89\u88c5\u6570\u636e\u5e93 \/ ERP\/MES \u73af\u5883&#xff0c;\u8fd0\u884c EXE \u5373\u53ef&#xff0c;\u7a0b\u5e8f\u4f1a\u81ea\u52a8\u521b\u5efa\u672c\u5730\u6570\u636e\u5e93\u548c\u65e5\u5fd7\u76ee\u5f55\u3002<\/li>\n<h4>2. \u6838\u5fc3\u529f\u80fd\u64cd\u4f5c\u6d41\u7a0b<\/h4>\n<li>\u65b0\u589e ERP \u5de5\u5355&#xff1a;\u70b9\u51fb\u300c\u65b0\u589e\u5de5\u5355\u300d&#xff0c;\u7a0b\u5e8f\u81ea\u52a8\u751f\u6210\u6807\u51c6\u5316 ERP \u5de5\u5355&#xff08;\u5305\u542b\u5de5\u5355\u53f7\u3001\u4ea7\u54c1\u3001\u8ba1\u5212\u4ea7\u91cf&#xff09;&#xff1b;<\/li>\n<li>\u65b0\u589e\u5de5\u5e8f\u8282\u70b9&#xff1a;\u9009\u62e9\u5de5\u5355&#xff0c;\u70b9\u51fb\u300c\u65b0\u589e\u5de5\u5e8f\u300d&#xff0c;\u751f\u6210\u5de5\u5e8f\u8282\u70b9\u5e76\u6dfb\u52a0\u5230\u5de5\u827a\u8def\u7ebf&#xff1b;<\/li>\n<li>\u59d4\u6258\u62fc\u63a5&#xff1a;\u9009\u62e9\u5de5\u5e8f&#xff0c;\u70b9\u51fb\u300c\u59d4\u6258\u62fc\u63a5\u300d&#xff0c;\u7a0b\u5e8f\u81ea\u52a8\u5c06\u901a\u7528\u9ed8\u8ba4\u57fa\u7840\u59d4\u6258\u62fc\u63a5\u6210\u5de5\u5e8f\u7ea7\u7ec4\u5408\u59d4\u6258\u5e76\u7ed1\u5b9a\u5230\u5de5\u5e8f&#xff1b;<\/li>\n<li>\u9009\u62e9\u6267\u884c\u6a21\u5f0f&#xff1a;\u4e0b\u62c9\u6846\u9009\u62e9\u300cWinFormUI\u300d&#xff08;\u5e26 UI \u8fdb\u5ea6\u53cd\u9988&#xff09;\u6216\u300cBackendService\u300d&#xff08;\u540e\u53f0\u65e0 UI \u963b\u585e\u8fd0\u884c&#xff09;&#xff1b;<\/li>\n<li>\u542f\u52a8\u6d41\u7a0b&#xff1a;\u70b9\u51fb\u300c\u542f\u52a8\u6d41\u7a0b\u300d&#xff0c;\u7a0b\u5e8f\u81ea\u52a8\u62fc\u63a5\u4ea7\u7ebf\u7ea7\u6d41\u7a0b\u59d4\u6258\u5e76\u6267\u884c&#xff0c;\u65e5\u5fd7\u5b9e\u65f6\u540c\u6b65\u5230 UI&#xff1b;<\/li>\n<li>\u6d41\u7a0b\u63a7\u5236&#xff1a;\u70b9\u51fb\u300c\u6682\u505c\u6d41\u7a0b \/ \u7ec8\u6b62\u6d41\u7a0b\u300d&#xff0c;\u53ef\u5bf9\u8fd0\u884c\u4e2d\u7684\u6d41\u7a0b\u8fdb\u884c\u7ba1\u63a7&#xff1b;<\/li>\n<li>\u540e\u53f0\u8fd0\u884c&#xff1a;\u5c06\u7a97\u4f53\u6700\u5c0f\u5316&#xff0c;\u7a0b\u5e8f\u81ea\u52a8\u9690\u85cf\u5230\u7cfb\u7edf\u6258\u76d8&#xff0c;\u540e\u53f0\u7ee7\u7eed\u6267\u884c\u6d41\u7a0b\u3002<\/li>\n<h4>3. \u4e0e\u5b9e\u9645 MES\/ERP \u5bf9\u63a5\u6269\u5c55<\/h4>\n<li>ERP \u5de5\u5355\u5bfc\u5165&#xff1a;\u4fee\u6539BtnAddOrder_Click\u65b9\u6cd5&#xff0c;\u6dfb\u52a0 ERP \u63a5\u53e3\u8c03\u7528\u4ee3\u7801&#xff08;\u5982 WebAPI\/WS\/SQL&#xff09;&#xff0c;\u5b9e\u73b0\u4ece\u5b9e\u9645 ERP \u7cfb\u7edf\u5bfc\u5165\u5de5\u5355&#xff1b;<\/li>\n<li>MES \u6570\u636e\u4ea4\u4e92&#xff1a;\u6269\u5c55DefaultDelegates\u7c7b&#xff0c;\u6dfb\u52a0 MES \u63a5\u53e3\u59d4\u6258&#xff08;\u5982 MES \u5de5\u5e8f\u72b6\u6001\u4e0a\u62a5\u3001MES \u4ea7\u91cf\u540c\u6b65&#xff09;&#xff1b;<\/li>\n<li>\u8bbe\u5907\u5bf9\u63a5&#xff1a;\u5728\u57fa\u7840\u59d4\u6258\u4e2d\u6dfb\u52a0 PLC \/ \u4f20\u611f\u5668 \/ \u673a\u5668\u4eba\u901a\u4fe1\u4ee3\u7801&#xff08;\u5982S7.Net\u3001Modbus\u3001OPC UA&#xff09;&#xff0c;\u5b9e\u73b0\u5de5\u5e8f\u6267\u884c\u7684\u8bbe\u5907\u63a7\u5236&#xff1b;<\/li>\n<li>\u590d\u6742\u8868\u8fbe\u5f0f&#xff1a;\u96c6\u6210 NCalc\/EL \u8868\u8fbe\u5f0f\u5f15\u64ce&#xff0c;\u66ff\u6362EvaluateConditionalExpr\u65b9\u6cd5&#xff0c;\u652f\u6301\u590d\u6742\u7684\u59d4\u6258\u6761\u4ef6\u6267\u884c\u8868\u8fbe\u5f0f&#xff1b;<\/li>\n<li>\u53ef\u89c6\u5316\u59d4\u6258\u914d\u7f6e&#xff1a;\u65b0\u589e\u59d4\u6258\u914d\u7f6e\u7a97\u4f53&#xff0c;\u5b9e\u73b0\u57fa\u7840\u59d4\u6258\u7684\u53ef\u89c6\u5316\u9009\u62e9\u3001\u53c2\u6570\u914d\u7f6e\u3001\u62fc\u63a5\u89c4\u5219\u8bbe\u7f6e\u3002<\/li>\n<h3>\u516b\u3001\u6838\u5fc3\u4f18\u52bf\u4e0e\u603b\u7ed3<\/h3>\n<h4>\u6838\u5fc3\u4f18\u52bf<\/h4>\n<li>\u59d4\u6258\u591a\u7ed3\u6784\u62fc\u63a5&#xff1a;\u72ec\u521b\u300c\u57fa\u7840\u59d4\u6258 &#043; \u5de5\u5e8f\u7ec4\u5408\u59d4\u6258 &#043; \u6d41\u7a0b\u62fc\u63a5\u59d4\u6258\u300d\u4e09\u7ea7\u4f53\u7cfb&#xff0c;\u652f\u6301\u4efb\u610f\u5de5\u5e8f\u6d41\u7a0b\u7ed3\u6784&#xff0c;\u9002\u914d MES\/ERP \u7684\u4e2a\u6027\u5316\u5de5\u827a\u8def\u7ebf&#xff1b;<\/li>\n<li>\u4e1a\u52a1\u4e0e UI \u5b8c\u5168\u89e3\u8026&#xff1a;\u59d4\u6258\u51fd\u6570\u5c01\u88c5\u6240\u6709\u4e1a\u52a1\u903b\u8f91&#xff0c;UI \u5c42\u4ec5\u8d1f\u8d23\u5c55\u793a\u548c\u64cd\u4f5c&#xff0c;\u4fee\u6539\u4e1a\u52a1\u903b\u8f91\u65e0\u9700\u6539\u52a8 UI \u4ee3\u7801&#xff1b;<\/li>\n<li>\u540e\u53f0\u670d\u52a1\u5316\u80fd\u529b&#xff1a;\u652f\u6301 WinForm UI \u6267\u884c\u548c\u540e\u53f0\u65e0 UI \u963b\u585e\u6267\u884c&#xff0c;\u6700\u5c0f\u5316\u5230\u7cfb\u7edf\u6258\u76d8&#xff0c;\u9002\u914d\u4ea7\u7ebf 7*24 \u5c0f\u65f6\u8fd0\u884c\u9700\u6c42&#xff1b;<\/li>\n<li>\u914d\u7f6e\u6301\u4e45\u5316&#xff1a;\u57fa\u4e8e SQLite \u672c\u5730\u6570\u636e\u5e93&#xff0c;\u59d4\u6258\u62fc\u63a5\u5173\u7cfb\u3001\u5de5\u5355\u3001\u5de5\u827a\u8def\u7ebf\u3001\u5de5\u5e8f\u72b6\u6001\u5168\u6301\u4e45\u5316&#xff0c;\u7a0b\u5e8f\u91cd\u542f\u4e0d\u4e22\u5931&#xff1b;<\/li>\n<li>\u9ad8\u6269\u5c55\u6027&#xff1a;\u9ed8\u8ba4\u63d0\u4f9b MES\/ERP \u901a\u7528\u57fa\u7840\u59d4\u6258&#xff0c;\u652f\u6301\u81ea\u5b9a\u4e49\u57fa\u7840\u59d4\u6258\u6ce8\u518c\u3001\u59d4\u6258\u7ec4\u5408\u89c4\u5219\u4fee\u6539\u3001\u8bbe\u5907 \/ \u7cfb\u7edf\u63a5\u53e3\u6269\u5c55&#xff1b;<\/li>\n<li>\u7ebf\u7a0b\u5b89\u5168&#xff1a;\u5168\u6d41\u7a0b\u52a0\u9501 &#043; \u5f02\u6b65\u6267\u884c &#043; \u53d6\u6d88\u4ee4\u724c&#xff0c;\u652f\u6301\u591a\u6d41\u7a0b\u5e76\u53d1\u6267\u884c&#xff0c;\u65e0\u8d44\u6e90\u7ade\u4e89\u548c UI \u963b\u585e\u95ee\u9898\u3002<\/li>\n<h4>\u5173\u952e\u70b9\u56de\u987e<\/h4>\n<li>\u56f4\u7ed5MES\/ERP \u5de5\u5e8f\u6d41\u7a0b\u751f\u547d\u5468\u671f\u5c01\u88c5\u6807\u51c6\u5316\u4e8b\u4ef6\u59d4\u6258&#xff0c;\u8986\u76d6\u4ece\u6d41\u7a0b\u542f\u52a8\u5230\u5b8c\u6210\u7684\u5168\u8282\u70b9&#xff1b;<\/li>\n<li>\u91c7\u7528\u4e09\u7ea7\u59d4\u6258\u62fc\u63a5\u8bbe\u8ba1&#xff0c;\u5b9e\u73b0\u539f\u5b50\u64cd\u4f5c\u5230\u5b8c\u6574\u6d41\u7a0b\u7684\u65e0\u9650\u7ec4\u5408&#xff0c;\u89e3\u51b3\u5de5\u5e8f\u6d41\u7a0b\u914d\u7f6e\u5316\u95ee\u9898&#xff1b;<\/li>\n<li>\u8bbe\u8ba1\u540e\u53f0\u670d\u52a1\u7ba1\u7406\u5668&#xff0c;\u5b9e\u73b0 WinForm \u754c\u9762\u4e0e\u540e\u53f0\u6267\u884c\u89e3\u8026&#xff0c;\u652f\u6301\u7cfb\u7edf\u6258\u76d8\u540e\u53f0\u8fd0\u884c&#xff1b;<\/li>\n<li>\u57fa\u4e8eSQLite \u672c\u5730\u6570\u636e\u5e93\u5b9e\u73b0\u914d\u7f6e\u548c\u72b6\u6001\u6301\u4e45\u5316&#xff0c;\u964d\u4f4e\u90e8\u7f72\u4f9d\u8d56&#xff0c;\u9002\u914d\u4ea7\u7ebf\u8fb9\u7f18\u8282\u70b9&#xff1b;<\/li>\n<li>\u63d0\u4f9b\u5f00\u7bb1\u5373\u7528\u7684\u901a\u7528\u57fa\u7840\u59d4\u6258&#xff0c;\u540c\u65f6\u652f\u6301\u81ea\u5b9a\u4e49\u59d4\u6258\u6ce8\u518c&#xff0c;\u5feb\u901f\u5bf9\u63a5\u5b9e\u9645 MES\/ERP\/ \u8bbe\u5907\u7cfb\u7edf\u3002<\/li>\n<p>\u8be5\u4e2d\u95f4\u4ef6\u5b8c\u5168\u9002\u914d MES\/ERP \u7684\u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u573a\u666f&#xff0c;\u53ef\u76f4\u63a5\u4f5c\u4e3a\u72ec\u7acb\u7684\u5de5\u5e8f\u6267\u884c\u4e2d\u95f4\u4ef6\u4f7f\u7528&#xff0c;\u4e5f\u53ef\u96c6\u6210\u5230\u73b0\u6709 MES\/ERP \u7cfb\u7edf\u4e2d&#xff0c;\u4f5c\u4e3a\u5de5\u5e8f\u6d41\u7a0b\u6267\u884c\u7684\u6838\u5fc3\u5f15\u64ce\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>MES\/ERP \u573a\u666f\u6253\u9020\u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u7684\u591a\u5c42\u7ea7\u59d4\u6258\u62fc\u63a5\u5c01\u88c5WinForm \u4fe1\u606f\u4e2d\u95f4\u4ef6<br \/>\nMES\/ERP \u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u5c01\u88c5 WinForm \u4e2d\u95f4\u4ef6\u89e3\u51b3\u65b9\u6848<br \/>\n\u672c\u6b21\u4e3a MES\/ERP \u573a\u666f\u6253\u9020\u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u7684\u591a\u5c42\u7ea7\u59d4\u6258\u62fc\u63a5\u5c01\u88c5WinForm \u4fe1\u606f\u4e2d\u95f4\u4ef6&#xff0c;\u6838\u5fc3\u56f4\u7ed5\u5de5\u5e8f\u6d41\u7a0b\u7684\u751f\u547d\u5468\u671f\u4e8b\u4ef6&#xff08;\u521d\u59cb\u5316\u3001\u542f\u52a8\u3001\u6267\u884c\u3001\u5b8c\u6210\u3001\u5f02\u5e38\u3001\u8df3\u8f6c\u7b49&#xff09;&#xff0c;\u8bbe\u8ba1\u591a\u7ed3\u6784\u62fc\u63a5\u7684\u59d4\u6258\u51fd\u6570\u4f53\u7cfb&#xff08;\u57fa\u7840\u59d4\u6258\u3001\u7ec4\u5408\u59d4\u6258\u3001\u6d41\u7a0b\u59d4\u6258&amp;#xf<\/p>\n","protected":false},"author":2,"featured_media":72578,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[7639,7638,7637],"topic":[],"class_list":["post-72579","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-server","tag-7639","tag-7638","tag-7637"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>MES\/ERP \u573a\u666f\u591a\u5c42\u7ea7\u59d4\u6258\u5c01\u88c5\u7ec4\u4ef6 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.wsisp.com\/helps\/72579.html\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"MES\/ERP \u573a\u666f\u591a\u5c42\u7ea7\u59d4\u6258\u5c01\u88c5\u7ec4\u4ef6 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"og:description\" content=\"MES\/ERP \u573a\u666f\u6253\u9020\u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u7684\u591a\u5c42\u7ea7\u59d4\u6258\u62fc\u63a5\u5c01\u88c5WinForm \u4fe1\u606f\u4e2d\u95f4\u4ef6 MES\/ERP \u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u5c01\u88c5 WinForm \u4e2d\u95f4\u4ef6\u89e3\u51b3\u65b9\u6848 \u672c\u6b21\u4e3a MES\/ERP \u573a\u666f\u6253\u9020\u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u7684\u591a\u5c42\u7ea7\u59d4\u6258\u62fc\u63a5\u5c01\u88c5WinForm \u4fe1\u606f\u4e2d\u95f4\u4ef6&#xff0c;\u6838\u5fc3\u56f4\u7ed5\u5de5\u5e8f\u6d41\u7a0b\u7684\u751f\u547d\u5468\u671f\u4e8b\u4ef6&#xff08;\u521d\u59cb\u5316\u3001\u542f\u52a8\u3001\u6267\u884c\u3001\u5b8c\u6210\u3001\u5f02\u5e38\u3001\u8df3\u8f6c\u7b49&#xff09;&#xff0c;\u8bbe\u8ba1\u591a\u7ed3\u6784\u62fc\u63a5\u7684\u59d4\u6258\u51fd\u6570\u4f53\u7cfb&#xff08;\u57fa\u7840\u59d4\u6258\u3001\u7ec4\u5408\u59d4\u6258\u3001\u6d41\u7a0b\u59d4\u6258&amp;#xf\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.wsisp.com\/helps\/72579.html\" \/>\n<meta property=\"og:site_name\" content=\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"article:published_time\" content=\"2026-02-05T17:16:46+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/02\/20260205171643-6984d07ba2b2f.png\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"26 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/72579.html\",\"url\":\"https:\/\/www.wsisp.com\/helps\/72579.html\",\"name\":\"MES\/ERP \u573a\u666f\u591a\u5c42\u7ea7\u59d4\u6258\u5c01\u88c5\u7ec4\u4ef6 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"isPartOf\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\"},\"datePublished\":\"2026-02-05T17:16:46+00:00\",\"dateModified\":\"2026-02-05T17:16:46+00:00\",\"author\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/72579.html#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.wsisp.com\/helps\/72579.html\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/72579.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/www.wsisp.com\/helps\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"MES\/ERP \u573a\u666f\u591a\u5c42\u7ea7\u59d4\u6258\u5c01\u88c5\u7ec4\u4ef6\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\",\"url\":\"https:\/\/www.wsisp.com\/helps\/\",\"name\":\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"description\":\"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"contentUrl\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"caption\":\"admin\"},\"sameAs\":[\"http:\/\/wp.wsisp.com\"],\"url\":\"https:\/\/www.wsisp.com\/helps\/author\/admin\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"MES\/ERP \u573a\u666f\u591a\u5c42\u7ea7\u59d4\u6258\u5c01\u88c5\u7ec4\u4ef6 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.wsisp.com\/helps\/72579.html","og_locale":"zh_CN","og_type":"article","og_title":"MES\/ERP \u573a\u666f\u591a\u5c42\u7ea7\u59d4\u6258\u5c01\u88c5\u7ec4\u4ef6 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","og_description":"MES\/ERP \u573a\u666f\u6253\u9020\u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u7684\u591a\u5c42\u7ea7\u59d4\u6258\u62fc\u63a5\u5c01\u88c5WinForm \u4fe1\u606f\u4e2d\u95f4\u4ef6 MES\/ERP \u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u5c01\u88c5 WinForm \u4e2d\u95f4\u4ef6\u89e3\u51b3\u65b9\u6848 \u672c\u6b21\u4e3a MES\/ERP \u573a\u666f\u6253\u9020\u5de5\u5e8f\u6d41\u7a0b\u4e1a\u52a1\u6267\u884c\u4e8b\u4ef6\u7684\u591a\u5c42\u7ea7\u59d4\u6258\u62fc\u63a5\u5c01\u88c5WinForm \u4fe1\u606f\u4e2d\u95f4\u4ef6&#xff0c;\u6838\u5fc3\u56f4\u7ed5\u5de5\u5e8f\u6d41\u7a0b\u7684\u751f\u547d\u5468\u671f\u4e8b\u4ef6&#xff08;\u521d\u59cb\u5316\u3001\u542f\u52a8\u3001\u6267\u884c\u3001\u5b8c\u6210\u3001\u5f02\u5e38\u3001\u8df3\u8f6c\u7b49&#xff09;&#xff0c;\u8bbe\u8ba1\u591a\u7ed3\u6784\u62fc\u63a5\u7684\u59d4\u6258\u51fd\u6570\u4f53\u7cfb&#xff08;\u57fa\u7840\u59d4\u6258\u3001\u7ec4\u5408\u59d4\u6258\u3001\u6d41\u7a0b\u59d4\u6258&amp;#xf","og_url":"https:\/\/www.wsisp.com\/helps\/72579.html","og_site_name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","article_published_time":"2026-02-05T17:16:46+00:00","og_image":[{"url":"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/02\/20260205171643-6984d07ba2b2f.png"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"admin","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"26 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.wsisp.com\/helps\/72579.html","url":"https:\/\/www.wsisp.com\/helps\/72579.html","name":"MES\/ERP \u573a\u666f\u591a\u5c42\u7ea7\u59d4\u6258\u5c01\u88c5\u7ec4\u4ef6 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","isPartOf":{"@id":"https:\/\/www.wsisp.com\/helps\/#website"},"datePublished":"2026-02-05T17:16:46+00:00","dateModified":"2026-02-05T17:16:46+00:00","author":{"@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41"},"breadcrumb":{"@id":"https:\/\/www.wsisp.com\/helps\/72579.html#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.wsisp.com\/helps\/72579.html"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.wsisp.com\/helps\/72579.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/www.wsisp.com\/helps"},{"@type":"ListItem","position":2,"name":"MES\/ERP \u573a\u666f\u591a\u5c42\u7ea7\u59d4\u6258\u5c01\u88c5\u7ec4\u4ef6"}]},{"@type":"WebSite","@id":"https:\/\/www.wsisp.com\/helps\/#website","url":"https:\/\/www.wsisp.com\/helps\/","name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","description":"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"zh-Hans"},{"@type":"Person","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41","name":"admin","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/","url":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","contentUrl":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","caption":"admin"},"sameAs":["http:\/\/wp.wsisp.com"],"url":"https:\/\/www.wsisp.com\/helps\/author\/admin"}]}},"_links":{"self":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/72579","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/comments?post=72579"}],"version-history":[{"count":0,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/72579\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media\/72578"}],"wp:attachment":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media?parent=72579"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/categories?post=72579"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/tags?post=72579"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/topic?post=72579"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}