package com.kingdee.eas.custom.esign.osf; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.kingdee.bos.BOSException; import com.kingdee.bos.Context; import com.kingdee.bos.bsf.service.app.IHRMsfService; import com.kingdee.eas.common.EASBizException; import com.kingdee.eas.custom.esign.bizEnum.EsignStatusEnum; import com.kingdee.eas.custom.esign.tsign.hz.comm.EsignHttpResponse; import com.kingdee.eas.custom.esign.tsign.hz.exception.EsignException; import com.kingdee.eas.custom.esign.util.EsignConfig; import com.kingdee.eas.custom.esign.util.EsignHttpUtil; import org.apache.commons.lang3.StringUtils; import java.net.URISyntaxException; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * description: Create_by_fileOSFService
* date: 28/11/2025 上午 9:46
* author: lhbj
* version: 1.0
*/ public class Create_by_fileOSFService implements IHRMsfService { private static final String SUCCESS_CODE = "0"; private static final String PREVIEW_OPERATION = "预览"; private static final int MAX_RETRY_COUNT = 20; private static final int RETRY_INTERVAL_SECONDS = 3; @Override public Object process(Context context, Map map) throws EASBizException, BOSException { String mack = (String) map.get("mack"); String data = (String) map.get("data"); Map resul = Maps.newHashMap(); try { JSONObject jsonObject = JSON.parseObject(data); if ("preview".equals(mack)) { for (Map.Entry fileEntry : jsonObject.entrySet()) { JSONObject fieldObject = (JSONObject) fileEntry.getValue(); EsignHttpResponse response = this.previewFile(context, fieldObject, "预览"); if (response.getStatus() >= 200 && response.getStatus() < 300) { resul.putAll(this.processFileResponse(response, context)); } else { resul.put("code", response.getStatus()); resul.put("message", "网络异常"); resul.put("data", null); } } } else { Map signMap = Maps.newHashMap(); String sourceId = jsonObject.getString("sourceId"); String signFlowTitle = jsonObject.getString("signFlowTitle"); String personId = jsonObject.getString("personId"); String operatorId = jsonObject.getString("operatorId"); //文件 JSONObject templateInfo = jsonObject.getJSONObject("templateInfo"); //设置待签署文件信息 try { List> docs = this.addDocs(context, jsonObject, templateInfo); signMap.put("docs", docs); }catch (EsignException e){ e.printStackTrace(); resul.putAll(JSON.parseObject(e.getMessage())); } List> docs = (List>) signMap.get("docs"); if(null!=docs) { //签署流程配置项 Map signFlowConfig = this.addSignFlowConfig(context, signFlowTitle, jsonObject); signMap.put("signFlowConfig", signFlowConfig); //签署方信息 JSONObject signInfo = jsonObject.getJSONObject("signInfo"); JSONArray signs = signInfo.getJSONArray("signers"); List> signers = this.addSigners(context, signs, docs, sourceId); signMap.put("signers", signers); //抄送方信息 JSONArray copis = signInfo.getJSONArray("copiers"); List> copiers = this.addCopiers(context, copis); if (copiers.size() > 0) { signMap.put("copiers", copiers); } //必须要存在签署文件才能签署 if ( docs.size() > 0) { EsignHttpResponse response = EsignHttpUtil.create_by_file(context, personId, signFlowTitle,operatorId , sourceId, JSON.toJSONString(signMap)); if (response.getStatus() >= 200 && response.getStatus() < 300) { JSONObject body = JSON.parseObject(response.getBody()); resul.putAll(body); } else { resul.put("code", response.getStatus()); resul.put("message", "网络异常"); resul.put("data", null); } } } } } catch (EsignException | URISyntaxException e) { e.printStackTrace(); } return resul; } /** * 根据”填写模板生成文件“返回的文件id查询文件处理状态 * 需要阻塞等待这个状态变为2或者5才能调用发起签署接口 * @param response * @param context * @return */ public Map processFileResponse(EsignHttpResponse response, Context context) { Map result = Maps.newHashMap(); JSONObject body = JSON.parseObject(response.getBody()); if (!SUCCESS_CODE.equals(String.valueOf(body.get("code")))) { result.putAll(body); return result; } JSONObject jsonData = body.getJSONObject("data"); String fileId = jsonData.getString("fileId"); JSONObject pollData = pollFileDownloadUrl(context, fileId); if (null!=pollData && StringUtils.isNotBlank(pollData.getString("fileDownloadUrl"))) { jsonData.putAll(pollData); } result.putAll(body); return result; } /** * 阻塞循环调用获取文件信息接口 * @param context * @param fileId * @return */ private JSONObject pollFileDownloadUrl(Context context, String fileId) { for (int i = MAX_RETRY_COUNT; i > 0; i--) { try { JSONObject data = tryGetFileDownloadUrl(context, fileId); if (null!=data && StringUtils.isNotBlank(data.getString("fileDownloadUrl"))) { return data; } TimeUnit.SECONDS.sleep(RETRY_INTERVAL_SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } catch (Exception e) { // 记录日志,继续重试 e.printStackTrace(); } } return null; } /** * 等待这个状态变为2或者5才返回 * * @param context * @param fileId * @return */ private JSONObject tryGetFileDownloadUrl(Context context, String fileId) { try { EsignHttpResponse response = EsignHttpUtil.getFileStatus(context, fileId, PREVIEW_OPERATION); if (response.getStatus() < 200 || response.getStatus() >= 300) { return null; } JSONObject responseBody = JSON.parseObject(response.getBody()); if (!SUCCESS_CODE.equals(String.valueOf(responseBody.get("code")))) { return null; } JSONObject data = responseBody.getJSONObject("data"); String fileStatus = data.getString("fileStatus"); Set COMPLETED_STATUS = Sets.newHashSet(); COMPLETED_STATUS.add("2"); COMPLETED_STATUS.add("5"); if (COMPLETED_STATUS.contains(fileStatus)) { return data; } }catch (EsignException e){ e.printStackTrace(); } return null; } /** * 设置待签署文件信息 * * @param context * @param jsonObject * @param templateInfo * @return * @throws EsignException */ public List> addDocs(Context context, JSONObject jsonObject, JSONObject templateInfo) throws EsignException { //设置待签署文件信息 Map resul = Maps.newHashMap(); List> docs = Lists.newArrayList(); for (Map.Entry fileEntry : templateInfo.entrySet()) { JSONObject fieldObject = (JSONObject) fileEntry.getValue(); String docTemplateId = fieldObject.getString("id"); String fileName = fieldObject.getString("name"); Object order = fieldObject.get("order"); String sourceId = jsonObject.getString("sourceId"); EsignHttpResponse response = this.previewFile(context, fieldObject, sourceId); resul.put("id",docTemplateId); resul.put("name",fileName); if (response.getStatus() >= 200 && response.getStatus() < 300) { Map body = this.processFileResponse(response,context); if ("0".equals(String.valueOf(body.get("code")))) { JSONObject file = (JSONObject) body.get("data"); Map doc = Maps.newHashMap(); doc.put("fileId", file.get("fileId")); doc.put("fileName", fileName); if (null != order) { doc.put("order", Integer.parseInt(String.valueOf(order))); } docs.add(doc); } else { //文件模板生成失败 resul.putAll(body); throw new EsignException(JSON.toJSONString(resul)); } } else { throw new EsignException(JSON.toJSONString(response)); } } return docs; } /** * 抄送方信息 * * @param copis * @return */ public List> addCopiers(Context context, JSONArray copis) { List> copiers = Lists.newArrayList(); for (int i = 0; i < copis.size(); i++) { JSONObject cop = copis.getJSONObject(i); Map copierPsn = Maps.newHashMap(); Map copierPsnInfo = Maps.newHashMap(); copierPsnInfo.put("psnAccount", cop.getString("psnAccount")); copierPsn.put("copierPsnInfo", copierPsnInfo); copiers.add(copierPsn); } return copiers; } /** * 添加签署组织 * @param context * @param orgSignerInfo * @param docs * @param sourceId * @return * @throws EsignException * @throws URISyntaxException */ public Map addOrgSignerInfo(Context context, JSONObject orgSignerInfo, List> docs, String sourceId) throws EsignException, URISyntaxException { Map resul = Maps.newHashMap(); Map signMapInfo = Maps.newHashMap(); Map orgPsnSignMap = Maps.newHashMap(); String orgName = orgSignerInfo.getString("orgName"); EsignHttpResponse response = EsignHttpUtil.getOrgIdentity_infoByOrgName(context, orgName); if (response.getStatus() >= 200 && response.getStatus() < 300) { JSONObject body = JSON.parseObject(response.getBody()); if ("0".equals(String.valueOf(body.get("code")))) { JSONObject orgPsn = body.getJSONObject("data"); String orgId = orgPsn.getString("orgId"); orgName = orgPsn.getString("orgName"); orgPsnSignMap.put("orgId", orgId); orgPsnSignMap.put("orgName", orgName); orgPsnSignMap.put("orgInfo", orgPsn.getJSONObject("orgInfo")); } else { //失败 resul.putAll(body); throw new EsignException(JSON.toJSONString(resul)); } } else { throw new EsignException("网络异常:" + JSON.toJSONString(response)); } Integer signOrder = orgSignerInfo.getInteger("signOrder"); //设置签署方的签署顺序 if (null != signOrder) { Map signConfig = Maps.newHashMap(); signConfig.put("signOrder", signOrder); signMapInfo.put("signConfig", signConfig); } signMapInfo.put("orgSignerInfo", orgPsnSignMap); //设置签署方的通知方式 String noticeTypes = orgSignerInfo.getString("noticeTypes"); Map noticeConfig = Maps.newHashMap(); if (StringUtils.isNotBlank(noticeTypes)) { noticeConfig.put("noticeTypes", noticeTypes); } else { noticeConfig.put("noticeTypes", "1,2"); } signMapInfo.put("noticeConfig", noticeConfig); //签署区信息 List> signFields = Lists.newArrayList(); for (Map doc : docs) { Map field = Maps.newHashMap(); field.put("fileId", doc.get("fileId")); field.put("customBizNum", sourceId); field.put("signFieldType", 0); //签章区配置项(指定signFieldType为 0 - 签章区时,该参数为必传项) Map normalSignFieldConfig = Maps.newHashMap(); normalSignFieldConfig.put("freeMode", true); field.put("normalSignFieldConfig", normalSignFieldConfig); signFields.add(field); } signMapInfo.put("signFields", signFields); signMapInfo.put("signerType", 1); return signMapInfo; } /** * 添加签署人 * @param context * @param psnSignerInfo * @param docs * @param sourceId * @return * @throws EsignException * @throws URISyntaxException */ public Map addPsnSignerInfo(Context context, JSONObject psnSignerInfo, List> docs, String sourceId) throws EsignException, URISyntaxException { Map resul = Maps.newHashMap(); Map signMapInfo = Maps.newHashMap(); Map orgPsnSignMap = Maps.newHashMap(); //个人签署方信息 String psnAccount = psnSignerInfo.getString("psnAccount"); String psnName = psnSignerInfo.getString("psnName"); Integer signOrder = psnSignerInfo.getInteger("signOrder"); //设置签署方的签署顺序 if (null != signOrder) { Map signConfig = Maps.newHashMap(); signConfig.put("signOrder", signOrder); signMapInfo.put("signConfig", signConfig); } orgPsnSignMap.put("psnAccount", psnAccount); Map psnInfo = Maps.newHashMap(); psnInfo.put("psnName", psnName); orgPsnSignMap.put("psnInfo", psnInfo); //个人签署方信息 signMapInfo.put("psnSignerInfo", orgPsnSignMap); //设置签署方的通知方式 String noticeTypes = psnSignerInfo.getString("noticeTypes"); Map noticeConfig = Maps.newHashMap(); if (StringUtils.isNotBlank(noticeTypes)) { noticeConfig.put("noticeTypes", noticeTypes); } else { noticeConfig.put("noticeTypes", "1,2"); } signMapInfo.put("noticeConfig", noticeConfig); //签署区信息 List> signFields = Lists.newArrayList(); for (Map doc : docs) { Map field = Maps.newHashMap(); field.put("fileId", doc.get("fileId")); field.put("customBizNum", sourceId); field.put("signFieldType", 0); //签章区配置项(指定signFieldType为 0 - 签章区时,该参数为必传项) Map normalSignFieldConfig = Maps.newHashMap(); normalSignFieldConfig.put("freeMode", true); field.put("normalSignFieldConfig", normalSignFieldConfig); signFields.add(field); } signMapInfo.put("signFields", signFields); signMapInfo.put("signerType", 0); return signMapInfo; } /** * 处理签署方信息 * * @param context * @param signs * @param docs * @param sourceId * @return * @throws EsignException * @throws URISyntaxException */ public List> addSigners(Context context, JSONArray signs, List> docs, String sourceId) throws EsignException, URISyntaxException { List> signers = Lists.newArrayList(); for (int i = 0; i < signs.size(); i++) { Map signMapInfo = Maps.newHashMap(); JSONObject sign = signs.getJSONObject(i); Map orgPsnSignMap = Maps.newHashMap(); JSONObject orgSignerInfo = sign.getJSONObject("orgSignerInfo"); JSONObject psnSignerInfo = sign.getJSONObject("psnSignerInfo"); //企业/机构签署方信息 if (null != orgSignerInfo) { signMapInfo = addOrgSignerInfo(context, orgSignerInfo, docs, sourceId); } else { //个人签署方信息 signMapInfo = addPsnSignerInfo(context, psnSignerInfo, docs, sourceId); } signers.add(signMapInfo); } return signers; } /** * 签署流程配置项 * * @param context * @param signFlowTitle * @param jsonObject * @return */ public Map addSignFlowConfig(Context context, String signFlowTitle, JSONObject jsonObject) { Map signFlowConfig = Maps.newHashMap(); signFlowConfig.put("signFlowTitle", signFlowTitle); Boolean autoStart = jsonObject.getBoolean("autoStart"); if (null != autoStart) { signFlowConfig.put("autoStart", autoStart); } else { signFlowConfig.put("autoStart", true); } Boolean autoFinish = jsonObject.getBoolean("autoFinish"); if (null != autoFinish) { signFlowConfig.put("autoFinish", autoFinish); } else { signFlowConfig.put("autoFinish", true); } String notifyUrl = jsonObject.getString("notifyUrl"); if (null != notifyUrl) { signFlowConfig.put("notifyUrl", notifyUrl); } else { notifyUrl = EsignConfig.getInstance().get("notifyUrl"); signFlowConfig.put("notifyUrl", notifyUrl); } return signFlowConfig; } /** * 根据传入数据调用 ”填写模板生成文件“ 接口 * @param jsonObject * @return */ public EsignHttpResponse previewFile(Context context, JSONObject jsonObject, String sourceId) throws EsignException { String docTemplateId = jsonObject.getString("id"); String fileName = jsonObject.getString("name"); if (StringUtils.isBlank(docTemplateId)) { docTemplateId = jsonObject.getString("tableId"); } if (StringUtils.isBlank(fileName)) { fileName = jsonObject.getString("tableName"); } Map map = Maps.newHashMap(); map.put("docTemplateId", docTemplateId); map.put("fileName", fileName); List> components = Lists.newArrayList(); JSONObject fields = jsonObject.getJSONObject("fields"); for (Map.Entry field : fields.entrySet()) { Map component = Maps.newHashMap(); component.put("componentId", field.getKey()); JSONObject fieldInfo = (JSONObject) field.getValue(); component.put("componentValue", fieldInfo.get("value")); components.add(component); } map.put("components", components); EsignHttpResponse response = EsignHttpUtil.createByDocTemplate(context, JSON.toJSONString(map), sourceId); return response; } }