全部產品
Search
文件中心

Dataphin:自訂審批系統對接介面

更新時間:May 30, 2026

本文為您介紹Dataphin BPMS支援對接第三方審批流,包括開放了哪些介面能力,如何接入審批能力以及審批案例等內容。

前提條件

已在Dataphin系統配置審批設定,如何配置,詳情請參見審批設定。營運租戶參見審批設定選擇審批系統其他配置項。

審批介面

重要
  • {url}:為第三方審批流對接地址,http(s)://{url}為提交審批申請URL。

  • accessToken:調用服務端API的憑證。Dataphin頁面自動產生,或使用者產生填入Dataphin。

  • 串連測試

    請求方式:POST。

    請求地址:http(s)://{url}/dataphin/bpms/check/connect。

    請求時序圖

    image

    Query參數

    參數名稱

    參數類型

    描述

    accessToken

    String

    調用服務端API的憑證。

    Dataphin頁面自動產生,或使用者產生填入Dataphin。如:6d1bxxxx。

    timestamp

    String

    單位毫秒,資訊發送時間戳記。如:1654156836531。

    Body參數

    checkEvent(String):檢查事件為串連測試。如:connectivity。

    返回參數

    checkResult(String):檢查結果:成功,僅當傳回值為success時,認定為成功。其他值均被認為測試連接失敗。如:success。

  • 提交審批

    請求方式:POST。

    請求地址:http(s)://{url}/dataphin/bpms/processinstance/create。

    提交審批的時序圖

    image

    Query參數

    參數名稱

    參數類型

    描述

    accessToken

    String

    調用服務端API的憑證。

    Dataphin頁面自動產生,或使用者產生填入Dataphin。如:6d1bxxxx。

    timestamp

    String

    單位毫秒,資訊發送時間戳記。如:1654156836531。

    Body參數

    參數名稱

    參數類型

    描述

    applyId

    String

    Dataphin審批單Id。如:1223。

    title

    String

    Dataphin審批單標題。如:dataphin bpms。

    content

    String

    Dataphin審批單內容。如:bpms content。請參見審批訊息內容中繼資料描述

    type

    String

    • 審批單類型:APPROVAL_DOC_TYPE。

    • 代碼審核:CODE_REVIEW。

    • 發布管控:PUBLISH。

    • 業務規劃:BIZ_PLANNING。

    • 許可權審批:AUTH。

    • 預設:DEFAULT。

    templateCode

    String

    審批模板code,可為空白,與頁面配置有關。

    審批訊息內容中繼資料描述

    參數名稱

    參數類型

    描述

    resourceType

    String

    審批任務類型,為枚舉值,包含以下的類型:

    • PhysicalTable:物理表。

    • LogicTable:邏輯表。

    • MetaTable:即時元表。

    • MirrorTable:鏡像表。

    • Fun:函數。

    • DataSource:資料來源。

    • FeatureConfig:功能許可權配置。

    • OSAPP:資料服務APP。

    • OSAPI:資料服務API 。

    • OSLogicUnit:資料服務單元。

    • OSDS:資料服務資料來源。

    • SECRET_KEY:密鑰。

    • GLOBAL_PARAM:全域變數。

    grantToUsers

    List<GrantToUser>

    授權使用者列表。請參見GrantToUser

    bpmsEnvironment

    BpmsEnvironment

    審批系統內容資訊。請參見BpmsEnvironment

    operates

    List<String>

    申請的權限類別型,包含以下類型:

    • SYNC_READ:資料來源同步讀。

    • SYNC_WRITE:資料來源同步寫。

    • SQL_QUERY:物理表及邏輯表的SQL查詢。

    • SQL_WRITE:物理表的SQL寫入。

    • SQL_ALTER:物理表的SQL修改。

    • SQL_DROP:物理表及邏輯表的SQL刪除。

    • SELECT:資料服務API的查詢。

    • WRITE:寫入許可權。

    • DEV:開發許可權。

    • USE:使用許可權。

    • UPDATE:改表資料。

    • PIPELINE_ENCRY:整合加密。

    • PIPELINE_DECRY:整合解密。

    levels

    List<String>

    許可權等級,包含三個等級:HIGH、MIDDLE、LOW。

    operations

    List<String>

    權限類別型,包含以下類型:

    • SELECT-查詢。

    • DESCRIBE-查表結構。

    • UPDATE-改表資料。

    • ALTER-改表結構。

    • DELETE-刪除表。

    • COPY_TASK-複製。

    resources

    List<BpmsResource>

    資源內容,請參見BpmsResource

    applyObject

    ApplyObject

    申請對象資訊,請參見ApplyObject

    reason

    String

    申請原因。

    GrantToUser

    參數名稱

    參數類型

    描述

    account

    Account

    授權賬戶列表,請參見Account

    period

    Period

    有效期間,請參見Period

    Account

    參數名稱

    參數類型

    描述

    accountType

    String

    授與類型,包括個人帳號生產帳號應用帳號三種。

    • PERSONAL:個人帳號。

    • PRODUCE:生產帳號。

    • APPLICATION:應用帳號。

    userName

    String

    結束時間格式為yyyy-mm-dd。如:2022-09-11。

    Period

    參數名稱

    參數類型

    描述

    periodType

    String

    有效期間類型支援長期(LONG_TERM

    periodEnd

    String

    許可權到期時間格式為yyyy-mm-dd。如:2022-09-11。

    BpmsEnvironment

    參數名稱

    參數類型

    描述

    projectName

    String

    專案名稱。

    bizUnitName

    String

    業務板塊名稱。

    resourceEnv

    String

    環境分為生產環境和開發環境兩種環境。

    • PROD:生產。

    • DEV:開發。

    BpmsResource

    參數名稱

    參數類型

    描述

    resourceType

    String

    請參見審批訊息內容中繼資料描述

    resourceName

    String

    資源名稱。

    children

    List<Children>

    欄位列表,請參見Children

    operations

    List<String>

    請參見審批訊息內容中繼資料描述的operates。

    authTypes

    String

    申請權限類別型。

    Children

    參數名稱

    參數類型

    描述

    resourceName

    String

    欄位名稱。

    resourceProperties

    String

    欄位屬性。

    ResourceProperties

    參數名稱

    參數類型

    描述

    columnType

    String

    欄位類型。

    columnIsPartition

    String

    是否為分區欄位。

    columnIsPk

    String

    是否為主鍵。

    ApplyObject

    參數名稱

    參數類型

    描述

    objectName

    String

    對象名稱。

    codeContent

    String

    代碼內容。

    name

    String

    商務活動英文名稱。

    bizObjectType

    String

    物件類型。

    bizObjectChangeType

    String

    變更類型。

    bizObjectPkField

    String

    組件欄位。

    bizObjectParent

    String

    所屬父物件。

    bizObjectChildren

    String

    下遊子物件。

    bizProcessCn

    String

    活動名稱。

    bizProcessType

    String

    活動類型。

    bizProcessChangeType

    String

    變更類型。

    bizProcessNodes

    String

    流程節點。

    atomicIndexCn

    String

    指標名稱。

    dataType

    String

    資料類型。

    unit

    String

    計量單位。

    bizProcess

    String

    活動名稱。

    des

    String

    活動口徑。

    derivedLogic

    String

    衍生公司。

    protoLogics

    String

    計算邏輯。

    返回參數

    processInstanceId(String):第三方審批執行個體ID。如:6d1bxxxx。

  • 撤銷審批

    說明

    提交審批後,若申請內容不符合預期,可撤銷審批。

    請求方式:POST。

    請求地址:http(s)://{url}/dataphin/bpms/processinstance/revoke。

    Query參數

    參數名稱

    參數類型

    描述

    accessToken

    String

    調用服務端API的憑證。

    Dataphin頁面自動產生,或使用者產生填入Dataphin。如:6d1bxxxx。

    timestamp

    String

    單位毫秒,資訊發送時間戳記。如:1654156836531。

    Body參數

    參數名稱

    參數類型

    描述

    applyId

    String

    Dataphin審批單Id。如:1223。

    processInstanceId

    String

    第三方審批執行個體Id。如:6d1bxxxx。

    operatingUserId

    String

    操作人工號。如:6d1bxxxx。

    返回參數

    result(String):撤回結果資訊。僅當傳回值為success時被認定為成功。

  • 查詢審批執行個體URL

    請求方式:GET。

    請求地址:http(s)://{url}/dataphin/bpms/processinstance/apply。

    Query參數

    參數名稱

    參數類型

    描述

    accessToken

    String

    調用服務端API的憑證。

    Dataphin頁面自動產生,或使用者產生填入Dataphin。如:6d1bxxxx。

    timestamp

    String

    單位毫秒,資訊發送時間戳記。如:1654156836531。

回調介面

重要
  • https://{callbackUrl}即為審批設定其他配置的Callback URL。

  • accessToken:調用服務端API的憑證。Dataphin頁面自動產生,或使用者產生填入Dataphin。

  • callBackAesKey:回調認證憑證。Dataphin頁面自動產生,或使用者產生填入Dataphin。

  • 回調方法

    請求方式:POST。

    請求地址:http://{callbackUrl}/api/oa/bpms/integration/thirdparty/callback。

    Query參數

    參數名稱

    參數類型

    描述

    signature

    String

    訊息認證資訊,請參見加解密方法。

    timestamp

    String

    單位毫秒,資訊發送時間戳記。如:1654156836531。

    nonce

    String

    隨機數,請參見加解密方法。

    Body參數

    encrypt(String):加密資訊。如:ajls384k。

    返回參數

    encrypt(String):回調狀態加密資訊。如:ajls384k。

  • 回調加密內容

    回調連通性校正

    • 傳入加密參數:

      applyStatus(String):檢查連通性參數。如:CHECK。

    • 返回加密參數:

      success:成功回調傳回值。

    回調返回bpms執行個體審批結果

    • 傳入加密參數:

      參數名稱

      參數類型

      描述

      applyId

      String

      Dataphin審批單Id。如:1223。

      processInstanceId

      String

      第三方審批執行個體Id。如:6d1bxxxx。

      comment

      String

      審批執行個體評論資訊。

      applyStatus

      String

      • accept:接受。

      • reject:拒絕。

      • revoke:撤銷。

      nodes

      List<ApplyCallBackNode>

      第三方審批系統側的審批節點列表,非必填,當對接方式選擇為顯示三方審批的所有節點時填入。

    • ApplyCallBackNode

      參數名稱

      參數類型

      描述

      nodeId

      String

      節點ID,從1到n進行排序。

      nodeStatus

      String

      節點狀態(ApproveNodeStatusEnum)。

      • APPROVING:審批中。

      • ACCEPTED:已通過。

      • REJECTED:已駁回。

      • REVOKED:已撤回。

      nodeName

      String

      節點名稱。

      approverRelation

      String

      審批關係(ApproveOperatorEnum)。

      • AND:多人均要審批方可通過。

      • OR:任何一人審批通過即可。

      pendingApprovers

      List<ExternalApproverInfo>

      當前待審批人列表,如果相關審批人已審批,則該欄位為空白。

      records

      List<OperationRecord>

      當審批人員執行操作時填入,按照時間先後順序排序。

    • ExternalApproverInfo

      參數名稱

      參數類型

      描述

      userId

      String

      第三方審批系統側的使用者ID。如:1223。

      userName

      String

      第三方審批系統側的使用者名稱。如:張三。

    • OperationRecord

      參數名稱

      參數類型

      描述

      operateUser

      ExternalApproverInfo

      操作人資訊。如:{"userId":"1111", "userName":"張三"}。

      action

      String

      操作類型(ApproveNodeActionType)。

      • ASSIGN_APPROVE:轉交許可權申請。

      • COUNTER_SIGN_APPROVE:加簽許可權申請。

      • REVOKE_APPLY:撤回許可權申請。

      • APPROVE:審批許可權。

      comment

      String

      操作評論。

      operatedTime

      String

      操作時間。

      transferApprovers

      List<ExternalApproverInfo>

      轉移目標人列表,非必填,當執行加簽或者轉交操作時填寫。如:[{"userId":"2222", "userName":"李四"}]。

    • 返回加密參數

      success:成功回調傳回值。

  • 回調返回bpms執行個體審批結果範例

    當前第三方審批系統支援僅顯示最終審批結果顯示三方審批的所有節點兩種對接方式。不同對接方式下,第三方審批系統返回的審批資訊內容各不相同。

    • 僅顯示最終審批結果

      該對接方式僅需返回審批系統的最終結果,系統中僅展示開始節點和結束節點。僅當第三方審批系統側的整體審批次程序結束後,調用該回調介面即可。回傳內容結構體範例如下:

      {
         "applyId": "123456",
         "applyStatus": "accept",
         "comment": "同意審批",
         "processInstanceId": "qwqwqqq"
      }
      說明
      • nodes欄位在該對接方式下無需填寫。

      • applyStatus欄位可填寫acceptrejectrevokeapproving表示審批中,為中間態,僅供顯示三方審批的所有節點對接方式下使用。

    • 顯示三方審批的所有節點

      該對接方式需返回審批系統每個節點的詳情,系統中將同步展示所有審批節點。當某一審批人員執行審批操作時,即可通過該回調介面將當前的審批節點語音總機至Dataphin系統。

      涉及欄位的解釋說明
      • 該對接方法下需要傳入applyIdapplyStatusprocessInstanceIdnodes欄位。

      • 整體審批單狀態applyStatus,可選值:approving、accept、reject、revoke。

        • 當存在某一審批節點流程尚未結束時,applyStatus設定為approving。

        • 當所有審批節點均審批通過時,applyStatus設定為accept。

        • 當存在某一審批節點被審批拒絕時,applyStatus設定為reject。

        • 當存在某一審批節點被審批撤回時,applyStatus設定為revoke。

      • 審批節點列表根據內部的nodeId進行排序,nodeId格式為1,2,3。

      • 審批節點狀態nodeStatus,可選值:APPROVING、ACCEPTED、REJECTED和REVOKED。

        • 如果當前審批節點流程未結束,nodeStatus設定為APPROVING。

        • 如果當前審批節點審批通過,nodeStatus設定為ACCEPTED。

        • 如果當前審批節點審批拒絕,nodeStatus設定為REJECTED。

        • 如果當前審批節點審批撤回,nodeStatus設定為REVOKED。

      • 審批節點展示名稱nodeName。

      • 節點審批關係approverRelation,可選值:OR和AND。

        • OR:任一審批人審批處理即可通過。

        • AND:所有人審批處理方可通過。

      • 待審批人列表pendingApprovers。

        • 未執行審批處理的審批人列表。

        • 當有相關人員執行審批操作時,下次傳輸時,需要從待審批列表中移除,然後寫入到操作記錄列表中。

      • 操作記錄列表records。

        • 執行操作的操作人資訊operateUser。

        • 操作評論comment。

        • 操作時間operatedTime,操作記錄列表按照操作時間升序排列。

        • 操作類型action,可選值:ASSIGN_APPROVE、COUNTER_SIGN_APPROVE、REVOKE_APPLY、APPROVE。

          • 當節點審批關係為OR,待審批人中某一審批人通過,action設定為APPROVE,同時審批節點狀態nodeStatus設定為ACCEPTED。

          • 當節點審批關係為OR,待審批人中某一審批人拒絕,action設定為APPROVE,同時審批節點狀態nodeStatus設定為REJECTED。

          • 當節點審批關係為OR,待審批人中某一審批人撤回,action設定為REVOKE_APPLY,同時審批節點狀態nodeStatus設定為REVOKED。

          • 當待審批人中某一審批人執行轉交操作,action設定為ASSIGN_APPROVE,同時審批節點狀態nodeStatus設定為ACCEPTED。

          • 當待審批人中某一審批人執行加簽操作,action設定為COUNTER_SIGN_APPROVE,同時審批節點狀態nodeStatus設定為ACCEPTED。

          • 當節點審批關係為AND,某一審批人通過,action設定為APPROVE。如果剩餘人未執行審批,審批節點狀態仍為APPROVING;當所有人審批通過時,審批節點狀態nodeStatus設定為ACCEPTED。

          • 當節點審批關係為AND,某一審批人拒絕,action設定為APPROVE,審批節點狀態nodeStatus設定為ACCEPTED。

          • 當節點審批關係為AND,某一審批人撤回,action設定為REVOKE_APPLY,審批節點狀態nodeStatus設定為ACCEPTED。

        • 轉移目標人列表transferApprovers,記錄被加簽或者轉交的人員資訊。

      不同審批情境下審批內容的結構體
      • 多個審批節點,審批關係為或關係,每個審批節點均未進行審批。

        範例如下:

        某審批單在第三方審批系統側存在兩個審批節點,節點間通過nodeId排序。

        第一個審批節點,待審批人張三和李四均未進行審批,待審批人列表pendingApprovers中記錄張三和李四的相關資訊,節點審批狀態nodeStatus為APPROVING,因兩位待審批人均未執行操作,所以records欄位為空白。

        第二個審批節點,待審批人王五未進行審批,待審批人列表pendingApprovers中記錄王五的相關資訊,且因王五還未進行審批,所以records欄位為空白。

        兩個審批節點均不是終態,整體審批單狀態為approving。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "approving",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "OR",
                 "pendingApprovers": [
                    {
                       "userId": "1111",
                       "userName": "張三"
                    },
                    {
                       "userId": "2222",
                       "userName": "李四"
                    }
                 ],
                 "records": []
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "OR",
                 "pendingApprovers": [
                    {
                       "userId": "33333",
                       "userName": "王五"
                    }
                 ],
                 "records": []
              }
           ]
        }
      • 多個審批節點,審批關係為或關係,第一個審批節點中部分審批人審批通過,部分審批人未進行審批,第二個審批節點中所有審批人均未進行審批操作。

        範例如下:

        某審批單在第三方審批系統側存在兩個審批節點,節點間通過nodeId排序。

        第一個審批節點中,張三同意了審批,李四未執行審批相關操作。待審批人列表pendingApprovers中僅記錄李四的資訊。審批節點審批關係為OR,因張三同意了審批,所以節點審批狀態nodeStatus為ACCEPTED。操作記錄列表records中記錄張三的操作資訊,張三執行了同意審批,action欄位為APPROVE,comment欄位填寫操作評論資訊。

        第二個審批節點,所有審批人未執行審批操作。節點審批狀態nodeStatus為APPROVING,待審批人列表pendingApprovers中記錄待審批人王五的資訊。

        該審批單總共存在兩個審批節點,因第二個審批節點沒有人執行審批操作,所以整體審批單狀態applyStatus為approving。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "approving",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "ACCEPTED",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "OR",
                 "pendingApprovers": [
                    {
                       "userId": "2222",
                       "userName": "李四"
                    }
                 ],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "1111",
                          "userName": "張三"
                       },
                       "action": "APPROVE",
                       "comment": "同意該表許可權審批",
                       "operatedTime": "2026-03-19 19:01:23"
                    }
                 ]
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "OR",
                 "pendingApprovers": [
                    {
                       "userId": "33333",
                       "userName": "王五"
                    }
                 ],
                 "records": []
              }
           ]
        }
      • 多個審批節點,審批關係為或關係,第一個審批節點中部分審批人審批通過,部分審批人未執行審批操作,第二個審批節點所有審批人審批通過。

        範例如下:

        某審批單在第三方審批系統側存在兩個審批節點,節點間通過nodeId排序。

        第一個審批節點中,張三同意了審批,李四未執行審批相關操作。待審批人列表pendingApprovers中僅記錄李四的資訊。審批節點審批關係為OR,因張三同意了審批,所以節點審批狀態nodeStatus為ACCEPTED。操作記錄列表records中記錄張三的操作資訊,張三執行了同意審批,action欄位為APPROVE,comment欄位填寫操作評論資訊。

        第二個審批節點,待審批人只有王五, 且同意了審批。節點審批狀態nodeStatus為 ACCEPTED,待審批人列表pendingApprovers為空白。操作記錄列表records中記錄王五的操作資訊,王五執行了同意審批,action欄位為APPROVE,comment欄位填寫操作評論資訊。

        該審批單總共存在兩個審批節點,並且每個審批節點均已達到終態,審批通過,所以整體審批單狀態applyStatus為accept。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "accept",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "ACCEPTED",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "OR",
                 "pendingApprovers": [
                    {
                       "userId": "2222",
                       "userName": "李四"
                    }
                 ],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "1111",
                          "userName": "張三"
                       },
                       "action": "APPROVE",
                       "comment": "同意該表許可權審批",
                       "operatedTime": "2026-03-19 19:01:23"
                    }
                 ]
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "ACCEPTED",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "OR",
                 "pendingApprovers": [],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "33333",
                          "userName": "王五"
                       },
                       "action": "APPROVE",
                       "comment": "王五同意許可權審批",
                       "operatedTime": "2026-03-19 19:07:21"
                    }
                 ]
              }
           ]
        }
      • 多個審批節點,審批關係為或關係,第一個審批節點中部分審批人審批通過,部分審批人未執行審批操作,第二個審批節點中所有審批人審批拒絕。

        範例如下:

        某審批單在第三方審批系統側存在兩個審批節點,節點間通過nodeId排序。

        第一個審批節點中,張三同意了審批,李四未執行審批相關操作。待審批人列表pendingApprovers中僅記錄李四的資訊。審批節點審批關係為OR,因張三同意了審批,所以節點審批狀態nodeStatus為ACCEPTED。操作記錄列表records中記錄張三的操作資訊,張三執行了同意審批,action欄位設定為APPROVE,comment欄位填寫操作評論資訊。

        第二個審批節點,待審批人只有王五, 且拒絕了審批。節點審批狀態nodeStatus為REJECTED,待審批人列表pendingApprovers為空白。操作記錄列表records中記錄王五的操作資訊,王五執行了拒絕操作,action欄位為APPROVE,comment 欄位填寫操作評論資訊。

        該審批單總共存在兩個審批節點,每個審批節點均已達到終態,其中第二個審批節點審批拒絕,所以整體審批單狀態applyStatus為reject。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "reject",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "ACCEPTED",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "OR",
                 "pendingApprovers": [
                    {
                       "userId": "2222",
                       "userName": "李四"
                    }
                 ],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "1111",
                          "userName": "張三"
                       },
                       "action": "APPROVE",
                       "comment": "同意該表許可權審批",
                       "operatedTime": "2026-03-19 19:01:23"
                    }
                 ]
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "REJECTED",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "OR",
                 "pendingApprovers": [],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "33333",
                          "userName": "王五"
                       },
                       "action": "APPROVE",
                       "comment": "王五拒絕了許可權審批",
                       "operatedTime": "2026-03-19 19:07:21"
                    }
                 ]
              }
           ]
        }
      • 多個審批節點,審批關係為或關係,第一個審批節點中部分審批人審批通過,部分審批人未執行審批操作,第二個審批節點中審批人審批撤回。

        範例介紹:

        某審批單在第三方審批系統側存在兩個審批節點,節點間通過nodeId排序。

        第一個審批節點中,張三同意了審批,李四未執行審批相關操作。待審批人列表pendingApprovers中僅記錄李四的資訊。審批節點審批關係為OR,因張三同意了審批,所以節點審批狀態nodeStatus為ACCEPTED。操作記錄列表records記錄張三的操作資訊,張三執行了同意審批,action欄位為APPROVE,comment欄位填寫操作評論資訊。

        第二個審批節點,待審批人只有王五, 且執行了審批撤回。節點審批狀態nodeStatus為REVOKED,待審批人列表pendingApprovers 為空白。操作記錄列表 records記錄王五的操作資訊,王五執行了審批撤回操作,action欄位為REVOKE_APPLY,comment欄位填寫操作評論資訊。

        該審批單總共存在兩個審批節點,每個審批節點均已達到終態,其中第二個審批節點審批撤回 ,所以整體審批單狀態applyStatus為revoke。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "revoke",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "ACCEPTED",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "OR",
                 "pendingApprovers": [
                    {
                       "userId": "2222",
                       "userName": "李四"
                    }
                 ],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "1111",
                          "userName": "張三"
                       },
                       "action": "APPROVE",
                       "comment": "同意該表許可權審批",
                       "operatedTime": "2026-03-19 19:01:23"
                    }
                 ]
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "REVOKED",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "OR",
                 "pendingApprovers": [],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "33333",
                          "userName": "王五"
                       },
                       "action": "REVOKE_APPLY",
                       "comment": "王五撤回該許可權審批",
                       "operatedTime": "2026-03-19 19:07:21"
                    }
                 ]
              }
           ]
        }
      • 多個審批節點,審批關係為且關係,第一個審批節點部分審批人審批通過,部分審批人未執行審批操作,第二個審批節點所有審批人未執行審批操作。

        範例如下:

        某審批單在第三方審批系統側存在兩個審批節點,節點間通過nodeId排序。

        第一個審批節點中,張三同意了審批,李四未執行審批相關操作。待審批人列表pendingApprovers中僅記錄李四的資訊。審批節點審批關係為AND,張三同意了審批,李四未進行審批,節點審批狀態為APPROVING。操作記錄列表records記錄張三的操作資訊,張三執行了同意審批,action欄位設定為APPROVE,comment欄位填寫操作評論資訊。

        第二個審批節點,所有審批人未執行審批操作。節點審批狀態nodeStatus為 APPROVING,待審批人列表pendingApprovers記錄待審批人王五的資訊。

        該審批單總共存在兩個審批節點,每個審批節點均未達到終態,所以整體審批單狀態applyStatus為approving。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "approving",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "2222",
                       "userName": "李四"
                    }
                 ],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "1111",
                          "userName": "張三"
                       },
                       "action": "APPROVE",
                       "comment": "同意該表許可權審批",
                       "operatedTime": "2026-03-19 19:01:23"
                    }
                 ]
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "33333",
                       "userName": "王五"
                    }
                 ],
                 "records": []
              }
           ]
        }
      • 多個審批節點,審批關係為且關係,第一個審批節點所有人審批通過,第二個審批節點所有審批人未執行審批操作。

        範例如下:

        某審批單在第三方審批系統側存在兩個審批節點,節點間通過nodeId排序。

        第一個審批節點中,張三和李四均同意了審批。待審批人列表pendingApprovers為空白。審批節點審批關係為AND,張三和李四均同意了審批,所以節點審批狀態為ACCEPTED。操作記錄列表records中記錄張三和李四的操作資訊,張三執行了同意審批,action欄位為APPROVE,comment欄位填寫操作評論資訊;李四執行了同意審批,action欄位設定為APPROVE。

        第二個審批節點,所有審批人未執行審批操作。節點審批狀態nodeStatus設定為APPROVING。待審批人列表pendingApprovers記錄待審批人王五的資訊。

        該審批單總共存在兩個審批節點,第一個審批節點到達終態,第二個節點沒有達到終態,所以整體審批單狀態applyStatus設定為approving。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "approving",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "ACCEPTED",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "AND",
                 "pendingApprovers": [],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "1111",
                          "userName": "張三"
                       },
                       "action": "APPROVE",
                       "comment": "同意該表許可權審批",
                       "operatedTime": "2026-03-19 19:01:23"
                    },
                    {
                       "operateUser": {
                          "userId": "2222",
                          "userName": "李四"
                       },
                       "action": "APPROVE",
                       "comment": "同意該表許可權審批",
                       "operatedTime": "2026-03-19 19:02:23"
                    }
                 ]
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "33333",
                       "userName": "王五"
                    }
                 ],
                 "records": []
              }
           ]
        }
      • 多個審批節點,審批關係為且關係,第一個審批節點部分審批人執行拒絕操作,第二個審批節點所有審批人未執行審批操作。

        範例如下:

        某審批單在第三方審批系統側存在兩個審批節點,節點間通過nodeId排序。

        第一個審批節點中,存在兩個審批人,張三同意了審批,李四拒絕了審批。兩個審批人均執行了操作,所以待審批人列表pendingApprovers為空白。審批節點審批關係為AND, 因李四拒絕了審批,所以節點審批狀態設定為REJECTED。操作記錄列表records記錄張三和李四的操作資訊,張三執行了同意審批,action欄位為APPROVE,comment欄位填寫操作評論資訊;李四執行了拒絕審批,action欄位為 APPROVE。

        第二個審批節點,所有審批人未執行審批操作。節點審批狀態nodeStatus為APPROVING。待審批人列表pendingApprovers記錄待審批人王五的資訊。

        該審批單總共存在兩個審批節點,第一個審批節點中審批人執行了拒絕操作,終止了審批次程序,所以整體審批單狀態applyStatus為reject。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "reject",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "REJECTED",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "AND",
                 "pendingApprovers": [],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "1111",
                          "userName": "張三"
                       },
                       "action": "APPROVE",
                       "comment": "同意該表許可權審批",
                       "operatedTime": "2026-03-19 19:01:23"
                    },
                    {
                       "operateUser": {
                          "userId": "2222",
                          "userName": "李四"
                       },
                       "action": "APPROVE",
                       "comment": "拒絕該表許可權審批",
                       "operatedTime": "2026-03-19 19:02:23"
                    }
                 ]
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "33333",
                       "userName": "王五"
                    }
                 ],
                 "records": []
              }
           ]
        }
      • 多個審批節點,審批關係為且關係,第一個審批節點部分審批人執行撤回操作,第二個審批節點所有審批人未執行審批操作。

        範例如下:

        某審批單在第三方審批系統側存在兩個審批節點,節點間通過nodeId排序。

        第一個審批節點中,存在兩個審批人,張三同意了審批,李四執行了撤回審批。兩個審批人均執行了操作,所以待審批人列表pendingApprovers為空白。審批節點審批關係為AND, 因李四執行了撤回審批,所以節點審批狀態設定為REVOKED。操作記錄列表records中記錄張三和李四的操作資訊,張三執行了同意審批,action欄位為APPROVE,comment欄位填寫操作評論資訊;李四執行了撤回審批,action欄位為REVOKE_APPLY。

        第二個審批節點,所有審批人均未執行審批操作。節點審批狀態nodeStatus設定為 APPROVING。待審批人列表pendingApprovers記錄待審批人王五的資訊。

        該審批單總共存在兩個審批節點,第一個審批節點中審批人執行了撤回操作,終止了審批次程序,所以整體審批單狀態applyStatus設定為revoke。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "revoke",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "REVOKED",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "AND",
                 "pendingApprovers": [],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "1111",
                          "userName": "張三"
                       },
                       "action": "APPROVE",
                       "comment": "同意該表許可權審批",
                       "operatedTime": "2026-03-19 19:01:23"
                    },
                    {
                       "operateUser": {
                          "userId": "2222",
                          "userName": "李四"
                       },
                       "action": "REVOKE_APPLY",
                       "comment": "撤回該表許可權審批",
                       "operatedTime": "2026-03-19 19:02:23"
                    }
                 ]
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "33333",
                       "userName": "王五"
                    }
                 ],
                 "records": []
              }
           ]
        }
      • 多個審批節點,審批關係為且關係,第一個審批節點審批人執行了轉交操作,第二個審批節點所有審批人均未執行審批操作。

        範例如下:

        某審批單起初存在兩個審批節點,第一個審批節點存在審批人張三和李四,第二個審批節點存在審批人王五,兩個節點均處於待審批的狀態。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "approving",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "1111",
                       "userName": "張三"
                    },
                    {
                       "userId": "2222",
                       "userName": "李四"
                    }
                 ],
                 "records": []
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "33333",
                       "userName": "王五"
                    }
                 ],
                 "records": []
              }
           ]
        }

        隨後,第一個審批節點中審批人張三執行了轉交操作,轉交給小七進行審批。 此時將新增一個審批節點,代錶轉交的流程。待審批人列表pendingApprovers為空白。操作記錄列表records記錄張三轉交至小七的操作,action為ASSIGN_APPROVE,代錶轉交操作。comment填寫評論資訊,operateUser填寫操作人張三的資訊,transferApprovers填寫被轉交人小七的資訊。新增審批節點的節點狀態nodeStatus為ACCEPTED。

        張三將審批轉交至小七後,待審批人已更新為李四和小七,李四和小七未進行審批,pendingApprovers中記錄兩者資訊。操作記錄列表records記錄為空白,節點審批狀態為APPROVING。因為上述新增了一個審批節點,代錶轉交流程,則起初的兩個審批節點進行順延,nodeId變為2和3。

        第二個審批節點,所有審批人未執行審批操作。節點審批狀態nodeStatus設定為 APPROVING。待審批人列表pendingApprovers記錄待審批人王五的資訊。

        第二個和第三個審批節點狀態未到達終態,整體節點審批狀態設定為approving。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "approving",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "ACCEPTED",
                 "nodeName": "轉交節點測試1",
                 "approverRelation": "AND",
                 "pendingApprovers": [],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "1111",
                          "userName": "張三"
                       },
                       "action": "ASSIGN_APPROVE",
                       "comment": "轉交至七仔審批",
                       "transferApprovers": {
                          "userId": "7777",
                          "userName": "七仔"
                       },
                       "operatedTime": "2026-03-19 19:01:23"
                    }
                 ]
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "7777",
                       "userName": "七仔"
                    },
                    {
                       "userId": "2222",
                       "userName": "李四"
                    }
                 ],
                 "records": []
              },
              {
                 "nodeId": "3",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "33333",
                       "userName": "王五"
                    }
                 ],
                 "records": []
              }
           ]
        }
      • 多個審批節點,審批關係為且關係,第一個審批節點審批人執行了加簽操作,第二個審批節點所有審批人未執行審批操作。

        範例如下:

        某審批單起初存在兩個審批節點,第一個審批節點存在審批人張三和李四,第二個審批節點存在審批人王五,兩個節點均處於待審批的狀態。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "approving",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點1",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "1111",
                       "userName": "張三"
                    },
                    {
                       "userId": "2222",
                       "userName": "李四"
                    }
                 ],
                 "records": []
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "33333",
                       "userName": "王五"
                    }
                 ],
                 "records": []
              }
           ]
        }

        隨後,第一個審批節點中審批人張三執行了加簽操作,先讓小七進行審批。操作記錄列表records記錄張三轉交至小七的操作。action為ASSIGN_APPROVE, 表示轉交操作。comment填寫評論資訊,operateUser填寫操作人張三的資訊。transferApprovers填寫被轉交人小七的資訊。新增審批節點的節點狀態nodeStatus為ACCEPTED。

        新增兩個審批節點,代表張三加簽和小七審批的流程。

        其中新增的第一個審批節點,代表張三加簽小七,待審批人列表pendingApprovers設定為空白,節點審批狀nodeStatus為ACCEPTED,操作記錄列表records記錄張三加簽至小七的操作,action為COUNTER_SIGN_APPROVE,表示加簽操作。comment填寫評論資訊,operateUser填寫操作人張三的相關資訊,transferApprovers填寫被加簽人小七的相關資訊。

        新增的第二個審批節點,代表被加簽人小七待審批,待審批人列表pendingApprovers記錄待審批人小七的相關資訊。操作記錄列表records記錄為空白,節點審批狀態nodeStatus為APPROVING。

        因為上述新增了兩個審批節點代表加簽流程,起初的兩個審批節點進行順延,nodeId變為3和4。

        第三個審批節點,待審批人張三和李四均未進行審批,待審批人列表pendingApprovers記錄張三和李四的相關資訊。節點審批狀態nodeStatus為 APPROVING。兩位待審批人均未執行操作,records欄位為空白。

        第四個審批節點,所有審批人未執行審批操作。節點審批狀態nodeStatus為APPROVING。待審批人列表pendingApprovers記錄待審批人王五的資訊。

        因為第二個、第三個和第四個審批節點狀態均未達到終態,整體節點審批狀態為approving。

        {
           "applyId": "123456",
           "processInstanceId": "qwqwqeqe",
           "applyStatus": "approving",
           "nodes": [
              {
                 "nodeId": "1",
                 "nodeStatus": "ACCEPTED",
                 "nodeName": "加簽節點測試1",
                 "approverRelation": "AND",
                 "pendingApprovers": [],
                 "records": [
                    {
                       "operateUser": {
                          "userId": "1111",
                          "userName": "張三"
                       },
                       "action": "COUNTER_SIGN_APPROVE",
                       "comment": "加簽至七仔審批",
                       "transferApprovers": {
                          "userId": "7777",
                          "userName": "七仔"
                       },
                       "operatedTime": "2026-03-19 19:01:23"
                    }
                 ]
              },
              {
                 "nodeId": "2",
                 "nodeStatus": "APPROVING",
                 "nodeName": "加簽節點七仔審批",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "7777",
                       "userName": "七仔"
                    }
                 ],
                 "records": []
              },
              {
                 "nodeId": "3",
                 "nodeStatus": "APPROVING",
                 "nodeName": "",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "1111",
                       "userName": "張三"
                    },
                    {
                       "userId": "2222",
                       "userName": "李四"
                    }
                 ],
                 "records": []
              },
              {
                 "nodeId": "4",
                 "nodeStatus": "APPROVING",
                 "nodeName": "審批測試節點2",
                 "approverRelation": "AND",
                 "pendingApprovers": [
                    {
                       "userId": "33333",
                       "userName": "王五"
                    }
                 ],
                 "records": []
              }
           ]
        }
  • 加解密方法

    加密(以bpms接收回調為例):

    Map<String, String> callBackJson = Maps.newHashMap();
    callBackJson.put("applyId", "1");
    callBackJson.put("applyStatus", "accept");
    callBackJson.put("processInstanceId", "2");
    callBackJson.put("comment", "test");
    // 如果對接方式選擇 “顯示三方審批的所有節點”,需要填充 nodes 欄位資訊
    String nodesJson = "[{"approverRelation":"OR","nodeId":"1","nodeName":"執行通過節點測試","nodeStatus":"ACCEPTED","pendingApprovers":[],"records":[{"action":"APPROVE","comment":"同意審批","operateUser":{"userId":"22222","userName":"張三"},"operatedTime":"2026-03-18 15:14:23"}]}]";
    callBackJson.put("nodes", nodesJson)
    String callBackResult = JSON.toJSONString(callBackJson);
    
    ThirdPartyCrypto callbackCrypto = new ThirdPartyCrypto(token, aesKey);
    
    String timestamp = String.valueOf(System.currentTimeMillis());
    String nonce = ThirdPartyCrypto.Utils.getRandomStr(16);
    String encrypt = callbackCrypto.encrypt(nonce, callBackResult);
    String signature = callbackCrypto.getSignature(token, timestamp, nonce, encrypt);

    解密(以bpms返回成功資訊為例):

    String encryptMsg = JSONObject.parseObject(encrypt);
    String decryptMsg = callbackCrypto.getDecryptMsg(signature, timestamp, nonce, encryptMsg);
    if ("success".equalsIgnoreCase(decryptMsg)) {
     log.error("call back success");
    }

    加解密工具

    import com.alibaba.fastjson.JSON;
    import org.apache.commons.codec.binary.Base64;
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.ByteArrayOutputStream;
    import java.lang.reflect.Field;
    import java.nio.charset.Charset;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.security.Permission;
    import java.security.PermissionCollection;
    import java.security.Security;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Random;
    
    /**
     * Dataphin開放平台加解密方法
     */
    public class ThirdPartyCrypto {
    
     private static final Charset CHARSET = StandardCharsets.UTF_8;
     private static final Base64 BASE_64 = new Base64();
     private final byte[] AES_KEY;
     private final String TOKEN;
    
     /**
     * ask getPaddingBytes key固定長度
     **/
     private static final Integer AES_ENCODE_KEY_LENGTH = 43;
     /**
     * 加密隨機字串位元組長度
     **/
     private static final Integer RANDOM_LENGTH = 16;
    
     /**
     * 建構函式
     *
     * @param token 開發人員設定的token
     * @param encodingAesKey 開發人員設定的EncodingAESKey
     *
     * @throws ThirdPartyEncryptException 執行失敗,請查看該異常的錯誤碼和具體的錯誤資訊
     */
     public ThirdPartyCrypto(String token, String encodingAesKey) throws ThirdPartyEncryptException {
     if (null == encodingAesKey || encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.AES_KEY_ILLEGAL);
     }
     this.TOKEN = token;
     AES_KEY = Base64.decodeBase64(encodingAesKey + "=");
     }
    
     public Map<String, String> getEncryptedMap(String plaintext) throws ThirdPartyEncryptException {
     return getEncryptedMap(plaintext, System.currentTimeMillis(), Utils.getRandomStr(16));
     }
    
     /**
     * 將和Dataphin同步的訊息體加密,返回加密Map
     *
     * @param plaintext 傳遞的訊息體明文
     * @param timeStamp 時間戳記
     * @param nonce 隨機字串
     * @return
     * @throws ThirdPartyEncryptException
     */
     public Map<String, String> getEncryptedMap(String plaintext, Long timeStamp, String nonce)
     throws ThirdPartyEncryptException {
     if (null == plaintext) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);
     }
     if (null == timeStamp) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL);
     }
     if (null == nonce) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.ENCRYPTION_NONCE_ILLEGAL);
     }
     // 加密
     String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);
     String signature = getSignature(TOKEN, String.valueOf(timeStamp), nonce, encrypt);
     Map<String, String> resultMap = new HashMap<String, String>();
     resultMap.put("msg_signature", signature);
     resultMap.put("encrypt", encrypt);
     resultMap.put("timeStamp", String.valueOf(timeStamp));
     resultMap.put("nonce", nonce);
     return resultMap;
     }
    
     /**
     * 密文解密
     *
     * @param msgSignature 簽名串
     * @param timeStamp 時間戳記
     * @param nonce 隨機串
     * @param encryptMsg 密文
     * @return 解密後的原文
     * @throws ThirdPartyEncryptException
     */
     public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)
     throws ThirdPartyEncryptException {
     //校正簽名
     String signature = getSignature(TOKEN, timeStamp, nonce, encryptMsg);
     if (!signature.equals(msgSignature)) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_SIGNATURE_ERROR);
     }
     // 解密
     return decrypt(encryptMsg);
     }
    
     /**
     * 對明文加密.
     * @param plaintext 需要加密的明文
     * @return 加密後base64編碼的字串
     */
     public String encrypt(String random, String plaintext) throws ThirdPartyEncryptException {
     try {
     byte[] randomBytes = random.getBytes(CHARSET);
     byte[] plainTextBytes = plaintext.getBytes(CHARSET);
     byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);
     ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
     byteStream.write(randomBytes);
     byteStream.write(lengthByte);
     byteStream.write(plainTextBytes);
     byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());
     byteStream.write(padBytes);
     byte[] unencrypted = byteStream.toByteArray();
     byteStream.close();
     Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
     SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, "AES");
     IvParameterSpec iv = new IvParameterSpec(AES_KEY, 0, 16);
     cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
     byte[] encrypted = cipher.doFinal(unencrypted);
     String result = BASE_64.encodeToString(encrypted);
     return result;
     } catch (Exception e) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);
     }
     }
    
     /**
     * 對密文進行解密.
     * @param text 需要解密的密文
     * @return 解密得到的明文
     */
     private String decrypt(String text) throws ThirdPartyEncryptException {
     byte[] originalArr;
     try {
     // 設定解密模式為AES的CBC模式
     Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
     SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, "AES");
     IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(AES_KEY, 0, 16));
     cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
     // 使用BASE64對密文進行解碼
     byte[] encrypted = Base64.decodeBase64(text);
     // 解密
     originalArr = cipher.doFinal(encrypted);
     } catch (Exception e) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);
     }
    
     String plainText;
     try {
     // 去除補位字元
     byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);
     // 分離16位隨機字串,網路位元組序和corpId
     byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
     int plainTextLegth = Utils.bytes2int(networkOrder);
     plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);
     } catch (Exception e) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);
     }
    
     return plainText;
     }
    
     /**
     * 數位簽章
     *
     * @param token isv token
     * @param timestamp 時間戳記
     * @param nonce 隨機串
     * @param encrypt 加密文本
     * @return
     * @throws ThirdPartyEncryptException
     */
     public String getSignature(String token, String timestamp, String nonce, String encrypt)
     throws ThirdPartyEncryptException {
     try {
     String[] array = new String[] {token, timestamp, nonce, encrypt};
     Arrays.sort(array);
     System.out.println(JSON.toJSONString(array));
     StringBuffer sb = new StringBuffer();
     for (int i = 0; i < 4; i++) {
     sb.append(array[i]);
     }
     String str = sb.toString();
     System.out.println(str);
     MessageDigest md = MessageDigest.getInstance("SHA-1");
     md.update(str.getBytes());
     byte[] digest = md.digest();
    
     StringBuffer hexstr = new StringBuffer();
     String shaHex = "";
     for (int i = 0; i < digest.length; i++) {
     shaHex = Integer.toHexString(digest[i] & 0xFF);
     if (shaHex.length() < 2) {
     hexstr.append(0);
     }
     hexstr.append(shaHex);
     }
     return hexstr.toString();
     } catch (Exception e) {
     throw new ThirdPartyEncryptException(ThirdPartyEncryptException.COMPUTE_SIGNATURE_ERROR);
     }
     }
    
     public static class Utils {
     public Utils() {
     }
    
     public static String getRandomStr(int count) {
     String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
     Random random = new Random();
     StringBuffer sb = new StringBuffer();
    
     for (int i = 0; i < count; ++i) {
     int number = random.nextInt(base.length());
     sb.append(base.charAt(number));
     }
    
     return sb.toString();
     }
    
     public static byte[] int2Bytes(int count) {
     byte[] byteArr = new byte[] {(byte)(count >> 24 & 255), (byte)(count >> 16 & 255), (byte)(count >> 8 & 255),
     (byte)(count & 255)};
     return byteArr;
     }
    
     public static int bytes2int(byte[] byteArr) {
     int count = 0;
    
     for (int i = 0; i < 4; ++i) {
     count <<= 8;
     count |= byteArr[i] & 255;
     }
    
     return count;
     }
     }
    
     public static class PKCS7Padding {
     private static final Charset CHARSET = StandardCharsets.UTF_8;
     private static final int BLOCK_SIZE = 32;
    
     public PKCS7Padding() {
     }
    
     public static byte[] getPaddingBytes(int count) {
     int amountToPad = 32 - count % 32;
     if (amountToPad == 0) {
     amountToPad = 32;
     }
    
     char padChr = chr(amountToPad);
     String tmp = new String();
    
     for (int index = 0; index < amountToPad; ++index) {
     tmp = tmp + padChr;
     }
    
     return tmp.getBytes(CHARSET);
     }
    
     public static byte[] removePaddingBytes(byte[] decrypted) {
     int pad = decrypted[decrypted.length - 1];
     if (pad < 1 || pad > 32) {
     pad = 0;
     }
    
     return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
     }
    
     private static char chr(int a) {
     byte target = (byte)(a & 255);
     return (char)target;
     }
     }
    
     public static class ThirdPartyEncryptException extends Exception {
     public static final int SUCCESS = 0;
     public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;
     public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;
     public static final int ENCRYPTION_NONCE_ILLEGAL = 900003;
     public static final int AES_KEY_ILLEGAL = 900004;
     public static final int SIGNATURE_NOT_MATCH = 900005;
     public static final int COMPUTE_SIGNATURE_ERROR = 900006;
     public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 900007;
     public static final int COMPUTE_DECRYPT_TEXT_ERROR = 900008;
     public static final int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009;
     public static final int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010;
     private static Map<Integer, String> msgMap = new HashMap();
     private Integer code;
    
     static {
     msgMap.put(0, "成功");
     msgMap.put(900001, "加密明文文本非法");
     msgMap.put(900002, "加密時間戳記參數非法");
     msgMap.put(900003, "加密隨機字串參數非法");
     msgMap.put(900005, "簽名不匹配");
     msgMap.put(900006, "簽名計算失敗");
     msgMap.put(900004, "不合法的aes key");
     msgMap.put(900007, "計算加密文字錯誤");
     msgMap.put(900008, "計算解密文字錯誤");
     msgMap.put(900009, "計算解密文字長度不匹配");
     msgMap.put(900010, "計算解密文字corpid不匹配");
     }
    
     public Integer getCode() {
     return this.code;
     }
    
     public ThirdPartyEncryptException(Integer exceptionCode) {
     super((String)msgMap.get(exceptionCode));
     this.code = exceptionCode;
     }
     }
     static {
     try {
     Security.setProperty("crypto.policy", "limited");
     RemoveCryptographyRestrictions();
     } catch (Exception var1) {
     }
    
     }
     private static void RemoveCryptographyRestrictions() throws Exception {
     Class<?> jceSecurity = getClazz("javax.crypto.JceSecurity");
     Class<?> cryptoPermissions = getClazz("javax.crypto.CryptoPermissions");
     Class<?> cryptoAllPermission = getClazz("javax.crypto.CryptoAllPermission");
     if (jceSecurity != null) {
     setFinalStaticValue(jceSecurity, "isRestricted", false);
     PermissionCollection defaultPolicy = (PermissionCollection)getFieldValue(jceSecurity, "defaultPolicy", (Object)null, PermissionCollection.class);
     if (cryptoPermissions != null) {
     Map<?, ?> map = (Map)getFieldValue(cryptoPermissions, "perms", defaultPolicy, Map.class);
     map.clear();
     }
    
     if (cryptoAllPermission != null) {
     Permission permission = (Permission)getFieldValue(cryptoAllPermission, "INSTANCE", (Object)null, Permission.class);
     defaultPolicy.add(permission);
     }
     }
    
     }
     private static Class<?> getClazz(String className) {
     Class clazz = null;
    
     try {
     clazz = Class.forName(className);
     } catch (Exception var3) {
     }
    
     return clazz;
     }
     private static void setFinalStaticValue(Class<?> srcClazz, String fieldName, Object newValue) throws Exception {
     Field field = srcClazz.getDeclaredField(fieldName);
     field.setAccessible(true);
     Field modifiersField = Field.class.getDeclaredField("modifiers");
     modifiersField.setAccessible(true);
     modifiersField.setInt(field, field.getModifiers() & -17);
     field.set((Object)null, newValue);
     }
     private static <T> T getFieldValue(Class<?> srcClazz, String fieldName, Object owner, Class<T> dstClazz) throws Exception {
     Field field = srcClazz.getDeclaredField(fieldName);
     field.setAccessible(true);
     return dstClazz.cast(field.get(owner));
     }
    
    }