UOF2 應用程式站點 教學 | EIP |一等一科技

by | 4 月 2, 2026 | 一等一UOF系統, 程式 | 0 comments

Views: 1

如果要做很複雜的簽核流程,就只能靠應用程式站點了,應用程式站點有個限制不能模擬流程。

準備工作-建立測試帳號

我先建立一些測試帳號

Image

大致上就是A-H

建立測試表單

(略)

DLL參考

Image

主要要參考網站bin資料夾下

  • Ede.Uof.WKF.dll
  • Ede.Uof.WKF.Design.dll

開始寫程式

專案同樣是要用.Net 類別庫 ,版本是.Net 4.8 這個過程可以參考Trigger那邊,我這邊建立了一個類別

Image

DemoExternal.cs 這邊依照自己需求命名寫就好

然後介面需要繼承ICallExternalDllSites

using Ede.Uof.Utility.Data;
using Ede.Uof.WKF.CustomExternal;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Xml;

namespace UOF_DEV
{
    public class DemoExternal : ICallExternalDllSites
    {
        public void Finally()
        {

        }
        public string GetExternalDllSites(string formInfo)
        {           
            return "";
        }
        public void OnError(Exception errorException)
        {

        }
    }
}

簡單寫寫如上

GetExternalDllSites() 的傳入資料formInfo

<ExternalFlowSite>
  <!--申請資訊-->
  <ApplicantInfo taskId="task代號" account="簽核帳號" formId="表單編號"
      formVersionId="表單版本編號" ></ApplicantInfo>

      <!--欄位結構-->
      <FormFieldValue>
      <FieldItem enableSearch="True" realValue=""
         fieldValue="BPM200700001" fieldId="no"/>
      <FieldItem enableSearch="True" realValue="" fieldValue="test"
         fieldId="test1" fillSiteId="" fillerAccount="admin" fillerUserGuid="admin"
         fillerName="系統管理員"/>
       </FormFieldValue>

       <!--站點歷程-->
       <SiteHistory>
          <Site siteId="" siteType="站點型態" order="0" signType="簽核型態">
          <Signer account ="簽核帳號" comment="意見" signResult="簽核結果"
             signTime="簽核時間!"></Signer>
          </Site>
       </SiteHistory>
  </ExternalFlowSite>

這邊資料大致上什麼都有了

  • 申請者是誰
  • TASK ID
  • 簽核歷程(站點歷程)
  • 填寫的資料內容(欄位結構)

基本上就是從填寫的資料內容判斷要給誰

從formInfo 取XML值的範例

private string GetValue(System.Xml.XmlDocument doc)
{
    //擷取 FormFieldValue
        var formFieldValueNode = doc.SelectSingleNode("/ExternalFlowSite/FormFieldValue");
        if (formFieldValueNode != null)
        {
            //在這裡可以根據表單欄位的值決定流程走向
            //例如:
            var fieldItemNode = formFieldValueNode.SelectSingleNode("FieldItem[@fieldId='test1']");
            if (fieldItemNode != null)
            {
                return  fieldItemNode.Attributes["fieldValue"].Value;
                //後續根據 fieldValue 的值決定流程走向
                //例如,如果 fieldValue 是 "test",則走簽核者A的流程;如果是 "test2",則走簽核者B的流程
            }
    }
    return null;
}

取好該取的值之後,我們就能處理回傳流程了

回傳簽核流程

空流程

/// <summary>
/// 空的流程
/// </summary>
/// <returns></returns>
private string EmptyFlow = "<ReturnValue><EmptyFlow /></ReturnValue>";

簽核流程xml

<ReturnValue>
    <Flow>
        <!--第一站點-->
        <!--signType:簽核型態; 0:一般;1:並簽;2:會簽-->
        <Site order="0" signType="0|1|2">
            <!--簽核者-->
            <Signers>
                <!--帳號必需要存在UOF-->
                <!--方法一、僅新增帳號-->
                <Signer account="簽核者帳號" />
            </Signers>
            <!--知會人員-->
            <Alerts>
                <!--帳號必需要存在UOF-->
                <Alert account="知會者帳號"/>
            </Alerts>
            <!--自訂簽核字詞 Approve:同意,Disapprove:否決,Return:退簽-->
            <CustomWords>
                <Approve zh-TW="自訂字詞" zh-CN="自訂字詞" en-US="自訂字詞" />
                <Disapprove zh-TW="自訂字詞" zh-CN="自訂字詞" en-US="自訂字詞" />
                <Return zh-TW="自訂字詞" zh-CN="自訂字詞" en-US="自訂字詞" />
            </CustomWords>
        </Site>
        <!--第二站點-->
        <!--方法二、新增帳號、部門、職級-->
        <Site order="1" signType="簽核型態">
            <Signers>
                <Signer account="簽核者帳號" groupId="簽核者部門" titleId="簽核者職級"/>
                <Signer account="簽核者帳號" groupId="簽核者部門" titleId="簽核者職級"/>
            </Signers>
            <Alerts>
                <Alert account="知會者帳號"/>
                <Alert account="知會者帳號"/>
            </Alerts>
            <CustomWords>
                <Approve zh-TW="自訂字詞" zh-CN="自訂字詞" en-US="自訂字詞" />
                <Disapprove zh-TW="自訂字詞" zh-CN="自訂字詞" en-US="自訂字詞" />
                <Return zh-TW="自訂字詞" zh-CN="自訂字詞" en-US="自訂字詞" />
            </CustomWords>
        </Site>
    </Flow>
</ReturnValue>

這邊主要處理幾件事

  1. 站點
  2. 簽核帳號
  3. 知會者
  4. 字詞
  5. 簽核型態

普遍只要處理 站點順序 簽核型態 簽核者 知會者

整理流程需求

我們可以簡單的把需求整理成這樣

  1. 流程1
    • 第一站 簽核者A B 「並簽」
    • 第二站 簽核者D 知會E 「一般簽核」
    • 第二站 簽核者 F G 「會簽」

簡化簽核程式

理論上可以用字串硬幹,不過也可以用物件導向的方式解決,我先把簽核的結構做成一個類別

簽核類別

/// <summary>
/// 簽核站點資訊  這邊沒處理字詞  只處理簽核者和知會者
/// </summary>
private class SiteInfo
{
    /// <summary>
    /// 順序 1 2 3 4 
    /// </summary>
    public string order { get; set; }

    /// <summary>
    /// 0:一般;1:並簽;2:會簽
    /// </summary>
    public string signType { get; set; }

    /// <summary>
    /// 簽核人員帳號
    /// </summary>
    public List<string> signers { get; set; }

    /// <summary>
    /// 知會者帳號
    /// </summary>
    public List<string> alerts { get; set; }
}

將物件建立流程xml

這邊照貼就好

/// <summary>
/// 根據站點資訊列表建立流程XML
/// </summary>
/// <param name="siteInfos">站點資訊列表</param>
/// <returns></returns>
private System.Xml.XmlDocument BuildFlowXml(List<SiteInfo> siteInfos)
{
    var doc = new System.Xml.XmlDocument();

    XmlElement returnValue = doc.CreateElement("ReturnValue");
    doc.AppendChild(returnValue);

    XmlElement flow = doc.CreateElement("Flow");
    returnValue.AppendChild(flow);

    foreach (var siteInfo in siteInfos)
    {
        XmlElement site = doc.CreateElement("Site");
        site.SetAttribute("order", siteInfo.order);
        site.SetAttribute("signType", siteInfo.signType);

        XmlElement signers = doc.CreateElement("Signers");
        if (siteInfo.signers != null)
        {
            foreach (var signerAccount in siteInfo.signers)
            {
                XmlElement signer = doc.CreateElement("Signer");
                signer.SetAttribute("account", signerAccount);
                signers.AppendChild(signer);
            }
        }
        site.AppendChild(signers);

        XmlElement alerts = doc.CreateElement("Alerts");
        if (siteInfo.alerts != null)
        {
            foreach (var alertAccount in siteInfo.alerts)
            {
                XmlElement alert = doc.CreateElement("Alert");
                alert.SetAttribute("account", alertAccount);
                alerts.AppendChild(alert);
            }
        }
        site.AppendChild(alerts);

        flow.AppendChild(site);
    }

    return doc;
}

設定簽核點

//這邊模擬簽核者
//第一站A 一般簽核
//第二站B、C 並簽 ,知會者D
//第三站F、G 會簽 知會者H
var siteInfos = new List<SiteInfo>();

//第一站:A 一般簽核
siteInfos.Add(new SiteInfo
{
    order = "0",
    signType = "0",
    signers = new List<string> { "A" },
    alerts = new List<string>()
});

//第二站:B、C 並簽,知會者D
siteInfos.Add(new SiteInfo
{
    order = "1",
    signType = "1",
    signers = new List<string> { "B", "C" },
    alerts = new List<string> { "D" }
});

//第三站:F、G 會簽,知會者H、I
siteInfos.Add(new SiteInfo
{
    order = "2",
    signType = "2",
    signers = new List<string> { "F", "G" },
    alerts = new List<string> { "H" }
});

var docSign = BuildFlowXml(siteInfos);

return docSign.OuterXml;

實際流程就看你需求兜一下,改成這樣應該好處理很多。

編譯 設定外部組件

Image

電子簽核 > 外部組件註冊

Image

設定簽核流程

Image

Image

Image

測試送出

Image

這邊我自己測試,沒辦法使用模擬的功能,務必要一站一站測試

完整程式碼

using Ede.Uof.Utility.Data;
using Ede.Uof.WKF.CustomExternal;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Xml;

namespace UOF_DEV
{
    public class DemoExternal : ICallExternalDllSites
    {
        public void Finally()
        {

        }

        public string GetExternalDllSites(string formInfo)
        {
            //傳入資訊

            // 取得表單資訊
         
            var doc = new System.Xml.XmlDocument();
            doc.LoadXml(formInfo);

            //取得TaskID
            var taskId = doc.SelectSingleNode("/ExternalFlowSite/ApplicantInfo").Attributes["taskId"].Value;

            //從表單內容擷取欄位值
            var fieldValue = GetValue(doc);

            //TODO: 從表單內容決定流程走向
            //假如符合空流程的條件 回傳空流程
            //if (true)
            //{
            //    return EmptyFlow;
            //}

            //假如符合簽核者的條件 回傳簽核者的XML範本
            if (true)
            {
                //這邊模擬簽核者
                //第一站A 一般簽核
                //第二站B、C 並簽 ,知會者D
                //第三站F、G 會簽 知會者H、I

                var siteInfos = new List<SiteInfo>();

                //第一站:A 一般簽核
                siteInfos.Add(new SiteInfo
                {
                    order = "0",
                    signType = "0",
                    signers = new List<string> { "A" },
                    alerts = new List<string>()
                });

                //第二站:B、C 並簽,知會者D
                siteInfos.Add(new SiteInfo
                {
                    order = "1",
                    signType = "1",
                    signers = new List<string> { "B", "C" },
                    alerts = new List<string> { "D" }
                });

                //第三站:F、G 會簽,知會者H、I
                siteInfos.Add(new SiteInfo
                {
                    order = "2",
                    signType = "2",
                    signers = new List<string> { "F", "G" },
                    alerts = new List<string> { "H" }
                });

                var docSign = BuildFlowXml(siteInfos);

                return docSign.OuterXml;
            }

            //其他情況回傳空流程
            return EmptyFlow;

        }


        /// <summary>
        /// 傳回簽核者的XML範本
        /// </summary>
        /// <returns></returns>
        public System.Xml.XmlDocument SiteReturnTemplate()
        {
            string template = @"

<!--傳給電子簽核資料-->
<ReturnValue>
	<Flow>
		<Site order=""0"" signType=""2"">
			<Signers>

			</Signers>			
            <Alerts>
            </Alerts>
		</Site>		
	</Flow>
</ReturnValue>
";
            var doc = new System.Xml.XmlDocument();
            doc.LoadXml(template);
            return doc;
        }

        /// <summary>
        /// 空的流程
        /// </summary>
        /// <returns></returns>
        private string EmptyFlow = "<ReturnValue><EmptyFlow /></ReturnValue>";

        /// <summary>
        /// 根據站點資訊列表建立流程XML
        /// </summary>
        /// <param name="siteInfos">站點資訊列表</param>
        /// <returns></returns>
        private System.Xml.XmlDocument BuildFlowXml(List<SiteInfo> siteInfos)
        {
            var doc = new System.Xml.XmlDocument();

            XmlElement returnValue = doc.CreateElement("ReturnValue");
            doc.AppendChild(returnValue);

            XmlElement flow = doc.CreateElement("Flow");
            returnValue.AppendChild(flow);

            foreach (var siteInfo in siteInfos)
            {
                XmlElement site = doc.CreateElement("Site");
                site.SetAttribute("order", siteInfo.order);
                site.SetAttribute("signType", siteInfo.signType);

                XmlElement signers = doc.CreateElement("Signers");
                if (siteInfo.signers != null)
                {
                    foreach (var signerAccount in siteInfo.signers)
                    {
                        XmlElement signer = doc.CreateElement("Signer");
                        signer.SetAttribute("account", signerAccount);
                        signers.AppendChild(signer);
                    }
                }
                site.AppendChild(signers);

                XmlElement alerts = doc.CreateElement("Alerts");
                if (siteInfo.alerts != null)
                {
                    foreach (var alertAccount in siteInfo.alerts)
                    {
                        XmlElement alert = doc.CreateElement("Alert");
                        alert.SetAttribute("account", alertAccount);
                        alerts.AppendChild(alert);
                    }
                }
                site.AppendChild(alerts);

                flow.AppendChild(site);
            }

            return doc;
        }


        private string GetValue(System.Xml.XmlDocument doc)
        {
            //擷取 FormFieldValue
                var formFieldValueNode = doc.SelectSingleNode("/ExternalFlowSite/FormFieldValue");
                if (formFieldValueNode != null)
                {
                    //在這裡可以根據表單欄位的值決定流程走向
                    //例如:
                    var fieldItemNode = formFieldValueNode.SelectSingleNode("FieldItem[@fieldId='test1']");
                    if (fieldItemNode != null)
                    {
                        return  fieldItemNode.Attributes["fieldValue"].Value;
                        //根據 fieldValue 的值決定流程走向
                        //例如,如果 fieldValue 是 "test",則走簽核者A的流程;如果是 "test2",則走簽核者B的流程
                    }
            }
            return null;
        }

        public void OnError(Exception errorException)
        {

        }

        /// <summary>
        /// 簽核站點資訊  這邊沒處理字詞  只處理簽核者和知會者
        /// </summary>
        private class SiteInfo
        {
            /// <summary>
            /// 順序 1 2 3 4 
            /// </summary>
            public string order { get; set; }

            /// <summary>
            /// 0:一般;1:並簽;2:會簽
            /// </summary>
            public string signType { get; set; }

            /// <summary>
            /// 簽核人員帳號
            /// </summary>
            public List<string> signers { get; set; }

            /// <summary>
            /// 知會者帳號
            /// </summary>
            public List<string> alerts { get; set; }

        }
    }
}

0 Comments

Submit a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *