Groovy代码示例


package com.facishare.function.core.template.groovy

import com.fxiaoke.functions.Fx
import com.fxiaoke.functions.http.HttpResult
import com.fxiaoke.functions.service.esig.ElectionSignPlugin
import com.fxiaoke.functions.service.esig.model.AddEmployees
import com.fxiaoke.functions.service.esig.model.CancelSign
import com.fxiaoke.functions.service.esig.model.DownloadSignFile
import com.fxiaoke.functions.service.esig.model.FileAttachment
import com.fxiaoke.functions.service.esig.model.GetSignUrl
import com.fxiaoke.functions.service.esig.model.InitiateSign
import com.fxiaoke.functions.service.esig.model.InnerAppSignerData
import com.fxiaoke.functions.service.esig.model.OuterAppSignerData
import com.fxiaoke.functions.service.esig.model.RemoveEmployees
import com.fxiaoke.functions.service.esig.model.SignCallback
import com.fxiaoke.functions.service.esig.model.SignLocation
import com.fxiaoke.functions.service.esig.model.TaskStatus
import com.fxiaoke.functions.service.esig.model.UrgeSign
import com.fxiaoke.functions.time.DateTime
import com.fxiaoke.functions.utils.Strings

import static com.fxiaoke.functions.Fx.log
/**
 * @type classes
 * @returntype
 * @namespace electronic_sign
 */
class Esign implements ElectionSignPlugin {

    //电子厂商url, 此处要替换
    private static final String BASE_URL = "https://smlopenapi.esign.cn"
    private static final String CONTENT_TYPE = "application/json; charset=UTF-8"
    private static final String CONTENT_TYPE_UPLOAD = "application/octet-stream"
    private static final String ACCEPT = "*/*"

    //应用id,此处要替换
    private static final String APP_ID = "7438894881";
    //应用key,此处要替换
    private static final String APP_KEY = "3155ca27042ed83469cf5baef3de84ef";
    //先用该方法获取租户账号id,写到上边, createCompanyAccount("xx公司"), 此处要替换
    private static final String AUTHORIZED_ACCOUNT_ID = "d8e826359a8c4eecbac9295f58b49603"
    //电子厂商通知纷享的接口url, 此处要替换
    private static final String CALLBACK_URL = "https://www.fxiaoke.com/customerprovider/callback/signCallback/xxx企业id加密值"

    /**
     * 拼接http请求头
     */
    private static Map getHeaders(String accept, String contentType, String signOpenCaSignature, String contentMD5) {
        DateTime d = DateTime.now();
        Map headerMap = [:];
        headerMap.put("X-Tsign-Open-App-Id", APP_ID);
        headerMap.put("X-Tsign-Open-Auth-Mode", "Signature");
        headerMap.put("X-Tsign-Open-Ca-Timestamp", String.valueOf(d.toTimestamp()));
        headerMap.put("Accept", accept);
        headerMap.put("Content-Type", contentType);
        headerMap.put("X-Tsign-Open-Ca-Signature", signOpenCaSignature);
        headerMap.put("Content-MD5", contentMD5);
        return headerMap;
    }
    /**
     * 拼接纯文本
     */
    private static String getPlaintext(String method, String accept, String contentMD5, String contentType, String uri) {
        String date = "";
        String plaintext = method + "\n" + accept + "\n" + contentMD5 + "\n" + contentType + "\n" + date + "\n" + "" + uri;
        return plaintext;
    }
    /**
     * 编码纯文本
     */
    private static String encodePlainText(String plaintext) {
        def signatureBytes = Fx.crypto.hmac.encrypt("HmacSHA256", APP_KEY, plaintext);
        return Fx.crypto.base64.encode(signatureBytes);
    }
    /**
     * 取md5值
     */
    private static String getContentMD5(Map map) {
        //body转成String
        def requestBodyJson = Fx.json.toJson(map)
        //String转成bytes
        def bytes = Strings.toUTF8Bytes(requestBodyJson)
        //进行MD5加密
        def message = Fx.crypto.MD5.encode2Bytes(bytes);
        //进行Base64加密,返回最终需要的MD5密文
        return Fx.crypto.base64.encode(message);
    }
    /**
     * 取md5值
     */
    private static String getContentMD5(byte[] bytes) {
        //进行MD5加密
        def message = Fx.crypto.MD5.encode2Bytes(bytes);
        //进行Base64加密,返回最终需要的MD5密文
        return Fx.crypto.base64.encode(message);
    }

    //增加员工
    public AddEmployees.Result addEmployees(AddEmployees.Arg arg) {
        //标准log,请保留
        log.info("addEmployees arg:" + arg)
        List<AddEmployees.EmployeeData> employeeDataList = arg.getEmployeeDataList()

        employeeDataList.each { employeeData ->
            String accountId = createPersonalAccount(employeeData.getUserName(), employeeData.getMobile())
            log.info("添加人员 accountId:" + accountId)
            //此处人员必须标记为已认证
            Map objectData = [:]
            objectData.put("esign_provider_user_id", accountId);
            objectData.put("status", "verified");
            Fx.object.update("ExternalPlatPerCompObj", employeeData.getObjectId(), objectData);
        }
        AddEmployees.Result result = AddEmployees.Result.builder().success(true).build();
        //标准log,请保留
        log.info("addEmployees result:" + result)
        return result;
    }

    //创建个人账号
    private String createPersonalAccount(String userName, String mobile) {
        //标准log,请保留
        log.info("createPersonalAccount userName:" + userName + "mobile:" + mobile)
        Map requestBody = [:]
        requestBody.put("thirdPartyUserId", mobile)
        requestBody.put("name", userName)
        requestBody.put("mobile", mobile)
        //生成md5
        String contentMD5 = Esign.getContentMD5(requestBody)
        //创建人员接口
        String uri = "/v1/accounts/createByThirdPartyUserId"
        //获取签名字段
        String plaintext = Esign.getPlaintext("POST", ACCEPT, contentMD5, CONTENT_TYPE, uri);
        //签名认证
        def signOpenCaSignature = Esign.encodePlainText(plaintext)
        //构建header
        Map headerMap = Esign.getHeaders(ACCEPT, CONTENT_TYPE, signOpenCaSignature, contentMD5);
        //构建url
        String url = BASE_URL + uri
        //标准log,请保留
        log.info("createPersonalAccount http requestBody:" + requestBody)
        def result = Fx.http.post(url, headerMap, requestBody)
        //标准log,请保留
        log.info("createPersonalAccount http response:" + result)
        String accountId = result[1]["content"]["data"]["accountId"] as String
        //标准log,请保留
        log.info("createPersonalAccount accountId:" + accountId)
        return accountId;
    }

    //移除员工
    public RemoveEmployees.Result removeEmployees(RemoveEmployees.Arg arg) {
        //标准log,请保留
        log.info("removeEmployees arg:" + arg)
        //标准log,请保留
        log.info("removeEmployees result:ll")
        return null;
    }

    //查询e签宝的文件转换后的状态
    private String queryFileStatus(String fileId) {
        //标准log,请保留
        log.info("queryFileStatus fileId:" + fileId)
        String uri = "/v1/files/{fileId}/status";
        uri = uri.replaceAll("\\{[fileId^}]*\\}", "" + fileId + "");
        String statusCode;
        //构建签名字段
        String plainText = Esign.getPlaintext("GET", ACCEPT, "", CONTENT_TYPE, uri);
        //签名认证
        def signOpenCaSignature = Esign.encodePlainText(plainText)
        //构建header
        Map headers = Esign.getHeaders(ACCEPT, CONTENT_TYPE, signOpenCaSignature, "");
        //调用put接口
        def result = Fx.http.get(BASE_URL + uri, headers, 10000, false, 0);
        log.info(result)
        if (result[1]["statusCode"] != 200) {
            log.info("转pdf失败,再次查询")
            statusCode = "error"
        } else {
            Map contentss = result[1]["content"] as Map;
            log.info("检查文件转换状态:" + contentss)
            if (contentss['code'] as String != "0") {
                statusCode = contentss['message'];
            } else {
                statusCode = contentss['data']['status'];
            }
        }
        //标准log,请保留
        log.info("queryFileStatus statusCode:" + statusCode)
        return statusCode;
    }

    //发起签署任务
    public InitiateSign.Result initiateSign(InitiateSign.Arg arg) {
        //标准log,请保留
        log.info("initiateSign arg:" + arg)
        //e签宝返回的失败提示语?
        //Fx.message.throwErrorMessage("测试提示语")
        //创建签署的入参
        Map createFlowArg = [:]
        List<FileAttachment> fileAttachments = arg.getFileAttachments()
        //上传文件
        Map<String, String> fileMap = uploadFile(fileAttachments)
        //构建签署人信息
        List signers = covertSigners(arg.getInnerAppSignerDataList(), arg.getOuterAppSignerDataList(), fileMap)
        log.info("构建签署人信息:" + signers)
        //设置签署人数据
        createFlowArg.put("signers", signers)

        Map flowConfigInfo = [:]
        //签署回调地址
        flowConfigInfo.put("noticeDeveloperUrl", CALLBACK_URL)
        //签署通知方式
        flowConfigInfo.put("noticeType", "1")
        Map flowInfo = [:]
        //签署主题
        flowInfo.put("businessScene", arg.getTaskSubject())
        //设置自动归档(归档才能进行下载)
        flowInfo.put("autoArchive", true)
        //签署过期时间
        flowInfo.put("contractValidity", arg.getExpireTime())
        //签署配置
        flowInfo.put("flowConfigInfo", flowConfigInfo)
        //设置签署流程信息
        createFlowArg.put("flowInfo", flowInfo)

        //构建签署文件信息
        List docs = covertDocs(fileAttachments, fileMap)
        createFlowArg.put("docs", docs)
        String flowId = startSignFlow(createFlowArg)
        if (flowId.isEmpty()) {
            Fx.message.throwErrorMessage("发起签署流程失败")
        }
        InitiateSign.Result result = InitiateSign.Result.builder().taskId(flowId).build()
        //标准log,请保留
        log.info("initiateSign result:" + result)
        return result;
    }

    //发起签署任务
    private String startSignFlow(Map createFlowArg) {
        //标准log,请勿删除
        log.info("startSignFlow arg:" + createFlowArg)
        String createFlowUri = "/api/v2/signflows/createFlowOneStep"
        String createFlowUrl = BASE_URL + createFlowUri;
        //转为json串
        def requestBodyJson = Fx.json.toJson(createFlowArg)
        String createFlowContentMD5 = Esign.getContentMD5(createFlowArg)
        //获取签名字段
        String createFlowPlaintext = Esign.getPlaintext("POST", ACCEPT, createFlowContentMD5, CONTENT_TYPE, createFlowUri);
        //签名认证
        def createFlowSignOpenCaSignature = Esign.encodePlainText(createFlowPlaintext)
        //构建header
        Map createFlowHeader = Esign.getHeaders(ACCEPT, CONTENT_TYPE, createFlowSignOpenCaSignature, createFlowContentMD5);
        //标准log,请勿删除
        log.info("startSignFlow http arg:" + requestBodyJson)
        def createFlowResult = Fx.http.post(createFlowUrl, createFlowHeader, requestBodyJson, 60000, true, 3)
        //标准log,请勿删除
        log.info("startSignFlow http response:" + createFlowResult)

        if (createFlowResult[1]["statusCode"] != 200) {
            String contentJson = createFlowResult[1]["content"] as String
            Map contentMap = Fx.json.parse(contentJson)
            Fx.message.throwErrorMessage(contentMap["message"] as String)
        }
        Map contentMap = createFlowResult[1]["content"] as Map;
        if (contentMap["code"] != 0) {
            Fx.message.throwErrorMessage(contentMap["message"] as String)
        }
        String flowId = createFlowResult[1]["content"]["data"]["flowId"] as String

        //--------------------------------------发起签署流程--------------------------------------
        String startFlowUri = "/v1/signflows/{flowId}/start"
        startFlowUri = startFlowUri.replaceAll("\\{[flowId^}]*\\}", "" + flowId + "");
        String startFlowUrl = BASE_URL + startFlowUri
        //构建签名字段
        String startFlowPlainText = Esign.getPlaintext("PUT", ACCEPT, "", CONTENT_TYPE, startFlowUri);
        //签名认证
        def startFlowSignOpenCaSignature = Esign.encodePlainText(startFlowPlainText)
        //构建header
        Map startFlowHeader = Esign.getHeaders(ACCEPT, CONTENT_TYPE, startFlowSignOpenCaSignature, "");
        //调用put接口
        //标准log,请勿删除
        log.info("startSignFlow start http arg:" + "")
        def startFlowResult = Fx.http.put(startFlowUrl, startFlowHeader, "");
        log.info("startSignFlow start http result:" + startFlowResult)
        //异常检查
        if (startFlowResult[1]["statusCode"] != 200) {
            flowId = null;
        }
        //标准log,请勿删除
        log.info("startSignFlow result:" + flowId)
        return flowId
    }

    //上传文件
    private Map<String, String> uploadFile(List<FileAttachment> fileAttachments) {
        //标准log,请保留
        log.info("uploadFile arg:" + fileAttachments)
        Map<String, String> fileMap = [:]
        List<String> need2PdfFileIds = []
        fileAttachments.each { item ->
            String nPath = item.getPath() as String
            String ext = item.getExt() as String
            //下载文件,并获取byte流
            def ret = Fx.file.downloadFile(nPath)
            def fileDowloadData = ret[1]
            if (fileDowloadData == null) {
                log.info("downloadFile:" + ret[0] + "/" + ret[2])
                Fx.message.throwErrorMessage("下载文件失败:" + ret[2])
            }
            def fileData = fileDowloadData['fileData'] as byte[]

            //--------------------------------------上传文件--------------------------------------
            //进行MD5加密
            String getUploadUrlContentMd5 = Esign.getContentMD5(fileData)
            //入参body
            Map getUploadUrlArg = [:]
            if (ext != 'pdf') {
                getUploadUrlArg.put("convert2Pdf", "true")
            }
            getUploadUrlArg.put("contentMd5", getUploadUrlContentMd5)
            getUploadUrlArg.put("contentType", CONTENT_TYPE_UPLOAD)
            getUploadUrlArg.put("fileName", item.getFilename())
            getUploadUrlArg.put("fileSize", fileDowloadData["fileSize"] as Long);
            //上传接口
            String uri = "/v1/files/getUploadUrl"
            String url = BASE_URL + uri
            //签名字段
            String plaintext = Esign.getPlaintext("POST", ACCEPT, "", CONTENT_TYPE, uri);
            //签名认证
            String signOpenCaSignature = Esign.encodePlainText(plaintext)
            //构建header
            Map getUploadUrlHeader = Esign.getHeaders(ACCEPT, CONTENT_TYPE, signOpenCaSignature, "")
            //获取签署url
            def result = Fx.http.post(url, getUploadUrlHeader, getUploadUrlArg)
            log.info("获取签署url:" + result)
            def uploadUrl = result[1]["content"]["data"]["uploadUrl"]
            def fileId = result[1]["content"]["data"]["fileId"] as String
            log.info("fileId:" + fileId)
            Map putHeaderMap = [:]
            putHeaderMap.put("Content-MD5", getUploadUrlContentMd5)
            putHeaderMap.put("Content-Type", "application/octet-stream")
            //将文件put上去
            def putResult = Fx.http.put(uploadUrl as String, putHeaderMap, fileData, 50000, false, 0)
            log.info("putResult :" + putResult)
            if (putResult[1]['statusCode'] == 200) {
                fileMap.put(nPath, fileId)
            }
            if (ext != 'pdf') {
                need2PdfFileIds.add(fileId)
            }
        }
        //尝试睡眠
        trySleep(need2PdfFileIds)
        //标准log,请保留
        log.info("uploadFile result:" + fileMap)
        return fileMap
    }

    //尝试睡眠,等待文件转pdf
    private void trySleep(List<String> need2PdfFileIds) {
        //标准log,请保留
        log.info("trySleep arg:" + need2PdfFileIds)
        Boolean toPdfOk = false
        Range range = Ranges.of(1, 40)
        range.each {
            if (toPdfOk) {
                return
            }
            Boolean needWaiting2Pdf = false;
            need2PdfFileIds.each { fileId ->
                String statusfileId = queryFileStatus(fileId);
                log.info("状态值:" + statusfileId + "," + fileId)
                if (statusfileId != "5") {
                    needWaiting2Pdf = true
                }
            }
            //查询文件的上传状态,如果不是成功,则继续睡眠
            if (needWaiting2Pdf) {
                sleep(1000)
            } else {
                toPdfOk = true
            }
        }
        //标准log,请保留
        log.info("trySleep result")
    }

    //构建签署人信息
    private List covertSigners(List<InnerAppSignerData> innerAppSignerDataList,
                               List<OuterAppSignerData> outerAppSignerDataList,
                               Map<String, String> fileMap) {
        //标准log,请保留
        log.info("covertSigners arg innerAppSignerDataList:" + innerAppSignerDataList)
        log.info("covertSigners arg outerAppSignerDataList:" + outerAppSignerDataList)
        log.info("covertSigners arg fileMap:" + fileMap)
        //签署人信息
        List signers = []
        if (!innerAppSignerDataList.isEmpty()) {
            List innerSigners = covertInnerSigner(innerAppSignerDataList, fileMap)
            signers.addAll(innerSigners)
        }
        if (!outerAppSignerDataList.isEmpty()) {
            List outerSigners = covertOuterSigner(outerAppSignerDataList, fileMap)
            signers.addAll(outerSigners)
        }
        //标准log,请保留
        log.info("covertSigners result:" + signers)
        return signers;
    }

    //构建内部签署人信息
    private List covertInnerSigner(List<InnerAppSignerData> innerAppSignerDataList,
                                   Map<String, String> fileMap) {
        //标准log,请保留
        log.info("covertInnerSigner arg innerAppSignerDataList:" + innerAppSignerDataList)
        log.info("covertInnerSigner argfileMap:" + fileMap)
        //签署人信息
        List signers = []
        innerAppSignerDataList.each { item ->
            Map signer = [:]
            //签署人账号
            Map signerAccount = [:]
            signerAccount.put("signerAccountId", item.getSignerId())
            signerAccount.put("noticeType", "1")

            Integer signType = item.getSignType() as Integer

            if (signType == 2) {
                signerAccount.put("authorizedAccountId", AUTHORIZED_ACCOUNT_ID)
            }

            signer.put("signerAccount", signerAccount)

            //签署文件相关信息
            List signfields = []

            item.getSignLocationList().each { signLocation ->
                List covertSignfields = covertSignfields(signType, signLocation, fileMap)
                signfields.addAll(covertSignfields)
            }

            signer.put("signfields", signfields)
            signer.put("signOrder", item.getSignOrder())
            signers.add(signer)
        }
        //标准log,请保留
        log.info("covertInnerSigner result:" + signers)
        return signers
    }

    private List queryFieldKeyWords(SignLocation signLocation) {
        List keyWords = []
        keyWords.add(signLocation.getKeyWord())
        keyWords.add(signLocation.getTimeKeyWord())
        keyWords.add(signLocation.getCrossPageKeyWord())

        return keyWords
    }

    //构建外部签署人信息
    private List covertOuterSigner(List<OuterAppSignerData> outerAppSignerDataList,
                                   Map<String, String> fileMap) {
        //标准log,请保留
        log.info("covertOuterSigner arg outerAppSignerDataList:" + outerAppSignerDataList)
        log.info("covertOuterSigner arg fileMap:" + fileMap)
        //签署人信息
        List signers = []
        outerAppSignerDataList.each { item ->
            Integer signType = item.getSignType() as Integer
            //个人签署
            if (signType == 1) {
                Map outerPersonal = covertOuterPersonal(item, fileMap)
                signers.add(outerPersonal)
            }

            //企业签署
            if (signType == 2) {
                Map companySigner = covertOuterCompany(item, fileMap)
                signers.add(companySigner)
            }
        }
        //标准log,请保留
        log.info("covertOuterSigner result:" + signers)
        return signers;
    }

    private List covertKeyWordFields(String field,
                                     Integer signType,
                                     String keyWord,
                                     String timeKeyWord,
                                     Map keyWordMap) {
        //标准log,请保留
        log.info("covertKeyWordFields arg field:" + field)
        log.info("covertKeyWordFields arg signType:" + signType)
        log.info("covertKeyWordFields arg keyWord:" + keyWord)
        log.info("covertKeyWordFields arg timeKeyWord:" + timeKeyWord)
        log.info("covertKeyWordFields arg keyWordMap:" + keyWordMap)
        List signFields = []
        if (keyWord.isEmpty()) {
            Map signField = covertCommonSignField(field, signType)
            signFields.add(signField)
            return signFields
        }
        keyWord = keyWord.replaceAll("#", "") as String
        timeKeyWord = timeKeyWord.replaceAll("#", "") as String

        List positionList = keyWordMap[(keyWord)] as List
        positionList.each { position ->
            List coordinateList = position[("coordinateList")] as List
            if (!coordinateList.isEmpty()) {
                coordinateList.each { coordinate ->
                    Map signField = covertCommonSignField(field, signType)
                    signField.put("assignedPosbean", true)
                    signField.put("signType", 1)
                    Map posBean = [:]
                    posBean.put("posPage", position[("pageIndex")] as Integer)
                    posBean.put("posX", coordinate[("posx")])
                    posBean.put("posY", coordinate[("posy")])
                    signField.put("posBean", posBean)

                    Map timeKeyWordPosition = queryTimeKeywordPositionMap(timeKeyWord, keyWordMap)
                    if (timeKeyWordPosition != null) {
                        Map signDateBean = [:]
                        signDateBean.put("posPage", timeKeyWordPosition[("pageIndex")] as Integer)
                        signDateBean.put("posX", timeKeyWordPosition[("posx")])
                        signDateBean.put("posY", timeKeyWordPosition[("posy")])
                        signField.put("signDateBean", posBean)
                        signField.put("signDateBeanType", 1)
                    }
                    signFields.add(signField)
                }
            }
        }

        if (signFields.isEmpty()) {
            Map signField = covertCommonSignField(field, signType)
            signFields.add(signField)
            return signFields
        }
        //标准log,请保留
        log.info("covertKeyWordFields result:" + signFields)
        return signFields
    }

    private static Map queryTimeKeywordPositionMap(String timeKeyword, Map keywordMap) {
        //标准log,请保留
        log.info("queryTimeKeywordPositionMap arg timeKeyword:" + timeKeyword)
        log.info("queryTimeKeywordPositionMap arg keywordMap:" + keywordMap)
        if (timeKeyword.isEmpty()) {
            return [:]
        }
        log.info("queryTimeKeywordPositionMap timeKeyword:{}, keywordMap:{}" + timeKeyword + keywordMap)
        Map resultMap = [:]
        List keyword = keywordMap[(timeKeyword)] as List
        if (keyword.isEmpty()) {
            return [:]
        }
        resultMap.put("pageIndex", keyword[0]["pageIndex"] as Integer)
        List coordinateList = keyword[0]["coordinateList"] as List
        if (coordinateList.isEmpty()) {
            return [:]
        }
        Map xy = coordinateList[0] as Map
        resultMap.put("posx", xy["posx"] as String)
        resultMap.put("posy", xy["posy"] as String)
        log.info("queryTimeKeywordPositionMap result:{}" + resultMap)
        return resultMap
    }


    private List covertCrossKeyWordSignField(String field,
                                             Integer signType,
                                             String crossKeyWord,
                                             Map keyWordMap) {
        //标准log,请保留
        log.info("covertCrossKeyWordSignField arg field:" + field)
        log.info("covertCrossKeyWordSignField arg signType:" + signType)
        log.info("covertCrossKeyWordSignField arg crossKeyWord:" + crossKeyWord)
        log.info("covertCrossKeyWordSignField arg keyWordMap:" + keyWordMap)
        if (crossKeyWord.isEmpty()) {
            return null
        }
        crossKeyWord = crossKeyWord.replaceAll("#", "") as String
        log.info("crossKeyWord" + crossKeyWord)
        List signFields = []
        List positionList = keyWordMap[(crossKeyWord)] as List
        log.info("positionList" + positionList)
        positionList.each { position ->
            List coordinateList = position[("coordinateList")] as List
            if (!coordinateList.isEmpty()) {
                def coordinate = coordinateList[0]
                Map signField = covertCommonSignField(field, signType)
                signField.put("assignedPosbean", true)
                signField.put("signType", 2)
                Map posBean = [:]
                posBean.put("posPage", "all")
                posBean.put("posY", coordinate[("posy")])
                signField.put("posBean", posBean)
                signFields.add(signField)
                return signFields
            }
        }
        //标准log,请保留
        log.info("covertCrossKeyWordSignField result:" + signFields)
        return signFields
    }


    /**
     * 通用signField的构建
     * @param field
     * @param signType
     * @return
     */
    private Map covertCommonSignField(String field, Integer signType) {
        //标准log,请保留
        log.info("covertCommonSignField arg field:" + field)
        log.info("covertCommonSignField arg signType:" + signType)
        Map signField = [:]
        signField.put("signType", 0)
        signField.put("fileId", field)
        signField.put("assignedPosbean", false)
        //个人签署
        if (signType == 1) {
            signField.put("sealType", 0)
        }
        //企业签署
        if (signType == 2) {
            signField.put("sealType", 1)
            signField.put("actorIndentityType", 2)
        }
        //标准log,请保留
        log.info("covertCommonSignField result:" + signField)
        return signField;
    }

    private static Map covertPositionMap(List positionList) {
        Map positionMap = [:]
        positionList.each { item ->
            positionMap.put(item[("keyword")] as String, item[("positionList")])
        }
        return positionMap
    }

    //创建企业账号
    public String createCompanyAccount(String companyName) {
        //标准log,请保留
        log.info("createCompanyAccount arg:" + companyName)
        Map requestBody = [:]
        requestBody.put("thirdPartyUserId", companyName)
        requestBody.put("name", companyName)

        String contentMD5 = Esign.getContentMD5(requestBody)

        String uri = "/v1/organizations/createByThirdPartyUserId"
        //获取签名字段
        String plaintext = Esign.getPlaintext("POST", ACCEPT, contentMD5, CONTENT_TYPE, uri);
        //签名认证
        def signOpenCaSignature = Esign.encodePlainText(plaintext)
        //构建header
        Map headerMap = Esign.getHeaders(ACCEPT, CONTENT_TYPE, signOpenCaSignature, contentMD5);
        //构建url
        String url = BASE_URL + uri
        def result = Fx.http.post(url, headerMap, requestBody)
        if (result[1]["statusCode"] != 200) {
            Fx.message.throwErrorMessage("创建企业失败")
        }
        String enterpriseAccount = result[1]["content"]["data"]["orgId"];
        //标准log,请保留
        log.info("createCompanyAccount result:" + enterpriseAccount)
        return enterpriseAccount
    }

    //构建外部签署人个人的入参
    private Map covertOuterPersonal(OuterAppSignerData outerAppSignerData, Map<String, String> fileMap) {
        //标准log,请保留
        log.info("covertOuterPersonal arg outerAppSignerData:" + outerAppSignerData)
        log.info("covertOuterPersonal arg fileMap:" + fileMap)
        String accountId = createPersonalAccount(outerAppSignerData.getSignerName(), outerAppSignerData.getNotifyAddress())
        Map signer = [:]
        //签署人账号
        Map signerAccount = [:]
        signerAccount.put("signerAccountId", accountId)
        signerAccount.put("noticeType", "1")
        signerAccount.put("actorIndentityType", 0)

        signer.put("signerAccount", signerAccount)

        //签署文件相关信息
        List signfields = []
        outerAppSignerData.getSignLocationList().each { signLocation ->
            List covertSignfields = covertSignfields(outerAppSignerData.getSignType(), signLocation, fileMap)
            signfields.addAll(covertSignfields)
        }

        signer.put("signfields", signfields)
        signer.put("signOrder", outerAppSignerData.getSignOrder())
        //标准log,请保留
        log.info("covertOuterPersonal result:" + signer)
        return signer
    }

    private List covertSignfields(Integer signType, SignLocation signLocation, Map fileMap) {
        //标准log,请保留
        log.info("covertSignfields arg signType:" + signType)
        log.info("covertSignfields arg signLocation:" + signLocation)
        log.info("covertSignfields arg fileMap:" + fileMap)
        List signfields = []
        String fileId = fileMap[(signLocation.getFilePath())]
        List positionList = searchWordsPosition(fileId, queryFieldKeyWords(signLocation))
        Map positionMap = covertPositionMap(positionList)
        List keyWordFields = covertKeyWordFields(fileId, signType, signLocation.getKeyWord(), signLocation.getTimeKeyWord(), positionMap)
        if (!keyWordFields.isEmpty()) {
            signfields.addAll(keyWordFields)
        }
        List crossKeyWords = covertCrossKeyWordSignField(fileId, signType, signLocation.getCrossPageKeyWord(), positionMap)
        if (!crossKeyWords.isEmpty()) {
            signfields.addAll(crossKeyWords)
        }
        //标准log,请保留
        log.info("covertSignfields result:" + signfields)
        return signfields
    }

    //构建外部签署人个人的入参
    private Map covertOuterCompany(OuterAppSignerData outerAppSignerData, Map<String, String> fileMap) {
        //标准log,请保留
        log.info("covertOuterCompany arg outerAppSignerData:" + outerAppSignerData)
        log.info("covertOuterCompany arg fileMap:" + fileMap)
        String companyAccountId = createCompanyAccount(outerAppSignerData.getCompanyName())
        String accountId = createPersonalAccount(outerAppSignerData.getSignerName(), outerAppSignerData.getNotifyAddress())
        Map signer = [:]
        //签署企业账号
        Map signerAccount = [:]
        signerAccount.put("signerAccountId", accountId)
        signerAccount.put("authorizedAccountId", companyAccountId)
        signerAccount.put("noticeType", "1")
        signerAccount.put("actorIndentityType", 2)
        signer.put("signerAccount", signerAccount)

        //签署文件相关信息
        List signfields = []
        outerAppSignerData.getSignLocationList().each { signLocation ->
            List covertSignfields = covertSignfields(outerAppSignerData.getSignType(), signLocation, fileMap)
            signfields.addAll(covertSignfields)
        }
        signer.put("signfields", signfields)
        signer.put("signOrder", outerAppSignerData.getSignOrder())
        //标准log,请保留
        log.info("covertOuterCompany result:" + signer)
        return signer
    }

    private List covertDocs(List<FileAttachment> fileAttachments, Map<String, String> fileMap) {
        //标准log,请保留
        log.info("covertDocs arg fileAttachments:" + fileAttachments)
        log.info("covertDocs arg fileMap:" + fileMap)
        //签署文件
        List docs = []
        if (fileAttachments.isEmpty()) {
            return docs
        }
        fileAttachments.each { item ->
            Map doc = [:]
            doc.put("fileId", fileMap[(item.getPath())])
            docs.add(doc)
        }
        //标准log,请保留
        log.info("covertDocs result:" + docs)
        return docs
    }

    //搜索关键字坐标
    public List searchWordsPosition(String fileId, List<String> keyWords) {
        //标准log,请保留
        log.info("searchWordsPosition arg fileId:" + fileId)
        log.info("searchWordsPosition arg keyWords:" + keyWords)
        String uri = "/v1/documents/{fileId}/searchWordsPosition";
        uri = uri.replaceAll("\\{[fileId^}]*\\}", "" + fileId + "");

        String query = "?keywords="
        keyWords.each { item ->
            query = query + item.replaceAll("#", "") as String
            query = query + ","
        }

        uri = uri + query;
        String url = BASE_URL + uri
        //构建签名字段
        String plainText = Esign.getPlaintext("GET", ACCEPT, "", CONTENT_TYPE, uri);
        //签名认证
        def signOpenCaSignature = Esign.encodePlainText(plainText)
        //构建header
        Map headers = Esign.getHeaders(ACCEPT, CONTENT_TYPE, signOpenCaSignature, "");
        //调用get接口
        //标准log,请保留
        log.info("searchWordsPosition http request")
        def result = Fx.http.get(url, headers, 10000, false, 0);
        //标准log,请保留
        log.info("searchWordsPosition http response:" + result)
        def content = result[1]["content"]
        List wordsPositionList = []
        if (content["code"] == 0) {
            wordsPositionList = Fx.json.parseList(Fx.json.toJson(content["data"]))
        }
        //标准log,请保留
        log.info("searchWordsPosition result:" + wordsPositionList)
        return wordsPositionList
    }

    //撤销电子签
    public CancelSign.Result cancelSign(CancelSign.Arg arg) {
        //标准log,请保留
        log.info("cancelSign arg:" + arg)
        String flowId = arg.getTaskId();
        String uri = "/v1/signflows/{flowId}/revoke";
        uri = uri.replaceAll("\\{[flowId^}]*\\}", "" + flowId + "");
        String url = BASE_URL + uri;
        Map requestBody = [:]
        //生成md5
        String contentMD5 = Esign.getContentMD5(requestBody)
        //构建签名字段
        String plainText = Esign.getPlaintext("PUT", ACCEPT, contentMD5, CONTENT_TYPE, uri);
        //签名认证
        def signOpenCaSignature = Esign.encodePlainText(plainText)
        //构建header
        Map headers = Esign.getHeaders(ACCEPT, CONTENT_TYPE, signOpenCaSignature, contentMD5);
        //调用put接口
        //标准log,请保留
        log.info("cancelSign http request:" + requestBody)
        def response = Fx.http.put(url, headers, requestBody);
        //标准log,请保留
        log.info("cancelSign http response:" + response)
        if (response[1]["statusCode"] != 200) {
            Fx.message.throwErrorMessage("撤销失败")
        }
        CancelSign.Result result = CancelSign.Result.builder().success(true).build();
        //标准log,请保留
        log.info("cancelSign result:" + result)
        return result
    }

    //下载签署文件
    public DownloadSignFile.Result downloadSignFile(DownloadSignFile.Arg arg) {
        //标准log,请保留
        log.info("downloadSignFile arg:" + arg)
        String flowId = arg.getTaskId();
        String uri = "/v1/signflows/{flowId}/documents";
        uri = uri.replaceAll("\\{[flowId^}]*\\}", "" + flowId + "");
        //构建签名字段
        String plainText = Esign.getPlaintext("GET", ACCEPT, "", CONTENT_TYPE, uri);
        //签名认证
        def signOpenCaSignature = Esign.encodePlainText(plainText)
        //构建header
        Map headers = Esign.getHeaders(ACCEPT, CONTENT_TYPE, signOpenCaSignature, "");
        //调用put接口
        //标准log,请保留
        log.info("downloadSignFile http request")
        def response = Fx.http.get(BASE_URL + uri, headers, 10000, false, 0);
        //标准log,请保留
        log.info("downloadSignFile http response:" + response)
        if (response[1]["statusCode"] != 200) {
            Fx.message.throwErrorMessage("下载失败")
        }
        log.info("下载文件:" + response)
        List files = response[1]["content"]["data"]["docs"] as List
        //返回值
        List<FileAttachment> fileAttachments = []
        files.each() { x ->
            String fileUrl = x["fileUrl"] as String
            def (Boolean error, HttpResult reuslt, String errorMessage) = Fx.http.get(fileUrl, [:])
            if (error) {
                Fx.log.info("http 下载文件请求出错 : " + errorMessage)
                return null;
            }
            if (reuslt.statusCode != 200) {
                Fx.log.info("http 下载文件响应错误 :" + reuslt.content)
            }
            //上传图片到文件服务器
            byte[] bytes = reuslt.bytes
            String fileName = x["fileName"] as String
            String ext = getExt(fileName)
            def uploadReusult = Fx.file.uploadFile(ext, bytes.length, bytes)
            if (uploadReusult[0]) {
                Fx.log.info("上传图片错误 :" + uploadReusult[2])
                return null;
            }
            //将返回的路径更新到对象字段下
            String path = uploadReusult[1]['path'] as String

            FileAttachment fileAttachment = FileAttachment.builder().
                    filename(fileName).
                    ext(ext).
                    path(path).
                    size(uploadReusult[1]['size'] as Long).
                    build()

            fileAttachments.add(fileAttachment)
        }
        DownloadSignFile.Result result = DownloadSignFile.Result.builder().fileAttachments(fileAttachments).build();
        //标准log,请保留
        log.info("downloadSignFile result:" + result)
        return result;
    }

    //获取文件的后缀
    private static String getExt(String fileName) {
        String[] arr = fileName.split("\\.")
        return arr[-1]
    }

    //获取签署链接
    public GetSignUrl.Result getSignUrl(GetSignUrl.Arg arg) {
        //标准log,请保留
        log.info("getSignUrl arg:" + arg)
        String flowId = arg.getTaskId();
        String uri = "/v1/signflows/{flowId}/executeUrl";
        uri = uri.replaceAll("\\{[flowId^}]*\\}", "" + flowId + "");
        String accountId = arg.getSignerId()
        uri = uri + "?accountId=" + accountId
        uri = uri + "&amp;organizeId=" + "0"
        //构建签名字段
        String plainText = Esign.getPlaintext("GET", ACCEPT, "", CONTENT_TYPE, uri);
        //签名认证
        def signOpenCaSignature = Esign.encodePlainText(plainText)
        //构建header
        Map headers = Esign.getHeaders(ACCEPT, CONTENT_TYPE, signOpenCaSignature, "");
        //调用put接口
        def response = Fx.http.get(BASE_URL + uri, headers, 10000, false, 0);
        log.info("getSignUrl http response:" + response)
        if (response[1]["statusCode"] != 200) {
            Fx.message.throwErrorMessage("获取签署url失败")
        }
        if (response[1]["content"]["code"] == 1437114) {
            Fx.message.throwErrorMessage(response[1]["content"]["message"] as String)
        }
        GetSignUrl.Result result = GetSignUrl.Result.builder().url(response[1]["content"]["data"]["shortUrl"] as String).build();
        //标准log,请保留
        log.info("getSignUrl result:" + result)
        return result;
    }

    //签署回调解析参数
    public SignCallback.Result signCallback(SignCallback.Arg arg) {
        //标准log,请保留
        log.info("signCallback arg:" + arg)
        String requestBody = arg.getRequestBodyByContent() as String;
        Map requestBodyMap = Fx.json.parse(requestBody)
        switch (requestBodyMap[("action")] as String) {
        //签署人签署完成
            case "SIGN_FLOW_UPDATE":
                SignCallback.Result result = buildFlowUpdateResult(requestBodyMap);
                //标准log,请保留
                log.info("signCallback update result:" + result)
                return result;
                //签署流程结束
            case "SIGN_FLOW_FINISH":
                SignCallback.Result result = buildFlowFinish(requestBodyMap);
                //标准log,请保留
                log.info("signCallback finish result:" + result)
                return result;
            default:
                //标准log,请保留
                log.info("signCallback error")
                return null;
        }
    }

    private SignCallback.Result buildFlowUpdateResult(Map requestBodyMap) {
        //标准log,请保留
        log.info("buildFlowUpdateResult arg:" + requestBodyMap)
        TaskStatus taskStatus;
        switch (requestBodyMap[("signResult")] as String) {
            case "2":
                taskStatus = TaskStatus.Signed;
                break;
            case "4":
                taskStatus = TaskStatus.VisaDenied;
                break;
            default:
                taskStatus = null;
                break;
        }
        SignCallback.Result result = SignCallback.Result.builder().
                taskId(requestBodyMap[("flowId")] as String).
                taskStatus(taskStatus).
                refusalReason(requestBodyMap["resultDescription"] as String).
                signerId(requestBodyMap[("accountId")] as String).
                callBackEvent(CallBackEvent.SignEvent).
                successMsg("success").
                successCode(200).
                build();
        //标准log,请保留
        log.info("buildFlowUpdateResult result:" + result)
        return result;
    }

    private SignCallback.Result buildFlowFinish(Map requestBodyMap) {
        //标准log,请保留
        log.info("buildFlowFinish arg:" + requestBodyMap)
        def flowStatus = requestBodyMap[("signResult")] as String
        TaskStatus taskStatus;
        if (flowStatus != null && !flowStatus.isEmpty() && flowStatus == '3' ) {
            taskStatus = TaskStatus.Revoked;
        } else {
            taskStatus = TaskStatus.Completed;
        }
        SignCallback.Result result = SignCallback.Result.builder().
                taskId(requestBodyMap[("flowId")] as String).
                taskStatus(taskStatus).
                refusalReason(requestBodyMap["statusDescription"] as String).
                callBackEvent(CallBackEvent.SignEvent).
                successMsg("success").
                successCode(200).
                build();
        //标准log,请保留
        log.info("buildFlowFinish result:" + result)
        return result;
    }

    //催签
    public UrgeSign.Result urgeSign(UrgeSign.Arg arg) {
        //标准log,请保留
        log.info("urgeSign arg:" + arg)
        String flowId = arg.getTaskId();
        String uri = "/v1/signflows/{flowId}/signers/rushsign";
        uri = uri.replaceAll("\\{[flowId^}]*\\}", "" + flowId + "");

        Map requestBody = [:]
        //生成md5
        String contentMD5 = Esign.getContentMD5(requestBody)
        //构建签名字段
        String plainText = Esign.getPlaintext("PUT", ACCEPT, contentMD5, CONTENT_TYPE, uri);
        //签名认证
        def signOpenCaSignature = Esign.encodePlainText(plainText)
        //构建header
        Map headers = Esign.getHeaders(ACCEPT, CONTENT_TYPE, signOpenCaSignature, contentMD5);
        //调用put接口
        def response = Fx.http.put(BASE_URL + uri, headers, requestBody);
        if (response[1]["statusCode"] != 200) {
            Fx.message.throwErrorMessage("催签失败")
        }
        UrgeSign.Result result = UrgeSign.Result.builder().success(true).build();
        //标准log,请保留
        log.info("urgeSign result:" + result)
        return result;
    }


    public static void main(String[] args) {
        // addEmployeesTest()
        // String ff = queryFileStatus("6c49acee65884f67ad7bbf2cff19ac00");
        // log.info(ff)
        // initiateSignTest()
        // cancelSignTest()
        // urgeSignTest()
        // downloadSignFileTest()
        // Sign sign = Fx.klass.newInstance('Sign') as Sign
        // List result = sign.searchWordsPosition("7fd63a8808214f06a2bf105a332af1fe", ["#{signer_1}", "#{signer_time_1}", "#{crosspage_signer_1}"])
        // log.info(result)
        // getSignUrlTest()
    }

    private static void initiateSignTest() {
        String arg = "{\"externalType\": 1, \"fileAttachments\": [{\"create_time\": 1641350684526, \"ext\": \"pdf\", \"filename\": \"电子签发起打印模板.pdf\", \"path\": \"N_202201_05_e7881c36d8e247c98eb8af61b6e8cc08\", \"size\": 5834 } ], \"innerAppSignerDataList\": [{\"notifyAddress\": \"17745113979\", \"signLocationList\": [{\"crossPageKeyWord\": \"#{crosspage_signer_1}\", \"filePath\": \"N_202112_24_5dd31bf0ddb1415ba587ec338c3af0d5.pdf\", \"keyWord\": \"#{signer_1}\", \"timeKeyWord\": \"#{signer_time_1}\"} ], \"signOrder\": 1, \"signType\": 1, \"signWay\": 1, \"signerId\": \"f0947f852b41404098dfdfab9139ee79\", \"signerName\": \"张宇\"} ], \"innerSignType\": 1, \"outerAppSignerDataList\": [], \"signWay\": 1, \"taskSubject\": \"测试签署电子合同\"}"
        InitiateSign.Arg initiateSignArg = Fx.json.parseObject(arg, InitiateSign.Arg.class)
        Esign sign = Fx.klass.newInstance('Esign') as Esign
        sign.initiateSign(initiateSignArg)
    }

    private static void addEmployeesTest() {
        Esign sign = Fx.klass.newInstance('Esign') as Esign

        AddEmployees.EmployeeData employeeData = AddEmployees.EmployeeData.builder().
                mobile("17745113979").
                objectId("61c56342a89376000199dfb1").
                userName("张宇").
                build()
        List employeeDataList = []
        employeeDataList.add(employeeData)

        AddEmployees.Arg arg = AddEmployees.Arg.builder().
                employeeDataList(employeeDataList).
                build()

        sign.addEmployees(arg)
    }

    private static void cancelSignTest() {
        Esign sign = Fx.klass.newInstance('Esign') as Esign
        CancelSign.Arg arg = CancelSign.Arg.builder().taskId("3c9a2be6db774c28bde575680d7cc1b7").build()
        sign.cancelSign(arg)
    }

    private static void urgeSignTest() {
        Esign sign = Fx.klass.newInstance('Esign') as Esign
        UrgeSign.Arg arg = UrgeSign.Arg.builder().taskId("3c9a2be6db774c28bde575680d7cc1b7").build()
        sign.urgeSign(arg)
    }

    private static void downloadSignFileTest() {
        Esign sign = Fx.klass.newInstance('Esign') as Esign
        DownloadSignFile.Arg arg = DownloadSignFile.Arg.builder().taskId("710d20bd61f44345b871e8ffa5a3c06f").build();
        sign.downloadSignFile(arg)
    }

    private static void getSignUrlTest() {
        Esign sign = Fx.klass.newInstance('Esign') as Esign
        GetSignUrl.Arg arg = GetSignUrl.Arg.builder().taskId("dcd85e52b07d4f3d92038047f7e856a2").signerId("addb6aceb3fb4d0ea9dc9ef4280e1a83").build()
        def result = sign.getSignUrl(arg)
        log.info(result)
    }
}
2024-10-22
0 0