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 + "&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)
}
}
Groovy代码示例
2024-08-16