全部產品
Search
文件中心

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

更新時間:Jan 25, 2025

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

前提條件

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

審批介面

重要

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

  1. 串連測試。

    請求方式:POST。

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

    請求時序圖

    image

    Query參數

    參數名稱

    參數類型

    描述

    accessToken

    String

    調用服務端API的憑證。

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

    timestamp

    String

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

    Body參數

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

    返回參數

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

  2. 提交審批。

    請求方式:POST。

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

    提交審批的時序圖

    image

    Query參數

    參數名稱

    參數類型

    描述

    accessToken

    String

    調用服務端API的憑證。

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

    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。如:6d1b2n1c。

    撤銷審批。

    說明

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

    請求方式:POST。

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

    Query參數

    參數名稱

    參數類型

    描述

    accessToken

    String

    調用服務端API的憑證。

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

    timestamp

    String

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

    Body參數

    參數名稱

    參數類型

    描述

    applyId

    String

    Dataphin審批單Id。如:1223。

    processInstanceId

    String

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

    operatingUserId

    String

    操作人工號。如:6d1bx1d2。

    返回參數

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

  3. 查詢審批執行個體Url。

    請求方式:GET。

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

    Query參數

    參數名稱

    參數類型

    描述

    accessToken

    String

    調用服務端API的憑證。

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

    timestamp

    String

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

回調介面

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

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

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

  • 回調方法。

    請求方式:POST。

    請求地址:http://{callbackUrl}。

    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。如:6d1b2a3c。

      comment

      String

      審批執行個體評論資訊。

      applyStatus

      String

      • accept:接受。

      • reject:拒絕。

      • revoke:撤銷。

    • 返回加密參數:

      success:成功回調傳回值。

  • 加解密方法。

    加密樣本:

    Map<String, String> callBackJson = Maps.newHashMap();
    callBackJson.put("applyId", "1");
    callBackJson.put("applyStatus", "accept");
    callBackJson.put("processInstanceId", "2");
    callBackJson.put("comment", "test");
    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);

    解密樣本:

    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));
     }
    
    }